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: + * + * + * Or as cgi in html POST: +
+ + + + + +
width:
height:
tree:
 
+
+ + */ + + +#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("
"); + else + puts(""); + + cartSaveSession(cart); + puts(""); + puts(""); + puts(""); + puts(""); + puts(""); + puts(""); + puts(""); + puts(""); + puts(""); + puts(""); + puts(""); + puts(""); + puts(""); + + printf(""); + puts(""); + puts("
Width:"); cartMakeIntVar(cart, "phyloPng_width", width, 4); puts("
Height:"); cartMakeIntVar(cart, "phyloPng_height", height, 4); puts("
Use branch lengths?"); cartMakeCheckBox(cart, "phyloPng_branchLengths", branchLengths); puts("
  Show length ruler?"); cartMakeCheckBox(cart, "phyloPng_lengthLegend", lengthLegend); puts("
  Show length values?"); cartMakeCheckBox(cart, "phyloPng_branchLabels", branchLabels); puts("
  How many decimal places?"); cartMakeIntVar(cart, "phyloPng_branchDecimals", branchDecimals,1); puts("
  Multiply branch length by factor?"); cartMakeIntVar(cart, "phyloPng_branchMultiplier", branchMultiplier,5); puts("
Strip underscore-suffixes?"); cartMakeCheckBox(cart, "phyloPng_undersuff_strip", stripUnderscoreSuff); puts("
Change dash to space?"); cartMakeCheckBox(cart, "phyloPng_dash_to_space", dashToSpace); puts("
Change underscore to space?"); cartMakeCheckBox(cart, "phyloPng_under_to_space", underToSpace); puts("
Wrap in html page?"); cartMakeCheckBox(cart, "phyloPng_htmlPage", htmlPageWrapper); puts("
Monospace font?"); cartMakeCheckBox(cart, "phyloPng_monospace", monospace); puts("
TREE:"); + puts("

"); + + puts("
"); + puts("
 "); + puts(""); + puts("
"); + puts("
"); + webNewSection("Notes"); + puts( +"\n" +"1. Length-ruler and length-values cannot be shown unless use-branch-lengths is also checked.
\n" +"
\n" +"2. If \"Strip underscore-suffixes?\" is checked, underscores and anything following them are stripped from node labels.
\n" +"
\n" +"3. For backwards compatibility, options exist to convert a dash or underscore to a space in a node label.
\n" +"
\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.
\n" +"
\n" +"Examples:
\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"
\n"
+"((A:0.1,B:0.1):0.2,C:0.15);\n"
+"
\n" +"\n" +"
\n"
+"((A:0.1,B:0.1)D:0.2,C:0.15)E;\n"
+"
\n" +"\n" +"
(internal or ancestral node labels)\n" +"
\n"
+"  ((((\n"
+"   (\n"
+"     ((mouse,rat),human),\n"
+"       (dog,cow)\n"
+"    ),\n"
+"     opossum),\n"
+"     chicken),\n"
+"     xenopus),\n"
+"    (tetraodon,zebrafish));\n"
+"
\n" +"\n" +"
\n" +"We have extended the Newick format to allow spaces
\n" +"and other non-alphanumeric characters in node labels.
\n" +"If you need a backslash, comma, semi-colon, colon, or parenthesis,
\n" +"it must be escaped with a back-slash character.
\n" +"
\n"
+"((Brandt's myotis \\(bat\\):0.1,\n"
+"  White-tailed eagle:0.1):0.2,\n"
+" S. purpuratus:0.15);\n"
+"
\n" +"\n" +"
\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.
\n" +"
\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" +"
" + ); + 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("Phylogenetic Tree"); + printf(""); + 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("PhyloTree parse error
");
+	/* 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("
"); + } + 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("PhyloTree error
");
+	    printf("input tree: [%s]\n\n%s",cgiString("phyloPng_tree"),layoutErrMsg);
+	    puts("
"); + } + 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; +} +