e199edebab9458a3b535bb2d0e32c52167d8fdae jcasper Fri Apr 15 14:09:59 2022 -0700 Adding pack/squish to hic tracks, plus interaction distance filters. refs #29280 diff --git src/hg/lib/hicUi.c src/hg/lib/hicUi.c index a43fa23..1ff21ad 100644 --- src/hg/lib/hicUi.c +++ src/hg/lib/hicUi.c @@ -1,373 +1,415 @@ /* hic track controls */ /* Copyright (C) 2019 The Regents of the University of California * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */ #include "cheapcgi.h" #include "cart.h" #include "hui.h" #include "web.h" #include "trackDb.h" #include "hicUi.h" #include "cStraw.h" #include "regexHelper.h" #include "obscure.h" #include "htmshell.h" #include "htmlColor.h" char *hicUiFetchNormalization(struct cart *cart, struct trackDb *tdb, struct hicMeta *meta) /* Return the current normalization selection, or the default if none * has been selected. Right now this is a hard-coded set specifically for * .hic files, but in the future this list might be dynamically determined by * the contents and format of the Hi-C file. */ { char cartVar[1024]; safef(cartVar, sizeof(cartVar), "%s.%s", tdb->track, HIC_NORMALIZATION); char *selected = cartNonemptyString(cart, cartVar); if (selected == NULL) selected = trackDbSetting(tdb, HIC_TDB_NORMALIZATION); char *menu[] = {"NONE", "VC", "VC_SQRT", "KR"}; int i; char *result = menu[0]; for (i=1; i<4; i++) { if (sameWordOk(selected, menu[i])) result = menu[i]; } return result; } void hicUiNormalizationDropDown(struct cart *cart, struct trackDb *tdb, struct hicMeta *meta) { char cartVar[1024]; char* selected = hicUiFetchNormalization(cart, tdb, meta); char *menu[] = {"NONE", "VC", "VC_SQRT", "KR"}; safef(cartVar, sizeof(cartVar), "%s.%s", tdb->track, HIC_NORMALIZATION); cgiMakeDropList(cartVar, menu, 4, selected); } void hicUiNormalizationMenu(struct cart *cart, struct trackDb *tdb, struct hicMeta *meta) /* Draw a menu to select the normalization method to use. */ { printf("Score normalization: "); hicUiNormalizationDropDown(cart, tdb, meta); } char *hicUiFetchResolution(struct cart *cart, struct trackDb *tdb, struct hicMeta *meta) /* Return the current resolution selection, or the default if none * has been selected. */ { char cartVar[1024]; safef(cartVar, sizeof(cartVar), "%s.%s", tdb->track, HIC_RESOLUTION); char *selected = cartNonemptyString(cart, cartVar); if (selected == NULL) selected = trackDbSetting(tdb, HIC_TDB_RESOLUTION); int i; char *result = "Auto"; for (i=0; inRes; i++) { if (sameOk(selected, meta->resolutions[i])) result = selected; } return result; } int hicUiFetchResolutionAsInt(struct cart *cart, struct trackDb *tdb, struct hicMeta *meta, int windowSize) /* Return the current resolution selection as an integer. If there is no selection, or if "Auto" * has been selected, return the largest available value that still partitions the window into at * least 500 bins. */ { char *resolutionString = hicUiFetchResolution(cart, tdb, meta); int result; if (sameWordOk(resolutionString, "Auto")) { int idealRes = windowSize/500; int autoRes = atoi(meta->resolutions[meta->nRes-1]); int smallestRes = autoRes; // in case the ideal resolution is smaller than anything available int i, success = 0; for (i=meta->nRes-1; i>= 0; i--) { int thisRes = atoi(meta->resolutions[i]); if (thisRes < smallestRes) smallestRes = thisRes; // not sure about the sort order of the list if (thisRes < idealRes && thisRes >= autoRes) { autoRes = thisRes; success = 1; } } if (success) result = autoRes; else result = smallestRes; } else result = atoi(resolutionString); return result; } void hicUiResolutionDropDown(struct cart *cart, struct trackDb *tdb, struct hicMeta *meta) { char cartVar[1024]; char autoscale[10] = "Auto"; safef(cartVar, sizeof(cartVar), "%s.%s", tdb->track, HIC_RESOLUTION); char **menu = NULL; AllocArray(menu, meta->nRes+1); char **values = NULL; AllocArray(values, meta->nRes+1); menu[0] = autoscale; values[0] = autoscale; int i; for (i=1; inRes+1; i++) { char buffer[1024]; long long value = atoll(meta->resolutions[i-1]); sprintWithMetricBaseUnit(buffer, sizeof(buffer), value); menu[i] = cloneString(buffer); values[i] = cloneString(meta->resolutions[i-1]); } char *selected = hicUiFetchResolution(cart, tdb, meta); cgiMakeDropListWithVals(cartVar, menu, values, meta->nRes+1, selected); freeMem(menu); } void hicUiResolutionMenu(struct cart *cart, struct trackDb *tdb, struct hicMeta *meta) /* Draw a menu to select which binSize to use for fetching data */ { printf("Resolution: "); hicUiResolutionDropDown(cart, tdb, meta); } char *hicUiFetchDrawMode(struct cart *cart, struct trackDb *tdb) /* Return the current draw mode selection, or the default if none * has been selected. */ { char *selected = cartOptionalStringClosestToHome(cart, tdb, FALSE, HIC_DRAW_MODE); if (selected == NULL) { selected = trackDbSetting(tdb, HIC_TDB_DRAW_MODE); } char *result = HIC_DRAW_MODE_DEFAULT; if (sameWordOk(selected, HIC_DRAW_MODE_SQUARE)) result = HIC_DRAW_MODE_SQUARE; else if (sameWordOk(selected, HIC_DRAW_MODE_ARC)) result = HIC_DRAW_MODE_ARC; else if (sameWordOk(selected, HIC_DRAW_MODE_TRIANGLE)) result = HIC_DRAW_MODE_TRIANGLE; return result; } void hicUiDrawMenu(struct cart *cart, struct trackDb *tdb) /* Draw the list of draw mode options for Hi-C tracks. Square is the * standard square-shaped heatmap with the chromosome position axis on * a diagonal from top left to bottom right. Triangle is the top half * of that square, after rotating it 45 degrees to have the position * axis lie horizontally. Arc draws arcs between related positions, * but skips over self-relations. */ { char cartVar[1024]; printf("Draw mode: "); safef(cartVar, sizeof(cartVar), "%s.%s", tdb->track, HIC_DRAW_MODE); char *menu[] = {HIC_DRAW_MODE_SQUARE, HIC_DRAW_MODE_TRIANGLE, HIC_DRAW_MODE_ARC}; char* selected = hicUiFetchDrawMode(cart, tdb); cgiMakeDropList(cartVar, menu, 3, selected); } char *hicUiFetchDrawColor(struct cart *cart, struct trackDb *tdb) /* Retrieve the HTML hex code for the color to draw the * track values in (e.g., #00ffa1) */ { // Color might have been specified in the cart, probably in #aabbcc format char* selected = cartOptionalStringClosestToHome(cart, tdb, FALSE, HIC_DRAW_COLOR); if (selected == NULL) // Or color might be in trackDb, probably in R,G,B format selected = trackDbSettingClosestToHomeOrDefault(tdb, HIC_TDB_COLOR, HIC_DRAW_COLOR_DEFAULT); const char *commaColorExpr = "^[0-9]+,[0-9]+,[0-9]+$"; if (regexMatch(selected, commaColorExpr)) { // Parse a color in %d,%d,%d format and convert to something the html color tool can use. // Don't want to use pre-parsed trackDb color values just in case string came from the cart instead unsigned char r, g, b; unsigned unifiedColor; parseColor(selected, &r, &g, &b); htmlColorFromRGB(&unifiedColor, r, g, b); char *hexColor = htmlColorToCode(unifiedColor); selected = hexColor; } const char *colorExpr ="^#[0-9a-fA-F]{6}$"; if (!regexMatch(selected, colorExpr)) { selected = HIC_DRAW_COLOR_DEFAULT; } return selected; } char *hicUiFetchBgColor(struct cart *cart, struct trackDb *tdb) /* Retrieve the HTML hex code of the background color for the * track. This is the color associated with scores at or close to 0. */ { char* selected = cartOptionalStringClosestToHome(cart, tdb, FALSE, HIC_DRAW_BG_COLOR); if (selected == NULL) selected = HIC_DRAW_BG_COLOR_DEFAULT; const char *colorExpr ="^#[0-9a-fA-F]{6}$"; if (!regexMatch(selected, colorExpr)) { selected = HIC_DRAW_BG_COLOR_DEFAULT; } return selected; } double hicUiFetchMaxValue(struct cart *cart, struct trackDb *tdb) /* Retrieve the score value at which the draw color reaches its * its maximum intensity. Any scores above this value will * share that same draw color. */ { double defaultValue = HIC_DRAW_MAX_VALUE_DEFAULT; char *tdbString = trackDbSetting(tdb, HIC_TDB_MAX_VALUE); if (!isEmpty(tdbString)) defaultValue = atof(tdbString); return cartUsualDoubleClosestToHome(cart, tdb, FALSE, HIC_DRAW_MAX_VALUE, defaultValue); } void hicUiAddAutoScaleJS(struct cart *cart, char *track) /* Write out a bit of javascript to associate checking/unchecking the autoscale * checkbox with deactivating/activating (respectively) the maximum score * input. */ { struct dyString *new = dyStringNew(0); dyStringPrintf(new, "$('input[name=\"%s.%s\"]')[0].onclick = function() {", track, HIC_DRAW_AUTOSCALE); dyStringPrintf(new, "if (this.checked) {$('input[name=\"%s.%s\"]')[0].disabled = true; $('span#hicMaxText').css('color', 'gray');}", track, HIC_DRAW_MAX_VALUE); dyStringPrintf(new, "else {$('input[name=\"%s.%s\"]')[0].disabled = false; $('span#hicMaxText').css('color', '');} };\n", track, HIC_DRAW_MAX_VALUE); dyStringPrintf(new, "if ($('input[name=\"%s.%s\"]')[0].checked) {$('input[name=\"%s.%s\"]')[0].disabled = true; $('span#hicMaxText').css('color', 'gray');}\n", track, HIC_DRAW_AUTOSCALE, track, HIC_DRAW_MAX_VALUE); jsInline(dyStringContents(new)); dyStringFree(&new); } boolean hicUiFetchAutoScale(struct cart *cart, struct trackDb *tdb) /* Returns whether the track is configured to automatically scale its color range * depending on the scores present in the window, or if it should stick to a * gradient based on the user's selected maximum value. */ { boolean defaultVal = TRUE; char *tdbSetting = trackDbSetting(tdb, HIC_TDB_AUTOSCALE); if (tdbSetting != NULL) defaultVal = trackDbSettingClosestToHomeOn(tdb, HIC_TDB_AUTOSCALE); return cartUsualBooleanClosestToHome(cart, tdb, FALSE, HIC_DRAW_AUTOSCALE, defaultVal); } void hicUiColorMenu(struct cart *cart, struct trackDb *tdb) /* Draw the menu inputs associated with selecting draw colors for the track. */ { char cartVar[1024]; printf("Color: "); safef(cartVar, sizeof(cartVar), "%s.%s", tdb->track, HIC_DRAW_COLOR); char* selected = hicUiFetchDrawColor(cart, tdb); printf("\n", cartVar, selected); // Leaving out background color options for now. We'll see if this option is requested. /* printf("Background color: "); safef(cartVar, sizeof(cartVar), "%s.%s", track, HIC_DRAW_BG_COLOR); selected = hicUiFetchBgColor(cart, track); printf("\n", cartVar, selected); */ } void hicUiMaxOptionsMenu(struct cart *cart, struct trackDb *tdb, boolean isComposite) /* Draw the menu inputs associated with selecting whether the track should automatically * scale its color gradient based on the scores present in the view window, or whether it * should stick to a gradient based on a user-selected maximum score. */ { char cartVar[1024]; safef(cartVar, sizeof(cartVar), "%s.%s", tdb->track, HIC_DRAW_MAX_VALUE); double currentMax = hicUiFetchMaxValue(cart, tdb); printf("%sMaximum: ", isComposite ? "Score " : ""); cgiMakeDoubleVar(cartVar, currentMax, 6); printf("  Auto-scale: "); safef(cartVar, sizeof(cartVar), "%s.%s", tdb->track, HIC_DRAW_AUTOSCALE); boolean autoscaleChecked = hicUiFetchAutoScale(cart, tdb); cgiMakeCheckBox(cartVar, autoscaleChecked); hicUiAddAutoScaleJS(cart, tdb->track); } void hicUiFileDetails(struct hicMeta *trackMeta) { int i; printf("


