commit/galaxy-central: 2 new changesets
2 new commits in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/changeset/f6d9557b6d77/ changeset: f6d9557b6d77 user: Tomithy date: 2012-08-26 12:22:23 summary: re-integrated phyloviz to a new fork of galaxy-central with a = tat of minor touch up; save and edit now works affected #: 19 files diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 lib/galaxy/datatypes/data.py --- a/lib/galaxy/datatypes/data.py +++ b/lib/galaxy/datatypes/data.py @@ -719,7 +719,49 @@ pass =20 class Newick( Text ): - pass + """New Hampshire/Newick Format""" + file_ext =3D "nhx" + + MetadataElement( name=3D"columns", default=3D3, desc=3D"Number of colu= mns", readonly=3DTrue ) + + def __init__(self, **kwd): + """Initialize foobar datatype""" + Text.__init__(self, **kwd) + + def init_meta( self, dataset, copy_from=3DNone ): + Text.init_meta( self, dataset, copy_from=3Dcopy_from ) + + + def sniff( self, filename ): + """ Returning false as the newick format is too general and cannot= be sniffed.""" + return False + + +class Nexus( Text ): + """Nexus format as used By Paup, Mr Bayes, etc""" + file_ext =3D "nex" + + MetadataElement( name=3D"columns", default=3D3, desc=3D"Number of colu= mns", readonly=3DTrue ) + + def __init__(self, **kwd): + """Initialize foobar datatype""" + Text.__init__(self, **kwd) + + def init_meta( self, dataset, copy_from=3DNone ): + Text.init_meta( self, dataset, copy_from=3Dcopy_from ) + + + def sniff( self, filename ): + """All Nexus Files Simply puts a '#NEXUS' in its first line""" + f =3D open(filename, "r") + firstline =3D f.readline().upper() + f.close() + + if "#NEXUS" in firstline: + return True + else: + return False + =20 # ------------- Utility methods -------------- =20 diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 lib/galaxy/datatypes/xml.py --- a/lib/galaxy/datatypes/xml.py +++ b/lib/galaxy/datatypes/xml.py @@ -76,3 +76,24 @@ dataset.blurb =3D 'file purged from disk' def sniff( self, filename ): return False + +class Phyloxml( GenericXml ): + """Format for defining phyloxml data http://www.phyloxml.org/""" + file_ext =3D "phyloxml" + def set_peek( self, dataset, is_multi_byte=3DFalse ): + """Set the peek and blurb text""" + if not dataset.dataset.purged: + dataset.peek =3D data.get_file_peek( dataset.file_name, is_mul= ti_byte=3Dis_multi_byte ) + dataset.blurb =3D 'Phyloxml data' + else: + dataset.peek =3D 'file does not exist' + dataset.blurb =3D 'file purged from disk' + + def sniff( self, filename ): + """"Checking for keyword - 'phyloxml' always in lowercase in the f= irst few lines""" + f =3D open(filename, "r") + firstlines =3D "".join(f.readlines(5)) + f.close() + if "phyloxml" in firstlines: + return True + return False \ No newline at end of file diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 lib/galaxy/visualization/phyloviz/__init__.py --- /dev/null +++ b/lib/galaxy/visualization/phyloviz/__init__.py @@ -0,0 +1,1 @@ +__author__ =3D 'Tomithy' diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 lib/galaxy/visualization/phyloviz/baseparser.py --- /dev/null +++ b/lib/galaxy/visualization/phyloviz/baseparser.py @@ -0,0 +1,125 @@ +import json + +class Node(object): + """Node class of PhyloTree, which represents a CLAUDE in a phylogeneti= c tree""" + def __init__(self, nodeName, **kwargs): + """Creates a node and adds in the typical annotations""" + self.name, self.id =3D nodeName, kwargs.get("id", 0) + self.depth =3D kwargs.get("depth", 0) + self.children =3D [] + + self.isInternal =3D kwargs.get("isInternal", 0) + self.length, self.bootstrap =3D kwargs.get("length", 0), kwargs.ge= t("bootstrap", None) + self.events =3D kwargs.get("events", "") + + # clean up boot strap values + if self.bootstrap =3D=3D -1: + self.bootstrap =3D None + + def addChildNode(self, child): + """Adds a child node to the current node""" + if isinstance(child, Node): + self.children.append(child) + else: + self.children +=3D child + + + def __str__(self): + return self.name + " id:" + str(self.id) + ", depth: " + str(self.= depth) + + + def toJson(self): + """Converts the data in the node to a dict representation of json"= "" + thisJson =3D { + "name" : self.name, + "id" : self.id, + "depth" : self.depth, + "dist" : self.length + } + thisJson =3D self.addChildrenToJson(thisJson) + thisJson =3D self.addMiscToJson(thisJson) + return thisJson + + def addChildrenToJson(self, jsonDict): + """Needs a special method to addChildren, such that the key does n= ot appear in the Jsondict when the children is empty + this requirement is due to the layout algorithm used by d3 layout = for hiding subtree """ + if len(self.children) > 0: + children =3D [ node.toJson() for node in self.children] + jsonDict["children"] =3D children + return jsonDict + + + def addMiscToJson(self, jsonDict): + """Adds other misc attributes to json if they are present""" + if not self.events =3D=3D "": + jsonDict["events"] =3D self.events + if not self.bootstrap =3D=3D None: + jsonDict["bootstrap"] =3D self.bootstrap + return jsonDict + + + +class PhyloTree(object): + """Standardized python based class to represent the phylogenetic tree = parsed from different + phylogenetic file formats.""" + + def __init__(self): + self.root, self.rootAttr =3D None, {} + self.nodes =3D {} + self.title =3D None + self.id =3D 1 + + def addAttributesToRoot(self, attrDict): + """Adds attributes to root, but first we put it in a temp store an= d bind it with root when .toJson is called""" + for key, value in attrDict.items(): + self.rootAttr[key] =3D value + + def makeNode(self, nodeName, **kwargs): + """Called to make a node within PhyloTree, arbitrary kwargs can be= passed to annotate nodes + Tracks the number of nodes via internally incremented id""" + kwargs["id"] =3D self.id + self.id +=3D 1 + return Node(nodeName, **kwargs) + + def addRoot(self, root): + """Creates a root for phyloTree""" + assert isinstance(root, Node) + root.parent =3D None + self.root =3D root + + def generateJsonableDict(self): + """Changes itself into a dictonary by recurssively calling the toj= son on all its nodes. Think of it + as a dict in an array of dict in an array of dict and so on...""" + jsonTree =3D "" + if self.root: + assert isinstance(self.root, Node) + jsonTree =3D self.root.toJson() + for key, value in self.rootAttr.items(): + # transfer temporary stored attr to root + jsonTree[key] =3D value + else: + raise Exception("Root is not assigned!") + return jsonTree + + + +class Base_Parser(object): + """Base parsers contain all the methods to handle phylogeny tree creat= ion and + converting the data to json that all parsers should have""" + + def __init__(self): + self.phyloTrees =3D [] + + def parseFile(self, filePath): + """Base method that all phylogeny file parser should have""" + raise Exception("Base method for phylogeny file parsers is not imp= lemented") + + def toJson(self, jsonDict): + """Convenience method to get a json string from a python json dict= """ + return json.dumps(jsonDict) + + def _writeJsonToFile(self, filepath, json): + """Writes the file out to the system""" + f =3D open(filepath, "w") + f.writelines(json) + f.close() diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 lib/galaxy/visualization/phyloviz/newickparser.py --- /dev/null +++ b/lib/galaxy/visualization/phyloviz/newickparser.py @@ -0,0 +1,185 @@ +from baseparser import Base_Parser, PhyloTree +import re + +class Newick_Parser(Base_Parser): + """For parsing trees stored in the newick format (.nhx) + It is necessarily more complex because this parser is later extended b= y Nexus for parsing newick as well..""" + + + def __init__(self): + super(Newick_Parser, self).__init__() + + + def parseFile(self, filePath): + """Parses a newick file to obtain the string inside. Returns: json= ableDict""" + with open(filePath, "r") as newickFile: + newickString =3D newickFile.read() + newickString =3D newickString.replace("\n", "").replace("\r", = "") + return [self.parseData(newickString)], "Success" + + + def parseData(self, newickString): + """To be called on a newickString directly to parse it. Returns: j= sonableDict""" + return self._parseNewickToJson(newickString) + + + def _parseNewickToJson(self, newickString, treeName=3DNone, nameMap=3D= None): + """parses a newick representation of a tree into a PhyloTree data = structure, + which can be easily converted to json""" + self.phyloTree =3D PhyloTree() + newickString =3D self.cleanNewickString(newickString) + if nameMap: + newickString =3D self._mapName(newickString, nameMap) + + self.phyloTree.root =3D self.parseNode(newickString, 0) + if nameMap: + self.phyloTree.addAttributesToRoot({"treeName": treeName}) + + return self.phyloTree.generateJsonableDict() + + + def cleanNewickString(self, rawNewick): + """removing semi colon, and illegal json characters (\,',") and wh= ite spaces""" + return re.sub(r'\s|;|\"|\'|\\', '', rawNewick) + + + def _makeNodesFromString(self, string, depth): + """elements separated by comma could be empty""" + + if string.find("(") !=3D -1: + raise Exception("Tree is not well form, location: " + string) + + childrenString =3D string.split(",") + childrenNodes =3D [] + + for childString in childrenString: + if len(childString) =3D=3D 0: + continue + nodeInfo =3D childString.split(":") + name, length, bootstrap =3D "", None, -1 + if len(nodeInfo) =3D=3D 2: # has length info + length =3D nodeInfo[1] + # checking for bootstap values + name =3D nodeInfo[0] + try: # Nexus may bootstrap in names position + name =3D float(name) + if 0<=3D name <=3D 1: + bootstrap =3D name + elif 1 <=3D name <=3D 100: + bootstrap =3D name / 100 + name =3D "" + except ValueError: + name =3D nodeInfo[0] + else: + name =3D nodeInfo[0] # string only contains name + node =3D self.phyloTree.makeNode(name, length=3Dlength, depth= =3Ddepth, bootstrap=3D bootstrap) + childrenNodes +=3D [node] + return childrenNodes + + + + def _mapName(self, newickString, nameMap): + """ + Necessary to replace names of terms inside nexus representation + Also, its here because Mailaud's doesnt deal with id_strings outsi= de of quotes(" ") + """ + newString =3D "" + start =3D 0 + end =3D 0 + + for i in xrange(len(newickString)): + if newickString[i] =3D=3D "(" or newickString[i] =3D=3D ",": + if re.match(r"[,(]", newickString[i+1:]): + continue + else: + end =3D i + 1 + # i now refers to the starting position of the term to= be replaced, + # we will next find j which is the ending pos of the t= erm + for j in xrange(i+1, len(newickString)): + enclosingSymbol =3D newickString[j] # the immedi= ate symbol after a common or left bracket which denotes the end of a term + if enclosingSymbol =3D=3D ")" or enclosingSymbol = =3D=3D ":" or enclosingSymbol =3D=3D ",": + termToReplace =3D newickString[end:j] + + newString +=3D newickString[start : end] + na= meMap[termToReplace] #+ "'" "'" + + start =3D j + break + + newString +=3D newickString[start:] + return newString + + + def parseNode(self, string, depth): + """ Recursive method for parsing newick string, works by stripping= down the string into substring + of newick contained with brackers, which is used to call itself. + Eg ... ( A, B, (D, E)C, F, G ) ... + We will make the preceeding nodes first A, B, then the internal no= de C, its children D, E, + and finally the succeeding nodes F, G""" + + # Base case where there is only an empty string + if string =3D=3D "": + return + # Base case there its only an internal claude + if string.find("(") =3D=3D -1: + return self._makeNodesFromString(string, depth) + + nodes, children =3D [], [] # nodes refer to the nodes on this= level, children refers to the child of the + start =3D 0 + lenOfPreceedingInternalNodeString =3D 0 + bracketStack =3D [] + + for j in xrange(len(string)): + if string[j] =3D=3D "(": #finding the positions of all the = open brackets + bracketStack.append(j) + continue + if string[j] =3D=3D ")": #finding the positions of all the = closed brackets to extract claude + i =3D bracketStack.pop() + + if len(bracketStack) =3D=3D 0: # is child of current node + + InternalNode =3D None + + #First flat call to make nodes of the same depth but f= rom the preceeding string. + startSubstring =3D string[start + lenOfPreceedingInter= nalNodeString: i] + preceedingNodes =3D self._makeNodesFromString(startSu= bstring, depth) + nodes +=3D preceedingNodes + + # Then We will try to see if the substring has any int= ernal nodes first, make it then make nodes preceeding it and succeeding it. + if j + 1 < len(string): + stringRightOfBracket =3D string[j+1:] # Eg. '= (b:0.4,a:0.3)c:0.3, stringRightOfBracket =3D c:0.3 + match =3D re.search(r"[\)\,\(]", stringRightOfBrac= ket) + if match: + indexOfNextSymbol =3D match.start() + stringRepOfInternalNode =3D stringRightOfBrack= et[:indexOfNextSymbol] + internalNodes =3D self._makeNodesFromString( s= tringRepOfInternalNode, depth) + if len(internalNodes) > 0: + InternalNode =3D internalNodes[0] + lenOfPreceedingInternalNodeString =3D len(stri= ngRepOfInternalNode) + else: # sometimes the node can be the last eleme= nt of a string + InternalNode =3D self._makeNodesFromString(str= ing[j+1:], depth)[0] + lenOfPreceedingInternalNodeString =3D len(stri= ng) - j + if InternalNode =3D=3D None: #creating a generic= node if it is unnamed + InternalNode =3D self.phyloTree.makeNode( "", dept= h=3Ddepth, isInternal=3DTrue ) #"internal-" + str(depth) + lenOfPreceedingInternalNodeString =3D 0 + + # recussive call to make the internal claude + childSubString =3D string[ i + 1 : j ] + InternalNode.addChildNode(self.parseNode(childSubStrin= g, depth + 1)) + + nodes.append(InternalNode) # we append the internal n= ode later to preserve order + + start =3D j + 1 + continue + + if depth =3D=3D 0: # if its the root node, we do nothing about = it and return + return nodes[0] + + # Adding last most set of children + endString =3D string[start:] + if string[start-1] =3D=3D ")": # if the symbol belongs to an inte= rnal node which is created previously, then we remove it from the string le= ft to parse + match =3D re.search(r"[\)\,\(]", endString) + if match: + endOfNodeName =3D start + match.start() + 1 + endString =3D string[endOfNodeName:] + nodes +=3D self._makeNodesFromString(endString, depth) + + return nodes diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 lib/galaxy/visualization/phyloviz/nexusparser.py --- /dev/null +++ b/lib/galaxy/visualization/phyloviz/nexusparser.py @@ -0,0 +1,107 @@ +from newickparser import Newick_Parser +import re + +MAX_READLINES =3D 200000 + + +class Nexus_Parser(Newick_Parser): + + def __init__(self): + super(Nexus_Parser, self).__init__() + + def parseFile(self, filePath): + """passes a file and extracts its Nexus content.""" + return self.parseNexus(filePath) + + + def parseNexus(self, filename): + """ Nexus data is stored in blocks between a line starting with be= gin and another line starting with end; + Commends inside square brackets are to be ignored, + For more information: http://wiki.christophchamp.com/index.php/NEX= US_file_format + Nexus can store multiple trees + """ + + with open( filename, "rt") as nex_file: + nexlines =3D nex_file.readlines() + + rowCount =3D 0 + inTreeBlock =3D False # sentinel to check if we are in a t= ree block + intranslateBlock =3D False # sentinel to check if we are in the= translate region of the tree. Stores synonyms of the labellings + self.inCommentBlock =3D False + self.nameMapping =3D None # stores mapping representation us= ed in nexus format + treeNames =3D [] + + for line in nexlines: + line =3D line.replace(";\n", "") + lline =3D line.lower() + + if rowCount > MAX_READLINES or (not nex_file) : + break + rowCount +=3D1 + # We are only interested in the tree block. + if "begin" in lline and "tree" in lline and not inTreeBlock: + inTreeBlock =3D True + continue + if inTreeBlock and "end" in lline[:3]: + inTreeBlock, currPhyloTree =3D False, None + continue + + if inTreeBlock: + + if "title" in lline: # Adding title to the tree + titleLoc =3D lline.find("title") + title =3D line[titleLoc + 5:].replace(" ", "") + + continue + + if "translate" in lline: + intranslateBlock =3D True + self.nameMapping =3D {} + continue + + if intranslateBlock: + mappingLine =3D self.splitLinebyWhitespaces(line) + key, value =3D mappingLine[1], mappingLine[2].replace(= ",", "").replace("'","") #replacing illegal json characters + self.nameMapping[key] =3D value + + # Extracting newick Trees + if "tree" in lline: + intranslateBlock =3D False + + treeLineCols =3D self.splitLinebyWhitespaces(line) + treeName, newick =3D treeLineCols[2], treeLineCols[-1] + + if newick =3D=3D "": # Empty lines can be found in = tree blocks + continue + + currPhyloTree =3D self._parseNewickToJson(newick, tree= Name, nameMap=3Dself.nameMapping) + + self.phyloTrees.append(currPhyloTree) + treeIndex =3D len(self.phyloTrees) - 1 + treeNames.append( (treeName, treeIndex) ) # appendi= ng name of tree, and its index + continue + + return self.phyloTrees, treeNames + + + def splitLinebyWhitespaces(self, line): + """replace tabs and write spaces to a single write space, so we ca= n properly split it.""" + return re.split(r"\s+", line) + + + def checkComments(self, line): + """Check to see if the line/lines is a comment.""" + if not self.inCommentBlock: + if "[" in line: + if "]" not in line: + self.inCommentBlock =3D True + else: + return "Nextline" # need to move on to the nextline = after getting out of comment + else : + if "]" in line: + if line.rfind("[") > line.rfind("]"): + pass # a comment block is closed but an= other is open. + else: + self.inCommentBlock =3D False + return "Nextline" # need to move on to the nextline = after getting out of comment + return "" \ No newline at end of file diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 lib/galaxy/visualization/phyloviz/phyloviz_dataprovider.py --- /dev/null +++ b/lib/galaxy/visualization/phyloviz/phyloviz_dataprovider.py @@ -0,0 +1,35 @@ +from newickparser import Newick_Parser +from nexusparser import Nexus_Parser +from phyloxmlparser import Phyloxml_Parser + +class Phyloviz_DataProvider(object): + + def __init__(self): + pass + + def parseFile(self, filepath, fileExt): + """returns [trees], meta + Trees are actually an array of JsonDicts. It's usually one tre= e, except in the case of Nexus + """ + jsonDicts, meta =3D [], {} + try: + if fileExt =3D=3D "nhx": # parses newick files + newickParser =3D Newick_Parser() + jsonDicts, parseMsg =3D newickParser.parseFile(filepath) + elif fileExt =3D=3D "phyloxml": # parses phyloXML files + phyloxmlParser =3D Phyloxml_Parser() + jsonDicts, parseMsg =3D phyloxmlParser.parseFile(filepath) + elif fileExt =3D=3D "nex": # parses nexus files + nexusParser =3D Nexus_Parser() + jsonDicts, parseMsg =3D nexusParser.parseFile(filepath) + meta["trees"] =3D parseMsg + else: + raise Exception("File type is not supported") + + meta["msg"] =3D parseMsg + + except Exception: + jsonDicts, meta["msg"] =3D [], "Parse failed" + + return jsonDicts, meta + diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 lib/galaxy/visualization/phyloviz/phyloxmlparser.py --- /dev/null +++ b/lib/galaxy/visualization/phyloviz/phyloxmlparser.py @@ -0,0 +1,145 @@ +from baseparser import Base_Parser, PhyloTree, Node +from lxml import etree + +class Phyloxml_Parser(Base_Parser): + """Parses a phyloxml file into a json file that will be passed to Phyl= oViz for display""" + + def __init__(self): + super(Phyloxml_Parser, self).__init__() + self.phyloTree =3D PhyloTree() + self.tagsOfInterest =3D { + "clade": "", + "name" : "name", + "branch_length" : "length", + "confidence" : "bootstrap", + "events" : "events" + } + + def parseFile(self, filePath): + """passes a file and extracts its Phylogeny Tree content.""" + phyloXmlFile =3D open(filePath, "r") + + xmlTree =3D etree.parse(phyloXmlFile) + xmlRoot =3D xmlTree.getroot()[0] + self.nameSpaceIndex =3D xmlRoot.tag.rfind("}") + 1 # used later by= the clean tag method to remove the name space in every element.tag + + phyloRoot =3D None + for child in xmlRoot: + childTag =3D self.cleanTag(child.tag) + if childTag =3D=3D "clade": + phyloRoot =3D child + elif childTag =3D=3D "name": + self.phyloTree.title =3D child.text + + self.phyloTree.root =3D self.parseNode(phyloRoot, 0) + jsonDict =3D self.phyloTree.generateJsonableDict() + return [jsonDict], "Success" + + + def parseNode(self, node, depth): + """Parses any node within a phyloxml tree and looks out for claude= , which signals the creation of + nodes - internal OR leaf""" + assert isinstance(node, etree._Element) + + tag =3D self.cleanTag(node.tag) + if not tag =3D=3D "clade": + return None + hasInnerClade =3D False + + # peeking once for parent and once for child to check if the node = is internal + for child in node: + childTag =3D self.cleanTag(child.tag) + if childTag =3D=3D "clade": + hasInnerClade =3D True + break + + if hasInnerClade: # this node is an internal node + currentNode =3D self._makeInternalNode(node, depth=3D depth) + for child in node: + child =3D self.parseNode(child, depth + 1) + if isinstance(child, Node): + currentNode.addChildNode(child) + + else: # this node is a leaf node + currentNode =3D self._makeLeafNode(node, depth=3Ddepth+1) + + return currentNode + + + def _makeLeafNode(self, leafNode, depth =3D 0 ): + """Makes leaf nodes by calling Phylotree methods""" + node =3D {} + for child in leafNode: + childTag =3D self.cleanTag(child.tag) + if childTag in self.tagsOfInterest: + key =3D self.tagsOfInterest[childTag] # need to map phy= loxml terms to ours + node[key] =3D child.text + + node["depth"] =3D depth + return self.phyloTree.makeNode(self._getNodeName(leafNode), **node) + + def _getNodeName(self, node, depth=3D-1): + """Gets the name of a claude. It handles the case where a taxonomy= node is involved""" + + def getTagFromTaxonomyNode(node): + """Returns the name of a taxonomy node. A taxonomy node have t= o be treated differently as the name + is embedded one level deeper""" + phyloxmlTaxoNames =3D { + "common_name" : "", + "scientific_name" : "", + "code" : "" + } + for child in node: + childTag =3D self.cleanTag(child.tag) + if childTag in phyloxmlTaxoNames: + return child.text + return "" + + nodeName =3D "" + for child in node: + childTag =3D self.cleanTag(child.tag) + if childTag =3D=3D "name" : + nodeName =3D child.text + break + elif childTag =3D=3D "taxonomy": + nodeName =3D getTagFromTaxonomyNode(child) + break + + return nodeName + + + def _makeInternalNode(self, internalNode, depth=3D0): + """ Makes an internal node from an element object that is gurantee= d to be a parent node. + Gets the value of interests like events and appends it to a custom= node object that will be passed to PhyloTree to make nodes + """ + node =3D {} + for child in internalNode: + childTag =3D self.cleanTag(child.tag) + if childTag =3D=3D "clade": + continue + elif childTag in self.tagsOfInterest: + if childTag =3D=3D "events": # events is nested 1 more = level deeper than others + key, text =3D "events", self.cleanTag(child[0].tag) + else: + key =3D self.tagsOfInterest[childTag] + text =3D child.text + node[key] =3D text + + + return self.phyloTree.makeNode(self._getNodeName(internalNode, dep= th), **node) + + + def cleanTag(self, tagString): + return tagString[self.nameSpaceIndex:] + + +if __name__=3D=3D"__main__": + + # Files tested against + parser =3D Phyloxml_Parser() + filepath =3D "../data/" +"apaf.xml" + # filepath =3D "../data/" +"12_multiple_supports.xml" + + # filepath =3D "../data/" +"bcl_2.xml" + # filepath =3D "../data/" +"reducedXml.xml" + parser.parseFile(filepath) diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 lib/galaxy/web/controllers/phyloviz.py --- /dev/null +++ b/lib/galaxy/web/controllers/phyloviz.py @@ -0,0 +1,97 @@ +import pkg_resources +pkg_resources.require( "bx-python" ) + +from galaxy.util.json import to_json_string, from_json_string +from galaxy.web.base.controller import * +from galaxy.visualization.phyloviz.phyloviz_dataprovider import Phyloviz_D= ataProvider + + +class PhyloVizController( BaseUIController, UsesVisualizationMixin, UsesHi= storyDatasetAssociationMixin, SharableMixin ): + """ + Controller for phyloViz browser interface. + """ + def __init__(self, app ): + BaseUIController.__init__( self, app ) + + @web.expose + @web.require_login() + def index( self, trans, dataset_id =3D None, **kwargs ): + """ + The index method is called using phyloviz/ with a dataset id passe= d in. + The relevant data set is then retrieved via get_json_from_datasetI= d which interfaces with the parser + The json representation of the phylogenetic tree along with the co= nfig is then written in the .mako template and passed back to the user + """ + json, config =3D self.get_json_from_datasetId(trans, dataset_id) + config["saved_visualization"] =3D False + return trans.fill_template( "visualization/phyloviz.mako", data = =3D json, config=3Dconfig) + + + @web.expose + def visualization(self, trans, id): + """ + Called using a viz_id (id) to retrieved stored visualization data = (in json format) and all the viz_config + """ + viz =3D self.get_visualization(trans, id) + config =3D self.get_visualization_config(trans, viz) + config["saved_visualization"] =3D True + data =3D config["root"] + + return trans.fill_template( "visualization/phyloviz.mako", data = =3D data, config=3Dconfig) + + + @web.expose + @web.json + def load_visualization_json(self, trans, viz_id): + """ + Though not used in current implementation, this provides user with= a convenient method to retrieve the viz_data & viz_config via json. + """ + viz =3D self.get_visualization(trans, viz_id) + viz_config =3D self.get_visualization_config(trans, viz) + viz_config["saved_visualization"] =3D True + return { + "data" : viz_config["root"], + "config" : viz_config + } + + + @web.expose + @web.json + def getJsonData(self, trans, dataset_id, treeIndex=3D0): + """ + Method to retrieve data asynchronously via json format. Retriving = from here rather than + making a direct datasets/ call allows for some processing and even= t capturing + """ + treeIndex =3D int(treeIndex) + json, config =3D self.get_json_from_datasetId(trans, dataset_id, t= reeIndex) + packedJson =3D { + "data" : json, + "config" : config + } + + return packedJson + + + def get_json_from_datasetId(self, trans, dataset_id, treeIndex=3D0): + """ + For interfacing phyloviz controllers with phyloviz visualization d= ata provider (parsers) + """ + dataset =3D self.get_dataset(trans, dataset_id) + fileExt, filepath =3D dataset.ext, dataset.file_name # .name= stores the name of the dataset from the orginal upload + json, config =3D "", {} # config contains propertie= s of the tree and file + + if fileExt =3D=3D "json": + something, json =3D self.get_data(dataset) + else: + try: + pd =3D Phyloviz_DataProvider() + json, config =3D pd.parseFile(filepath, fileExt) + json =3D json[treeIndex] + except Exception: + pass + + config["title"] =3D dataset.display_name() + config["ext"] =3D fileExt + config["dataset_id"] =3D dataset_id + config["treeIndex"] =3D treeIndex + + return json, config diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 lib/galaxy/web/controllers/visualization.py --- a/lib/galaxy/web/controllers/visualization.py +++ b/lib/galaxy/web/controllers/visualization.py @@ -16,6 +16,10 @@ action =3D "paramamonster" elif item.type =3D=3D "circster": action =3D "circster" + elif item.type =3D=3D "phyloviz": + # Support phyloviz + controller =3D "phyloviz" + action =3D "visualization" return dict( controller=3Dcontroller, action=3Daction, id=3Ditem.i= d ) =20 # Grid definition diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 static/scripts/viz/phyloviz.js --- /dev/null +++ b/static/scripts/viz/phyloviz.js @@ -0,0 +1,955 @@ +var UserMenuBase =3D Backbone.View.extend({ + /** + * Base class of any menus that takes in user interaction. Contains ch= ecking methods. + */ + + className: 'UserMenuBase', + + isAcceptableValue : function ($inputKey, min, max) { + /** + * Check if an input value is a number and falls within max min. + */ + var self =3D this, + value =3D $inputKey.val(), + fieldName =3D $inputKey.attr("displayLabel") || $inputKey.attr= ("id").replace("phyloViz", ""); + + function isNumeric(n) { + return !isNaN(parseFloat(n)) && isFinite(n); + } + + if (!isNumeric(value)){ + alert(fieldName + " is not a number!"); + return false; + } + + if ( value > max){ + alert(fieldName + " is too large."); + return false; + } else if ( value < min) { + alert(fieldName + " is too small."); + return false; + } + return true; + }, + + hasIllegalJsonCharacters : function($inputKey) { + /** + * Check if any user string inputs has illegal characters that jso= n cannot accept + */ + if ($inputKey.val().search(/"|'|\\/) !=3D=3D -1){ + alert("Named fields cannot contain these illegal characters: d= ouble quote(\"), single guote(\'), or back slash(\\). "); + return true; + } + return false; + } +}); + + +function PhyloTreeLayout() { + /** + * -- Custom Layout call for phyloViz to suit the needs of a phylogene= tic tree. + * -- Specifically: 1) Nodes have a display display of (=3D evo dist X= depth separation) from their parent + * 2) Nodes must appear in other after they have expa= nd and contracted + */ + + var self =3D this, + hierarchy =3D d3.layout.hierarchy().sort(null).value(null), + height =3D 360, // ! represents both the layout angle and the heig= ht of the layout, in px + layoutMode =3D "Linear", + leafHeight =3D 18, // height of each individual leaf node + depthSeparation =3D 200, // separation between nodes of different = depth, in px + leafIndex =3D 0, // change to recurssive call + defaultDist =3D 0.5, // tree defaults to 0.5 dist if no dist is sp= ecified + maxTextWidth =3D 50; // maximum length of the text labels + + + self.leafHeight =3D function(inputLeafHeight){ + if (typeof inputLeafHeight =3D=3D=3D "undefined"){ return leafHeig= ht; } + else { leafHeight =3D inputLeafHeight; return self;} + }; + + self.layoutMode =3D function(mode){ + if (typeof mode =3D=3D=3D "undefined"){ return layoutMode; } + else { layoutMode =3D mode; return self;} + }; + + self.layoutAngle =3D function(angle) { // changes the layout angle = of the display, which is really changing the height + if (typeof angle =3D=3D=3D "undefined"){ return height; } + if (isNaN(angle) || angle < 0 || angle > 360) { return self; } // = to use default if the user puts in strange values + else { height =3D angle; return self;} + }; + + self.separation =3D function(dist){ // changes the dist between the = nodes of different depth + if (typeof dist =3D=3D=3D "undefined"){ return depthSeparation; } + else { depthSeparation =3D dist; return self;} + }; + + self.links =3D function (nodes) { // uses d3 native method to gene= rate links. Done. + return d3.layout.tree().links(nodes); + }; + + // -- Custom method for laying out phylogeny tree in a linear fashion + self.nodes =3D function (d, i) { + var _nodes =3D hierarchy.call(self, d, i), // self is to f= ind the depth of all the nodes, assumes root is passed in + nodes =3D [], + maxDepth =3D 0, + numLeaves =3D 0; + + // changing from hierarchy's custom format for data to usable form= at + _nodes.forEach(function (_node){ + var node =3D _node.data; + node.depth =3D _node.depth; + maxDepth =3D node.depth > maxDepth ? node.depth : maxDepth; /= /finding max depth of tree + nodes.push(node); + }); + // counting the number of leaf nodes and assigning max depth to no= des that do not have children to flush all the leave nodes + nodes.forEach(function(node){ + if ( !node.children ) { //&& !node._children + numLeaves +=3D 1; + node.depth =3D maxDepth; // if a leaf has no child it woul= d be assigned max depth + } + }); + + leafHeight =3D layoutMode =3D=3D=3D "Circular" ? height / numLeave= s : leafHeight; + leafIndex =3D 0; + layout(nodes[0], maxDepth, leafHeight, null); + + return nodes; + }; + + + function layout (node, maxDepth, vertSeparation, parent) { + /** + * -- Function with side effect of adding x0, y0 to all child; tak= e in the root as starting point + * assuming that the leave nodes would be sorted in presented ord= er + * horizontal(y0) is calculated according to (=3D evo dis= t X depth separation) from their parent + * vertical (x0) - if leave node: find its order in all o= f the leave node =3D=3D=3D node.id, then multiply by verticalSeparation + * - if parent node: is place in the mid point al= l of its children nodes + * -- The layout will first calculate the y0 field going towards t= he leaves, and x0 when returning + */ + var children =3D node.children, + sumChildVertSeparation =3D 0; + + // calculation of node's dist from parents, going down. + var dist =3D node.dist || defaultDist; + dist =3D dist > 1 ? 1 : dist; // We constrain all dist to be l= ess than one + node.dist =3D dist; + if (parent !=3D=3D null){ + node.y0 =3D parent.y0 + dist * depthSeparation; + } else { //root node + node.y0 =3D maxTextWidth; + } + + + // if a node have no children, we will treat it as a leaf and star= t laying it out first + if (!children) { + node.x0 =3D leafIndex++ * vertSeparation; + } else { + // if it has children, we will visit all its children and calc= ulate its position from its children + children.forEach( function (child) { + child.parent =3D node; + sumChildVertSeparation +=3D layout(child, maxDepth, vertSe= paration, node); + }); + node.x0 =3D sumChildVertSeparation / children.length; + } + + // adding properties to the newly created node + node.x =3D node.x0; + node.y =3D node.y0; + return node.x0; + } + return self; +} + + +/** + * -- PhyloTree Model -- + */ +var PhyloTree =3D Visualization.extend({ + defaults : { + layout: "Linear", + separation : 250, // px dist between nodes of different depth t= o represent 1 evolutionary until + leafHeight: 18, + type : "phyloviz", // visualization type + title : "Title", + scaleFactor: 1, + translate: [0,0], + fontSize: 12, //fontSize of node label + selectedNode : null, + nodeAttrChangedTime : 0 + }, + + root : {}, // Root has to be its own independent object because it is = not part of the viz_config + + toggle : function (d) { + /** + * Mechanism to expand or contract a single node. Expanded nodes h= ave a children list, while for + * contracted nodes the list is stored in _children. Nodes with th= eir children data stored in _children will not have their + * children rendered. + */ + if(typeof d =3D=3D=3D "undefined") {return ;} + if (d.children ) { + d._children =3D d.children; + d.children =3D null; + } else { + d.children =3D d._children; + d._children =3D null; + } + }, + + toggleAll : function(d) { + /** + * Contracts the phylotree to a single node by repeatedly calling= itself to place all the list + * of children under _children. + */ + if (d.children && d.children.length !=3D=3D 0) { + d.children.forEach(this.toggleAll); + toggle(d); + } + }, + + getData : function (){ + /** + * Return the data of the tree. Used for preserving state. + */ + return this.root; + }, + + save: function() { + /** + * Overriding the default save mechanism to do some clean of circu= lar reference of the + * phyloTree and to include phyloTree in the saved json + */ + var root =3D this.root; + cleanTree(root); + this.set("root", root); + + function cleanTree(node){ + // we need to remove parent to delete circular reference + delete node.parent; + + // removing unnecessary attributes + if (node._selected){ delete node._selected;} + + node.children ? node.children.forEach(cleanTree) : 0; + node._children ? node._children.forEach(cleanTree) : 0; + } + + var config =3D jQuery.extend(true, {}, this.attributes); + config["selectedNode"] =3D null; + + show_message("Saving to Galaxy", "progress"); + + return $.ajax({ + url: this.url(), + type: "POST", + dataType: "json", + data: { + vis_json: JSON.stringify(config) + }, + success: function(res){ + var viz_id =3D res.url.split("id=3D")[1].split("&")[0], + viz_url =3D "/phyloviz/visualization?id=3D" + viz_id; + window.history.pushState({}, "", viz_url + window.location= .hash); + hide_modal(); + } + }); + } +}); + + + +/** + * -- Views -- + */ +var PhylovizLayoutBase =3D Backbone.View.extend({ + /** + * Stores the default variable for setting up the visualization + */ + defaults : { + nodeRadius : 4.5 // radius of each node in the diagram + }, + + + stdInit : function (options) { + /** + * Common initialization in layouts + */ + + var self =3D this; + self.model.on("change:separation change:leafHeight change:fontSize= change:nodeAttrChangedTime", self.updateAndRender, self); + + self.vis =3D options.vis; + self.i =3D 0; + self.maxDepth =3D -1; // stores the max depth of the tree + + self.width =3D options.width; + self.height =3D options.height; + }, + + + updateAndRender : function(source) { + /** + * Updates the visualization whenever there are changes in the ex= pansion and contraction of nodes + * AND possibly when the tree is edited. + */ + var vis =3D d3.select(".vis"), + self =3D this; + source =3D source || self.model.root; + + self.renderNodes(source); + self.renderLinks(source); + self.addTooltips(); + }, + + + renderLinks : function(source) { + /** + * Renders the links for the visualization. + */ + var self =3D this; + var diagonal =3D self.diagonal; + var duration =3D self.duration; + var layoutMode =3D self.layoutMode; + var link =3D self.vis.selectAll("g.completeLink") + .data(self.tree.links(self.nodes), function(d) { return d.targ= et.id; }); + + var calcalateLinePos =3D function(d) { + d.pos0 =3D d.source.y0 + " " + d.source.x0; // position of t= he source node <=3D> starting location of the line drawn + d.pos1 =3D d.source.y0 + " " + d.target.x0; // position where= the line makes a right angle bend + d.pos2 =3D d.target.y0 + " " + d.target.x0; // point where= the horizontal line becomes a dotted line + }; + + var linkEnter =3D link.enter().insert("svg:g","g.node") + .attr("class", "completeLink"); + + + linkEnter.append("svg:path") + .attr("class", "link") + .attr("d", function(d) { + calcalateLinePos(d); + return "M " + d.pos0 + " L " + d.pos1; + }); + + var linkUpdate =3D link.transition().duration(500); + + linkUpdate.select("path.link") + .attr("d", function(d) { + calcalateLinePos(d); + return "M " + d.pos0 + " L " + d.pos1 + " L " + d.pos2; + }); + + var linkExit =3D link.exit().remove(); + + }, + + // User Interaction methods below + + selectNode : function(node){ + /** + * Displays the information for editting + */ + var self =3D this; + d3.selectAll("g.node") + .classed("selectedHighlight", function(d){ + if (node.id =3D=3D=3D d.id){ + if(node._selected) { // for de=3Dselecting node. + delete node._selected; + return false; + } else { + node._selected =3D true; + return true; + } + } + return false; + }); + + self.model.set("selectedNode", node); + $("#phyloVizSelectedNodeName").val(node.name); + $("#phyloVizSelectedNodeDist").val(node.dist); + $("#phyloVizSelectedNodeAnnotation").val(node.annotation || ""); + }, + + addTooltips : function (){ + /** + * Creates bootstrap tooltip for the visualization. Has to be cal= led repeatedly due to newly generated + * enterNodes + */ + $(".bs-tooltip").remove(); //clean up tooltip, just in case i= ts listeners are removed by d3 + $(".node") + .attr("data-original-title", function(){ + var d =3D this.__data__, + annotation =3D d.annotation || "None" ; + return d ? (d.name ? d.name + "<br/>" : "") + "Dist: " + d= .dist + " <br/>Annotation: " + annotation: ""; + }) + .tooltip({'placement':'top', 'trigger' : 'hover'}); + + } +}); + + + + +var PhylovizLinearView =3D PhylovizLayoutBase.extend({ + /** + * Linea layout class of Phyloviz, is responsible for rendering the no= des + * calls PhyloTreeLayout to determine the positions of the nodes + */ + initialize : function(options){ + // Default values of linear layout + var self =3D this; + self.margins =3D options.margins; + self.layoutMode =3D "Linear"; + + self.stdInit(options); + + self.layout(); + self.updateAndRender(self.model.root); + }, + + layout : function() { + /** + * Creates the basic layout of a linear tree by precalculating fix= ed values. + * One of calculations are also made here + */ + + var self =3D this; + + self.tree =3D new PhyloTreeLayout().layoutMode("Linear"); + self.diagonal =3D d3.svg.diagonal() + .projection(function(d) { return [d.y, d.x ]; }); + }, + + renderNodes : function (source) { + /** + * Renders the nodes base on Linear layout. + */ + var self =3D this, + fontSize =3D self.model.get("fontSize") + "px"; + + // assigning properties from models + self.tree.separation(self.model.get("separation")).leafHeight(self= .model.get("leafHeight")); + + var duration =3D 500, + nodes =3D self.tree.separation(self.model.get("separation")).n= odes(self.model.root); + + var node =3D self.vis.selectAll("g.node") + .data(nodes, function(d) { return d.name + d.id || (d.id =3D += +self.i); }); + + // These variables has to be passed into update links which are in= the base methods + self.nodes =3D nodes; + self.duration =3D duration; + + // ------- D3 ENTRY -------- + // Enter any new nodes at the parent's previous position. + var nodeEnter =3D node.enter().append("svg:g") + .attr("class", "node") + .on("dblclick", function(){ d3.event.stopPropagation(); }) + .on("click", function(d) { + if (d3.event.altKey) { + self.selectNode(d); // display info if alt is p= ressed + } else { + if(d.children && d.children.length =3D=3D=3D 0){ retur= n;} // there is no need to toggle leaves + self.model.toggle(d); // contract/expand nodes at da= ta level + self.updateAndRender(d); // re-render the tree + } + }); + + nodeEnter.attr("transform", function(d) { return "translate(" + so= urce.y0 + "," + source.x0 + ")"; }); + + nodeEnter.append("svg:circle") + .attr("r", 1e-6) + .style("fill", function(d) { return d._children ? "lightsteelb= lue" : "#fff"; }); + + nodeEnter.append("svg:text") + .attr("class", "nodeLabel") + .attr("x", function(d) { return d.children || d._children ? -1= 0 : 10; }) + .attr("dy", ".35em") + .attr("text-anchor", function(d) { return d.children || d._chi= ldren ? "end" : "start"; }) + .style("fill-opacity", 1e-6); + + // ------- D3 TRANSITION -------- + // Transition nodes to their new position. + var nodeUpdate =3D node.transition() + .duration(duration); + + nodeUpdate.attr("transform", function(d) { + return "translate(" + d.y + "," + d.x + ")"; }); + + nodeUpdate.select("circle") + .attr("r", self.defaults.nodeRadius) + .style("fill", function(d) { return d._children ? "lightsteelb= lue" : "#fff"; }); + + nodeUpdate.select("text") + .style("fill-opacity", 1) + .style("font-size", fontSize) + .text(function(d) { return d.name; }); + + // ------- D3 EXIT -------- + // Transition exiting nodes to the parent's new position. + var nodeExit =3Dnode.exit().transition() + .duration(duration) + .remove(); + + nodeExit.select("circle") + .attr("r", 1e-6); + + nodeExit.select("text") + .style("fill-opacity", 1e-6); + + // Stash the old positions for transition. + nodes.forEach(function(d) { + d.x0 =3D d.x; // we need the x0, y0 for parents with children + d.y0 =3D d.y; + }); + } + +}); + +var PhylovizView =3D Backbone.View.extend({ + + className: 'phyloviz', + + initialize: function(options) { + var self =3D this; + // -- Default values of the vis + self.MIN_SCALE =3D 0.05; //for zooming + self.MAX_SCALE =3D 5; + self.MAX_DISPLACEMENT =3D 500; + self.margins =3D [10, 60, 10, 80]; + + self.width =3D $("#PhyloViz").width(); + self.height =3D $("#PhyloViz").height(); + self.radius =3D self.width; + self.data =3D options.data; + + // -- Events Phyloviz view responses to + $(window).resize(function(){ + self.width =3D $("#PhyloViz").width(); + self.height =3D $("#PhyloViz").height(); + self.render(); + }); + + // -- Create phyloTree model + self.phyloTree =3D new PhyloTree(options.config); + self.phyloTree.root =3D self.data; + + // -- Set up UI functions of main view + self.zoomFunc =3D d3.behavior.zoom().scaleExtent([self.MIN_SCALE, = self.MAX_SCALE]); + self.zoomFunc.translate(self.phyloTree.get("translate")); + self.zoomFunc.scale(self.phyloTree.get("scaleFactor")); + + // -- set up header buttons, search and settings menu + self.navMenu =3D new HeaderButtons(self); + self.settingsMenu =3D new SettingsMenu({phyloTree : self.phyloTree= }); + self.nodeSelectionView =3D new NodeSelectionView({phyloTree : self= .phyloTree}); + self.search =3D new PhyloVizSearch(); + + + setTimeout(function(){ // using settimeout to call the zoomAn= dPan function according to the stored attributes in viz_config + self.zoomAndPan(); + }, 1000); + }, + + render: function(){ + // -- Creating helper function for vis. -- + var self =3D this; + $("#PhyloViz").empty(); + + // -- Layout viz. -- + self.mainSVG =3D d3.select("#PhyloViz").append("svg:svg") + .attr("width", self.width) + .attr("height", self.height) + .attr("pointer-events", "all") + .call(self.zoomFunc.on("zoom", function(){ + self.zoomAndPan(); + })); + + self.boundingRect =3D self.mainSVG.append("svg:rect") + .attr("class", "boundingRect") + .attr("width", self.width) + .attr("height", self.height) + .attr("stroke", "black") + .attr("fill", "white"); + + self.vis =3D self.mainSVG + .append("svg:g") + .attr("class", "vis"); + + self.layoutOptions =3D { + model : self.phyloTree, + width : self.width, + height : self.height, + vis: self.vis, + margins: self.margins + }; + + // -- Creating Title + $("#title").text("Phylogenetic Tree from " + self.phyloTree.get("t= itle") + ":"); + + // -- Create Linear view instance -- + var linearView =3D new PhylovizLinearView(self.layoutOptions) + }, + + zoomAndPan : function(event){ + /** + * Function to zoom and pan the svg element which the entire tree = is contained within + * Uses d3.zoom events, and extend them to allow manual updates an= d keeping states in model + */ + if (typeof event !=3D=3D "undefined") { + var zoomParams =3D event.zoom, + translateParams =3D event.translate; + } + + var self =3D this, + scaleFactor =3D self.zoomFunc.scale(), + translationCoor =3D self.zoomFunc.translate(), + zoomStatement =3D "", + translateStatement =3D ""; + + // Do manual scaling. + switch (zoomParams) { + case "reset": + scaleFactor =3D 1.0; + translationCoor =3D [0,0]; break; + case "+": + scaleFactor *=3D 1.1; break; + case "-": + scaleFactor *=3D 0.9; break; + default: + if (typeof zoomParams =3D=3D=3D "number") { + scaleFactor =3D zoomParams; + } else if (d3.event !=3D=3D null) { + scaleFactor =3D d3.event.scale; + } + } + if (scaleFactor < self.MIN_SCALE || scaleFactor > self.MAX_SCALE) = { return;} + self.zoomFunc.scale(scaleFactor); //update scale Factor + zoomStatement =3D "translate(" + self.margins[3] + "," + self.mar= gins[0] + ")" + + " scale(" + scaleFactor + ")"; + + // Do manual translation. + if( d3.event !=3D=3D null) { + translateStatement =3D "translate(" + d3.event.translate + ")"; + } else { + if(typeof translateParams !=3D=3D "undefined") { + var x =3D translateParams.split(",")[0]; + var y =3D translateParams.split(",")[1]; + if (!isNaN(x) && !isNaN(y)){ + translationCoor =3D [translationCoor[0] + parseFloat(x= ), translationCoor[1] + parseFloat(y)]; + } + } + self.zoomFunc.translate(translationCoor); // update zoomFunc + translateStatement =3D "translate(" + translationCoor + ")"; + } + + self.phyloTree.set("scaleFactor", scaleFactor); + self.phyloTree.set("translate", translationCoor); + self.vis.attr("transform", translateStatement + zoomStatement); //= refers to the view that we are actually zooming + }, + + + reloadViz : function() { + /** + * Primes the Ajax URL to load another Nexus tree + */ + var self =3D this, + treeIndex =3D $("#phylovizNexSelector :selected").val(), + dataset_id =3D self.phyloTree.get("dataset_id"), + url =3D "phyloviz/getJsonData?dataset_id=3D" + dataset_id + "&= treeIndex=3D" + String(treeIndex); + $.getJSON(url, function(packedJson){ + window.initPhyloViz(packedJson.data, packedJson.config); + }); + } +}); + + +var HeaderButtons =3D Backbone.View.extend({ + + initialize : function(phylovizView){ + var self =3D this; + self.phylovizView =3D phylovizView; + + // Clean up code - if the class initialized more than once + $("#panelHeaderRightBtns").empty(); + $("#phyloVizNavBtns").empty(); + $("#phylovizNexSelector").off(); + + self.initNavBtns(); + self.initRightHeaderBtns(); + + // Initial a tree selector in the case of nexus + $("#phylovizNexSelector").off().on("change", function() {self.phy= lovizView.reloadViz();} ); + + }, + + initRightHeaderBtns : function(){ + var self =3D this; + + rightMenu =3D create_icon_buttons_menu([ + { icon_class: 'gear', title: 'PhyloViz Settings', on_click: fu= nction(){ + $("#SettingsMenu").show(); + self.settingsMenu.updateUI(); + } }, + { icon_class: 'disk', title: 'Save visualization', on_click: f= unction() { + var nexSelected =3D $("#phylovizNexSelector option:selecte= d").text(); + if(nexSelected) { + self.phylovizView.phyloTree.set("title", nexSelected); + } + self.phylovizView.phyloTree.save(); + } }, + { icon_class: 'chevron-expand', title: 'Search / Edit Nodes', = on_click: function() { + $("#nodeSelectionView").show(); + } }, + { icon_class: 'information', title: 'Phyloviz Help', on_click:= function() { + window.open('http://wiki.g2.bx.psu.edu/Learn/Visualization= /PhylogeneticTree'); + // https://docs.google.com/document/d/1AXFoJgEpxr21H3LICRs= 3EyMe1B1X_KFPouzIgrCz3zk/edit + } } + ], + { + tooltip_config: { placement: 'bottom' } + }); + $("#panelHeaderRightBtns").append(rightMenu.$el); + }, + + initNavBtns: function() { + var self =3D this, + navMenu =3D create_icon_buttons_menu([ + { icon_class: 'zoom-in', title: 'Zoom in', on_click: funct= ion() { + self.phylovizView.zoomAndPan({ zoom : "+"}); + } }, + { icon_class: 'zoom-out', title: 'Zoom out', on_click: fun= ction() { + self.phylovizView.zoomAndPan({ zoom : "-"}); + } }, + { icon_class: 'arrow-circle', title: 'Reset Zoom/Pan', on_= click: function() { + self.phylovizView.zoomAndPan({ zoom : "reset"}); + } } + ], + { + tooltip_config: { placement: 'bottom' } + }); + $("#phyloVizNavBtns").append(navMenu.$el); + } +}); + + +var SettingsMenu =3D UserMenuBase.extend({ + + className: 'Settings', + + initialize: function(options){ + // settings needs to directly interact with the phyloviz model so = it will get access to it. + var self =3D this; + self.phyloTree =3D options.phyloTree; + self.el =3D $("#SettingsMenu"); + self.inputs =3D { + separation : $("#phyloVizTreeSeparation"), + leafHeight : $("#phyloVizTreeLeafHeight"), + fontSize : $("#phyloVizTreeFontSize") + }; + + //init all buttons of settings + $("#settingsCloseBtn").off().on("click", function() { self.el.hide= (); }); + $("#phylovizResetSettingsBtn").off().on("click", function() { self= .resetToDefaults(); }); + $("#phylovizApplySettingsBtn").off().on("click", function() { self= .apply(); }); + }, + + apply : function(){ + /** + * Applying user values to phylotree model. + */ + var self =3D this; + if (!self.isAcceptableValue(self.inputs["separation"], 50, 2500) || + !self.isAcceptableValue(self.inputs["leafHeight"], 5, 30) || + !self.isAcceptableValue(self.inputs["fontSize"], 5, 20)){ + return; + } + $.each(self.inputs, function(key, $input){ + self.phyloTree.set(key, $input.val()); + }); + }, + updateUI : function(){ + /** + * Called to update the values input to that stored in the model + */ + var self =3D this; + $.each(self.inputs, function(key, $input){ + $input.val(self.phyloTree.get(key)); + }); + }, + resetToDefaults : function(){ + /** + * Resets the value of the phyloTree model to its default + */ + $(".bs-tooltip").remove(); // just in case the tool tip was n= ot removed + var self =3D this; + $.each(self.phyloTree.defaults, function(key, value) { + self.phyloTree.set(key, value); + }); + self.updateUI(); + }, + + render: function(){ + + } + +}); + + +var NodeSelectionView =3D UserMenuBase.extend({ + /** + * View for inspecting node properties and editing them + */ + className: 'Settings', + + initialize : function (options){ + var self =3D this; + self.el =3D $("#nodeSelectionView"); + self.phyloTree =3D options.phyloTree; + + self.UI =3D { + enableEdit : $('#phylovizEditNodesCheck'), + saveChanges : $('#phylovizNodeSaveChanges'), + cancelChanges : $("#phylovizNodeCancelChanges"), + name : $("#phyloVizSelectedNodeName"), + dist : $("#phyloVizSelectedNodeDist"), + annotation : $("#phyloVizSelectedNodeAnnotation") + }; + + self.valuesOfConcern =3D { + name : null, + dist : null, + annotation : null + }; // temporarily stores the values in case user change their mind + + //init UI buttons + $("#nodeSelCloseBtn").off().on("click", function() { self.el.hide(= ); }); + self.UI.saveChanges.off().on("click", function(){ self.updateNodes= (); }); + self.UI.cancelChanges.off().on("click", function(){ self.cancelCha= nges(); }); + + (function ($) { + // extending jquery fxn for enabling and disabling nodes. + $.fn.enable =3D function (isEnabled) { + return $(this).each(function () { + if(isEnabled){ + $(this).removeAttr('disabled'); + } else { + $(this).attr('disabled', 'disabled'); + } + }); + }; + })(jQuery); + + self.UI.enableEdit.off().on("click", function () { + self.toggleUI(); + }); + }, + + toggleUI : function(){ + /** + * For turning on and off the child elements + */ + var self =3D this, + checked =3D self.UI.enableEdit.is(':checked'); + + !checked ? self.cancelChanges() : ""; + + $.each(self.valuesOfConcern, function(key, value) { + self.UI[key].enable(checked); + }); + if(checked){ + self.UI.saveChanges.show(); + self.UI.cancelChanges.show(); + } else { + self.UI.saveChanges.hide(); + self.UI.cancelChanges.hide(); + } + + }, + + cancelChanges : function() { + /** + * Reverting to previous values in case user change their minds + */ + var self =3D this, + node =3D self.phyloTree.get("selectedNode"); + if (node){ + $.each(self.valuesOfConcern, function(key, value) { + self.UI[key].val(node[key]); + }); + } + }, + + updateNodes : function (){ + /** + * Changing the data in the underlying tree with user-specified va= lues + */ + var self =3D this, + node =3D self.phyloTree.get("selectedNode"); + if (node){ + if (!self.isAcceptableValue(self.UI.dist, 0, 1) || + self.hasIllegalJsonCharacters(self.UI.name) || + self.hasIllegalJsonCharacters(self.UI.annotation) ) { + return; + } + $.each(self.valuesOfConcern, function(key, value) { + (node[key]) =3D self.UI[key].val(); + }); + self.phyloTree.set("nodeAttrChangedTime", new Date()); + } else { + alert("No node selected"); + } + } + + +}); + + + +var PhyloVizSearch =3D UserMenuBase.extend({ + /** + * Initializes the search panel on phyloviz and handles its user inter= action + * It allows user to search the entire free based on some qualifer, li= ke dist <=3D val. + */ + initialize : function () { + var self =3D this; + + $("#phyloVizSearchBtn").on("click", function(){ + var searchTerm =3D $("#phyloVizSearchTerm"), + searchConditionVal =3D $("#phyloVizSearchCondition").val()= .split("-"), + attr =3D searchConditionVal[0], + condition =3D searchConditionVal[1]; + self.hasIllegalJsonCharacters(searchTerm); + + if (attr =3D=3D=3D "dist"){ + self.isAcceptableValue(searchTerm, 0, 1); + } + self.searchTree(attr, condition, searchTerm.val()); + }); + }, + + searchTree : function (attr, condition, val){ + /** + * Searches the entire tree and will highlight the nodes that matc= h the condition in green + */ + d3.selectAll("g.node") + .classed("searchHighlight", function(d){ + var attrVal =3D d[attr]; + if (typeof attrVal !=3D=3D "undefined" && attrVal !=3D=3D = null){ + if (attr =3D=3D=3D "dist"){ + switch (condition) { + case "greaterEqual": + return attrVal >=3D +val; + case "lesserEqual": + return attrVal <=3D +val; + default: + return; + } + + } else if (attr =3D=3D=3D "name" || attr =3D=3D=3D "an= notation") { + return attrVal.toLowerCase().indexOf(val.toLowerCa= se()) !=3D=3D -1; + } + } + }); + } +}); \ No newline at end of file diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 templates/root/history.mako --- a/templates/root/history.mako +++ b/templates/root/history.mako @@ -272,6 +272,17 @@ } =20 init_trackster_links(); + + function init_phyloviz_links() { + // PhyloViz links + // Add to trackster browser functionality + $(".phyloviz-add").live("click", function() { + var dataset =3D this, + dataset_jquery =3D $(this); + window.parent.location =3D dataset_jquery.attr("new-url"); + }); + } + init_phyloviz_links(); =20 // History rename functionality. async_save_text("history-name-container", "history-name", "${h.url_for= ( controller=3D"/history", action=3D"rename_async", id=3Dtrans.security.enc= ode_id(history.id) )}", "new_name", 18); diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 templates/root/history_common.mako --- a/templates/root/history_common.mako +++ b/templates/root/history_common.mako @@ -29,6 +29,9 @@ ## Render the dataset `data` as history item, using `hid` as the displayed= id <%def name=3D"render_dataset( data, hid, show_deleted_on_refresh =3D False= , for_editing =3D True, display_structured =3D False )"><% + + from galaxy.datatypes.xml import Phyloxml + from galaxy.datatypes.data import Newick, Nexus dataset_id =3D trans.security.encode_id( data.id ) =20 if data.state in ['no state','',None]: @@ -230,6 +233,14 @@ action-url=3D"${h.url_for( controller=3D't= racks', action=3D'browser', dataset_id=3Ddataset_id)}" new-url=3D"${h.url_for( controller=3D'trac= ks', action=3D'index', dataset_id=3Ddataset_id, default_dbkey=3Ddata.dbkey)= }" title=3D"View in Trackster"></a> %endif + <% + isPhylogenyData =3D isinstance(data.datatype, = (Phyloxml, Nexus, Newick)) + %> + %if isPhylogenyData: + <a href=3D"javascript:void(0)" class=3D"i= con-button chart_curve phyloviz-add" + action-url=3D"${h.url_for( controller= =3D'phyloviz', action=3D'-', dataset_id=3Ddataset_id)}" + new-url=3D"${h.url_for( controller=3D'p= hyloviz', action=3D'index', dataset_id=3Ddataset_id)}" title=3D"View in Phy= loviz"></a> + %endif %if trans.user: %if not display_structured: <div style=3D"float: right"> diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 templates/visualization/phyloviz.mako --- /dev/null +++ b/templates/visualization/phyloviz.mako @@ -0,0 +1,320 @@ +<%inherit file=3D"/webapps/galaxy/base_panels.mako"/> +## +<%def name=3D"init()"> + <% + self.has_left_panel=3DFalse + self.has_right_panel=3DFalse + self.active_view=3D"visualization" + self.message_box_visible=3DFalse + %> +</%def> + +<%def name=3D"stylesheets()"> + ${parent.stylesheets()} + <style> + + .node circle { + cursor: pointer; + fill: #fff; + stroke: steelblue; + stroke-width: 1.5px; + } + + .node.searchHighlight circle { + stroke-width: 3px; + stroke: #7adc26; + } + + .node.selectedHighlight circle { + stroke-width: 3px; + stroke: #dc143c; + } + + path.link { + fill: none; + stroke: #B5BBFF; + stroke-width: 4.0px; + } + + + div #phyloVizNavContainer{ + text-align: center; + width: 100%; + height: 0px; + } + + div #phyloVizNav{ + font-weight: bold; + display: inline-block; + background: transparent; + top: -2em; + position: relative; + } + + div .navControl{ + float: left; + } + + div#FloatingMenu { + left: 0; + top: 15%; + width:20%; + z-index:100; + padding: 5px; + + } + + div#SettingsMenu { + width: 25%; + top: 350px; + + } + + div#nodeSelectionView { + width: 25%; + top:70px; + } + + .Panel { + right: 0%; + z-index: 101; + position: fixed; + + ## Borrowed from galaxy modal_dialogues + background-color: white; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, 0.3); + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; + } + + span.PhylovizCloseBtn{ + cursor: pointer; + float : right; + } + + #PhyloViz{ + width: 100%; + height: 95%; + } + + h2.PhyloVizMenuTitle{ + color: white; + } + + ## Settings Menu + .SettingMenuRows{ + margin: 2px 0 2px 0; + } + + + ## Helper Styles + .PhyloVizFloatLeft{ + float : left; + } + .icon-button.zoom-in,.icon-button.zoom-out{display:inline-block;he= ight:16px;width:16px;margin-bottom:-3px;cursor:pointer;} + .icon-button.zoom-out{background:transparent url(../images/fugue/m= agnifier-zoom-out.png) center center no-repeat;} + .icon-button.zoom-in{margin-left:10px;background:transparent url(.= ./images/fugue/magnifier-zoom.png) center center no-repeat;} + + </style> +</%def> + + +<%def name=3D"javascripts()"> + ${parent.javascripts()} + ${h.js( "galaxy.panels", "libs/d3", "mvc/data", "viz/visualization", "= viz/phyloviz")} +</%def> + + + +<%def name=3D"center_panel()"> + + <div class=3D"unified-panel-header" unselectable=3D"on"> + <div class=3D"unified-panel-header-inner"> + <div style=3D"float:left;" id=3D"title"></div> + <div style=3D"float:right;" id=3D"panelHeaderRightBtns"></div> + </div> + <div style=3D"clear: both"></div> + </div> + + + <div id=3D"phyloVizNavContainer"> + <div id=3D"phyloVizNav"> + %if config["ext"] =3D=3D "nex" and not config["saved_visualiza= tion"]: + <div id =3D "phylovizNexInfo" class=3D"navControl"> + <p>Select a tree to view: + <select id=3D"phylovizNexSelector"> + % for tree, index in config["trees"]: + <option value=3D"${index}">${tree}</option> + % endfor + </select> + </p> + </div> + %endif + <div id=3D"phyloVizNavBtns" class=3D"navControl"> + </div> + <div class=3D"navControl"> + <p> | Alt+click to select nodes</p> + </div> + + + </div> + + </div> + + ## Node Selection Menu + <div id=3D"nodeSelectionView" class=3D"Panel"> + <div class=3D"modal-header"> + <h3 class=3D"PhyloVizMenuTitle">Search / Edit Nodes : + <span class=3D"PhylovizCloseBtn" id=3D"nodeSelCloseBtn"> X= </span> + </h3> + </div> + + <div class=3D"modal-body"> + + <div class=3D"SettingMenuRows"> + Search for nodes with: + <select id=3D"phyloVizSearchCondition" style=3D"width: 55%= "> + <option value=3D"name-containing">Name (containing)</o= ption> + <option value=3D"annotation-containing">Annotation (co= ntaining)</option> + <option value=3D"dist-greaterEqual">Distance (>=3D)</o= ption> + <option value=3D"dist-lesserEqual">Distance (<=3D)</op= tion> + </select> + <input type=3D"text" id=3D"phyloVizSearchTerm" value=3D"N= one" size=3D"15" displayLabel=3D"Distance"> + + <div class=3D"SettingMenuRows" style=3D"text-align: center= ;"> + <button id=3D"phyloVizSearchBtn" > Search! </button> + </div> + </div> + + <br/> + + <div class=3D"SettingMenuRows"> + Name: <input type=3D"text" id=3D"phyloVizSelectedNodeName"= value=3D"None" size=3D"15" disabled=3D"disabled" > + </div> + <div class=3D"SettingMenuRows"> + Dist: <input type=3D"text" id=3D"phyloVizSelectedNodeDist"= value=3D"None" size=3D"15" disabled=3D"disabled" displayLabel=3D"Distance"> + </div> + <div class=3D"SettingMenuRows"> + Annotation: + <textarea id=3D"phyloVizSelectedNodeAnnotation" disabled= =3D"disabled" ></textarea> + </div> + <div class=3D"SettingMenuRows"> + Edit: <input type=3D"checkbox" id=3D"phylovizEditNodesChec= k" value=3D"You can put custom annotations here and it will be saved"> + <button id=3D"phylovizNodeSaveChanges" style=3D"display: n= one;"> Save edits</button> + <button id=3D"phylovizNodeCancelChanges" style=3D"display:= none;"> Cancel</button> + </div> + </div> + </div> + + ## Settings Menus + <div id=3D"SettingsMenu" class=3D"Panel"> + <div class=3D"modal-header"> + <h3 class=3D"PhyloVizMenuTitle">Phyloviz Settings: + <span class=3D"PhylovizCloseBtn" id=3D"settingsCloseBtn"> = X </span> + </h3> + </div> + <div class=3D"modal-body"> + <div class=3D"SettingMenuRows"> + Phylogenetic Spacing (px per unit): <input id=3D"phyloVizT= reeSeparation" type=3D"text" value=3D"250" size=3D"10" displayLabel=3D"Phyl= ogenetic Separation"> (50-2500) + </div> + <div class=3D"SettingMenuRows"> + Vertical Spacing (px): <input type=3D"text" id=3D"phyloViz= TreeLeafHeight" value=3D"18" size=3D"10" displayLabel=3D"Vertical Spacing">= (5-30) + </div> + <div class=3D"SettingMenuRows"> + Font Size (px): <input type=3D"text" id=3D"phyloVizTreeFon= tSize" value=3D"12" size=3D"4" displayLabel=3D"Font Size"> (5-20) + </div> + + </div> + <div class=3D"modal-footer"> + <button id=3D"phylovizResetSettingsBtn" class=3D"PhyloVizFloat= Left" > Reset </button> + <button id=3D"phylovizApplySettingsBtn" class=3D"PhyloVizFloat= Right" > Apply </button> + </div> + </div> + + + + + + + <div class=3D"Panel" id=3D"FloatingMenu" style=3D"display: None;"> + + <h2>PhyloViz (<a onclick=3D"displayHelp()" href=3D"javascript:void= (0);">?</a>)</h2> + <div style=3D"display: none;"> + <h2>Summary of Interactions and Functions:</h2> + <div class=3D"hint">1. Expansion of Nodes: click or option-cli= ck to expand or collapse</div> + <div class=3D"hint">2. Zooming and translation: mousewheel, bu= ttons, click and drag, double click. Reset</div> + <div class=3D"hint">3. Tooltip: Displays "Name and Size" on mo= useOver on nodes</div> + <div class=3D"hint">4. Minimap: Currently displays an exact bu= t scaled down replicate of the tree, orange bounding box is correct for lin= ear only<br/> + Can be switched on or off</div> + <div class=3D"hint">5. Changing Layouts: Able to change betwee= n circular and linear layouts.</div> + + </div> + + <h5>Scaling & Rotation:</h5> + <button id=3D"phylovizZoomInBtn" class=3D"" > + </button> + <button id=3D"phylovizZoomOutBtn" class=3D"" > - </button> + + + <h5>Translation:</h5> + <button id=3D"phylovizTranslateUpBtn" > Up </button> + <button id=3D"phylovizTranslateDownBtn" > Down </button> + <br/> + <button id=3D"phylovizTranslateLeftBtn" > Left </button> + <button id=3D"phylovizTranslateRightBtn" > Right </button> + + + + <h5>Others:</h5> + <button id=3D"phylovizResetBtn" > Reset Zoom/Translate </button> + <button id=3D"phylovizSaveBtn" > Save vizualization </button> + <button id=3D"phylovizOpenSettingsBtn" > Settings </button> + </div> + + <div id=3D"PhyloViz" > + </div> + + <script type=3D"text/javascript"> + + function initPhyloViz(data, config) { + var phyloviz; + + // -- Initialization code |--> + phyloviz =3D new PhylovizView({ + data: data, + layout : "Linear", + config : config + }); + + // -- Render viz. -- + phyloviz.render(); + + } + + $(function firstVizLoad(){ // calls when viz is loaded for t= he first time + var config =3D JSON.parse( '${ h.to_json_string( config )}'); + var data =3D JSON.parse('${h.to_json_string(data)}'); + initPhyloViz(data, config); + }); + + </script> + +</%def> + + diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 test-data/visualization/phyloviz/1_nexus.nex --- /dev/null +++ b/test-data/visualization/phyloviz/1_nexus.nex @@ -0,0 +1,87 @@ +#NEXUS + +[!This data set was downloaded from TreeBASE, a relational database of phy= logenetic knowledge. TreeBASE has been supported by the NSF, Harvard Univer= sity, Yale University, SDSC and UC Davis. Please do not remove this acknowl= edgment from the Nexus file. + + +Generated on June 12, 2012; 23:00 GMT + +TreeBASE (cc) 1994-2008 + +Study reference: +Olariaga I., Grebenc T., Salcedo I., & Mart=C3=ADn M.P. 2012. Two new spec= ies of Hydnum +with ovoid basidiospores: H. ovoideisporum and H. vesterholtii. Mycologia,= . + +TreeBASE Study URI: http://purl.org/phylo/treebase/phylows/study/TB2:S128= 31] + +BEGIN TREES; + TITLE Hydnum_ITS_result; + LINK TAXA =3D Taxa1; + TRANSLATE + 1 Hydnum_aff_ellipsosporum_RUFHYD1_AJ535304, + 2 Hydnum_albidum_ALB_AY817135, + 3 Hydnum_albidum_ALBHYD1_AJ534974, + 4 Hydnum_albomagnum_ALM_DQ218305, + 5 Hydnum_ellipsosporum_ELL_AY817138, + 6 Hydnum_ellipsosporum_RUFHYD8_AJ547882, + 7 Hydnum_ovoidisporum_12317BIOFungi, + 8 Hydnum_ovoidisporum_12683BIOFungi, + 9 Hydnum_ovoidisporum_12902BIOFungi, + 10 Hydnum_ovoidisporum_14130BIOFungi, + 11 Hydnum_repandum_RE1_REP1_AJ889978, + 12 Hydnum_repandum_RE1_REP2_AJ889949, + 13 Hydnum_repandum_RE1_REP3_AY817136, + 14 Hydnum_repandum_RE1_REP6_UDB000025, + 15 Hydnum_repandum_RE1_REP7_UDB000096, + 16 Hydnum_repandum_RE1_REP8_UDB001479, + 17 Hydnum_repandum_RE1_REPHYD10_AJ547888, + 18 Hydnum_repandum_RE1_REPHYD11_AJ547886, + 19 Hydnum_repandum_RE1_REPHYD1_AJ547871, + 20 Hydnum_repandum_RE1_REPHYD3_AJ547874, + 21 Hydnum_repandum_RE1_REPHYD4_AJ547876, + 22 Hydnum_repandum_RE1_REPHYD5_AJ547875, + 23 Hydnum_repandum_RE1_REPHYD6_AJ547877, + 24 Hydnum_repandum_RE1_REPHYD7_AJ547878, + 25 Hydnum_repandum_RE1_REPHYD8_AJ547881, + 26 Hydnum_repandum_RE1_REPHYD9_AJ547883, + 27 Hydnum_repandum_RE1_RUFHYD10_AJ547866, + 28 Hydnum_repandum_RE1_RUFHYD11_AJ547889, + 29 Hydnum_repandum_RE1_RUFHYD9_AJ535305, + 30 Hydnum_rufescens_RU1_RUFHYD5_AJ547869, + 31 Hydnum_rufescens_RU1_RUFHYD6_AJ547884, + 32 Hydnum_rufescens_RU1_RUFHYD7_AJ547870, + 33 Hydnum_rufescens_RU2_REP5_DQ367902, + 34 Hydnum_rufescens_RU2_RUFHYD2_AJ535301, + 35 Hydnum_rufescens_RU3_12901BIOFungi, + 36 Hydnum_rufescens_RU3_REP4_DQ218306, + 37 Hydnum_rufescens_RU3_RUFHYD3_AJ535303, + 38 Hydnum_rufescens_RU3_RUFHYD4_AJ535302, + 39 Hydnum_rufescens_RU4_RUFHYD12_AJ839969, + 40 Hydnum_rufescens_RU4_RUFHYD16_AJ547868, + 41 Hydnum_rufescens_RU4_RUFHYD17_AJ547885, + 42 Hydnum_rufescens_RU4_UMB1_DQ367903, + 43 Hydnum_rufescens_RU5_12760BIOFungi, + 44 Hydnum_rufescens_RU5_ALBHYD2_AJ534975, + 45 Hydnum_rufescens_RU5_RUF2_DQ658890, + 46 Hydnum_rufescens_RU5_RUF4_UDB001465, + 47 Hydnum_rufescens_RU5_RUF5_UDB002423, + 48 Hydnum_rufescens_RU5_RUFHYD14_AJ547872, + 49 Hydnum_rufescens_RU6_RUF1_AY817137, + 50 Hydnum_rufescens_RU6_RUFHYD15_AJ547867, + 51 Hydnum_rufescens_wrong_taxonomy_RUF3_AM087246, + 52 Hydnum_umbilicatum_UMBHYD1_AJ534972, + 53 Hydnum_umbilicatum_UMBHYD2_AJ534973, + 54 Hydnum_vesterholtii_10429BIOFungi, + 55 Hydnum_vesterholtii_10452BIOFungi, + 56 Hydnum_vesterholtii_12330BIOFungi, + 57 Hydnum_vesterholtii_12904BIOFungi, + 58 Hydnum_vesterholtii_REPHYD12A_AJ547879, + 59 Hydnum_vesterholtii_REPHYD12C_AJ783968, + 60 Hydnum_vesterholtii_REPHYD13_AJ547887, + 61 Sistotrema_muscicola_AJ606040, + 62 Sistotrema_alboluteum_AJ606042; + TREE Fig._2 =3D [&R] ((62:100.0,(51:100.0,61:100.0):93.269997):49.66= ,((4:100.0,(2:100.0,3:100.0):100.0):60.639999,(((56:100.0,58:100.0,59:100.0= ):84.639999,(54:100.0,55:100.0,57:100.0,60:100.0):98.330002):92.5,(((30:100= .0,31:100.0,32:100.0):100.0,(11:100.0,12:100.0,13:100.0,14:100.0,15:100.0,1= 6:100.0,17:100.0,18:100.0,19:100.0,20:100.0,21:100.0,22:100.0,23:100.0,24:1= 00.0,25:100.0,26:100.0):99.93):68.690002,(((33:100.0,34:100.0):49.8050005,(= 35:100.0,36:100.0,37:100.0,38:100.0):99.989998):49.8050005,((7:100.0,8:100.= 0,9:100.0,10:100.0):100.0,(42:100.0,(39:100.0,40:100.0,41:100.0):98.449997)= :86.790001,((52:100.0,53:100.0):99.93,(1:100.0,(5:97.47999949999999,6:100.0= ):97.47999949999999):100.0):53.310001,(27:100.0,(28:100.0,29:100.0,49:100.0= ,50:100.0):47.404999):47.404999,(43:100.0,44:100.0,45:100.0,46:100.0,47:100= .0,48:100.0):99.459999):29.245001):29.245001):51.580002):61.540001):49.66); + TREE PAUP_1 =3D [&R] ((62:100.0,(51:100.0,61:100.0):93.269997):49.66= ,((4:100.0,(3:100.0,2:100.0):100.0):60.639999,(((58:100.0,59:100.0,56:100.0= ):84.639999,(60:100.0,54:100.0,55:100.0,57:100.0):98.330002):92.5,(((30:100= .0,31:100.0,32:100.0):100.0,(19:100.0,20:100.0,21:100.0,22:100.0,23:100.0,2= 4:100.0,25:100.0,26:100.0,17:100.0,18:100.0,11:100.0,12:100.0,13:100.0,14:1= 00.0,15:100.0,16:100.0):99.93):68.690002,((34:100.0,33:100.0):99.610001,(37= :100.0,38:100.0,35:100.0,36:100.0):99.989998,(42:100.0,(39:100.0,41:100.0,4= 0:100.0):98.449997):86.790001,(8:100.0,7:100.0,9:100.0,10:100.0):100.0,((52= :100.0,53:100.0):99.93,(1:100.0,(5:100.0,6:100.0):94.959999):100.0):53.3100= 01,(29:100.0,27:100.0,28:100.0,50:100.0,49:100.0):94.809998,(44:100.0,43:10= 0.0,48:100.0,45:100.0,46:100.0,47:100.0):99.459999):58.490002):51.580002):6= 1.540001):49.66); + + + +END; diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 test-data/visualization/phyloviz/2_nexus.nex --- /dev/null +++ b/test-data/visualization/phyloviz/2_nexus.nex @@ -0,0 +1,96 @@ +#NEXUS + +[!This data set was downloaded from TreeBASE, a relational database of phy= logenetic knowledge. TreeBASE has been supported by the NSF, Harvard Univer= sity, Yale University, SDSC and UC Davis. Please do not remove this acknowl= edgment from the Nexus file. + + +Generated on August 18, 2012; 12:14 GMT + +TreeBASE (cc) 1994-2008 + +Study reference: +Naish D., Dyke G., Cau A., & Escuilli=C3=A9 F. 2012. A gigantic bird from = the Upper Cretaceous +of Central Asia. Biology Letters, 8(1): 97-100. + +TreeBASE Study URI: http://purl.org/phylo/treebase/phylows/study/TB2:S130= 08] + +BEGIN TREES; + TITLE Imported_trees; + LINK TAXA =3D Taxa1; + TRANSLATE + 1 Herrerasaurus, + 2 Tawa, + 3 Allosaurus, + 4 Alvarezsaurus, + 5 Anchiornis, + 6 Archaeopteryx, + 7 Archaeorhynchus, + 8 Avimimus, + 9 Baryonyx, + 10 Beipiaosaurus, + 11 Caenagnathus, + 12 Caudipteryx, + 13 Ceratosaurus, + 14 Chirostenotes, + 15 Citipati, + 16 Compsognathus, + 17 Confuciusornis, + 18 Dilong, + 19 Dilophosaurus, + 20 Epidendrosaurus, + 21 Epidexipteryx, + 22 Erlicosaurus, + 23 Eustreptospondylus, + 24 Gallimimus, + 25 Garudimimus, + 26 Gobipteryx, + 27 Guanlong, + 28 Haplocheirus, + 29 Harpymimus, + 30 Hebeiornis, + 31 Hongshanornis, + 32 Huoshanornis, + 33 Iberomesornis, + 34 Ichthyornis, + 35 Incisivosaurus, + 36 Jeholornis, + 37 Limusaurus, + 38 Longicrusavis, + 39 Longipteryx, + 40 Longirostravis, + 41 Majungasaurus, + 42 Masiakasaurus, + 43 Monolophosaurus, + 44 Mononykus, + 45 Neornithes, + 46 Ornitholestes, + 47 Ornithomimus, + 48 Patagonykus, + 49 Patagopteryx, + 50 Pelecanimimus, + 51 Pengornis, + 52 Protarchaeopteryx, + 53 Protopteryx, + 54 Rinchenia, + 55 Sapeornis, + 56 Segnosaurus, + 57 Shenzhousaurus, + 58 Shuvuuia, + 59 Sinornithosaurus, + 60 Sinosauropteryx, + 61 Sinovenator, + 62 Sinraptor, + 63 Syntarsus_kayentakatae, + 64 Troodon, + 65 Tyrannosaurus, + 66 Velociraptor, + 67 Yanornis, + 68 Yixianornis, + 69 Zhongjianornis, + 70 Zhongornis, + 71 Zuolong, + 72 Samrukia; + TREE Figure_1A =3D [&R] (1,(2,(((((43,(3,62)),(71,((46,((((28,(4,(48= ,(44,58)))),((((5,(61,(64,(59,66)))),(6,((36,(55,(69,(((7,34,45,49,72,(31,3= 8),(67,68)),(33,((32,((26,30),(39,40))),(51,53)))),(17,70))))),(20,21)))),(= (11,(12,(8,(14,(15,54))))),(35,52))),(10,(22,56)))),(50,(57,(29,(25,(24,47)= ))))),(16,60))),(27,(18,65))))),(9,23)),(13,(41,(37,42)))),(19,63)))); + + + +END; diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 test-data/visualization/phyloviz/3_phyloxml.xml --- /dev/null +++ b/test-data/visualization/phyloviz/3_phyloxml.xml @@ -0,0 +1,257 @@ +<?xml version=3D"1.0" encoding=3D"UTF-8"?> +<phyloxml xmlns:xsi=3D"http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation=3D"http://www.phyloxml.org http://www.phyloxml.org/1= .10/phyloxml.xsd" + xmlns=3D"http://www.phyloxml.org"> + <phylogeny rooted=3D"true"> + <clade> + <clade> + <branch_length>0.18105</branch_length> + <confidence type=3D"unknown">89.0</confidence> + <clade> + <branch_length>0.07466</branch_length> + <confidence type=3D"unknown">32.0</confidence> + <clade> + <branch_length>0.26168</branch_length> + <confidence type=3D"unknown">100.0</confidence> + <clade> + <branch_length>0.22058</branch_length> + <confidence type=3D"unknown">89.0</confidence> + <clade> + <branch_length>0.28901</branch_length> + <confidence type=3D"unknown">100.0</confidence> + <clade> + <branch_length>0.06584</branch_length> + <confidence type=3D"unknown">100.0</confidence> + <clade> + <branch_length>0.02309</branch_length> + <confidence type=3D"unknown">43.0</confidenc= e> + <clade> + <branch_length>0.0746</branch_length> + <confidence type=3D"unknown">100.0</confi= dence> + <clade> + <branch_length>0.02365</branch_length> + <confidence type=3D"unknown">88.0</con= fidence> + <clade> + <name>22_MOUSE</name> + <branch_length>0.05998</branch_leng= th> + <taxonomy> + <code>MOUSE</code> + </taxonomy> + </clade> + <clade> + <name>Apaf-1_HUMAN</name> + <branch_length>0.01825</branch_leng= th> + <taxonomy> + <code>HUMAN</code> + </taxonomy> + </clade> + </clade> + <clade> + <name>12_CANFA</name> + <branch_length>0.04683</branch_length> + <taxonomy> + <code>CANFA</code> + </taxonomy> + </clade> + </clade> + <clade> + <name>11_CHICK</name> + <branch_length>0.15226</branch_length> + <taxonomy> + <code>CHICK</code> + </taxonomy> + </clade> + </clade> + <clade> + <name>16_XENLA</name> + <branch_length>0.4409</branch_length> + <taxonomy> + <code>XENLA</code> + </taxonomy> + </clade> + </clade> + <clade> + <branch_length>0.17031</branch_length> + <confidence type=3D"unknown">100.0</confidence> + <clade> + <branch_length>0.10929</branch_length> + <confidence type=3D"unknown">100.0</confiden= ce> + <clade> + <name>14_FUGRU</name> + <branch_length>0.02255</branch_length> + <taxonomy> + <code>FUGRU</code> + </taxonomy> + </clade> + <clade> + <name>15_TETNG</name> + <branch_length>0.09478</branch_length> + <taxonomy> + <code>TETNG</code> + </taxonomy> + </clade> + </clade> + <clade> + <name>17_BRARE</name> + <branch_length>0.1811</branch_length> + <taxonomy> + <code>BRARE</code> + </taxonomy> + </clade> + </clade> + </clade> + <clade> + <branch_length>0.01594</branch_length> + <confidence type=3D"unknown">53.0</confidence> + <clade> + <branch_length>0.10709</branch_length> + <confidence type=3D"unknown">68.0</confidence> + <clade> + <name>1_BRAFL</name> + <branch_length>0.26131</branch_length> + <taxonomy> + <code>BRAFL</code> + </taxonomy> + </clade> + <clade> + <name>18_NEMVE</name> + <branch_length>0.38014</branch_length> + <taxonomy> + <code>NEMVE</code> + </taxonomy> + </clade> + </clade> + <clade> + <name>23_STRPU</name> + <branch_length>0.48179</branch_length> + <taxonomy> + <code>STRPU</code> + </taxonomy> + </clade> + </clade> + </clade> + <clade> + <branch_length>0.34475</branch_length> + <confidence type=3D"unknown">100.0</confidence> + <clade> + <name>26_STRPU</name> + <branch_length>0.36374</branch_length> + <taxonomy> + <code>STRPU</code> + </taxonomy> + <sequence> + <domain_architecture length=3D"1319"> + <domain from=3D"18" to=3D"98" confidence=3D"= 3.4E-5">Death</domain> + <domain from=3D"189" to=3D"481" confidence= =3D"1.8E-10">NB-ARC</domain> + <domain from=3D"630" to=3D"668" confidence= =3D"8.2E-5">WD40</domain> + </domain_architecture> + </sequence> + </clade> + <clade> + <name>25_STRPU</name> + <branch_length>0.33137</branch_length> + <taxonomy> + <code>STRPU</code> + </taxonomy> + <sequence> + <domain_architecture length=3D"1947"> + <domain from=3D"143" to=3D"227" confidence= =3D"7.4E-5">Death</domain> + <domain from=3D"227" to=3D"550" confidence= =3D"2.0E-13">NB-ARC</domain> + <domain from=3D"697" to=3D"736" confidence= =3D"7.9E-4">WD40</domain> + <domain from=3D"745" to=3D"785" confidence= =3D"1.5">WD40</domain> + <domain from=3D"1741" to=3D"1836" confidence= =3D"2.0">Adeno_VII</domain> + </domain_architecture> + </sequence> + </clade> + </clade> + </clade> + <clade> + <branch_length>1.31498</branch_length> + <confidence type=3D"unknown">100.0</confidence> + <clade> + <name>CED4_CAEEL</name> + <branch_length>0.13241</branch_length> + <taxonomy> + <code>CAEEL</code> + </taxonomy> + <sequence> + <domain_architecture length=3D"714"> + <domain from=3D"7" to=3D"90" confidence=3D"9.2E= -14">CARD</domain> + <domain from=3D"116" to=3D"442" confidence=3D"5= .8E-151">NB-ARC</domain> + </domain_architecture> + </sequence> + </clade> + <clade> + <name>31_CAEBR</name> + <branch_length>0.04777</branch_length> + <taxonomy> + <code>CAEBR</code> + </taxonomy> + <sequence> + <domain_architecture length=3D"554"> + <domain from=3D"1" to=3D"75" confidence=3D"0.00= 46">CARD</domain> + <domain from=3D"101" to=3D"427" confidence=3D"2= .1E-123">NB-ARC</domain> + </domain_architecture> + </sequence> + </clade> + </clade> + </clade> + <clade> + <branch_length>0.13172</branch_length> + <confidence type=3D"unknown">45.0</confidence> + <clade> + <branch_length>0.24915</branch_length> + <confidence type=3D"unknown">95.0</confidence> + <clade> + <branch_length>0.76898</branch_length> + <confidence type=3D"unknown">100.0</confidence> + <clade> + <name>28_DROPS</name> + <branch_length>0.1732</branch_length> + <taxonomy> + <code>DROPS</code> + </taxonomy> + <sequence> + <domain_architecture length=3D"535"> + <domain from=3D"112" to=3D"399" confidence= =3D"1.4E-5">NB-ARC</domain> + </domain_architecture> + </sequence> + </clade> + <clade> + <name>Dark_DROME</name> + <branch_length>0.18863</branch_length> + <taxonomy> + <code>DROME</code> + </taxonomy> + <sequence> + <domain_architecture length=3D"1421"> + <domain from=3D"108" to=3D"397" confidence= =3D"2.1E-5">NB-ARC</domain> + </domain_architecture> + </sequence> + </clade> + </clade> + <clade> + <name>29_AEDAE</name> + <branch_length>0.86398</branch_length> + <taxonomy> + <code>AEDAE</code> + </taxonomy> + <sequence> + <domain_architecture length=3D"423"> + <domain from=3D"109" to=3D"421" confidence=3D"9= .3E-6">NB-ARC</domain> + </domain_architecture> + </sequence> + </clade> + </clade> + <clade> + <name>30_TRICA</name> + <branch_length>0.97698</branch_length> + <taxonomy> + <code>TRICA</code> + </taxonomy> + </clade> + </clade> + </clade> + </clade> + </phylogeny> +</phyloxml> diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 test-data/visualization/phyloviz/4_newick.nhx --- /dev/null +++ b/test-data/visualization/phyloviz/4_newick.nhx @@ -0,0 +1,33 @@ +(((BGIOSIBCA028421_ORYSA:0.423485[&&NHX:S=3DORYSA:O=3DBGIOSIBCA028421.1:G= =3DBGIOSIBCA028421], +At5g41150_ARATH:0.273135[&&NHX:S=3DARATH:O=3DAt5g41150.1:G=3DAt5g41150] +):0.690991[&&NHX:S=3DMagnoliophyta:D=3DN:B=3D100], +(rad16_SCHPO:0.718598[&&NHX:S=3DSCHPO:O=3DSPCC970.01:G=3DSPCC970.01], +RAD1_YEAST:1.05456[&&NHX:S=3DYEAST:O=3DYPL022W.1:G=3DYPL022W] +):0.344838[&&NHX:S=3DAscomycota:D=3DN:B=3D100] +):0.103849[&&NHX:S=3DEukaryota:D=3DN:B=3D61], +((((((((ERCC4_HUMAN:0.067531[&&NHX:S=3DHUMAN:O=3DENST00000311895.3:G=3DENS= G00000175595], +Ercc4_MOUSE:0.17422[&&NHX:S=3DMOUSE:O=3DENSMUST00000023206.5:G=3DENSMUSG00= 000022545] +):0.065513[&&NHX:S=3DEuarchontoglires:D=3DN:B=3D100], +ENSMODT00000006086_MONDO:0.104633[&&NHX:S=3DMONDO:O=3DENSMODT00000006086.2= :G=3DENSMODG00000004840] +):0.083764[&&NHX:S=3DTheria:D=3DN:B=3D100], +Q5ZJP8_CHICK:0.153132[&&NHX:S=3DCHICK:O=3DENSGALT00000004716.2:G=3DENSGALG= 00000002981] +):0.057998[&&NHX:S=3DAmniota:D=3DN:B=3D100], +ENSXETT00000024054_XENTR:0.288632[&&NHX:S=3DXENTR:O=3DENSXETT00000024054.2= :G=3DENSXETG00000010991] +):0.075713[&&NHX:S=3DTetrapoda:D=3DN:B=3D100], +(zgc-63468_BRARE:0.2218[&&NHX:S=3DBRARE:O=3DENSDART00000015780.4:G=3DENSDA= RG00000014161], +NEWSINFRUT00000137921_FUGRU:0.220441[&&NHX:S=3DFUGRU:O=3DNEWSINFRUT0000013= 7921.3:G=3DNEWSINFRUG00000130312] +):0.170605[&&NHX:S=3DClupeocephala:D=3DN:B=3D100] +):0.238713[&&NHX:S=3DEuteleostomi:D=3DN:B=3D100], +ENSCINT00000011737_CIOIN:0.623567[&&NHX:S=3DCIOIN:O=3DENSCINT00000011737.2= :G=3DENSCING00000005673] +):0.07499[&&NHX:S=3DChordata:D=3DN:B=3D100], +(Sm00.scaff00195.0600_SCHMA:0.784609[&&NHX:S=3DSCHMA:O=3DSm00.scaff00195.0= 600:G=3DSm00.scaff00195.0600], +(CBG03141_CAEBR:0.093703[&&NHX:S=3DCAEBR:O=3DCBG03141:G=3DCBG03141], +NP_496498_CAEEL:0.212236[&&NHX:S=3DCAEEL:O=3DC47D12.8.1:G=3DC47D12.8] +):1.47416[&&NHX:S=3DCaenorhabditis:D=3DN:B=3D94] +):0.26906[&&NHX:S=3DBilateria:D=3DN:B=3D97] +):0.071406[&&NHX:S=3DBilateria:D=3DN:B=3D1], +(mei-9-RA_DROME:0.170289[&&NHX:S=3DDROME:O=3DCG3697-RA.3:G=3DCG3697], +GA17620-PA_DROPS:0.154817[&&NHX:S=3DDROPS:O=3DGA17620-PA:G=3DGA17620] +):0.818474[&&NHX:S=3DSophophora:D=3DN:B=3D100] +):0 +)[&&NHX:S=3DEukaryota:D=3DN]; \ No newline at end of file diff -r b1f2c51d6bd8d8b1aecce8f62304cc2df278ccec -r f6d9557b6d77bde1c8049ba= ffb65374729d51b89 test-data/visualization/phyloviz/5_newick.nhx --- /dev/null +++ b/test-data/visualization/phyloviz/5_newick.nhx @@ -0,0 +1,1 @@ +(CAE_ELE_PORCN:0.303421 ,((((DRO_PER_PORCN:0.001000 ,DRO_PSE_PORCN:0.00100= 0 )67:0.141994 ,(DRO_ANA_PORCN:0.111899 ,(DRO_ERE_PORCN:0.030516 ,(DRO_MEL_= PORCN:0.021127 ,DRO_SEC_PORCN:0.021127 )38:0.030516 )35:0.111899 )18:0.1419= 94 )16:0.162611 ,(DRO_WIL_PORCN:0.152225 ,(DRO_VIR_PORCN:0.085057 ,DRO_MOJ_= PORCN:0.085057 )24:0.152225 )15:0.162611 )13:0.295081 ,(ANO_GAM_PORCN:0.287= 545 ,((CIO_INT_PORCN:0.100686 ,CIO_SAV_PORCN:0.100686 )19:0.275542 ,((LOA_L= OA_PORCN:0.036278 ,BRU_MAL_PORCN:0.036278 )29:0.272631 ,(((((DAN_RER_PORCN:= 0.086499 ,((TAK_RUB_PORCN:0.032609 ,TET_NIG_PORCN:0.032609 )32:0.048864 ,(G= AD_MOR_PORCN:0.039387 ,(ORY_LAT_PORCN:0.031729 ,(GAS_ACU_PORCN:0.021882 ,OR= E_NIL_PORCN:0.021882 )37:0.031729 )34:0.039387 )28:0.048864 )27:0.086499 )2= 3:0.119618 ,(LAT_CHA_PORCN:0.099348 ,((XEN_LAE_PORCN:0.033333 ,XEN_TRO_PORC= N:0.033333 )31:0.091250 ,(ANO_CAR_PORCN:0.086538 ,((MON_DOM_PORCN:0.014100 = ,(MAC_EUG_PORCN:0.005423 ,SAR_HAR_PORCN:0.005423 )57:0.014100 )42:0.062862 = ,(ORN_ANA_PORCN:0.057974 ,(GOR_GOR_PORCN:0.033876 ,(FEL_CAT_PORCN:0.022851 = ,(PRO_CAP_PORCN:0.019716 ,(CAV_POR_PORCN:0.018599 ,(ERI_EUR_PORCN:0.015518 = ,((DIP_ORD_PORCN:0.007231 ,(MUS_MUS_PORCN:0.001085 ,(RAT_NOR_PORCN:0.001000= ,CRI_GRI_PORCN:0.001000 )69:0.001085 )64:0.007231 )53:0.012954 ,(DAS_NOV_P= ORCN:0.011362 ,(LOX_AFR_PORCN:0.010575 ,(CAL_JAC_PORCN:0.010332 ,(OCH_PRI_P= ORCN:0.010063 ,(MIC_MUR_PORCN:0.009123 ,(SUS_SCR_PORCN:0.008880 ,(MYO_LUC_P= ORCN:0.008460 ,((CAN_FAM_PORCN:0.005423 ,AIL_MEL_PORCN:0.005423 )58:0.00809= 3 ,((PTE_VAM_PORCN:0.006508 ,BOS_TAU_PORCN:0.006508 )55:0.007494 ,((SPE_TRI= _PORCN:0.003254 ,TUP_BEL_PORCN:0.003254 )61:0.006929 ,((OTO_GAR_PORCN:0.001= 085 ,(ORY_CUN_PORCN:0.001000 ,TUR_TRU_PORCN:0.001000 )68:0.001085 )65:0.005= 965 ,(EQU_CAB_PORCN:0.003688 ,(MAC_MUL_PORCN:0.002711 ,(PAN_TRO_PORCN:0.001= 446 ,(HOM_SAP_PORCN:0.001085 ,(PON_ABE_PORCN:0.001000 ,NOM_LEU_PORCN:0.0010= 00 )70:0.001085 )66:0.001446 )63:0.002711 )62:0.003688 )60:0.005965 )56:0.0= 06929 )54:0.007494 )52:0.008093 )51:0.008460 )50:0.008880 )49:0.009123 )48:= 0.010063 )47:0.010332 )46:0.010575 )45:0.011362 )44:0.012954 )43:0.015518 )= 41:0.018599 )40:0.019716 )39:0.022851 )36:0.033876 )30:0.057974 )26:0.06286= 2 )25:0.086538 )22:0.091250 )21:0.099348 )20:0.119618 )17:0.214465 ,(BRA_FL= O_PORCN:0.189220 ,SAC_KOW_PORCN:0.189220 )12:0.214465 )11:0.257058 ,(NEM_VE= C_PORCN:0.246631 ,AMP_QUE_PORCN:0.246631 )9:0.257058 )8:0.266904 ,(TRI_CAS_= PORCN:0.259494 ,(PED_HUM_PORCN:0.227009 ,(NAS_VIT_PORCN:0.160241 ,(API_MEL_= PORCN:0.031851 ,(BOM_TER_PORCN:0.004808 ,BOM_IMP_PORCN:0.004808 )59:0.03185= 1 )33:0.160241 )14:0.227009 )10:0.259494 )7:0.266904 )6:0.272631 )5:0.27554= 2 )4:0.287545 )3:0.295081 )2:0.303421 )1:0.0001; https://bitbucket.org/galaxy/galaxy-central/changeset/75a03bacdc7a/ changeset: 75a03bacdc7a user: jgoecks date: 2012-08-27 20:08:55 summary: Merged in Tomithy/galaxy-central-phyloviz-2 (pull request #65) affected #: 19 files diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 lib/galaxy/datatypes/data.py --- a/lib/galaxy/datatypes/data.py +++ b/lib/galaxy/datatypes/data.py @@ -719,7 +719,49 @@ pass =20 class Newick( Text ): - pass + """New Hampshire/Newick Format""" + file_ext =3D "nhx" + + MetadataElement( name=3D"columns", default=3D3, desc=3D"Number of colu= mns", readonly=3DTrue ) + + def __init__(self, **kwd): + """Initialize foobar datatype""" + Text.__init__(self, **kwd) + + def init_meta( self, dataset, copy_from=3DNone ): + Text.init_meta( self, dataset, copy_from=3Dcopy_from ) + + + def sniff( self, filename ): + """ Returning false as the newick format is too general and cannot= be sniffed.""" + return False + + +class Nexus( Text ): + """Nexus format as used By Paup, Mr Bayes, etc""" + file_ext =3D "nex" + + MetadataElement( name=3D"columns", default=3D3, desc=3D"Number of colu= mns", readonly=3DTrue ) + + def __init__(self, **kwd): + """Initialize foobar datatype""" + Text.__init__(self, **kwd) + + def init_meta( self, dataset, copy_from=3DNone ): + Text.init_meta( self, dataset, copy_from=3Dcopy_from ) + + + def sniff( self, filename ): + """All Nexus Files Simply puts a '#NEXUS' in its first line""" + f =3D open(filename, "r") + firstline =3D f.readline().upper() + f.close() + + if "#NEXUS" in firstline: + return True + else: + return False + =20 # ------------- Utility methods -------------- =20 diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 lib/galaxy/datatypes/xml.py --- a/lib/galaxy/datatypes/xml.py +++ b/lib/galaxy/datatypes/xml.py @@ -76,3 +76,24 @@ dataset.blurb =3D 'file purged from disk' def sniff( self, filename ): return False + +class Phyloxml( GenericXml ): + """Format for defining phyloxml data http://www.phyloxml.org/""" + file_ext =3D "phyloxml" + def set_peek( self, dataset, is_multi_byte=3DFalse ): + """Set the peek and blurb text""" + if not dataset.dataset.purged: + dataset.peek =3D data.get_file_peek( dataset.file_name, is_mul= ti_byte=3Dis_multi_byte ) + dataset.blurb =3D 'Phyloxml data' + else: + dataset.peek =3D 'file does not exist' + dataset.blurb =3D 'file purged from disk' + + def sniff( self, filename ): + """"Checking for keyword - 'phyloxml' always in lowercase in the f= irst few lines""" + f =3D open(filename, "r") + firstlines =3D "".join(f.readlines(5)) + f.close() + if "phyloxml" in firstlines: + return True + return False \ No newline at end of file diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 lib/galaxy/visualization/phyloviz/__init__.py --- /dev/null +++ b/lib/galaxy/visualization/phyloviz/__init__.py @@ -0,0 +1,1 @@ +__author__ =3D 'Tomithy' diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 lib/galaxy/visualization/phyloviz/baseparser.py --- /dev/null +++ b/lib/galaxy/visualization/phyloviz/baseparser.py @@ -0,0 +1,125 @@ +import json + +class Node(object): + """Node class of PhyloTree, which represents a CLAUDE in a phylogeneti= c tree""" + def __init__(self, nodeName, **kwargs): + """Creates a node and adds in the typical annotations""" + self.name, self.id =3D nodeName, kwargs.get("id", 0) + self.depth =3D kwargs.get("depth", 0) + self.children =3D [] + + self.isInternal =3D kwargs.get("isInternal", 0) + self.length, self.bootstrap =3D kwargs.get("length", 0), kwargs.ge= t("bootstrap", None) + self.events =3D kwargs.get("events", "") + + # clean up boot strap values + if self.bootstrap =3D=3D -1: + self.bootstrap =3D None + + def addChildNode(self, child): + """Adds a child node to the current node""" + if isinstance(child, Node): + self.children.append(child) + else: + self.children +=3D child + + + def __str__(self): + return self.name + " id:" + str(self.id) + ", depth: " + str(self.= depth) + + + def toJson(self): + """Converts the data in the node to a dict representation of json"= "" + thisJson =3D { + "name" : self.name, + "id" : self.id, + "depth" : self.depth, + "dist" : self.length + } + thisJson =3D self.addChildrenToJson(thisJson) + thisJson =3D self.addMiscToJson(thisJson) + return thisJson + + def addChildrenToJson(self, jsonDict): + """Needs a special method to addChildren, such that the key does n= ot appear in the Jsondict when the children is empty + this requirement is due to the layout algorithm used by d3 layout = for hiding subtree """ + if len(self.children) > 0: + children =3D [ node.toJson() for node in self.children] + jsonDict["children"] =3D children + return jsonDict + + + def addMiscToJson(self, jsonDict): + """Adds other misc attributes to json if they are present""" + if not self.events =3D=3D "": + jsonDict["events"] =3D self.events + if not self.bootstrap =3D=3D None: + jsonDict["bootstrap"] =3D self.bootstrap + return jsonDict + + + +class PhyloTree(object): + """Standardized python based class to represent the phylogenetic tree = parsed from different + phylogenetic file formats.""" + + def __init__(self): + self.root, self.rootAttr =3D None, {} + self.nodes =3D {} + self.title =3D None + self.id =3D 1 + + def addAttributesToRoot(self, attrDict): + """Adds attributes to root, but first we put it in a temp store an= d bind it with root when .toJson is called""" + for key, value in attrDict.items(): + self.rootAttr[key] =3D value + + def makeNode(self, nodeName, **kwargs): + """Called to make a node within PhyloTree, arbitrary kwargs can be= passed to annotate nodes + Tracks the number of nodes via internally incremented id""" + kwargs["id"] =3D self.id + self.id +=3D 1 + return Node(nodeName, **kwargs) + + def addRoot(self, root): + """Creates a root for phyloTree""" + assert isinstance(root, Node) + root.parent =3D None + self.root =3D root + + def generateJsonableDict(self): + """Changes itself into a dictonary by recurssively calling the toj= son on all its nodes. Think of it + as a dict in an array of dict in an array of dict and so on...""" + jsonTree =3D "" + if self.root: + assert isinstance(self.root, Node) + jsonTree =3D self.root.toJson() + for key, value in self.rootAttr.items(): + # transfer temporary stored attr to root + jsonTree[key] =3D value + else: + raise Exception("Root is not assigned!") + return jsonTree + + + +class Base_Parser(object): + """Base parsers contain all the methods to handle phylogeny tree creat= ion and + converting the data to json that all parsers should have""" + + def __init__(self): + self.phyloTrees =3D [] + + def parseFile(self, filePath): + """Base method that all phylogeny file parser should have""" + raise Exception("Base method for phylogeny file parsers is not imp= lemented") + + def toJson(self, jsonDict): + """Convenience method to get a json string from a python json dict= """ + return json.dumps(jsonDict) + + def _writeJsonToFile(self, filepath, json): + """Writes the file out to the system""" + f =3D open(filepath, "w") + f.writelines(json) + f.close() diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 lib/galaxy/visualization/phyloviz/newickparser.py --- /dev/null +++ b/lib/galaxy/visualization/phyloviz/newickparser.py @@ -0,0 +1,185 @@ +from baseparser import Base_Parser, PhyloTree +import re + +class Newick_Parser(Base_Parser): + """For parsing trees stored in the newick format (.nhx) + It is necessarily more complex because this parser is later extended b= y Nexus for parsing newick as well..""" + + + def __init__(self): + super(Newick_Parser, self).__init__() + + + def parseFile(self, filePath): + """Parses a newick file to obtain the string inside. Returns: json= ableDict""" + with open(filePath, "r") as newickFile: + newickString =3D newickFile.read() + newickString =3D newickString.replace("\n", "").replace("\r", = "") + return [self.parseData(newickString)], "Success" + + + def parseData(self, newickString): + """To be called on a newickString directly to parse it. Returns: j= sonableDict""" + return self._parseNewickToJson(newickString) + + + def _parseNewickToJson(self, newickString, treeName=3DNone, nameMap=3D= None): + """parses a newick representation of a tree into a PhyloTree data = structure, + which can be easily converted to json""" + self.phyloTree =3D PhyloTree() + newickString =3D self.cleanNewickString(newickString) + if nameMap: + newickString =3D self._mapName(newickString, nameMap) + + self.phyloTree.root =3D self.parseNode(newickString, 0) + if nameMap: + self.phyloTree.addAttributesToRoot({"treeName": treeName}) + + return self.phyloTree.generateJsonableDict() + + + def cleanNewickString(self, rawNewick): + """removing semi colon, and illegal json characters (\,',") and wh= ite spaces""" + return re.sub(r'\s|;|\"|\'|\\', '', rawNewick) + + + def _makeNodesFromString(self, string, depth): + """elements separated by comma could be empty""" + + if string.find("(") !=3D -1: + raise Exception("Tree is not well form, location: " + string) + + childrenString =3D string.split(",") + childrenNodes =3D [] + + for childString in childrenString: + if len(childString) =3D=3D 0: + continue + nodeInfo =3D childString.split(":") + name, length, bootstrap =3D "", None, -1 + if len(nodeInfo) =3D=3D 2: # has length info + length =3D nodeInfo[1] + # checking for bootstap values + name =3D nodeInfo[0] + try: # Nexus may bootstrap in names position + name =3D float(name) + if 0<=3D name <=3D 1: + bootstrap =3D name + elif 1 <=3D name <=3D 100: + bootstrap =3D name / 100 + name =3D "" + except ValueError: + name =3D nodeInfo[0] + else: + name =3D nodeInfo[0] # string only contains name + node =3D self.phyloTree.makeNode(name, length=3Dlength, depth= =3Ddepth, bootstrap=3D bootstrap) + childrenNodes +=3D [node] + return childrenNodes + + + + def _mapName(self, newickString, nameMap): + """ + Necessary to replace names of terms inside nexus representation + Also, its here because Mailaud's doesnt deal with id_strings outsi= de of quotes(" ") + """ + newString =3D "" + start =3D 0 + end =3D 0 + + for i in xrange(len(newickString)): + if newickString[i] =3D=3D "(" or newickString[i] =3D=3D ",": + if re.match(r"[,(]", newickString[i+1:]): + continue + else: + end =3D i + 1 + # i now refers to the starting position of the term to= be replaced, + # we will next find j which is the ending pos of the t= erm + for j in xrange(i+1, len(newickString)): + enclosingSymbol =3D newickString[j] # the immedi= ate symbol after a common or left bracket which denotes the end of a term + if enclosingSymbol =3D=3D ")" or enclosingSymbol = =3D=3D ":" or enclosingSymbol =3D=3D ",": + termToReplace =3D newickString[end:j] + + newString +=3D newickString[start : end] + na= meMap[termToReplace] #+ "'" "'" + + start =3D j + break + + newString +=3D newickString[start:] + return newString + + + def parseNode(self, string, depth): + """ Recursive method for parsing newick string, works by stripping= down the string into substring + of newick contained with brackers, which is used to call itself. + Eg ... ( A, B, (D, E)C, F, G ) ... + We will make the preceeding nodes first A, B, then the internal no= de C, its children D, E, + and finally the succeeding nodes F, G""" + + # Base case where there is only an empty string + if string =3D=3D "": + return + # Base case there its only an internal claude + if string.find("(") =3D=3D -1: + return self._makeNodesFromString(string, depth) + + nodes, children =3D [], [] # nodes refer to the nodes on this= level, children refers to the child of the + start =3D 0 + lenOfPreceedingInternalNodeString =3D 0 + bracketStack =3D [] + + for j in xrange(len(string)): + if string[j] =3D=3D "(": #finding the positions of all the = open brackets + bracketStack.append(j) + continue + if string[j] =3D=3D ")": #finding the positions of all the = closed brackets to extract claude + i =3D bracketStack.pop() + + if len(bracketStack) =3D=3D 0: # is child of current node + + InternalNode =3D None + + #First flat call to make nodes of the same depth but f= rom the preceeding string. + startSubstring =3D string[start + lenOfPreceedingInter= nalNodeString: i] + preceedingNodes =3D self._makeNodesFromString(startSu= bstring, depth) + nodes +=3D preceedingNodes + + # Then We will try to see if the substring has any int= ernal nodes first, make it then make nodes preceeding it and succeeding it. + if j + 1 < len(string): + stringRightOfBracket =3D string[j+1:] # Eg. '= (b:0.4,a:0.3)c:0.3, stringRightOfBracket =3D c:0.3 + match =3D re.search(r"[\)\,\(]", stringRightOfBrac= ket) + if match: + indexOfNextSymbol =3D match.start() + stringRepOfInternalNode =3D stringRightOfBrack= et[:indexOfNextSymbol] + internalNodes =3D self._makeNodesFromString( s= tringRepOfInternalNode, depth) + if len(internalNodes) > 0: + InternalNode =3D internalNodes[0] + lenOfPreceedingInternalNodeString =3D len(stri= ngRepOfInternalNode) + else: # sometimes the node can be the last eleme= nt of a string + InternalNode =3D self._makeNodesFromString(str= ing[j+1:], depth)[0] + lenOfPreceedingInternalNodeString =3D len(stri= ng) - j + if InternalNode =3D=3D None: #creating a generic= node if it is unnamed + InternalNode =3D self.phyloTree.makeNode( "", dept= h=3Ddepth, isInternal=3DTrue ) #"internal-" + str(depth) + lenOfPreceedingInternalNodeString =3D 0 + + # recussive call to make the internal claude + childSubString =3D string[ i + 1 : j ] + InternalNode.addChildNode(self.parseNode(childSubStrin= g, depth + 1)) + + nodes.append(InternalNode) # we append the internal n= ode later to preserve order + + start =3D j + 1 + continue + + if depth =3D=3D 0: # if its the root node, we do nothing about = it and return + return nodes[0] + + # Adding last most set of children + endString =3D string[start:] + if string[start-1] =3D=3D ")": # if the symbol belongs to an inte= rnal node which is created previously, then we remove it from the string le= ft to parse + match =3D re.search(r"[\)\,\(]", endString) + if match: + endOfNodeName =3D start + match.start() + 1 + endString =3D string[endOfNodeName:] + nodes +=3D self._makeNodesFromString(endString, depth) + + return nodes diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 lib/galaxy/visualization/phyloviz/nexusparser.py --- /dev/null +++ b/lib/galaxy/visualization/phyloviz/nexusparser.py @@ -0,0 +1,107 @@ +from newickparser import Newick_Parser +import re + +MAX_READLINES =3D 200000 + + +class Nexus_Parser(Newick_Parser): + + def __init__(self): + super(Nexus_Parser, self).__init__() + + def parseFile(self, filePath): + """passes a file and extracts its Nexus content.""" + return self.parseNexus(filePath) + + + def parseNexus(self, filename): + """ Nexus data is stored in blocks between a line starting with be= gin and another line starting with end; + Commends inside square brackets are to be ignored, + For more information: http://wiki.christophchamp.com/index.php/NEX= US_file_format + Nexus can store multiple trees + """ + + with open( filename, "rt") as nex_file: + nexlines =3D nex_file.readlines() + + rowCount =3D 0 + inTreeBlock =3D False # sentinel to check if we are in a t= ree block + intranslateBlock =3D False # sentinel to check if we are in the= translate region of the tree. Stores synonyms of the labellings + self.inCommentBlock =3D False + self.nameMapping =3D None # stores mapping representation us= ed in nexus format + treeNames =3D [] + + for line in nexlines: + line =3D line.replace(";\n", "") + lline =3D line.lower() + + if rowCount > MAX_READLINES or (not nex_file) : + break + rowCount +=3D1 + # We are only interested in the tree block. + if "begin" in lline and "tree" in lline and not inTreeBlock: + inTreeBlock =3D True + continue + if inTreeBlock and "end" in lline[:3]: + inTreeBlock, currPhyloTree =3D False, None + continue + + if inTreeBlock: + + if "title" in lline: # Adding title to the tree + titleLoc =3D lline.find("title") + title =3D line[titleLoc + 5:].replace(" ", "") + + continue + + if "translate" in lline: + intranslateBlock =3D True + self.nameMapping =3D {} + continue + + if intranslateBlock: + mappingLine =3D self.splitLinebyWhitespaces(line) + key, value =3D mappingLine[1], mappingLine[2].replace(= ",", "").replace("'","") #replacing illegal json characters + self.nameMapping[key] =3D value + + # Extracting newick Trees + if "tree" in lline: + intranslateBlock =3D False + + treeLineCols =3D self.splitLinebyWhitespaces(line) + treeName, newick =3D treeLineCols[2], treeLineCols[-1] + + if newick =3D=3D "": # Empty lines can be found in = tree blocks + continue + + currPhyloTree =3D self._parseNewickToJson(newick, tree= Name, nameMap=3Dself.nameMapping) + + self.phyloTrees.append(currPhyloTree) + treeIndex =3D len(self.phyloTrees) - 1 + treeNames.append( (treeName, treeIndex) ) # appendi= ng name of tree, and its index + continue + + return self.phyloTrees, treeNames + + + def splitLinebyWhitespaces(self, line): + """replace tabs and write spaces to a single write space, so we ca= n properly split it.""" + return re.split(r"\s+", line) + + + def checkComments(self, line): + """Check to see if the line/lines is a comment.""" + if not self.inCommentBlock: + if "[" in line: + if "]" not in line: + self.inCommentBlock =3D True + else: + return "Nextline" # need to move on to the nextline = after getting out of comment + else : + if "]" in line: + if line.rfind("[") > line.rfind("]"): + pass # a comment block is closed but an= other is open. + else: + self.inCommentBlock =3D False + return "Nextline" # need to move on to the nextline = after getting out of comment + return "" \ No newline at end of file diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 lib/galaxy/visualization/phyloviz/phyloviz_dataprovider.py --- /dev/null +++ b/lib/galaxy/visualization/phyloviz/phyloviz_dataprovider.py @@ -0,0 +1,35 @@ +from newickparser import Newick_Parser +from nexusparser import Nexus_Parser +from phyloxmlparser import Phyloxml_Parser + +class Phyloviz_DataProvider(object): + + def __init__(self): + pass + + def parseFile(self, filepath, fileExt): + """returns [trees], meta + Trees are actually an array of JsonDicts. It's usually one tre= e, except in the case of Nexus + """ + jsonDicts, meta =3D [], {} + try: + if fileExt =3D=3D "nhx": # parses newick files + newickParser =3D Newick_Parser() + jsonDicts, parseMsg =3D newickParser.parseFile(filepath) + elif fileExt =3D=3D "phyloxml": # parses phyloXML files + phyloxmlParser =3D Phyloxml_Parser() + jsonDicts, parseMsg =3D phyloxmlParser.parseFile(filepath) + elif fileExt =3D=3D "nex": # parses nexus files + nexusParser =3D Nexus_Parser() + jsonDicts, parseMsg =3D nexusParser.parseFile(filepath) + meta["trees"] =3D parseMsg + else: + raise Exception("File type is not supported") + + meta["msg"] =3D parseMsg + + except Exception: + jsonDicts, meta["msg"] =3D [], "Parse failed" + + return jsonDicts, meta + diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 lib/galaxy/visualization/phyloviz/phyloxmlparser.py --- /dev/null +++ b/lib/galaxy/visualization/phyloviz/phyloxmlparser.py @@ -0,0 +1,145 @@ +from baseparser import Base_Parser, PhyloTree, Node +from lxml import etree + +class Phyloxml_Parser(Base_Parser): + """Parses a phyloxml file into a json file that will be passed to Phyl= oViz for display""" + + def __init__(self): + super(Phyloxml_Parser, self).__init__() + self.phyloTree =3D PhyloTree() + self.tagsOfInterest =3D { + "clade": "", + "name" : "name", + "branch_length" : "length", + "confidence" : "bootstrap", + "events" : "events" + } + + def parseFile(self, filePath): + """passes a file and extracts its Phylogeny Tree content.""" + phyloXmlFile =3D open(filePath, "r") + + xmlTree =3D etree.parse(phyloXmlFile) + xmlRoot =3D xmlTree.getroot()[0] + self.nameSpaceIndex =3D xmlRoot.tag.rfind("}") + 1 # used later by= the clean tag method to remove the name space in every element.tag + + phyloRoot =3D None + for child in xmlRoot: + childTag =3D self.cleanTag(child.tag) + if childTag =3D=3D "clade": + phyloRoot =3D child + elif childTag =3D=3D "name": + self.phyloTree.title =3D child.text + + self.phyloTree.root =3D self.parseNode(phyloRoot, 0) + jsonDict =3D self.phyloTree.generateJsonableDict() + return [jsonDict], "Success" + + + def parseNode(self, node, depth): + """Parses any node within a phyloxml tree and looks out for claude= , which signals the creation of + nodes - internal OR leaf""" + assert isinstance(node, etree._Element) + + tag =3D self.cleanTag(node.tag) + if not tag =3D=3D "clade": + return None + hasInnerClade =3D False + + # peeking once for parent and once for child to check if the node = is internal + for child in node: + childTag =3D self.cleanTag(child.tag) + if childTag =3D=3D "clade": + hasInnerClade =3D True + break + + if hasInnerClade: # this node is an internal node + currentNode =3D self._makeInternalNode(node, depth=3D depth) + for child in node: + child =3D self.parseNode(child, depth + 1) + if isinstance(child, Node): + currentNode.addChildNode(child) + + else: # this node is a leaf node + currentNode =3D self._makeLeafNode(node, depth=3Ddepth+1) + + return currentNode + + + def _makeLeafNode(self, leafNode, depth =3D 0 ): + """Makes leaf nodes by calling Phylotree methods""" + node =3D {} + for child in leafNode: + childTag =3D self.cleanTag(child.tag) + if childTag in self.tagsOfInterest: + key =3D self.tagsOfInterest[childTag] # need to map phy= loxml terms to ours + node[key] =3D child.text + + node["depth"] =3D depth + return self.phyloTree.makeNode(self._getNodeName(leafNode), **node) + + def _getNodeName(self, node, depth=3D-1): + """Gets the name of a claude. It handles the case where a taxonomy= node is involved""" + + def getTagFromTaxonomyNode(node): + """Returns the name of a taxonomy node. A taxonomy node have t= o be treated differently as the name + is embedded one level deeper""" + phyloxmlTaxoNames =3D { + "common_name" : "", + "scientific_name" : "", + "code" : "" + } + for child in node: + childTag =3D self.cleanTag(child.tag) + if childTag in phyloxmlTaxoNames: + return child.text + return "" + + nodeName =3D "" + for child in node: + childTag =3D self.cleanTag(child.tag) + if childTag =3D=3D "name" : + nodeName =3D child.text + break + elif childTag =3D=3D "taxonomy": + nodeName =3D getTagFromTaxonomyNode(child) + break + + return nodeName + + + def _makeInternalNode(self, internalNode, depth=3D0): + """ Makes an internal node from an element object that is gurantee= d to be a parent node. + Gets the value of interests like events and appends it to a custom= node object that will be passed to PhyloTree to make nodes + """ + node =3D {} + for child in internalNode: + childTag =3D self.cleanTag(child.tag) + if childTag =3D=3D "clade": + continue + elif childTag in self.tagsOfInterest: + if childTag =3D=3D "events": # events is nested 1 more = level deeper than others + key, text =3D "events", self.cleanTag(child[0].tag) + else: + key =3D self.tagsOfInterest[childTag] + text =3D child.text + node[key] =3D text + + + return self.phyloTree.makeNode(self._getNodeName(internalNode, dep= th), **node) + + + def cleanTag(self, tagString): + return tagString[self.nameSpaceIndex:] + + +if __name__=3D=3D"__main__": + + # Files tested against + parser =3D Phyloxml_Parser() + filepath =3D "../data/" +"apaf.xml" + # filepath =3D "../data/" +"12_multiple_supports.xml" + + # filepath =3D "../data/" +"bcl_2.xml" + # filepath =3D "../data/" +"reducedXml.xml" + parser.parseFile(filepath) diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 lib/galaxy/web/controllers/phyloviz.py --- /dev/null +++ b/lib/galaxy/web/controllers/phyloviz.py @@ -0,0 +1,97 @@ +import pkg_resources +pkg_resources.require( "bx-python" ) + +from galaxy.util.json import to_json_string, from_json_string +from galaxy.web.base.controller import * +from galaxy.visualization.phyloviz.phyloviz_dataprovider import Phyloviz_D= ataProvider + + +class PhyloVizController( BaseUIController, UsesVisualizationMixin, UsesHi= storyDatasetAssociationMixin, SharableMixin ): + """ + Controller for phyloViz browser interface. + """ + def __init__(self, app ): + BaseUIController.__init__( self, app ) + + @web.expose + @web.require_login() + def index( self, trans, dataset_id =3D None, **kwargs ): + """ + The index method is called using phyloviz/ with a dataset id passe= d in. + The relevant data set is then retrieved via get_json_from_datasetI= d which interfaces with the parser + The json representation of the phylogenetic tree along with the co= nfig is then written in the .mako template and passed back to the user + """ + json, config =3D self.get_json_from_datasetId(trans, dataset_id) + config["saved_visualization"] =3D False + return trans.fill_template( "visualization/phyloviz.mako", data = =3D json, config=3Dconfig) + + + @web.expose + def visualization(self, trans, id): + """ + Called using a viz_id (id) to retrieved stored visualization data = (in json format) and all the viz_config + """ + viz =3D self.get_visualization(trans, id) + config =3D self.get_visualization_config(trans, viz) + config["saved_visualization"] =3D True + data =3D config["root"] + + return trans.fill_template( "visualization/phyloviz.mako", data = =3D data, config=3Dconfig) + + + @web.expose + @web.json + def load_visualization_json(self, trans, viz_id): + """ + Though not used in current implementation, this provides user with= a convenient method to retrieve the viz_data & viz_config via json. + """ + viz =3D self.get_visualization(trans, viz_id) + viz_config =3D self.get_visualization_config(trans, viz) + viz_config["saved_visualization"] =3D True + return { + "data" : viz_config["root"], + "config" : viz_config + } + + + @web.expose + @web.json + def getJsonData(self, trans, dataset_id, treeIndex=3D0): + """ + Method to retrieve data asynchronously via json format. Retriving = from here rather than + making a direct datasets/ call allows for some processing and even= t capturing + """ + treeIndex =3D int(treeIndex) + json, config =3D self.get_json_from_datasetId(trans, dataset_id, t= reeIndex) + packedJson =3D { + "data" : json, + "config" : config + } + + return packedJson + + + def get_json_from_datasetId(self, trans, dataset_id, treeIndex=3D0): + """ + For interfacing phyloviz controllers with phyloviz visualization d= ata provider (parsers) + """ + dataset =3D self.get_dataset(trans, dataset_id) + fileExt, filepath =3D dataset.ext, dataset.file_name # .name= stores the name of the dataset from the orginal upload + json, config =3D "", {} # config contains propertie= s of the tree and file + + if fileExt =3D=3D "json": + something, json =3D self.get_data(dataset) + else: + try: + pd =3D Phyloviz_DataProvider() + json, config =3D pd.parseFile(filepath, fileExt) + json =3D json[treeIndex] + except Exception: + pass + + config["title"] =3D dataset.display_name() + config["ext"] =3D fileExt + config["dataset_id"] =3D dataset_id + config["treeIndex"] =3D treeIndex + + return json, config diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 lib/galaxy/web/controllers/visualization.py --- a/lib/galaxy/web/controllers/visualization.py +++ b/lib/galaxy/web/controllers/visualization.py @@ -16,6 +16,10 @@ action =3D "paramamonster" elif item.type =3D=3D "circster": action =3D "circster" + elif item.type =3D=3D "phyloviz": + # Support phyloviz + controller =3D "phyloviz" + action =3D "visualization" return dict( controller=3Dcontroller, action=3Daction, id=3Ditem.i= d ) =20 # Grid definition diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 static/scripts/viz/phyloviz.js --- /dev/null +++ b/static/scripts/viz/phyloviz.js @@ -0,0 +1,955 @@ +var UserMenuBase =3D Backbone.View.extend({ + /** + * Base class of any menus that takes in user interaction. Contains ch= ecking methods. + */ + + className: 'UserMenuBase', + + isAcceptableValue : function ($inputKey, min, max) { + /** + * Check if an input value is a number and falls within max min. + */ + var self =3D this, + value =3D $inputKey.val(), + fieldName =3D $inputKey.attr("displayLabel") || $inputKey.attr= ("id").replace("phyloViz", ""); + + function isNumeric(n) { + return !isNaN(parseFloat(n)) && isFinite(n); + } + + if (!isNumeric(value)){ + alert(fieldName + " is not a number!"); + return false; + } + + if ( value > max){ + alert(fieldName + " is too large."); + return false; + } else if ( value < min) { + alert(fieldName + " is too small."); + return false; + } + return true; + }, + + hasIllegalJsonCharacters : function($inputKey) { + /** + * Check if any user string inputs has illegal characters that jso= n cannot accept + */ + if ($inputKey.val().search(/"|'|\\/) !=3D=3D -1){ + alert("Named fields cannot contain these illegal characters: d= ouble quote(\"), single guote(\'), or back slash(\\). "); + return true; + } + return false; + } +}); + + +function PhyloTreeLayout() { + /** + * -- Custom Layout call for phyloViz to suit the needs of a phylogene= tic tree. + * -- Specifically: 1) Nodes have a display display of (=3D evo dist X= depth separation) from their parent + * 2) Nodes must appear in other after they have expa= nd and contracted + */ + + var self =3D this, + hierarchy =3D d3.layout.hierarchy().sort(null).value(null), + height =3D 360, // ! represents both the layout angle and the heig= ht of the layout, in px + layoutMode =3D "Linear", + leafHeight =3D 18, // height of each individual leaf node + depthSeparation =3D 200, // separation between nodes of different = depth, in px + leafIndex =3D 0, // change to recurssive call + defaultDist =3D 0.5, // tree defaults to 0.5 dist if no dist is sp= ecified + maxTextWidth =3D 50; // maximum length of the text labels + + + self.leafHeight =3D function(inputLeafHeight){ + if (typeof inputLeafHeight =3D=3D=3D "undefined"){ return leafHeig= ht; } + else { leafHeight =3D inputLeafHeight; return self;} + }; + + self.layoutMode =3D function(mode){ + if (typeof mode =3D=3D=3D "undefined"){ return layoutMode; } + else { layoutMode =3D mode; return self;} + }; + + self.layoutAngle =3D function(angle) { // changes the layout angle = of the display, which is really changing the height + if (typeof angle =3D=3D=3D "undefined"){ return height; } + if (isNaN(angle) || angle < 0 || angle > 360) { return self; } // = to use default if the user puts in strange values + else { height =3D angle; return self;} + }; + + self.separation =3D function(dist){ // changes the dist between the = nodes of different depth + if (typeof dist =3D=3D=3D "undefined"){ return depthSeparation; } + else { depthSeparation =3D dist; return self;} + }; + + self.links =3D function (nodes) { // uses d3 native method to gene= rate links. Done. + return d3.layout.tree().links(nodes); + }; + + // -- Custom method for laying out phylogeny tree in a linear fashion + self.nodes =3D function (d, i) { + var _nodes =3D hierarchy.call(self, d, i), // self is to f= ind the depth of all the nodes, assumes root is passed in + nodes =3D [], + maxDepth =3D 0, + numLeaves =3D 0; + + // changing from hierarchy's custom format for data to usable form= at + _nodes.forEach(function (_node){ + var node =3D _node.data; + node.depth =3D _node.depth; + maxDepth =3D node.depth > maxDepth ? node.depth : maxDepth; /= /finding max depth of tree + nodes.push(node); + }); + // counting the number of leaf nodes and assigning max depth to no= des that do not have children to flush all the leave nodes + nodes.forEach(function(node){ + if ( !node.children ) { //&& !node._children + numLeaves +=3D 1; + node.depth =3D maxDepth; // if a leaf has no child it woul= d be assigned max depth + } + }); + + leafHeight =3D layoutMode =3D=3D=3D "Circular" ? height / numLeave= s : leafHeight; + leafIndex =3D 0; + layout(nodes[0], maxDepth, leafHeight, null); + + return nodes; + }; + + + function layout (node, maxDepth, vertSeparation, parent) { + /** + * -- Function with side effect of adding x0, y0 to all child; tak= e in the root as starting point + * assuming that the leave nodes would be sorted in presented ord= er + * horizontal(y0) is calculated according to (=3D evo dis= t X depth separation) from their parent + * vertical (x0) - if leave node: find its order in all o= f the leave node =3D=3D=3D node.id, then multiply by verticalSeparation + * - if parent node: is place in the mid point al= l of its children nodes + * -- The layout will first calculate the y0 field going towards t= he leaves, and x0 when returning + */ + var children =3D node.children, + sumChildVertSeparation =3D 0; + + // calculation of node's dist from parents, going down. + var dist =3D node.dist || defaultDist; + dist =3D dist > 1 ? 1 : dist; // We constrain all dist to be l= ess than one + node.dist =3D dist; + if (parent !=3D=3D null){ + node.y0 =3D parent.y0 + dist * depthSeparation; + } else { //root node + node.y0 =3D maxTextWidth; + } + + + // if a node have no children, we will treat it as a leaf and star= t laying it out first + if (!children) { + node.x0 =3D leafIndex++ * vertSeparation; + } else { + // if it has children, we will visit all its children and calc= ulate its position from its children + children.forEach( function (child) { + child.parent =3D node; + sumChildVertSeparation +=3D layout(child, maxDepth, vertSe= paration, node); + }); + node.x0 =3D sumChildVertSeparation / children.length; + } + + // adding properties to the newly created node + node.x =3D node.x0; + node.y =3D node.y0; + return node.x0; + } + return self; +} + + +/** + * -- PhyloTree Model -- + */ +var PhyloTree =3D Visualization.extend({ + defaults : { + layout: "Linear", + separation : 250, // px dist between nodes of different depth t= o represent 1 evolutionary until + leafHeight: 18, + type : "phyloviz", // visualization type + title : "Title", + scaleFactor: 1, + translate: [0,0], + fontSize: 12, //fontSize of node label + selectedNode : null, + nodeAttrChangedTime : 0 + }, + + root : {}, // Root has to be its own independent object because it is = not part of the viz_config + + toggle : function (d) { + /** + * Mechanism to expand or contract a single node. Expanded nodes h= ave a children list, while for + * contracted nodes the list is stored in _children. Nodes with th= eir children data stored in _children will not have their + * children rendered. + */ + if(typeof d =3D=3D=3D "undefined") {return ;} + if (d.children ) { + d._children =3D d.children; + d.children =3D null; + } else { + d.children =3D d._children; + d._children =3D null; + } + }, + + toggleAll : function(d) { + /** + * Contracts the phylotree to a single node by repeatedly calling= itself to place all the list + * of children under _children. + */ + if (d.children && d.children.length !=3D=3D 0) { + d.children.forEach(this.toggleAll); + toggle(d); + } + }, + + getData : function (){ + /** + * Return the data of the tree. Used for preserving state. + */ + return this.root; + }, + + save: function() { + /** + * Overriding the default save mechanism to do some clean of circu= lar reference of the + * phyloTree and to include phyloTree in the saved json + */ + var root =3D this.root; + cleanTree(root); + this.set("root", root); + + function cleanTree(node){ + // we need to remove parent to delete circular reference + delete node.parent; + + // removing unnecessary attributes + if (node._selected){ delete node._selected;} + + node.children ? node.children.forEach(cleanTree) : 0; + node._children ? node._children.forEach(cleanTree) : 0; + } + + var config =3D jQuery.extend(true, {}, this.attributes); + config["selectedNode"] =3D null; + + show_message("Saving to Galaxy", "progress"); + + return $.ajax({ + url: this.url(), + type: "POST", + dataType: "json", + data: { + vis_json: JSON.stringify(config) + }, + success: function(res){ + var viz_id =3D res.url.split("id=3D")[1].split("&")[0], + viz_url =3D "/phyloviz/visualization?id=3D" + viz_id; + window.history.pushState({}, "", viz_url + window.location= .hash); + hide_modal(); + } + }); + } +}); + + + +/** + * -- Views -- + */ +var PhylovizLayoutBase =3D Backbone.View.extend({ + /** + * Stores the default variable for setting up the visualization + */ + defaults : { + nodeRadius : 4.5 // radius of each node in the diagram + }, + + + stdInit : function (options) { + /** + * Common initialization in layouts + */ + + var self =3D this; + self.model.on("change:separation change:leafHeight change:fontSize= change:nodeAttrChangedTime", self.updateAndRender, self); + + self.vis =3D options.vis; + self.i =3D 0; + self.maxDepth =3D -1; // stores the max depth of the tree + + self.width =3D options.width; + self.height =3D options.height; + }, + + + updateAndRender : function(source) { + /** + * Updates the visualization whenever there are changes in the ex= pansion and contraction of nodes + * AND possibly when the tree is edited. + */ + var vis =3D d3.select(".vis"), + self =3D this; + source =3D source || self.model.root; + + self.renderNodes(source); + self.renderLinks(source); + self.addTooltips(); + }, + + + renderLinks : function(source) { + /** + * Renders the links for the visualization. + */ + var self =3D this; + var diagonal =3D self.diagonal; + var duration =3D self.duration; + var layoutMode =3D self.layoutMode; + var link =3D self.vis.selectAll("g.completeLink") + .data(self.tree.links(self.nodes), function(d) { return d.targ= et.id; }); + + var calcalateLinePos =3D function(d) { + d.pos0 =3D d.source.y0 + " " + d.source.x0; // position of t= he source node <=3D> starting location of the line drawn + d.pos1 =3D d.source.y0 + " " + d.target.x0; // position where= the line makes a right angle bend + d.pos2 =3D d.target.y0 + " " + d.target.x0; // point where= the horizontal line becomes a dotted line + }; + + var linkEnter =3D link.enter().insert("svg:g","g.node") + .attr("class", "completeLink"); + + + linkEnter.append("svg:path") + .attr("class", "link") + .attr("d", function(d) { + calcalateLinePos(d); + return "M " + d.pos0 + " L " + d.pos1; + }); + + var linkUpdate =3D link.transition().duration(500); + + linkUpdate.select("path.link") + .attr("d", function(d) { + calcalateLinePos(d); + return "M " + d.pos0 + " L " + d.pos1 + " L " + d.pos2; + }); + + var linkExit =3D link.exit().remove(); + + }, + + // User Interaction methods below + + selectNode : function(node){ + /** + * Displays the information for editting + */ + var self =3D this; + d3.selectAll("g.node") + .classed("selectedHighlight", function(d){ + if (node.id =3D=3D=3D d.id){ + if(node._selected) { // for de=3Dselecting node. + delete node._selected; + return false; + } else { + node._selected =3D true; + return true; + } + } + return false; + }); + + self.model.set("selectedNode", node); + $("#phyloVizSelectedNodeName").val(node.name); + $("#phyloVizSelectedNodeDist").val(node.dist); + $("#phyloVizSelectedNodeAnnotation").val(node.annotation || ""); + }, + + addTooltips : function (){ + /** + * Creates bootstrap tooltip for the visualization. Has to be cal= led repeatedly due to newly generated + * enterNodes + */ + $(".bs-tooltip").remove(); //clean up tooltip, just in case i= ts listeners are removed by d3 + $(".node") + .attr("data-original-title", function(){ + var d =3D this.__data__, + annotation =3D d.annotation || "None" ; + return d ? (d.name ? d.name + "<br/>" : "") + "Dist: " + d= .dist + " <br/>Annotation: " + annotation: ""; + }) + .tooltip({'placement':'top', 'trigger' : 'hover'}); + + } +}); + + + + +var PhylovizLinearView =3D PhylovizLayoutBase.extend({ + /** + * Linea layout class of Phyloviz, is responsible for rendering the no= des + * calls PhyloTreeLayout to determine the positions of the nodes + */ + initialize : function(options){ + // Default values of linear layout + var self =3D this; + self.margins =3D options.margins; + self.layoutMode =3D "Linear"; + + self.stdInit(options); + + self.layout(); + self.updateAndRender(self.model.root); + }, + + layout : function() { + /** + * Creates the basic layout of a linear tree by precalculating fix= ed values. + * One of calculations are also made here + */ + + var self =3D this; + + self.tree =3D new PhyloTreeLayout().layoutMode("Linear"); + self.diagonal =3D d3.svg.diagonal() + .projection(function(d) { return [d.y, d.x ]; }); + }, + + renderNodes : function (source) { + /** + * Renders the nodes base on Linear layout. + */ + var self =3D this, + fontSize =3D self.model.get("fontSize") + "px"; + + // assigning properties from models + self.tree.separation(self.model.get("separation")).leafHeight(self= .model.get("leafHeight")); + + var duration =3D 500, + nodes =3D self.tree.separation(self.model.get("separation")).n= odes(self.model.root); + + var node =3D self.vis.selectAll("g.node") + .data(nodes, function(d) { return d.name + d.id || (d.id =3D += +self.i); }); + + // These variables has to be passed into update links which are in= the base methods + self.nodes =3D nodes; + self.duration =3D duration; + + // ------- D3 ENTRY -------- + // Enter any new nodes at the parent's previous position. + var nodeEnter =3D node.enter().append("svg:g") + .attr("class", "node") + .on("dblclick", function(){ d3.event.stopPropagation(); }) + .on("click", function(d) { + if (d3.event.altKey) { + self.selectNode(d); // display info if alt is p= ressed + } else { + if(d.children && d.children.length =3D=3D=3D 0){ retur= n;} // there is no need to toggle leaves + self.model.toggle(d); // contract/expand nodes at da= ta level + self.updateAndRender(d); // re-render the tree + } + }); + + nodeEnter.attr("transform", function(d) { return "translate(" + so= urce.y0 + "," + source.x0 + ")"; }); + + nodeEnter.append("svg:circle") + .attr("r", 1e-6) + .style("fill", function(d) { return d._children ? "lightsteelb= lue" : "#fff"; }); + + nodeEnter.append("svg:text") + .attr("class", "nodeLabel") + .attr("x", function(d) { return d.children || d._children ? -1= 0 : 10; }) + .attr("dy", ".35em") + .attr("text-anchor", function(d) { return d.children || d._chi= ldren ? "end" : "start"; }) + .style("fill-opacity", 1e-6); + + // ------- D3 TRANSITION -------- + // Transition nodes to their new position. + var nodeUpdate =3D node.transition() + .duration(duration); + + nodeUpdate.attr("transform", function(d) { + return "translate(" + d.y + "," + d.x + ")"; }); + + nodeUpdate.select("circle") + .attr("r", self.defaults.nodeRadius) + .style("fill", function(d) { return d._children ? "lightsteelb= lue" : "#fff"; }); + + nodeUpdate.select("text") + .style("fill-opacity", 1) + .style("font-size", fontSize) + .text(function(d) { return d.name; }); + + // ------- D3 EXIT -------- + // Transition exiting nodes to the parent's new position. + var nodeExit =3Dnode.exit().transition() + .duration(duration) + .remove(); + + nodeExit.select("circle") + .attr("r", 1e-6); + + nodeExit.select("text") + .style("fill-opacity", 1e-6); + + // Stash the old positions for transition. + nodes.forEach(function(d) { + d.x0 =3D d.x; // we need the x0, y0 for parents with children + d.y0 =3D d.y; + }); + } + +}); + +var PhylovizView =3D Backbone.View.extend({ + + className: 'phyloviz', + + initialize: function(options) { + var self =3D this; + // -- Default values of the vis + self.MIN_SCALE =3D 0.05; //for zooming + self.MAX_SCALE =3D 5; + self.MAX_DISPLACEMENT =3D 500; + self.margins =3D [10, 60, 10, 80]; + + self.width =3D $("#PhyloViz").width(); + self.height =3D $("#PhyloViz").height(); + self.radius =3D self.width; + self.data =3D options.data; + + // -- Events Phyloviz view responses to + $(window).resize(function(){ + self.width =3D $("#PhyloViz").width(); + self.height =3D $("#PhyloViz").height(); + self.render(); + }); + + // -- Create phyloTree model + self.phyloTree =3D new PhyloTree(options.config); + self.phyloTree.root =3D self.data; + + // -- Set up UI functions of main view + self.zoomFunc =3D d3.behavior.zoom().scaleExtent([self.MIN_SCALE, = self.MAX_SCALE]); + self.zoomFunc.translate(self.phyloTree.get("translate")); + self.zoomFunc.scale(self.phyloTree.get("scaleFactor")); + + // -- set up header buttons, search and settings menu + self.navMenu =3D new HeaderButtons(self); + self.settingsMenu =3D new SettingsMenu({phyloTree : self.phyloTree= }); + self.nodeSelectionView =3D new NodeSelectionView({phyloTree : self= .phyloTree}); + self.search =3D new PhyloVizSearch(); + + + setTimeout(function(){ // using settimeout to call the zoomAn= dPan function according to the stored attributes in viz_config + self.zoomAndPan(); + }, 1000); + }, + + render: function(){ + // -- Creating helper function for vis. -- + var self =3D this; + $("#PhyloViz").empty(); + + // -- Layout viz. -- + self.mainSVG =3D d3.select("#PhyloViz").append("svg:svg") + .attr("width", self.width) + .attr("height", self.height) + .attr("pointer-events", "all") + .call(self.zoomFunc.on("zoom", function(){ + self.zoomAndPan(); + })); + + self.boundingRect =3D self.mainSVG.append("svg:rect") + .attr("class", "boundingRect") + .attr("width", self.width) + .attr("height", self.height) + .attr("stroke", "black") + .attr("fill", "white"); + + self.vis =3D self.mainSVG + .append("svg:g") + .attr("class", "vis"); + + self.layoutOptions =3D { + model : self.phyloTree, + width : self.width, + height : self.height, + vis: self.vis, + margins: self.margins + }; + + // -- Creating Title + $("#title").text("Phylogenetic Tree from " + self.phyloTree.get("t= itle") + ":"); + + // -- Create Linear view instance -- + var linearView =3D new PhylovizLinearView(self.layoutOptions) + }, + + zoomAndPan : function(event){ + /** + * Function to zoom and pan the svg element which the entire tree = is contained within + * Uses d3.zoom events, and extend them to allow manual updates an= d keeping states in model + */ + if (typeof event !=3D=3D "undefined") { + var zoomParams =3D event.zoom, + translateParams =3D event.translate; + } + + var self =3D this, + scaleFactor =3D self.zoomFunc.scale(), + translationCoor =3D self.zoomFunc.translate(), + zoomStatement =3D "", + translateStatement =3D ""; + + // Do manual scaling. + switch (zoomParams) { + case "reset": + scaleFactor =3D 1.0; + translationCoor =3D [0,0]; break; + case "+": + scaleFactor *=3D 1.1; break; + case "-": + scaleFactor *=3D 0.9; break; + default: + if (typeof zoomParams =3D=3D=3D "number") { + scaleFactor =3D zoomParams; + } else if (d3.event !=3D=3D null) { + scaleFactor =3D d3.event.scale; + } + } + if (scaleFactor < self.MIN_SCALE || scaleFactor > self.MAX_SCALE) = { return;} + self.zoomFunc.scale(scaleFactor); //update scale Factor + zoomStatement =3D "translate(" + self.margins[3] + "," + self.mar= gins[0] + ")" + + " scale(" + scaleFactor + ")"; + + // Do manual translation. + if( d3.event !=3D=3D null) { + translateStatement =3D "translate(" + d3.event.translate + ")"; + } else { + if(typeof translateParams !=3D=3D "undefined") { + var x =3D translateParams.split(",")[0]; + var y =3D translateParams.split(",")[1]; + if (!isNaN(x) && !isNaN(y)){ + translationCoor =3D [translationCoor[0] + parseFloat(x= ), translationCoor[1] + parseFloat(y)]; + } + } + self.zoomFunc.translate(translationCoor); // update zoomFunc + translateStatement =3D "translate(" + translationCoor + ")"; + } + + self.phyloTree.set("scaleFactor", scaleFactor); + self.phyloTree.set("translate", translationCoor); + self.vis.attr("transform", translateStatement + zoomStatement); //= refers to the view that we are actually zooming + }, + + + reloadViz : function() { + /** + * Primes the Ajax URL to load another Nexus tree + */ + var self =3D this, + treeIndex =3D $("#phylovizNexSelector :selected").val(), + dataset_id =3D self.phyloTree.get("dataset_id"), + url =3D "phyloviz/getJsonData?dataset_id=3D" + dataset_id + "&= treeIndex=3D" + String(treeIndex); + $.getJSON(url, function(packedJson){ + window.initPhyloViz(packedJson.data, packedJson.config); + }); + } +}); + + +var HeaderButtons =3D Backbone.View.extend({ + + initialize : function(phylovizView){ + var self =3D this; + self.phylovizView =3D phylovizView; + + // Clean up code - if the class initialized more than once + $("#panelHeaderRightBtns").empty(); + $("#phyloVizNavBtns").empty(); + $("#phylovizNexSelector").off(); + + self.initNavBtns(); + self.initRightHeaderBtns(); + + // Initial a tree selector in the case of nexus + $("#phylovizNexSelector").off().on("change", function() {self.phy= lovizView.reloadViz();} ); + + }, + + initRightHeaderBtns : function(){ + var self =3D this; + + rightMenu =3D create_icon_buttons_menu([ + { icon_class: 'gear', title: 'PhyloViz Settings', on_click: fu= nction(){ + $("#SettingsMenu").show(); + self.settingsMenu.updateUI(); + } }, + { icon_class: 'disk', title: 'Save visualization', on_click: f= unction() { + var nexSelected =3D $("#phylovizNexSelector option:selecte= d").text(); + if(nexSelected) { + self.phylovizView.phyloTree.set("title", nexSelected); + } + self.phylovizView.phyloTree.save(); + } }, + { icon_class: 'chevron-expand', title: 'Search / Edit Nodes', = on_click: function() { + $("#nodeSelectionView").show(); + } }, + { icon_class: 'information', title: 'Phyloviz Help', on_click:= function() { + window.open('http://wiki.g2.bx.psu.edu/Learn/Visualization= /PhylogeneticTree'); + // https://docs.google.com/document/d/1AXFoJgEpxr21H3LICRs= 3EyMe1B1X_KFPouzIgrCz3zk/edit + } } + ], + { + tooltip_config: { placement: 'bottom' } + }); + $("#panelHeaderRightBtns").append(rightMenu.$el); + }, + + initNavBtns: function() { + var self =3D this, + navMenu =3D create_icon_buttons_menu([ + { icon_class: 'zoom-in', title: 'Zoom in', on_click: funct= ion() { + self.phylovizView.zoomAndPan({ zoom : "+"}); + } }, + { icon_class: 'zoom-out', title: 'Zoom out', on_click: fun= ction() { + self.phylovizView.zoomAndPan({ zoom : "-"}); + } }, + { icon_class: 'arrow-circle', title: 'Reset Zoom/Pan', on_= click: function() { + self.phylovizView.zoomAndPan({ zoom : "reset"}); + } } + ], + { + tooltip_config: { placement: 'bottom' } + }); + $("#phyloVizNavBtns").append(navMenu.$el); + } +}); + + +var SettingsMenu =3D UserMenuBase.extend({ + + className: 'Settings', + + initialize: function(options){ + // settings needs to directly interact with the phyloviz model so = it will get access to it. + var self =3D this; + self.phyloTree =3D options.phyloTree; + self.el =3D $("#SettingsMenu"); + self.inputs =3D { + separation : $("#phyloVizTreeSeparation"), + leafHeight : $("#phyloVizTreeLeafHeight"), + fontSize : $("#phyloVizTreeFontSize") + }; + + //init all buttons of settings + $("#settingsCloseBtn").off().on("click", function() { self.el.hide= (); }); + $("#phylovizResetSettingsBtn").off().on("click", function() { self= .resetToDefaults(); }); + $("#phylovizApplySettingsBtn").off().on("click", function() { self= .apply(); }); + }, + + apply : function(){ + /** + * Applying user values to phylotree model. + */ + var self =3D this; + if (!self.isAcceptableValue(self.inputs["separation"], 50, 2500) || + !self.isAcceptableValue(self.inputs["leafHeight"], 5, 30) || + !self.isAcceptableValue(self.inputs["fontSize"], 5, 20)){ + return; + } + $.each(self.inputs, function(key, $input){ + self.phyloTree.set(key, $input.val()); + }); + }, + updateUI : function(){ + /** + * Called to update the values input to that stored in the model + */ + var self =3D this; + $.each(self.inputs, function(key, $input){ + $input.val(self.phyloTree.get(key)); + }); + }, + resetToDefaults : function(){ + /** + * Resets the value of the phyloTree model to its default + */ + $(".bs-tooltip").remove(); // just in case the tool tip was n= ot removed + var self =3D this; + $.each(self.phyloTree.defaults, function(key, value) { + self.phyloTree.set(key, value); + }); + self.updateUI(); + }, + + render: function(){ + + } + +}); + + +var NodeSelectionView =3D UserMenuBase.extend({ + /** + * View for inspecting node properties and editing them + */ + className: 'Settings', + + initialize : function (options){ + var self =3D this; + self.el =3D $("#nodeSelectionView"); + self.phyloTree =3D options.phyloTree; + + self.UI =3D { + enableEdit : $('#phylovizEditNodesCheck'), + saveChanges : $('#phylovizNodeSaveChanges'), + cancelChanges : $("#phylovizNodeCancelChanges"), + name : $("#phyloVizSelectedNodeName"), + dist : $("#phyloVizSelectedNodeDist"), + annotation : $("#phyloVizSelectedNodeAnnotation") + }; + + self.valuesOfConcern =3D { + name : null, + dist : null, + annotation : null + }; // temporarily stores the values in case user change their mind + + //init UI buttons + $("#nodeSelCloseBtn").off().on("click", function() { self.el.hide(= ); }); + self.UI.saveChanges.off().on("click", function(){ self.updateNodes= (); }); + self.UI.cancelChanges.off().on("click", function(){ self.cancelCha= nges(); }); + + (function ($) { + // extending jquery fxn for enabling and disabling nodes. + $.fn.enable =3D function (isEnabled) { + return $(this).each(function () { + if(isEnabled){ + $(this).removeAttr('disabled'); + } else { + $(this).attr('disabled', 'disabled'); + } + }); + }; + })(jQuery); + + self.UI.enableEdit.off().on("click", function () { + self.toggleUI(); + }); + }, + + toggleUI : function(){ + /** + * For turning on and off the child elements + */ + var self =3D this, + checked =3D self.UI.enableEdit.is(':checked'); + + !checked ? self.cancelChanges() : ""; + + $.each(self.valuesOfConcern, function(key, value) { + self.UI[key].enable(checked); + }); + if(checked){ + self.UI.saveChanges.show(); + self.UI.cancelChanges.show(); + } else { + self.UI.saveChanges.hide(); + self.UI.cancelChanges.hide(); + } + + }, + + cancelChanges : function() { + /** + * Reverting to previous values in case user change their minds + */ + var self =3D this, + node =3D self.phyloTree.get("selectedNode"); + if (node){ + $.each(self.valuesOfConcern, function(key, value) { + self.UI[key].val(node[key]); + }); + } + }, + + updateNodes : function (){ + /** + * Changing the data in the underlying tree with user-specified va= lues + */ + var self =3D this, + node =3D self.phyloTree.get("selectedNode"); + if (node){ + if (!self.isAcceptableValue(self.UI.dist, 0, 1) || + self.hasIllegalJsonCharacters(self.UI.name) || + self.hasIllegalJsonCharacters(self.UI.annotation) ) { + return; + } + $.each(self.valuesOfConcern, function(key, value) { + (node[key]) =3D self.UI[key].val(); + }); + self.phyloTree.set("nodeAttrChangedTime", new Date()); + } else { + alert("No node selected"); + } + } + + +}); + + + +var PhyloVizSearch =3D UserMenuBase.extend({ + /** + * Initializes the search panel on phyloviz and handles its user inter= action + * It allows user to search the entire free based on some qualifer, li= ke dist <=3D val. + */ + initialize : function () { + var self =3D this; + + $("#phyloVizSearchBtn").on("click", function(){ + var searchTerm =3D $("#phyloVizSearchTerm"), + searchConditionVal =3D $("#phyloVizSearchCondition").val()= .split("-"), + attr =3D searchConditionVal[0], + condition =3D searchConditionVal[1]; + self.hasIllegalJsonCharacters(searchTerm); + + if (attr =3D=3D=3D "dist"){ + self.isAcceptableValue(searchTerm, 0, 1); + } + self.searchTree(attr, condition, searchTerm.val()); + }); + }, + + searchTree : function (attr, condition, val){ + /** + * Searches the entire tree and will highlight the nodes that matc= h the condition in green + */ + d3.selectAll("g.node") + .classed("searchHighlight", function(d){ + var attrVal =3D d[attr]; + if (typeof attrVal !=3D=3D "undefined" && attrVal !=3D=3D = null){ + if (attr =3D=3D=3D "dist"){ + switch (condition) { + case "greaterEqual": + return attrVal >=3D +val; + case "lesserEqual": + return attrVal <=3D +val; + default: + return; + } + + } else if (attr =3D=3D=3D "name" || attr =3D=3D=3D "an= notation") { + return attrVal.toLowerCase().indexOf(val.toLowerCa= se()) !=3D=3D -1; + } + } + }); + } +}); \ No newline at end of file diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 templates/root/history.mako --- a/templates/root/history.mako +++ b/templates/root/history.mako @@ -272,6 +272,17 @@ } =20 init_trackster_links(); + + function init_phyloviz_links() { + // PhyloViz links + // Add to trackster browser functionality + $(".phyloviz-add").live("click", function() { + var dataset =3D this, + dataset_jquery =3D $(this); + window.parent.location =3D dataset_jquery.attr("new-url"); + }); + } + init_phyloviz_links(); =20 // History rename functionality. async_save_text("history-name-container", "history-name", "${h.url_for= ( controller=3D"/history", action=3D"rename_async", id=3Dtrans.security.enc= ode_id(history.id) )}", "new_name", 18); diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 templates/root/history_common.mako --- a/templates/root/history_common.mako +++ b/templates/root/history_common.mako @@ -29,6 +29,9 @@ ## Render the dataset `data` as history item, using `hid` as the displayed= id <%def name=3D"render_dataset( data, hid, show_deleted_on_refresh =3D False= , for_editing =3D True, display_structured =3D False )"><% + + from galaxy.datatypes.xml import Phyloxml + from galaxy.datatypes.data import Newick, Nexus dataset_id =3D trans.security.encode_id( data.id ) =20 if data.state in ['no state','',None]: @@ -230,6 +233,14 @@ action-url=3D"${h.url_for( controller=3D't= racks', action=3D'browser', dataset_id=3Ddataset_id)}" new-url=3D"${h.url_for( controller=3D'trac= ks', action=3D'index', dataset_id=3Ddataset_id, default_dbkey=3Ddata.dbkey)= }" title=3D"View in Trackster"></a> %endif + <% + isPhylogenyData =3D isinstance(data.datatype, = (Phyloxml, Nexus, Newick)) + %> + %if isPhylogenyData: + <a href=3D"javascript:void(0)" class=3D"i= con-button chart_curve phyloviz-add" + action-url=3D"${h.url_for( controller= =3D'phyloviz', action=3D'-', dataset_id=3Ddataset_id)}" + new-url=3D"${h.url_for( controller=3D'p= hyloviz', action=3D'index', dataset_id=3Ddataset_id)}" title=3D"View in Phy= loviz"></a> + %endif %if trans.user: %if not display_structured: <div style=3D"float: right"> diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 templates/visualization/phyloviz.mako --- /dev/null +++ b/templates/visualization/phyloviz.mako @@ -0,0 +1,320 @@ +<%inherit file=3D"/webapps/galaxy/base_panels.mako"/> +## +<%def name=3D"init()"> + <% + self.has_left_panel=3DFalse + self.has_right_panel=3DFalse + self.active_view=3D"visualization" + self.message_box_visible=3DFalse + %> +</%def> + +<%def name=3D"stylesheets()"> + ${parent.stylesheets()} + <style> + + .node circle { + cursor: pointer; + fill: #fff; + stroke: steelblue; + stroke-width: 1.5px; + } + + .node.searchHighlight circle { + stroke-width: 3px; + stroke: #7adc26; + } + + .node.selectedHighlight circle { + stroke-width: 3px; + stroke: #dc143c; + } + + path.link { + fill: none; + stroke: #B5BBFF; + stroke-width: 4.0px; + } + + + div #phyloVizNavContainer{ + text-align: center; + width: 100%; + height: 0px; + } + + div #phyloVizNav{ + font-weight: bold; + display: inline-block; + background: transparent; + top: -2em; + position: relative; + } + + div .navControl{ + float: left; + } + + div#FloatingMenu { + left: 0; + top: 15%; + width:20%; + z-index:100; + padding: 5px; + + } + + div#SettingsMenu { + width: 25%; + top: 350px; + + } + + div#nodeSelectionView { + width: 25%; + top:70px; + } + + .Panel { + right: 0%; + z-index: 101; + position: fixed; + + ## Borrowed from galaxy modal_dialogues + background-color: white; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, 0.3); + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; + } + + span.PhylovizCloseBtn{ + cursor: pointer; + float : right; + } + + #PhyloViz{ + width: 100%; + height: 95%; + } + + h2.PhyloVizMenuTitle{ + color: white; + } + + ## Settings Menu + .SettingMenuRows{ + margin: 2px 0 2px 0; + } + + + ## Helper Styles + .PhyloVizFloatLeft{ + float : left; + } + .icon-button.zoom-in,.icon-button.zoom-out{display:inline-block;he= ight:16px;width:16px;margin-bottom:-3px;cursor:pointer;} + .icon-button.zoom-out{background:transparent url(../images/fugue/m= agnifier-zoom-out.png) center center no-repeat;} + .icon-button.zoom-in{margin-left:10px;background:transparent url(.= ./images/fugue/magnifier-zoom.png) center center no-repeat;} + + </style> +</%def> + + +<%def name=3D"javascripts()"> + ${parent.javascripts()} + ${h.js( "galaxy.panels", "libs/d3", "mvc/data", "viz/visualization", "= viz/phyloviz")} +</%def> + + + +<%def name=3D"center_panel()"> + + <div class=3D"unified-panel-header" unselectable=3D"on"> + <div class=3D"unified-panel-header-inner"> + <div style=3D"float:left;" id=3D"title"></div> + <div style=3D"float:right;" id=3D"panelHeaderRightBtns"></div> + </div> + <div style=3D"clear: both"></div> + </div> + + + <div id=3D"phyloVizNavContainer"> + <div id=3D"phyloVizNav"> + %if config["ext"] =3D=3D "nex" and not config["saved_visualiza= tion"]: + <div id =3D "phylovizNexInfo" class=3D"navControl"> + <p>Select a tree to view: + <select id=3D"phylovizNexSelector"> + % for tree, index in config["trees"]: + <option value=3D"${index}">${tree}</option> + % endfor + </select> + </p> + </div> + %endif + <div id=3D"phyloVizNavBtns" class=3D"navControl"> + </div> + <div class=3D"navControl"> + <p> | Alt+click to select nodes</p> + </div> + + + </div> + + </div> + + ## Node Selection Menu + <div id=3D"nodeSelectionView" class=3D"Panel"> + <div class=3D"modal-header"> + <h3 class=3D"PhyloVizMenuTitle">Search / Edit Nodes : + <span class=3D"PhylovizCloseBtn" id=3D"nodeSelCloseBtn"> X= </span> + </h3> + </div> + + <div class=3D"modal-body"> + + <div class=3D"SettingMenuRows"> + Search for nodes with: + <select id=3D"phyloVizSearchCondition" style=3D"width: 55%= "> + <option value=3D"name-containing">Name (containing)</o= ption> + <option value=3D"annotation-containing">Annotation (co= ntaining)</option> + <option value=3D"dist-greaterEqual">Distance (>=3D)</o= ption> + <option value=3D"dist-lesserEqual">Distance (<=3D)</op= tion> + </select> + <input type=3D"text" id=3D"phyloVizSearchTerm" value=3D"N= one" size=3D"15" displayLabel=3D"Distance"> + + <div class=3D"SettingMenuRows" style=3D"text-align: center= ;"> + <button id=3D"phyloVizSearchBtn" > Search! </button> + </div> + </div> + + <br/> + + <div class=3D"SettingMenuRows"> + Name: <input type=3D"text" id=3D"phyloVizSelectedNodeName"= value=3D"None" size=3D"15" disabled=3D"disabled" > + </div> + <div class=3D"SettingMenuRows"> + Dist: <input type=3D"text" id=3D"phyloVizSelectedNodeDist"= value=3D"None" size=3D"15" disabled=3D"disabled" displayLabel=3D"Distance"> + </div> + <div class=3D"SettingMenuRows"> + Annotation: + <textarea id=3D"phyloVizSelectedNodeAnnotation" disabled= =3D"disabled" ></textarea> + </div> + <div class=3D"SettingMenuRows"> + Edit: <input type=3D"checkbox" id=3D"phylovizEditNodesChec= k" value=3D"You can put custom annotations here and it will be saved"> + <button id=3D"phylovizNodeSaveChanges" style=3D"display: n= one;"> Save edits</button> + <button id=3D"phylovizNodeCancelChanges" style=3D"display:= none;"> Cancel</button> + </div> + </div> + </div> + + ## Settings Menus + <div id=3D"SettingsMenu" class=3D"Panel"> + <div class=3D"modal-header"> + <h3 class=3D"PhyloVizMenuTitle">Phyloviz Settings: + <span class=3D"PhylovizCloseBtn" id=3D"settingsCloseBtn"> = X </span> + </h3> + </div> + <div class=3D"modal-body"> + <div class=3D"SettingMenuRows"> + Phylogenetic Spacing (px per unit): <input id=3D"phyloVizT= reeSeparation" type=3D"text" value=3D"250" size=3D"10" displayLabel=3D"Phyl= ogenetic Separation"> (50-2500) + </div> + <div class=3D"SettingMenuRows"> + Vertical Spacing (px): <input type=3D"text" id=3D"phyloViz= TreeLeafHeight" value=3D"18" size=3D"10" displayLabel=3D"Vertical Spacing">= (5-30) + </div> + <div class=3D"SettingMenuRows"> + Font Size (px): <input type=3D"text" id=3D"phyloVizTreeFon= tSize" value=3D"12" size=3D"4" displayLabel=3D"Font Size"> (5-20) + </div> + + </div> + <div class=3D"modal-footer"> + <button id=3D"phylovizResetSettingsBtn" class=3D"PhyloVizFloat= Left" > Reset </button> + <button id=3D"phylovizApplySettingsBtn" class=3D"PhyloVizFloat= Right" > Apply </button> + </div> + </div> + + + + + + + <div class=3D"Panel" id=3D"FloatingMenu" style=3D"display: None;"> + + <h2>PhyloViz (<a onclick=3D"displayHelp()" href=3D"javascript:void= (0);">?</a>)</h2> + <div style=3D"display: none;"> + <h2>Summary of Interactions and Functions:</h2> + <div class=3D"hint">1. Expansion of Nodes: click or option-cli= ck to expand or collapse</div> + <div class=3D"hint">2. Zooming and translation: mousewheel, bu= ttons, click and drag, double click. Reset</div> + <div class=3D"hint">3. Tooltip: Displays "Name and Size" on mo= useOver on nodes</div> + <div class=3D"hint">4. Minimap: Currently displays an exact bu= t scaled down replicate of the tree, orange bounding box is correct for lin= ear only<br/> + Can be switched on or off</div> + <div class=3D"hint">5. Changing Layouts: Able to change betwee= n circular and linear layouts.</div> + + </div> + + <h5>Scaling & Rotation:</h5> + <button id=3D"phylovizZoomInBtn" class=3D"" > + </button> + <button id=3D"phylovizZoomOutBtn" class=3D"" > - </button> + + + <h5>Translation:</h5> + <button id=3D"phylovizTranslateUpBtn" > Up </button> + <button id=3D"phylovizTranslateDownBtn" > Down </button> + <br/> + <button id=3D"phylovizTranslateLeftBtn" > Left </button> + <button id=3D"phylovizTranslateRightBtn" > Right </button> + + + + <h5>Others:</h5> + <button id=3D"phylovizResetBtn" > Reset Zoom/Translate </button> + <button id=3D"phylovizSaveBtn" > Save vizualization </button> + <button id=3D"phylovizOpenSettingsBtn" > Settings </button> + </div> + + <div id=3D"PhyloViz" > + </div> + + <script type=3D"text/javascript"> + + function initPhyloViz(data, config) { + var phyloviz; + + // -- Initialization code |--> + phyloviz =3D new PhylovizView({ + data: data, + layout : "Linear", + config : config + }); + + // -- Render viz. -- + phyloviz.render(); + + } + + $(function firstVizLoad(){ // calls when viz is loaded for t= he first time + var config =3D JSON.parse( '${ h.to_json_string( config )}'); + var data =3D JSON.parse('${h.to_json_string(data)}'); + initPhyloViz(data, config); + }); + + </script> + +</%def> + + diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 test-data/visualization/phyloviz/1_nexus.nex --- /dev/null +++ b/test-data/visualization/phyloviz/1_nexus.nex @@ -0,0 +1,87 @@ +#NEXUS + +[!This data set was downloaded from TreeBASE, a relational database of phy= logenetic knowledge. TreeBASE has been supported by the NSF, Harvard Univer= sity, Yale University, SDSC and UC Davis. Please do not remove this acknowl= edgment from the Nexus file. + + +Generated on June 12, 2012; 23:00 GMT + +TreeBASE (cc) 1994-2008 + +Study reference: +Olariaga I., Grebenc T., Salcedo I., & Mart=C3=ADn M.P. 2012. Two new spec= ies of Hydnum +with ovoid basidiospores: H. ovoideisporum and H. vesterholtii. Mycologia,= . + +TreeBASE Study URI: http://purl.org/phylo/treebase/phylows/study/TB2:S128= 31] + +BEGIN TREES; + TITLE Hydnum_ITS_result; + LINK TAXA =3D Taxa1; + TRANSLATE + 1 Hydnum_aff_ellipsosporum_RUFHYD1_AJ535304, + 2 Hydnum_albidum_ALB_AY817135, + 3 Hydnum_albidum_ALBHYD1_AJ534974, + 4 Hydnum_albomagnum_ALM_DQ218305, + 5 Hydnum_ellipsosporum_ELL_AY817138, + 6 Hydnum_ellipsosporum_RUFHYD8_AJ547882, + 7 Hydnum_ovoidisporum_12317BIOFungi, + 8 Hydnum_ovoidisporum_12683BIOFungi, + 9 Hydnum_ovoidisporum_12902BIOFungi, + 10 Hydnum_ovoidisporum_14130BIOFungi, + 11 Hydnum_repandum_RE1_REP1_AJ889978, + 12 Hydnum_repandum_RE1_REP2_AJ889949, + 13 Hydnum_repandum_RE1_REP3_AY817136, + 14 Hydnum_repandum_RE1_REP6_UDB000025, + 15 Hydnum_repandum_RE1_REP7_UDB000096, + 16 Hydnum_repandum_RE1_REP8_UDB001479, + 17 Hydnum_repandum_RE1_REPHYD10_AJ547888, + 18 Hydnum_repandum_RE1_REPHYD11_AJ547886, + 19 Hydnum_repandum_RE1_REPHYD1_AJ547871, + 20 Hydnum_repandum_RE1_REPHYD3_AJ547874, + 21 Hydnum_repandum_RE1_REPHYD4_AJ547876, + 22 Hydnum_repandum_RE1_REPHYD5_AJ547875, + 23 Hydnum_repandum_RE1_REPHYD6_AJ547877, + 24 Hydnum_repandum_RE1_REPHYD7_AJ547878, + 25 Hydnum_repandum_RE1_REPHYD8_AJ547881, + 26 Hydnum_repandum_RE1_REPHYD9_AJ547883, + 27 Hydnum_repandum_RE1_RUFHYD10_AJ547866, + 28 Hydnum_repandum_RE1_RUFHYD11_AJ547889, + 29 Hydnum_repandum_RE1_RUFHYD9_AJ535305, + 30 Hydnum_rufescens_RU1_RUFHYD5_AJ547869, + 31 Hydnum_rufescens_RU1_RUFHYD6_AJ547884, + 32 Hydnum_rufescens_RU1_RUFHYD7_AJ547870, + 33 Hydnum_rufescens_RU2_REP5_DQ367902, + 34 Hydnum_rufescens_RU2_RUFHYD2_AJ535301, + 35 Hydnum_rufescens_RU3_12901BIOFungi, + 36 Hydnum_rufescens_RU3_REP4_DQ218306, + 37 Hydnum_rufescens_RU3_RUFHYD3_AJ535303, + 38 Hydnum_rufescens_RU3_RUFHYD4_AJ535302, + 39 Hydnum_rufescens_RU4_RUFHYD12_AJ839969, + 40 Hydnum_rufescens_RU4_RUFHYD16_AJ547868, + 41 Hydnum_rufescens_RU4_RUFHYD17_AJ547885, + 42 Hydnum_rufescens_RU4_UMB1_DQ367903, + 43 Hydnum_rufescens_RU5_12760BIOFungi, + 44 Hydnum_rufescens_RU5_ALBHYD2_AJ534975, + 45 Hydnum_rufescens_RU5_RUF2_DQ658890, + 46 Hydnum_rufescens_RU5_RUF4_UDB001465, + 47 Hydnum_rufescens_RU5_RUF5_UDB002423, + 48 Hydnum_rufescens_RU5_RUFHYD14_AJ547872, + 49 Hydnum_rufescens_RU6_RUF1_AY817137, + 50 Hydnum_rufescens_RU6_RUFHYD15_AJ547867, + 51 Hydnum_rufescens_wrong_taxonomy_RUF3_AM087246, + 52 Hydnum_umbilicatum_UMBHYD1_AJ534972, + 53 Hydnum_umbilicatum_UMBHYD2_AJ534973, + 54 Hydnum_vesterholtii_10429BIOFungi, + 55 Hydnum_vesterholtii_10452BIOFungi, + 56 Hydnum_vesterholtii_12330BIOFungi, + 57 Hydnum_vesterholtii_12904BIOFungi, + 58 Hydnum_vesterholtii_REPHYD12A_AJ547879, + 59 Hydnum_vesterholtii_REPHYD12C_AJ783968, + 60 Hydnum_vesterholtii_REPHYD13_AJ547887, + 61 Sistotrema_muscicola_AJ606040, + 62 Sistotrema_alboluteum_AJ606042; + TREE Fig._2 =3D [&R] ((62:100.0,(51:100.0,61:100.0):93.269997):49.66= ,((4:100.0,(2:100.0,3:100.0):100.0):60.639999,(((56:100.0,58:100.0,59:100.0= ):84.639999,(54:100.0,55:100.0,57:100.0,60:100.0):98.330002):92.5,(((30:100= .0,31:100.0,32:100.0):100.0,(11:100.0,12:100.0,13:100.0,14:100.0,15:100.0,1= 6:100.0,17:100.0,18:100.0,19:100.0,20:100.0,21:100.0,22:100.0,23:100.0,24:1= 00.0,25:100.0,26:100.0):99.93):68.690002,(((33:100.0,34:100.0):49.8050005,(= 35:100.0,36:100.0,37:100.0,38:100.0):99.989998):49.8050005,((7:100.0,8:100.= 0,9:100.0,10:100.0):100.0,(42:100.0,(39:100.0,40:100.0,41:100.0):98.449997)= :86.790001,((52:100.0,53:100.0):99.93,(1:100.0,(5:97.47999949999999,6:100.0= ):97.47999949999999):100.0):53.310001,(27:100.0,(28:100.0,29:100.0,49:100.0= ,50:100.0):47.404999):47.404999,(43:100.0,44:100.0,45:100.0,46:100.0,47:100= .0,48:100.0):99.459999):29.245001):29.245001):51.580002):61.540001):49.66); + TREE PAUP_1 =3D [&R] ((62:100.0,(51:100.0,61:100.0):93.269997):49.66= ,((4:100.0,(3:100.0,2:100.0):100.0):60.639999,(((58:100.0,59:100.0,56:100.0= ):84.639999,(60:100.0,54:100.0,55:100.0,57:100.0):98.330002):92.5,(((30:100= .0,31:100.0,32:100.0):100.0,(19:100.0,20:100.0,21:100.0,22:100.0,23:100.0,2= 4:100.0,25:100.0,26:100.0,17:100.0,18:100.0,11:100.0,12:100.0,13:100.0,14:1= 00.0,15:100.0,16:100.0):99.93):68.690002,((34:100.0,33:100.0):99.610001,(37= :100.0,38:100.0,35:100.0,36:100.0):99.989998,(42:100.0,(39:100.0,41:100.0,4= 0:100.0):98.449997):86.790001,(8:100.0,7:100.0,9:100.0,10:100.0):100.0,((52= :100.0,53:100.0):99.93,(1:100.0,(5:100.0,6:100.0):94.959999):100.0):53.3100= 01,(29:100.0,27:100.0,28:100.0,50:100.0,49:100.0):94.809998,(44:100.0,43:10= 0.0,48:100.0,45:100.0,46:100.0,47:100.0):99.459999):58.490002):51.580002):6= 1.540001):49.66); + + + +END; diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 test-data/visualization/phyloviz/2_nexus.nex --- /dev/null +++ b/test-data/visualization/phyloviz/2_nexus.nex @@ -0,0 +1,96 @@ +#NEXUS + +[!This data set was downloaded from TreeBASE, a relational database of phy= logenetic knowledge. TreeBASE has been supported by the NSF, Harvard Univer= sity, Yale University, SDSC and UC Davis. Please do not remove this acknowl= edgment from the Nexus file. + + +Generated on August 18, 2012; 12:14 GMT + +TreeBASE (cc) 1994-2008 + +Study reference: +Naish D., Dyke G., Cau A., & Escuilli=C3=A9 F. 2012. A gigantic bird from = the Upper Cretaceous +of Central Asia. Biology Letters, 8(1): 97-100. + +TreeBASE Study URI: http://purl.org/phylo/treebase/phylows/study/TB2:S130= 08] + +BEGIN TREES; + TITLE Imported_trees; + LINK TAXA =3D Taxa1; + TRANSLATE + 1 Herrerasaurus, + 2 Tawa, + 3 Allosaurus, + 4 Alvarezsaurus, + 5 Anchiornis, + 6 Archaeopteryx, + 7 Archaeorhynchus, + 8 Avimimus, + 9 Baryonyx, + 10 Beipiaosaurus, + 11 Caenagnathus, + 12 Caudipteryx, + 13 Ceratosaurus, + 14 Chirostenotes, + 15 Citipati, + 16 Compsognathus, + 17 Confuciusornis, + 18 Dilong, + 19 Dilophosaurus, + 20 Epidendrosaurus, + 21 Epidexipteryx, + 22 Erlicosaurus, + 23 Eustreptospondylus, + 24 Gallimimus, + 25 Garudimimus, + 26 Gobipteryx, + 27 Guanlong, + 28 Haplocheirus, + 29 Harpymimus, + 30 Hebeiornis, + 31 Hongshanornis, + 32 Huoshanornis, + 33 Iberomesornis, + 34 Ichthyornis, + 35 Incisivosaurus, + 36 Jeholornis, + 37 Limusaurus, + 38 Longicrusavis, + 39 Longipteryx, + 40 Longirostravis, + 41 Majungasaurus, + 42 Masiakasaurus, + 43 Monolophosaurus, + 44 Mononykus, + 45 Neornithes, + 46 Ornitholestes, + 47 Ornithomimus, + 48 Patagonykus, + 49 Patagopteryx, + 50 Pelecanimimus, + 51 Pengornis, + 52 Protarchaeopteryx, + 53 Protopteryx, + 54 Rinchenia, + 55 Sapeornis, + 56 Segnosaurus, + 57 Shenzhousaurus, + 58 Shuvuuia, + 59 Sinornithosaurus, + 60 Sinosauropteryx, + 61 Sinovenator, + 62 Sinraptor, + 63 Syntarsus_kayentakatae, + 64 Troodon, + 65 Tyrannosaurus, + 66 Velociraptor, + 67 Yanornis, + 68 Yixianornis, + 69 Zhongjianornis, + 70 Zhongornis, + 71 Zuolong, + 72 Samrukia; + TREE Figure_1A =3D [&R] (1,(2,(((((43,(3,62)),(71,((46,((((28,(4,(48= ,(44,58)))),((((5,(61,(64,(59,66)))),(6,((36,(55,(69,(((7,34,45,49,72,(31,3= 8),(67,68)),(33,((32,((26,30),(39,40))),(51,53)))),(17,70))))),(20,21)))),(= (11,(12,(8,(14,(15,54))))),(35,52))),(10,(22,56)))),(50,(57,(29,(25,(24,47)= ))))),(16,60))),(27,(18,65))))),(9,23)),(13,(41,(37,42)))),(19,63)))); + + + +END; diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 test-data/visualization/phyloviz/3_phyloxml.xml --- /dev/null +++ b/test-data/visualization/phyloviz/3_phyloxml.xml @@ -0,0 +1,257 @@ +<?xml version=3D"1.0" encoding=3D"UTF-8"?> +<phyloxml xmlns:xsi=3D"http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation=3D"http://www.phyloxml.org http://www.phyloxml.org/1= .10/phyloxml.xsd" + xmlns=3D"http://www.phyloxml.org"> + <phylogeny rooted=3D"true"> + <clade> + <clade> + <branch_length>0.18105</branch_length> + <confidence type=3D"unknown">89.0</confidence> + <clade> + <branch_length>0.07466</branch_length> + <confidence type=3D"unknown">32.0</confidence> + <clade> + <branch_length>0.26168</branch_length> + <confidence type=3D"unknown">100.0</confidence> + <clade> + <branch_length>0.22058</branch_length> + <confidence type=3D"unknown">89.0</confidence> + <clade> + <branch_length>0.28901</branch_length> + <confidence type=3D"unknown">100.0</confidence> + <clade> + <branch_length>0.06584</branch_length> + <confidence type=3D"unknown">100.0</confidence> + <clade> + <branch_length>0.02309</branch_length> + <confidence type=3D"unknown">43.0</confidenc= e> + <clade> + <branch_length>0.0746</branch_length> + <confidence type=3D"unknown">100.0</confi= dence> + <clade> + <branch_length>0.02365</branch_length> + <confidence type=3D"unknown">88.0</con= fidence> + <clade> + <name>22_MOUSE</name> + <branch_length>0.05998</branch_leng= th> + <taxonomy> + <code>MOUSE</code> + </taxonomy> + </clade> + <clade> + <name>Apaf-1_HUMAN</name> + <branch_length>0.01825</branch_leng= th> + <taxonomy> + <code>HUMAN</code> + </taxonomy> + </clade> + </clade> + <clade> + <name>12_CANFA</name> + <branch_length>0.04683</branch_length> + <taxonomy> + <code>CANFA</code> + </taxonomy> + </clade> + </clade> + <clade> + <name>11_CHICK</name> + <branch_length>0.15226</branch_length> + <taxonomy> + <code>CHICK</code> + </taxonomy> + </clade> + </clade> + <clade> + <name>16_XENLA</name> + <branch_length>0.4409</branch_length> + <taxonomy> + <code>XENLA</code> + </taxonomy> + </clade> + </clade> + <clade> + <branch_length>0.17031</branch_length> + <confidence type=3D"unknown">100.0</confidence> + <clade> + <branch_length>0.10929</branch_length> + <confidence type=3D"unknown">100.0</confiden= ce> + <clade> + <name>14_FUGRU</name> + <branch_length>0.02255</branch_length> + <taxonomy> + <code>FUGRU</code> + </taxonomy> + </clade> + <clade> + <name>15_TETNG</name> + <branch_length>0.09478</branch_length> + <taxonomy> + <code>TETNG</code> + </taxonomy> + </clade> + </clade> + <clade> + <name>17_BRARE</name> + <branch_length>0.1811</branch_length> + <taxonomy> + <code>BRARE</code> + </taxonomy> + </clade> + </clade> + </clade> + <clade> + <branch_length>0.01594</branch_length> + <confidence type=3D"unknown">53.0</confidence> + <clade> + <branch_length>0.10709</branch_length> + <confidence type=3D"unknown">68.0</confidence> + <clade> + <name>1_BRAFL</name> + <branch_length>0.26131</branch_length> + <taxonomy> + <code>BRAFL</code> + </taxonomy> + </clade> + <clade> + <name>18_NEMVE</name> + <branch_length>0.38014</branch_length> + <taxonomy> + <code>NEMVE</code> + </taxonomy> + </clade> + </clade> + <clade> + <name>23_STRPU</name> + <branch_length>0.48179</branch_length> + <taxonomy> + <code>STRPU</code> + </taxonomy> + </clade> + </clade> + </clade> + <clade> + <branch_length>0.34475</branch_length> + <confidence type=3D"unknown">100.0</confidence> + <clade> + <name>26_STRPU</name> + <branch_length>0.36374</branch_length> + <taxonomy> + <code>STRPU</code> + </taxonomy> + <sequence> + <domain_architecture length=3D"1319"> + <domain from=3D"18" to=3D"98" confidence=3D"= 3.4E-5">Death</domain> + <domain from=3D"189" to=3D"481" confidence= =3D"1.8E-10">NB-ARC</domain> + <domain from=3D"630" to=3D"668" confidence= =3D"8.2E-5">WD40</domain> + </domain_architecture> + </sequence> + </clade> + <clade> + <name>25_STRPU</name> + <branch_length>0.33137</branch_length> + <taxonomy> + <code>STRPU</code> + </taxonomy> + <sequence> + <domain_architecture length=3D"1947"> + <domain from=3D"143" to=3D"227" confidence= =3D"7.4E-5">Death</domain> + <domain from=3D"227" to=3D"550" confidence= =3D"2.0E-13">NB-ARC</domain> + <domain from=3D"697" to=3D"736" confidence= =3D"7.9E-4">WD40</domain> + <domain from=3D"745" to=3D"785" confidence= =3D"1.5">WD40</domain> + <domain from=3D"1741" to=3D"1836" confidence= =3D"2.0">Adeno_VII</domain> + </domain_architecture> + </sequence> + </clade> + </clade> + </clade> + <clade> + <branch_length>1.31498</branch_length> + <confidence type=3D"unknown">100.0</confidence> + <clade> + <name>CED4_CAEEL</name> + <branch_length>0.13241</branch_length> + <taxonomy> + <code>CAEEL</code> + </taxonomy> + <sequence> + <domain_architecture length=3D"714"> + <domain from=3D"7" to=3D"90" confidence=3D"9.2E= -14">CARD</domain> + <domain from=3D"116" to=3D"442" confidence=3D"5= .8E-151">NB-ARC</domain> + </domain_architecture> + </sequence> + </clade> + <clade> + <name>31_CAEBR</name> + <branch_length>0.04777</branch_length> + <taxonomy> + <code>CAEBR</code> + </taxonomy> + <sequence> + <domain_architecture length=3D"554"> + <domain from=3D"1" to=3D"75" confidence=3D"0.00= 46">CARD</domain> + <domain from=3D"101" to=3D"427" confidence=3D"2= .1E-123">NB-ARC</domain> + </domain_architecture> + </sequence> + </clade> + </clade> + </clade> + <clade> + <branch_length>0.13172</branch_length> + <confidence type=3D"unknown">45.0</confidence> + <clade> + <branch_length>0.24915</branch_length> + <confidence type=3D"unknown">95.0</confidence> + <clade> + <branch_length>0.76898</branch_length> + <confidence type=3D"unknown">100.0</confidence> + <clade> + <name>28_DROPS</name> + <branch_length>0.1732</branch_length> + <taxonomy> + <code>DROPS</code> + </taxonomy> + <sequence> + <domain_architecture length=3D"535"> + <domain from=3D"112" to=3D"399" confidence= =3D"1.4E-5">NB-ARC</domain> + </domain_architecture> + </sequence> + </clade> + <clade> + <name>Dark_DROME</name> + <branch_length>0.18863</branch_length> + <taxonomy> + <code>DROME</code> + </taxonomy> + <sequence> + <domain_architecture length=3D"1421"> + <domain from=3D"108" to=3D"397" confidence= =3D"2.1E-5">NB-ARC</domain> + </domain_architecture> + </sequence> + </clade> + </clade> + <clade> + <name>29_AEDAE</name> + <branch_length>0.86398</branch_length> + <taxonomy> + <code>AEDAE</code> + </taxonomy> + <sequence> + <domain_architecture length=3D"423"> + <domain from=3D"109" to=3D"421" confidence=3D"9= .3E-6">NB-ARC</domain> + </domain_architecture> + </sequence> + </clade> + </clade> + <clade> + <name>30_TRICA</name> + <branch_length>0.97698</branch_length> + <taxonomy> + <code>TRICA</code> + </taxonomy> + </clade> + </clade> + </clade> + </clade> + </phylogeny> +</phyloxml> diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 test-data/visualization/phyloviz/4_newick.nhx --- /dev/null +++ b/test-data/visualization/phyloviz/4_newick.nhx @@ -0,0 +1,33 @@ +(((BGIOSIBCA028421_ORYSA:0.423485[&&NHX:S=3DORYSA:O=3DBGIOSIBCA028421.1:G= =3DBGIOSIBCA028421], +At5g41150_ARATH:0.273135[&&NHX:S=3DARATH:O=3DAt5g41150.1:G=3DAt5g41150] +):0.690991[&&NHX:S=3DMagnoliophyta:D=3DN:B=3D100], +(rad16_SCHPO:0.718598[&&NHX:S=3DSCHPO:O=3DSPCC970.01:G=3DSPCC970.01], +RAD1_YEAST:1.05456[&&NHX:S=3DYEAST:O=3DYPL022W.1:G=3DYPL022W] +):0.344838[&&NHX:S=3DAscomycota:D=3DN:B=3D100] +):0.103849[&&NHX:S=3DEukaryota:D=3DN:B=3D61], +((((((((ERCC4_HUMAN:0.067531[&&NHX:S=3DHUMAN:O=3DENST00000311895.3:G=3DENS= G00000175595], +Ercc4_MOUSE:0.17422[&&NHX:S=3DMOUSE:O=3DENSMUST00000023206.5:G=3DENSMUSG00= 000022545] +):0.065513[&&NHX:S=3DEuarchontoglires:D=3DN:B=3D100], +ENSMODT00000006086_MONDO:0.104633[&&NHX:S=3DMONDO:O=3DENSMODT00000006086.2= :G=3DENSMODG00000004840] +):0.083764[&&NHX:S=3DTheria:D=3DN:B=3D100], +Q5ZJP8_CHICK:0.153132[&&NHX:S=3DCHICK:O=3DENSGALT00000004716.2:G=3DENSGALG= 00000002981] +):0.057998[&&NHX:S=3DAmniota:D=3DN:B=3D100], +ENSXETT00000024054_XENTR:0.288632[&&NHX:S=3DXENTR:O=3DENSXETT00000024054.2= :G=3DENSXETG00000010991] +):0.075713[&&NHX:S=3DTetrapoda:D=3DN:B=3D100], +(zgc-63468_BRARE:0.2218[&&NHX:S=3DBRARE:O=3DENSDART00000015780.4:G=3DENSDA= RG00000014161], +NEWSINFRUT00000137921_FUGRU:0.220441[&&NHX:S=3DFUGRU:O=3DNEWSINFRUT0000013= 7921.3:G=3DNEWSINFRUG00000130312] +):0.170605[&&NHX:S=3DClupeocephala:D=3DN:B=3D100] +):0.238713[&&NHX:S=3DEuteleostomi:D=3DN:B=3D100], +ENSCINT00000011737_CIOIN:0.623567[&&NHX:S=3DCIOIN:O=3DENSCINT00000011737.2= :G=3DENSCING00000005673] +):0.07499[&&NHX:S=3DChordata:D=3DN:B=3D100], +(Sm00.scaff00195.0600_SCHMA:0.784609[&&NHX:S=3DSCHMA:O=3DSm00.scaff00195.0= 600:G=3DSm00.scaff00195.0600], +(CBG03141_CAEBR:0.093703[&&NHX:S=3DCAEBR:O=3DCBG03141:G=3DCBG03141], +NP_496498_CAEEL:0.212236[&&NHX:S=3DCAEEL:O=3DC47D12.8.1:G=3DC47D12.8] +):1.47416[&&NHX:S=3DCaenorhabditis:D=3DN:B=3D94] +):0.26906[&&NHX:S=3DBilateria:D=3DN:B=3D97] +):0.071406[&&NHX:S=3DBilateria:D=3DN:B=3D1], +(mei-9-RA_DROME:0.170289[&&NHX:S=3DDROME:O=3DCG3697-RA.3:G=3DCG3697], +GA17620-PA_DROPS:0.154817[&&NHX:S=3DDROPS:O=3DGA17620-PA:G=3DGA17620] +):0.818474[&&NHX:S=3DSophophora:D=3DN:B=3D100] +):0 +)[&&NHX:S=3DEukaryota:D=3DN]; \ No newline at end of file diff -r 89dbce43ba88afdc3e6265feea7e7e042bb030a7 -r 75a03bacdc7a3dc5b1c03f8= b02df0ab383366955 test-data/visualization/phyloviz/5_newick.nhx --- /dev/null +++ b/test-data/visualization/phyloviz/5_newick.nhx @@ -0,0 +1,1 @@ +(CAE_ELE_PORCN:0.303421 ,((((DRO_PER_PORCN:0.001000 ,DRO_PSE_PORCN:0.00100= 0 )67:0.141994 ,(DRO_ANA_PORCN:0.111899 ,(DRO_ERE_PORCN:0.030516 ,(DRO_MEL_= PORCN:0.021127 ,DRO_SEC_PORCN:0.021127 )38:0.030516 )35:0.111899 )18:0.1419= 94 )16:0.162611 ,(DRO_WIL_PORCN:0.152225 ,(DRO_VIR_PORCN:0.085057 ,DRO_MOJ_= PORCN:0.085057 )24:0.152225 )15:0.162611 )13:0.295081 ,(ANO_GAM_PORCN:0.287= 545 ,((CIO_INT_PORCN:0.100686 ,CIO_SAV_PORCN:0.100686 )19:0.275542 ,((LOA_L= OA_PORCN:0.036278 ,BRU_MAL_PORCN:0.036278 )29:0.272631 ,(((((DAN_RER_PORCN:= 0.086499 ,((TAK_RUB_PORCN:0.032609 ,TET_NIG_PORCN:0.032609 )32:0.048864 ,(G= AD_MOR_PORCN:0.039387 ,(ORY_LAT_PORCN:0.031729 ,(GAS_ACU_PORCN:0.021882 ,OR= E_NIL_PORCN:0.021882 )37:0.031729 )34:0.039387 )28:0.048864 )27:0.086499 )2= 3:0.119618 ,(LAT_CHA_PORCN:0.099348 ,((XEN_LAE_PORCN:0.033333 ,XEN_TRO_PORC= N:0.033333 )31:0.091250 ,(ANO_CAR_PORCN:0.086538 ,((MON_DOM_PORCN:0.014100 = ,(MAC_EUG_PORCN:0.005423 ,SAR_HAR_PORCN:0.005423 )57:0.014100 )42:0.062862 = ,(ORN_ANA_PORCN:0.057974 ,(GOR_GOR_PORCN:0.033876 ,(FEL_CAT_PORCN:0.022851 = ,(PRO_CAP_PORCN:0.019716 ,(CAV_POR_PORCN:0.018599 ,(ERI_EUR_PORCN:0.015518 = ,((DIP_ORD_PORCN:0.007231 ,(MUS_MUS_PORCN:0.001085 ,(RAT_NOR_PORCN:0.001000= ,CRI_GRI_PORCN:0.001000 )69:0.001085 )64:0.007231 )53:0.012954 ,(DAS_NOV_P= ORCN:0.011362 ,(LOX_AFR_PORCN:0.010575 ,(CAL_JAC_PORCN:0.010332 ,(OCH_PRI_P= ORCN:0.010063 ,(MIC_MUR_PORCN:0.009123 ,(SUS_SCR_PORCN:0.008880 ,(MYO_LUC_P= ORCN:0.008460 ,((CAN_FAM_PORCN:0.005423 ,AIL_MEL_PORCN:0.005423 )58:0.00809= 3 ,((PTE_VAM_PORCN:0.006508 ,BOS_TAU_PORCN:0.006508 )55:0.007494 ,((SPE_TRI= _PORCN:0.003254 ,TUP_BEL_PORCN:0.003254 )61:0.006929 ,((OTO_GAR_PORCN:0.001= 085 ,(ORY_CUN_PORCN:0.001000 ,TUR_TRU_PORCN:0.001000 )68:0.001085 )65:0.005= 965 ,(EQU_CAB_PORCN:0.003688 ,(MAC_MUL_PORCN:0.002711 ,(PAN_TRO_PORCN:0.001= 446 ,(HOM_SAP_PORCN:0.001085 ,(PON_ABE_PORCN:0.001000 ,NOM_LEU_PORCN:0.0010= 00 )70:0.001085 )66:0.001446 )63:0.002711 )62:0.003688 )60:0.005965 )56:0.0= 06929 )54:0.007494 )52:0.008093 )51:0.008460 )50:0.008880 )49:0.009123 )48:= 0.010063 )47:0.010332 )46:0.010575 )45:0.011362 )44:0.012954 )43:0.015518 )= 41:0.018599 )40:0.019716 )39:0.022851 )36:0.033876 )30:0.057974 )26:0.06286= 2 )25:0.086538 )22:0.091250 )21:0.099348 )20:0.119618 )17:0.214465 ,(BRA_FL= O_PORCN:0.189220 ,SAC_KOW_PORCN:0.189220 )12:0.214465 )11:0.257058 ,(NEM_VE= C_PORCN:0.246631 ,AMP_QUE_PORCN:0.246631 )9:0.257058 )8:0.266904 ,(TRI_CAS_= PORCN:0.259494 ,(PED_HUM_PORCN:0.227009 ,(NAS_VIT_PORCN:0.160241 ,(API_MEL_= PORCN:0.031851 ,(BOM_TER_PORCN:0.004808 ,BOM_IMP_PORCN:0.004808 )59:0.03185= 1 )33:0.160241 )14:0.227009 )10:0.259494 )7:0.266904 )6:0.272631 )5:0.27554= 2 )4:0.287545 )3:0.295081 )2:0.303421 )1:0.0001; Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.
participants (1)
-
Bitbucket