be4311c07e14feb728abc6425ee606ffaa611a58 markd Fri Jan 22 06:46:58 2021 -0800 merge with master diff --git src/hg/hgc/barChartClick.c src/hg/hgc/barChartClick.c index f83f479..f0b5e10 100644 --- src/hg/hgc/barChartClick.c +++ src/hg/hgc/barChartClick.c @@ -1,36 +1,40 @@ /* Details pages for barChart tracks */ /* Copyright (C) 2015 The Regents of the University of California * See README in this or parent directory for licensing information. */ #include "common.h" #include "hash.h" #include "hdb.h" #include "hvGfx.h" #include "trashDir.h" #include "hCommon.h" #include "hui.h" #include "asParse.h" #include "hgc.h" #include "trackHub.h" +#include "memgfx.h" +#include "hgColors.h" +#include "fieldedTable.h" #include "barChartBed.h" #include "barChartCategory.h" #include "barChartData.h" #include "barChartSample.h" #include "barChartUi.h" +#include "hgConfig.h" #define EXTRA_FIELDS_SIZE 256 struct barChartItemData /* Measured value for a sample and the sample category at a locus. * Used for barChart track details (boxplot) */ { struct barChartItemData *next; /* Next in singly linked list. */ char *sample; /* Sample identifier */ char *category; /* Sample category (from barChartSample table or barChartSampleUrl file) */ double value; /* Measured value (e.g. expression level) */ }; static struct hash *getTrackCategories(struct trackDb *tdb) /* Get list of categories from trackDb. This may be a subset of those in matrix. @@ -359,30 +363,162 @@ // to help with QAing the change, we add the "oldFonts" CGI parameter so QA can compare // old and new fonts to make sure that things are still readible on mirrors and servers // without the new fonts installed. This only needed during the QA phase bool useOldFonts = cgiBoolean("oldFonts"); /* Exec R in quiet mode, without reading/saving environment or workspace */ dyStringPrintf(cmd, "Rscript --vanilla --slave hgcData/barChartBoxplot.R %s '%s' %s %s %s %s %d", item, units, colorFile, df, pngTn.forHtml, isEmpty(name2) ? "n/a" : name2, useOldFonts); int ret = system(cmd->string); if (ret == 0) printf("
\n", pngTn.forHtml); else warn("Error creating boxplot from sample data with command: %s", cmd->string); } +static double estimateStringWidth(char *s) +/* Get estimate of string width based on a memory font that is about the + * same size as svg will be using. After much research I don't think we + * can get the size from the server, would have to be in Javascript to get + * more precise */ +{ +MgFont *font = mgHelvetica14Font(); +return mgFontStringWidth(font, s); +} + +static double longestLabelSize(struct barChartCategory *categList) +/* Get estimate of longest label in pixels */ +{ +int longest = 0; +struct barChartCategory *categ; +for (categ = categList; categ != NULL; categ = categ->next) + { + int size = estimateStringWidth(categ->label); + if (size > longest) + longest = size; + } +return longest * 1.09; +} + +void deunderbarColumn(struct fieldedTable *ft, char *field) +/* Ununderbar all of a column inside table because space/underbar gets + * so confusing */ +{ +int fieldIx = fieldedTableFindFieldIx(ft, field); +struct fieldedRow *row; +for (row = ft->rowList; row != NULL; row = row->next) + replaceChar(row->row[fieldIx], '_', ' '); +} + +static void printBarChart(struct barChartBed *chart, struct trackDb *tdb, double maxVal, char *metric) +/* Plot bar chart without quartiles or anything fancy just using SVG */ +{ +/* Load up input labels, color, and data */ +struct barChartCategory *categs = barChartUiGetCategories(database, tdb); +int categCount = slCount(categs); +if (categCount != chart->expCount) + { + warn("Problem in %s barchart track. There are %d categories in trackDb and %d in data", + tdb->track, categCount, chart->expCount); + return; + } + +char *statsFile = trackDbSetting(tdb, "barChartStatsUrl"); +struct hash *statsHash = NULL; +int countStatIx = 0; +double statsSize = 0.0; +if (statsFile != NULL) + { + char *required[] = {"cluster", "count", "total"}; + struct fieldedTable *ft = fieldedTableFromTabFile( + statsFile, statsFile, required, ArraySize(required)); + deunderbarColumn(ft, "cluster"); + statsHash = fieldedTableIndex(ft, "cluster"); + countStatIx = fieldedTableFindFieldIx(ft, "count"); + statsSize = 8*(fieldedTableMaxColChars(ft, countStatIx)+1); + } + +/* Some constants that control layout */ +double heightPer=18.0; +double totalWidth=1250.0; +double borderSize = 1.0; + +double headerHeight = heightPer + 2*borderSize; +double innerHeight=heightPer-borderSize; +double labelWidth = longestLabelSize(categs) + 9; // Add some because size is just estimate +if (labelWidth > totalWidth/2) labelWidth = totalWidth/2; // Don't let labels take up more than half +double patchWidth = heightPer; +double labelOffset = patchWidth + 2*borderSize; +double statsOffset = labelOffset + labelWidth; +double barOffset = statsOffset + statsSize; +double statsRightOffset = barOffset - 9; +double barNumLabelWidth = estimateStringWidth(" 1234.000"); +double barMaxWidth = totalWidth-barOffset -barNumLabelWidth ; +double totalHeight = headerHeight + heightPer * categCount + borderSize; + +printf("\n", totalWidth, totalHeight); + +/* Draw header */ +printf("\n", totalWidth, headerHeight, HG_COL_HEADER); +printf("%s\n", + labelOffset, innerHeight-1, innerHeight-1, "Sample"); +if (statsSize > 0.0) + printf("%s\n", + statsRightOffset, innerHeight-1, innerHeight-1, "N"); +printf("%s %s\n", + barOffset, innerHeight-1, innerHeight-1, metric, "Value"); + +/* Set up clipping path for the pesky labels, which may be too long */ +printf("\n", + labelOffset, barOffset-labelOffset, totalHeight); + +double yPos = headerHeight; +struct barChartCategory *categ; +int i; +for (i=0, categ=categs; inext, yPos += heightPer) + { + double score = chart->expScores[i]; + double barWidth = 0; + if (maxVal > 0.0) + barWidth = barMaxWidth * score/maxVal; + char *deunder = cloneString(categ->label); + replaceChar(deunder, '_', ' '); + printf("\n", + yPos, innerHeight, categ->color); + printf("\n", + barOffset, yPos, barWidth, innerHeight, categ->color); + if (i&1) // every other time + printf("\n", + labelOffset, yPos, labelWidth+statsSize, innerHeight, 0xFFFFFF); + printf("%s\n", + labelOffset, yPos+innerHeight-1, innerHeight-1, deunder); + if (statsSize > 0.0) + { + struct fieldedRow *fr = hashFindVal(statsHash, deunder); + if (fr != NULL) + { + printf("%s\n", + statsRightOffset, yPos+innerHeight-1, innerHeight-1, fr->row[countStatIx]); + } + } + printf("%5.3f\n", + barOffset+barWidth+2, yPos+innerHeight-1, innerHeight-1, score); + } +printf(""); +} + + struct asColumn *asFindColByIx(struct asObject *as, int ix) /* Find AS column by index */ { struct asColumn *asCol; int i; for (i=0, asCol = as->columnList; asCol != NULL && inext, i++); return asCol; } void doBarChartDetails(struct trackDb *tdb, char *item) /* Details of barChart item */ { int start = cartInt(cart, "o"); int end = cartInt(cart, "t"); struct asObject *as = NULL; @@ -399,61 +535,67 @@ struct asColumn *nameCol = NULL, *name2Col = NULL; //struct asColumn *name2Col; char *nameLabel = NULL, *name2Label = NULL; if (as != NULL) { numColumns = slCount(as->columnList); nameCol = asFindColByIx(as, BARCHART_NAME_COLUMN_IX); name2Col = asFindColByIx(as, BARCHART_NAME2_COLUMN_IX); } nameLabel = trackDbSettingClosestToHomeOrDefault(tdb, "bedNameLabel", nameCol ? nameCol->comment : "Item"); if (trackDbSettingClosestToHomeOrDefault(tdb, "url", NULL) != NULL) printCustomUrl(tdb, item, TRUE); else printf("%s: %s
\n", nameLabel, chartItem->name); name2Label = name2Col ? name2Col->comment : "Alternative name"; -if (differentString(chartItem->name2, "")) { +if (differentString(chartItem->name2, "")) + { if (trackDbSettingClosestToHomeOrDefault(tdb, "url2", NULL) != NULL) printOtherCustomUrl(tdb, chartItem->name2, "url2", TRUE); else printf("%s: %s
\n", name2Label, chartItem->name2); } int categId; float highLevel = barChartMaxValue(chartItem, &categId); char *units = trackDbSettingClosestToHomeOrDefault(tdb, BAR_CHART_UNIT, "units"); char *metric = trackDbSettingClosestToHomeOrDefault(tdb, BAR_CHART_METRIC, ""); printf("Total all %s values: %0.2f %s
\n", metric, barChartTotalValue(chartItem), units); printf("Maximum %s value: %0.2f %s in %s
\n", metric, highLevel, units, barChartUiGetCategoryLabelById(categId, database, tdb)); printf("Score: %d
\n", chartItem->score); printf("Genomic position: " "%s %s:%d-%d
\n", database, hgTracksPathAndSettings(), database, chartItem->chrom, chartItem->chromStart+1, chartItem->chromEnd, chartItem->chrom, chartItem->chromStart+1, chartItem->chromEnd); printf("Strand: %s\n", chartItem->strand); // print any remaining extra fields if (numColumns > 0) { extraFieldsPrint(tdb, NULL, extraFields, extraFieldCount); } char *matrixUrl = NULL, *sampleUrl = NULL; struct barChartItemData *vals = getSampleVals(tdb, chartItem, &matrixUrl, &sampleUrl); +puts("

"); if (vals != NULL) { // Print boxplot - puts("

"); char *df = makeDataFrame(tdb->table, vals); char *colorFile = makeColorFile(tdb); printBoxplot(df, item, chartItem->name2, units, colorFile); printf("
View all data points for %s%s%s%s\n", df, chartItem->name, chartItem->name2 ? " (" : "", chartItem->name2 ? chartItem->name2 : "", chartItem->name2 ? ")" : ""); } +else + { + if (cfgOptionBooleanDefault("svgBarChart", FALSE)) + printBarChart(chartItem, tdb, highLevel, metric); + } puts("
"); }