\nMetadata from file header:
\n"); printf("
\n"); printf("", trackMeta->fileAssembly); char scriptline[2048]; for (i=0; inAttributes-1; i+=2) { printf("
", i, htmlEncode(trackMeta->attributes[i]), "../images/ab_right.gif"); printf("
\n", i); printf("
%s
", htmlEncode(trackMeta->attributes[i+1])); printf("
\n"); safef(scriptline, sizeof(scriptline), "$('label.trackUiHicAttrToggle%d').click(function() {$(this).children('img').toggleClass('open'); $('div.hicAttr%d').toggle();});", i, i); jsInline(scriptline); } printf("
\n"); printf("

For questions concerning the content of a file's metadata header, please contact the file creator.

\n"); } +double hicUiMaxInteractionRange(struct cart *cart, struct trackDb *tdb) +/* Retrieve the maximum range for an interaction to be drawn. Range is + * calculated from the left-most start to the right-most end of the interaction. */ +{ +double defaultValue = 0; +char *tdbString = trackDbSetting(tdb, HIC_TDB_MAX_DISTANCE); +if (!isEmpty(tdbString)) + defaultValue = atof(tdbString); +return cartUsualDoubleClosestToHome(cart, tdb, FALSE, HIC_DRAW_MAX_DISTANCE, defaultValue); +} + +double hicUiMinInteractionRange(struct cart *cart, struct trackDb *tdb) +/* Retrieve the minimum range for an interaction to be drawn. Range is + * calculated from the left-most start to the right-most end of the interaction. */ +{ +double defaultValue = 0; +char *tdbString = trackDbSetting(tdb, HIC_TDB_MIN_DISTANCE); +if (!isEmpty(tdbString)) + defaultValue = atof(tdbString); +return cartUsualDoubleClosestToHome(cart, tdb, FALSE, HIC_DRAW_MIN_DISTANCE, defaultValue); +} + +void hicUiMinMaxRangeMenu(struct cart *cart, struct trackDb *tdb) +{ +char cartVar[2048]; +double minRange = hicUiMinInteractionRange(cart, tdb); +double maxRange = hicUiMaxInteractionRange(cart, tdb); +printf("Filter by interaction distance in bp (0 for no limit): "); +safef(cartVar, sizeof(cartVar), "%s.%s", tdb->track, HIC_DRAW_MIN_DISTANCE); +printf("minimum "); +cgiMakeDoubleVarWithMin(cartVar, minRange, NULL, 0, 0); +safef(cartVar, sizeof(cartVar), "%s.%s", tdb->track, HIC_DRAW_MAX_DISTANCE); +printf(" maximum "); +cgiMakeDoubleVarWithMin(cartVar, maxRange, NULL, 0, 0); +} + void hicCfgUi(char *database, struct cart *cart, struct trackDb *tdb, char *track, char *title, boolean boxed) /* Draw the list of track configuration options for Hi-C tracks */ { boxed = cfgBeginBoxAndTitle(tdb, boxed, title); char *filename = trackDbSetting(tdb, "bigDataUrl"); if (filename == NULL) errAbort("Error: no bigDataUrl provided in trackDb for hic track %s", tdb->track); struct hicMeta *trackMeta; char *errMsg = hicLoadHeader(filename, &trackMeta, database); if (errMsg != NULL) errAbort("Error retrieving file info: %s", errMsg); puts("

