6894b3e9c5590a37147afd1485f21f07285c7ed1 galt Mon Sep 19 17:10:21 2016 -0700 Renamed phyloGif to phloPng for greater accuracy in the name. Because when we switched to PNG, only 16-bit color palettes are supported now. We lost the ability to do 8-bit color palette which is needed by GIF. diff --git src/hg/phyloPng/phyloPng.c src/hg/phyloPng/phyloPng.c new file mode 100644 index 0000000..e1c48ad --- /dev/null +++ src/hg/phyloPng/phyloPng.c @@ -0,0 +1,770 @@ +/* Copyright (C) 2012 The Regents of the University of California + * See README in this or parent directory for licensing information. */ + +/* phyloPng.c for parsing phylo tree and outputting a png. + * + * Author: Galt Barber 2006 + * + * Designed to be run either as a cgi or as a command-line utility. + * The input file spec matches the .nh tree format e.g. (cat:0.5,dog:0.6):1.2; + * The output png layout was originally designed to ignore branch lengths. + * However, options have now been added to allow it to use branchlengths, + * and to label the length of the branches. + * Any _suffix is stripped from labels both because pyloTree.c + * can't tolerate underscores and because we don't want suffixes anyway. + * A semi-colon is automatically appended to the input if left off. + * Because phyloTree.c does errAbort on bad input, this causes cgi err 500 + * if the input data has incorrect syntax. See the apache error_log. + * Added another option to place form output in a static html page + * so that we can prevent IE6 save-as bug, and FF auto-shrunk output. + * Added an option to display a length-scale legend or ruler at the bottom. + * Added an option to preserve underscores in input as spaces in output. + * + * + * One may use as a cgi in html GET: + * <IMG SRC="../cgi-bin/phyloPng?phyloPng_width=120&phyloPng_height=120&phyloPng_tree=(a:1,b:1);" > + * + * Or as cgi in html POST: +<form method="POST" action="../cgi-bin/phyloPng/phylo.png" name="mainForm"> +<table> +<tr><td>width:</td><td><INPUT type="text" name="phyloPng_width" value="240" size="4"></td></tr> +<tr><td>height:</td><td><INPUT type="text" name="phyloPng_height" value="512" size="4"></td></tr> +<tr><td>tree:</td><td><textarea name="phyloPng_tree" rows=14 cols=70> +((((((((( +(human_hg18:0.00669,chimp_panTro1:0.00757):0.0243, + macaque_rheMac2:0.0592):0.0240, + ((rat_rn4:0.0817,mouse_mm8:0.0770):0.229, + rabbit_oryCun1:0.207):0.107):0.0230, + (cow_bosTau2:0.159,dog_canFam2:0.148):0.0394):0.0285, + armadillo_dasNov1:0.150):0.0160, + (elephant_loxAfr1:0.105,tenrec_echTel1:0.260):0.0404):0.218, + monodelphis_monDom4:0.371):0.189, + chicken_galGal2:0.455):0.123, + xenopus_xenTro1:0.782):0.156, + ((tetraodon_tetNig1:0.199,fugu_fr1:0.239):0.493, + zebrafish_danRer3:0.783):0.156); +</textarea></td></tr> +<tr><td> </td><td><INPUT type="submit" name="phyloPng_submit" value="submit"></td></tr> +</table> +</form> + + */ + + +#include "common.h" +#include "linefile.h" +#include "dystring.h" +#include "net.h" +#include "cheapcgi.h" +#include "errAbort.h" +#include "phyloTree.h" +#include "portable.h" +#include "memgfx.h" + +#include "cart.h" +#include "hui.h" +#include "htmshell.h" +#include "web.h" + + +#include "errAbort.h" +#include "errCatch.h" + + +struct cart *cart=NULL; /* The user's ui state. */ +struct hash *oldVars = NULL; +boolean onWeb = FALSE; + +int width=240,height=512; +boolean branchLengths = FALSE; /* branch lengths */ +boolean lengthLegend = FALSE; /* length ruler*/ +boolean branchLabels = FALSE; /* labelled branch lengths */ +boolean htmlPageWrapper = FALSE; /* wrap output in an html page */ +boolean stripUnderscoreSuff = FALSE; /* strip underscore suffixes from labels in input */ +boolean dashToSpace = FALSE; /* convert dash to space */ +boolean underToSpace = FALSE; /* convert underscore to space */ +boolean monospace = FALSE; /* use monospace font */ +int branchDecimals = 2; /* show branch label length to two decimals by default */ +int branchMultiplier = 1; /* multiply branch length by factor */ +char layoutErrMsg[1024] = ""; + +/* Null terminated list of CGI Variables we don't want to save + * permanently. */ +char *excludeVars[] = {"Submit", "submit", "phyloPng_submit", "phyloPng_restore", NULL}; + +void usage(char *msg) +/* Explain usage and exit. */ +{ +errAbort( + "%s\n\n" + "phyloPng - parse and display phyloGenetic tree\n" + "\n" + "Command-line Usage Examples:\n" + " phyloPng -phyloPng_tree=tree.nh [options] > phylo.png \n" + " phyloPng -phyloPng_tree='(A:0.1,B:0.1)' [options] > phylo.png \n" + "CGI Usage Examples:\n" + " Create an interactive form:\n" + " http://someserver.com/cgi-bin/phyloPng\n" + " Create an image dynamically via URL:\n" + " http://someserver.com/cgi-bin/phyloPng?phyloPng_tree=(A:0.1,B:0.1)\n" + "Options/CGI-vars:\n" + " -phyloPng_width=N - width of output PNG, default %d\n" + " -phyloPng_height=N - height of output PNG, default %d\n" + " -phyloPng_tree= - data in format (nodeA:0.5,nodeB:0.6):1.2;\n" + " If running at the command-line, can put filename here or stdin\n" + " (this is actually required)\n" + " -phyloPng_branchLengths - use branch lengths for layout\n" + " -phyloPng_lengthLegend - show length ruler at bottom\n" + " -phyloPng_branchLabels - show length of branch as label\n" + " (used with -phyloPng_branchLengths)\n" + " -phyloPng_branchDecimals=N - show length of branch to N decimals, default %d\n" + " -phyloPng_branchMultiplier=N - multiply branch length by N default %d\n" + " -phyloPng_htmlPage - wrap the output in an html page (cgi only)\n" + " -phyloPng_underscores - preserve underscores in input as spaces in output\n" + " -phyloPng_monospace - use a monospace proportional font\n" + , msg, width, height, branchDecimals, branchMultiplier); +} + +struct phyloLayout +{ + int depth; /* leaves have depth=0 */ + double vPos; /* vertical position */ + double hPos; /* horizontal position */ +}; + + + +void stripUnderscoreSuffixes(char *s) +/* Strip out underscores in labels, modifies s. */ +{ +char *j=s; +char c = ' '; +boolean inUnderScore = FALSE; +while(TRUE) + { + c = *s++; + if (c == 0) + break; + if (inUnderScore) + { + if (!isalnum(c)) + { + *j++ = c; + inUnderScore = FALSE; + } + } + else + { + if (c=='_') + { + inUnderScore = TRUE; + } + else + { + *j++ = c; + } + } + } +*j = 0; +} + + +#define MARGIN 10 + +static void phyloTreeLayoutBL(struct phyloTree *phyloTree, + int *pMaxDepth, int *pNumLeafs, int depth, + MgFont *font, int *pMaxLabelWidth, int width, double *pMinMaxFactor, double parentHPos) +/* do a depth-first recursion over the tree, assigning depth and vPos to each node + * and keeping track of maxDepth and numLeafs to return */ +{ +struct phyloLayout *this = NULL; +if (depth > *pMaxDepth) + *pMaxDepth = depth; +phyloTree->priv = (void *) needMem(sizeof(struct phyloLayout)); +this = (struct phyloLayout *) phyloTree->priv; +this->hPos = parentHPos + phyloTree->ident->length; +if (branchLengths && phyloTree->ident->length == 0 && depth != 0) + safef(layoutErrMsg,sizeof(layoutErrMsg),"Branch length is missing for %s.\n", + phyloTree->ident->name ? phyloTree->ident->name : "an internal node"); +if (phyloTree->numEdges == 2) /* node */ + { + int maxDepth = 0; + struct phyloLayout *that = NULL; + double vPos = 0; + phyloTreeLayoutBL(phyloTree->edges[0], pMaxDepth, pNumLeafs, depth+1, + font, pMaxLabelWidth, width, pMinMaxFactor, this->hPos); + phyloTreeLayoutBL(phyloTree->edges[1], pMaxDepth, pNumLeafs, depth+1, + font, pMaxLabelWidth, width, pMinMaxFactor, this->hPos); + that = (struct phyloLayout *) phyloTree->edges[0]->priv; + if (that->depth > maxDepth) + maxDepth = that->depth; + vPos += that->vPos; + that = (struct phyloLayout *) phyloTree->edges[1]->priv; + if (that->depth > maxDepth) + maxDepth = that->depth; + vPos += that->vPos; + this->depth=maxDepth+1; + this->vPos=vPos/2; + } +else if (phyloTree->numEdges == 0) /* leaf */ + { + int w=0; + double factor=0.0; + this->depth=0; + this->vPos=*pNumLeafs; + (*pNumLeafs)++; + + if(!phyloTree->ident->name) + { + safef(layoutErrMsg,sizeof(layoutErrMsg), + "leaf is missing label\n"); + return; + } + + if(dashToSpace) + { + char *temp = phyloTree->ident->name; + phyloTree->ident->name = replaceChars(temp,"-"," "); + freez(&temp); + } + + if(underToSpace) + { + char *temp = phyloTree->ident->name; + phyloTree->ident->name = replaceChars(temp,"_"," "); + freez(&temp); + } + + /* strip underscore suffixes option */ + if (stripUnderscoreSuff) + stripUnderscoreSuffixes(phyloTree->ident->name); + + w=mgFontStringWidth(font,phyloTree->ident->name); + if (w > *pMaxLabelWidth) + *pMaxLabelWidth = w; + factor = (width - 3*MARGIN - w) / this->hPos; + if (*pMinMaxFactor == 0.0 || factor < *pMinMaxFactor) + *pMinMaxFactor = factor; + } +else + { + safef(layoutErrMsg,sizeof(layoutErrMsg), + "Expected tree nodes to have 0 or 2 edges, found %d.\n" + "Check for missing commas or missing data.\n" + ,phyloTree->numEdges); + } + +} + +static void phyloTreePng(struct phyloTree *phyloTree, + int maxDepth, int numLeafs, int maxLabelWidth, + int width, int height, struct memGfx *mg, MgFont *font) +/* do a depth-first recursion over the tree, printing tree to png */ +{ +struct phyloLayout *this = (struct phyloLayout *) phyloTree->priv; +int fHeight = mgFontPixelHeight(font); +int vdelta = numLeafs < 2 ? 0 : (height - 2*MARGIN - fHeight) / (numLeafs - 1); +int v = MARGIN + this->vPos * vdelta; +int h = width - MARGIN - maxLabelWidth; +int hl = MARGIN; +int hr = h - MARGIN; +int hw = hr - hl; +int deltaH = hw / (maxDepth+1); +if (phyloTree->parent) + { + struct phyloLayout *that = (struct phyloLayout *) phyloTree->parent->priv; + mgDrawLine(mg, hr-that->depth*deltaH, v+fHeight/2, hr-this->depth*deltaH, v+fHeight/2, MG_BLACK); + } + +if (phyloTree->numEdges == 2) + { + struct phyloLayout *that0 = (struct phyloLayout *) phyloTree->edges[0]->priv; + struct phyloLayout *that1 = (struct phyloLayout *) phyloTree->edges[1]->priv; + phyloTreePng(phyloTree->edges[0], maxDepth, numLeafs, maxLabelWidth, width, height, mg, font); + phyloTreePng(phyloTree->edges[1], maxDepth, numLeafs, maxLabelWidth, width, height, mg, font); + mgDrawLine(mg, hr-this->depth*deltaH, that0->vPos*vdelta+fHeight/2+MARGIN, + hr-this->depth*deltaH, that1->vPos*vdelta+fHeight/2+MARGIN, MG_BLACK); + if (phyloTree->ident->name) + mgText(mg, h, v, MG_BLACK, font, phyloTree->ident->name); + } +else if (phyloTree->numEdges == 0) + { + mgText(mg, h, v, MG_BLACK, font, phyloTree->ident->name); + } +else + errAbort("expected tree nodes to have 0 or 2 edges, found %d\n", phyloTree->numEdges); + + +} + +static void phyloTreePngBL(struct phyloTree *phyloTree, + int maxDepth, int numLeafs, int maxLabelWidth, + int width, int height, struct memGfx *mg, MgFont *font, double minMaxFactor, boolean isRightEdge) +/* do a depth-first recursion over the tree, printing tree to png */ +{ +struct phyloLayout *this = (struct phyloLayout *) phyloTree->priv; +int fHeight = mgFontPixelHeight(font); +int vdelta = numLeafs < 2 ? 0 : (height - 2*MARGIN - fHeight) / (numLeafs - 1); +int v = MARGIN + this->vPos * vdelta; +mgDrawLine(mg, MARGIN+(this->hPos-phyloTree->ident->length)*minMaxFactor, v+fHeight/2, + MARGIN+this->hPos*minMaxFactor, v+fHeight/2, MG_BLACK); +if (branchLabels) + { + if (phyloTree->ident->length > 0.0 && this->hPos > 0.0) + { + char patt[16]; + char label[256]; + safef(patt,sizeof(patt),"%%%d.%df",branchDecimals+2,branchDecimals); + safef(label,sizeof(label),patt,(phyloTree->ident->length)*branchMultiplier); /* was %6.4f */ + mgTextCentered(mg, MARGIN+(this->hPos-phyloTree->ident->length)*minMaxFactor, v+(fHeight/2)*(isRightEdge?1:-1), + phyloTree->ident->length*minMaxFactor, fHeight, MG_BLACK, font, label); + } + } + +if (phyloTree->numEdges == 2) + { + struct phyloLayout *that0 = (struct phyloLayout *) phyloTree->edges[0]->priv; + struct phyloLayout *that1 = (struct phyloLayout *) phyloTree->edges[1]->priv; + phyloTreePngBL(phyloTree->edges[0], maxDepth, numLeafs, maxLabelWidth, width, height, mg, font, minMaxFactor, FALSE); + phyloTreePngBL(phyloTree->edges[1], maxDepth, numLeafs, maxLabelWidth, width, height, mg, font, minMaxFactor, TRUE); + mgDrawLine(mg, MARGIN+this->hPos*minMaxFactor, that0->vPos*vdelta+fHeight/2+MARGIN, + MARGIN+this->hPos*minMaxFactor, that1->vPos*vdelta+fHeight/2+MARGIN, MG_BLACK); + if (phyloTree->ident->name) + mgText(mg, MARGIN+this->hPos*minMaxFactor+MARGIN, v, MG_BLACK, font, phyloTree->ident->name); + } +else if (phyloTree->numEdges == 0) + { + mgText(mg, MARGIN+this->hPos*minMaxFactor+MARGIN, v, MG_BLACK, font, phyloTree->ident->name); + } +else + errAbort("expected tree nodes to have 0 or 2 edges, found %d\n", phyloTree->numEdges); + + +} + + +int main(int argc, char *argv[]) +{ +char *phyloData = NULL, *temp = NULL; +struct phyloTree *phyloTree = NULL; +int maxDepth = 0, numLeafs = 0, maxLabelWidth = 0; +double minMaxFactor = 0.0; +struct memGfx *mg = NULL; +boolean useCart = FALSE; +oldVars = hashNew(8); +onWeb = cgiIsOnWeb(); +boolean isMSIE = FALSE; +char *userAgent = getenv("HTTP_USER_AGENT"); +if (userAgent && strstr(userAgent ,"MSIE")) + isMSIE = TRUE; + + +cgiSpoof(&argc, argv); +setUdcCacheDir(); +if (argc != 1) + usage("wrong number of args"); + +if (onWeb) + { + /* this will cause it to kick out the set-cookie: http response header line */ + cart = cartAndCookieNoContent(hUserCookie(), excludeVars, oldVars); + } +else + { + if (!cgiOptionalString("phyloPng_tree")) + usage("-phyloPng_tree is a required 'option' or cgi variable."); + } + +//cartWarnCatcher(doMiddle, cart, cartEarlyWarningHandler); + + +useCart = (!cgiOptionalString("phyloPng_tree") || cgiVarExists("phyloPng_restore")); + +htmlPageWrapper = cgiVarExists("phyloPng_htmlPage"); /* wrap output in a page */ + +if (onWeb && sameString(getenv("REQUEST_METHOD"),"HEAD")) + { /* tell browser it's static just so it can save it */ + if (htmlPageWrapper) + printf("Content-type: text/html\r\n"); + else + printf("Content-type: image/png\r\n"); + printf("\r\n"); + return 0; + } + +if (useCart) + { + width = cartUsualInt(cart,"phyloPng_width",width); + height = cartUsualInt(cart,"phyloPng_height",height); + phyloData = cloneString(cartOptionalString(cart,"phyloPng_tree")); + branchLengths = cartVarExists(cart,"phyloPng_branchLengths"); + lengthLegend = cartVarExists(cart,"phyloPng_lengthLegend"); + branchLabels = cartVarExists(cart,"phyloPng_branchLabels"); + branchDecimals = cartUsualInt(cart,"phyloPng_branchDecimals", branchDecimals); + branchMultiplier = cartUsualInt(cart,"phyloPng_branchMultiplier", branchMultiplier); + stripUnderscoreSuff = cartVarExists(cart,"phyloPng_undersuff_strip"); + dashToSpace = cartVarExists(cart,"phyloPng_dash_to_space"); + underToSpace = cartVarExists(cart,"phyloPng_under_to_space"); + monospace = cartVarExists(cart, "phyloPng_monospace"); + } +else + { + width = cgiUsualInt("phyloPng_width",width); + height = cgiUsualInt("phyloPng_height",height); + phyloData = cloneString(cgiOptionalString("phyloPng_tree")); + branchLengths = cgiVarExists("phyloPng_branchLengths"); + lengthLegend = cgiVarExists("phyloPng_lengthLegend"); + branchLabels = cgiVarExists("phyloPng_branchLabels"); + branchDecimals = cgiUsualInt("phyloPng_branchDecimals", branchDecimals); + branchMultiplier = cgiUsualInt("phyloPng_branchMultiplier", branchMultiplier); + stripUnderscoreSuff = cgiVarExists("phyloPng_undersuff_strip"); + dashToSpace = cgiVarExists("phyloPng_dash_to_space"); + underToSpace = cgiVarExists("phyloPng_under_to_space"); + monospace = cgiVarExists("phyloPng_monospace"); + } + +if (useCart) + { + if (onWeb) + { + printf("Content-type: text/html\r\n"); + printf("\r\n"); + cartWebStart(cart, NULL, "%s", "phyloPng Interactive Phylogenetic Tree Png Maker"); + + if (isMSIE) /* cannot handle long urls */ + puts("<form method=\"POST\" action=\"phyloPng\" name=\"mainForm\">"); + else + puts("<form method=\"GET\" action=\"phyloPng\" name=\"mainForm\">"); + + cartSaveSession(cart); + puts("<table>"); + puts("<tr><td>Width:</td><td>"); cartMakeIntVar(cart, "phyloPng_width", width, 4); puts("</td></tr>"); + puts("<tr><td>Height:</td><td>"); cartMakeIntVar(cart, "phyloPng_height", height, 4); puts("</td></tr>"); + puts("<tr><td>Use branch lengths?</td><td>"); cartMakeCheckBox(cart, "phyloPng_branchLengths", branchLengths); puts("</td></tr>"); + puts("<tr><td> Show length ruler?</td><td>"); cartMakeCheckBox(cart, "phyloPng_lengthLegend", lengthLegend); puts("</td></tr>"); + puts("<tr><td> Show length values?</td><td>"); cartMakeCheckBox(cart, "phyloPng_branchLabels", branchLabels); puts("</td></tr>"); + puts("<tr><td> How many decimal places?</td><td>"); cartMakeIntVar(cart, "phyloPng_branchDecimals", branchDecimals,1); puts("</td></tr>"); + puts("<tr><td> Multiply branch length by factor?</td><td>"); cartMakeIntVar(cart, "phyloPng_branchMultiplier", branchMultiplier,5); puts("</td></tr>"); + puts("<tr><td>Strip underscore-suffixes?</td><td>"); cartMakeCheckBox(cart, "phyloPng_undersuff_strip", stripUnderscoreSuff); puts("</td></tr>"); + puts("<tr><td>Change dash to space?</td><td>"); cartMakeCheckBox(cart, "phyloPng_dash_to_space", dashToSpace); puts("</td></tr>"); + puts("<tr><td>Change underscore to space?</td><td>"); cartMakeCheckBox(cart, "phyloPng_under_to_space", underToSpace); puts("</td></tr>"); + puts("<tr><td>Wrap in html page?</td><td>"); cartMakeCheckBox(cart, "phyloPng_htmlPage", htmlPageWrapper); puts("</td></tr>"); + puts("<tr><td>Monospace font?</td><td>"); cartMakeCheckBox(cart, "phyloPng_monospace", monospace); puts("</td></tr>"); + + printf("<tr><td><big>TREE:</big>"); + puts("<br><br><INPUT type=\"submit\" name=\"phyloPng_restore\" value=\"restore default\">"); + + puts("</td><td><textarea name=\"phyloPng_tree\" rows=14 cols=70>"); + if (NULL == phyloData || phyloData[0] == '\0' || cgiVarExists("phyloPng_restore")) + { + puts( +"(((((((((\n" +"(human_hg18:0.00669,chimp_panTro1:0.00757):0.0243,\n" +" macaque_rheMac2:0.0592):0.0240,\n" +" ((rat_rn4:0.0817,mouse_mm8:0.0770):0.229,\n" +" rabbit_oryCun1:0.207):0.107):0.0230,\n" +" (cow_bosTau2:0.159,dog_canFam2:0.148):0.0394):0.0285,\n" +" armadillo_dasNov1:0.150):0.0160,\n" +" (elephant_loxAfr1:0.105,tenrec_echTel1:0.260):0.0404):0.218,\n" +" monodelphis_monDom4:0.371):0.189,\n" +" chicken_galGal2:0.455):0.123,\n" +" xenopus_xenTro1:0.782):0.156,\n" +" ((tetraodon_tetNig1:0.199,fugu_fr1:0.239):0.493,\n" +" zebrafish_danRer3:0.783):0.156);" + ); + } + else + { + printf("%s",phyloData); + } + puts("</TEXTAREA>"); + puts("</td></tr>"); + puts("<tr><td> </td><td>"); + puts("<INPUT type=\"submit\" name=\"phyloPng_submit\" value=\"submit\">"); + puts("</td></tr>"); + puts("</table>"); + puts("</form>"); + webNewSection("Notes"); + puts( +"\n" +"1. Length-ruler and length-values cannot be shown unless use-branch-lengths is also checked.<br>\n" +"<br>\n" +"2. If \"Strip underscore-suffixes?\" is checked, underscores and anything following them are stripped from node labels.<br>\n" +"<br>\n" +"3. For backwards compatibility, options exist to convert a dash or underscore to a space in a node label.<br>\n" +"<br>\n" +"4. The tree is in the phastCons or .nh format name:length. Parentheses create a parent.\n" +"Parents must have two children. Length is not required if use-branch-lengths is not checked.\n" +"The length of the root branch is usually not specified.<br>\n" +"<br>\n" +"Examples:<br>\n" +"<table cellpadding=10>\n" +"<tr><td><PRE>\n" +"((A:0.1,B:0.1):0.2,C:0.15);\n" +"</PRE></td><td>\n" +"<IMG SRC=\"?phyloPng_width=200&phyloPng_height=120&phyloPng_branchLengths=1&phyloPng_tree=((A:0.1,B:0.1):0.2,C:0.15);\">\n" +"</td></tr>\n" +"<tr><td><PRE>\n" +"((A:0.1,B:0.1)D:0.2,C:0.15)E;\n" +"</PRE></td><td>\n" +"<IMG SRC=\"?phyloPng_width=200&phyloPng_height=120&phyloPng_branchLengths=1&phyloPng_tree=((A:0.1,B:0.1)D:0.2,C:0.15)E;\">\n" +"<br>(internal or ancestral node labels)\n" +"</td></tr>\n" +"<tr><td><PRE>\n" +" ((((\n" +" (\n" +" ((mouse,rat),human),\n" +" (dog,cow)\n" +" ),\n" +" opossum),\n" +" chicken),\n" +" xenopus),\n" +" (tetraodon,zebrafish));\n" +"</PRE></td><td>\n" +"<IMG SRC=\"?phyloPng_width=200&phyloPng_height=200&phyloPng_tree=(((((((mouse,rat),human),(dog,cow)),opossum),chicken),xenopus),(tetraodon,zebrafish));\">\n" +"</td></tr>\n" +"<tr><td>\n" +"We have extended the Newick format to allow spaces <br>\n" +"and other non-alphanumeric characters in node labels.<br>\n" +"If you need a backslash, comma, semi-colon, colon, or parenthesis,<br>\n" +"it must be escaped with a back-slash character. <br>\n" +"<PRE>\n" +"((Brandt's myotis \\(bat\\):0.1,\n" +" White-tailed eagle:0.1):0.2,\n" +" S. purpuratus:0.15);\n" +"</PRE></td><td>\n" +"<IMG SRC=\"?phyloPng_width=200&phyloPng_height=120&phyloPng_branchLengths=1&phyloPng_tree=((Brandt's myotis \\(bat\\):0.1,White-tailed eagle:0.1):0.2,S. purpuratus:0.15);\">\n" +"</td></tr>\n" +"</table>\n" +"5. PhastCons branch lengths are expected substitutions per site, allowing for\n" +"multiple hits. So a branch length of 0.5 means an average of one\n" +"substitution every two nucleotide sites, but the percent id will be\n" +"less than 50% because some of those substitutions are obscured by\n" +"subsequent substitutions. They are estimated from neutral sites,\n" +"sometimes fourfold degenerate sites in coding regions, or sometimes\n" +"\"nonconserved\" sites according to phastCons. The numbers are significant\n" +"to two or three figures.<br>\n" +"<br>\n" +"6. Wrap-in-html is useful when the browser automatically shinks a large image.\n" +"This option keeps the image view full in the browser automatically.\n" +"However, do not use with IE6 when performing save-as.\n" +"<br>" + ); + cartWebEnd(); + return 0; + } + else + usage("-phyloPng_tree is a required 'option' or cgi variable."); + } + +if (htmlPageWrapper) + { + printf("Content-type: text/html\r\n"); + printf("\r\n"); + puts("<html><head><title>Phylogenetic Tree</title></head><body>"); + printf("<IMAGE SRC=\"http://%s%s" + "?phyloPng_width=%d" + "&phyloPng_height=%d" + "&phyloPng_tree=%s" + ,getenv("SERVER_NAME"),getenv("SCRIPT_NAME"),width,height,phyloData); + if (branchLengths) + printf("&phyloPng_branchLengths=1"); + if (lengthLegend) + printf("&phyloPng_lengthLegend=1"); + if (branchLabels) + printf("&phyloPng_branchLabels=1"); + printf("&phyloPng_branchDecimals=%d",branchDecimals); + printf("&phyloPng_branchMultipliers=%d",branchMultiplier); + if (stripUnderscoreSuff) + printf("&phyloPng_underscores=1"); + if (dashToSpace) + printf("&phyloPng_dash_to_space=1"); + if (underToSpace) + printf("&phyloPng_under_to_space=1"); + if (monospace) + printf("&phyloPng_monospace=1"); + puts("\"></body></html>"); + freez(&phyloData); + return 0; + } + + + +if (!onWeb && phyloData[0] != '(') + { + int fd = 0; /* default to stdin */ + if (!sameString(phyloData,"stdin")) + fd = open(phyloData,O_RDONLY); + struct dyString *dy = netSlurpFile(fd); + if (fd) + close(fd); + freez(&phyloData); + phyloData = dyStringCannibalize(&dy); + } + +/* remove carriage returns which are a side-effect of html forms */ +if (strchr(phyloData,'\r')) + { + char *temp = phyloData; + phyloData = replaceChars(temp,"\r",""); + freez(&temp); + } + +/* add trailing semi-colon if it got stripped off */ +if (!strchr(phyloData,';')) + { + temp = phyloData; + phyloData = addSuffix(phyloData,";"); + freez(&temp); + } + +/* parse phyloTree, but catch errAborts if any */ + +{ +struct errCatch *errCatch = errCatchNew(); +char *errMsg = NULL; +if (errCatchStart(errCatch)) + { + phyloTree = phyloParseString(phyloData); + } +errCatchEnd(errCatch); +if (errCatch->gotError) + { + errMsg = cloneString(errCatch->message->string); + } +errCatchFree(&errCatch); +if (errMsg) + { + if (onWeb) + { + printf("Content-type: text/html\r\n"); + printf("\r\n"); + puts("<html><head><title>PhyloTree parse error</title></head><body><pre>"); + /* we dont think the specific error message coming back are correct or useful + * so supply a generic err msg */ + htmlPrintf("Original input tree:\n[%s]\n\n",cgiString("phyloPng_tree")); + htmlPrintf("Input tree as passed to parser:\n[%s]\n\n",phyloData); + printf("Parser syntax error:\n%s",errMsg); + puts("</pre></body></html>"); + } + else + { + warn("%s", errMsg); + } + freez(&errMsg); + freez(&phyloData); + return 0; + } +} + + + +MgFont *font = NULL; +if (monospace) + font = mgMenloMediumFont(); +else + font = mgMediumBoldFont(); + + +if (phyloTree) + { + mg = mgNew(width,height); + mgClearPixels(mg); + + lengthLegend = lengthLegend && branchLengths; /* moot without lengths */ + if (lengthLegend) + { + int fHeight = mgFontPixelHeight(font); + height -= (MARGIN+2*fHeight); + } + + phyloTreeLayoutBL(phyloTree, &maxDepth, &numLeafs, 0, font, &maxLabelWidth, width, &minMaxFactor, 0.0); + + if (layoutErrMsg[0] != 0) + { + if (onWeb) + { + printf("Content-type: text/html\r\n"); + printf("\r\n"); + puts("<html><head><title>PhyloTree error</title></head><body><pre>"); + printf("input tree: [%s]\n\n%s",cgiString("phyloPng_tree"),layoutErrMsg); + puts("</pre></body></html>"); + } + else + { + warn("%s", layoutErrMsg); + } + freez(&phyloData); + mgFree(&mg); + return 0; + } + + if (branchLengths) + phyloTreePngBL(phyloTree, maxDepth, numLeafs, maxLabelWidth, width, height, + mg, font, minMaxFactor, FALSE); + else + phyloTreePng(phyloTree, maxDepth, numLeafs, maxLabelWidth, width, height, mg, font); + + if (lengthLegend) + { + int i = 0; + char out[256]; + double scaleEnd = (width - 2*MARGIN); + int fHeight = mgFontPixelHeight(font); + int x=0; + int dh=0; + + mgDrawLine(mg, MARGIN, height+fHeight/2, + width-MARGIN, height+fHeight/2, MG_BLACK); + while(TRUE) + { + x=((minMaxFactor*i)/10); + if (x >= scaleEnd) + break; + if ((i % 5) == 0) + { + int y = mgFontCharWidth(font,'0'); + y += 0.5*mgFontCharWidth(font,'.'); + safef(out,sizeof(out),"%3.2f",branchMultiplier*i/10.0); + if (branchMultiplier > 10) + safef(out,sizeof(out),"%3.0f",branchMultiplier*i/10.0); + mgText(mg, MARGIN+x-y, height+fHeight, MG_BLACK, font, out); + dh=fHeight/2; + } + else + { + dh = fHeight / 4; + } + mgDrawLine(mg, MARGIN+x, height+fHeight/2-dh, + MARGIN+x, height+fHeight/2+dh, MG_BLACK); + ++i; + } + + + } + + } + +if (onWeb) + { + printf("Content-type: image/png\r\n"); + printf("\r\n"); + } + +if (!mgSaveToPng(stdout, mg, FALSE)) + { + errAbort("Couldn't save png to stdout"); + } + +if (cgiOptionalString("phyloPng_submit")) + cartCheckout(&cart); + +/* there's no code for freeing the phyloTree yet in phyloTree.c */ + +mgFree(&mg); +freez(&phyloData); + + +return 0; +} +