"); printf("Items are drawn in shades of the chosen color depending on score - scores above the " "chosen maximum are drawn at full intensity.

\n"); hicUiNormalizationMenu(cart, tdb, trackMeta); puts("  "); hicUiMaxOptionsMenu(cart, tdb, FALSE); puts("

"); hicUiDrawMenu(cart, tdb); puts("  "); hicUiResolutionMenu(cart, tdb, trackMeta); puts("  "); hicUiColorMenu(cart, tdb); puts("

\n"); +hicUiMinMaxRangeMenu(cart, tdb); +puts("

\n"); hicUiFileDetails(trackMeta); cfgEndBox(boxed); } void hicCfgUiComposite(struct cart *cart, struct trackDb *tdb, char *track, char *title, boolean boxed) /* Draw the (empty) list of track configuration options for a composite of Hi-C tracks */ { boxed = cfgBeginBoxAndTitle(tdb, boxed, title); puts("

"); printf("Items are drawn in shades of the chosen color depending on score - scores above the " "chosen maximum are drawn at full intensity.

\n"); hicUiMaxOptionsMenu(cart, tdb, TRUE); puts("

"); hicUiDrawMenu(cart, tdb); puts("  "); hicUiColorMenu(cart, tdb); puts("

\n"); +hicUiMinMaxRangeMenu(cart, tdb); +puts("

\n"); puts("Subtracks below have additional file-specific configuration options for resolution and normalization.\n

"); cfgEndBox(boxed); } + +