7d992e15b156dca5c6601a989dc33a4bf229ce45 angie Thu Aug 20 10:14:08 2020 -0700 Added support for highlightIds setting in VCF+treeFile custom tracks. refs #25943 diff --git src/hg/hgTracks/vcfTrack.c src/hg/hgTracks/vcfTrack.c index 7362509..b7cb2e2 100644 --- src/hg/hgTracks/vcfTrack.c +++ src/hg/hgTracks/vcfTrack.c @@ -1912,127 +1912,175 @@ } else { // Leaf (sample) double rankStart = leafOrderToHapOrderStart[leafIx]; double rankEnd = leafOrderToHapOrderStart[leafIx]; coords->rank = (rankStart + rankEnd) / 2.0; leafIx++; coords->depth = 0; } return leafIx; } static void rDrawPhyloTreeInLabelArea(struct phyloTree *node, struct hvGfx *hvg, int x, int yOff, double pxPerHap, MgFont *font, - boolean drawRectangle) + struct hash *highlightSamples) /* Recursively draw the tree in the left label area. */ { const int branchW = 4; int labelEnd = leftLabelX + leftLabelWidth; // Misuse the branch length value as RGB color (if it's the typical small number, will still // draw as approximately black): unsigned int rgb = node->ident->length; Color color = MAKECOLOR_32( ((rgb>>16)&0xff), ((rgb>>8)&0xff), (rgb&0xff) ); if (node->numEdges > 0) { // Draw each child and a horizontal line to child int minY = -1, maxY = 0; int ix; for (ix = 0; ix < node->numEdges; ix++) { struct phyloTree *child = node->edges[ix]; - rDrawPhyloTreeInLabelArea(child, hvg, x+branchW, yOff, pxPerHap, font, drawRectangle); + rDrawPhyloTreeInLabelArea(child, hvg, x+branchW, yOff, pxPerHap, font, highlightSamples); struct nodeCoords *childCoords = child->priv; int childY = yOff + ((0.5 + childCoords->rank) * pxPerHap); hvGfxLine(hvg, x, childY, x+branchW, childY, color); if (minY < 0 || childY < minY) minY = childY; if (childY > maxY) maxY = childY; } // Draw a vertical line to connect the children hvGfxLine(hvg, x, minY, x, maxY, color); } else { // Leaf node -- draw a horizontal line, and label if there is space to right of tree struct nodeCoords *coords = node->priv; - int y = yOff + ((0.5 + coords->rank) * pxPerHap); - hvGfxLine(hvg, x, y, x+branchW, y, color); + int yLine = yOff + ((0.5 + coords->rank) * pxPerHap); + int yBox = yLine - pxPerHap / 2; + int yText = yLine - tl.fontHeight / 2; + // Dunno why but the default font seems to draw with the baseline at y while the other fonts + // draw with the mid line at y. + if (sameOk(tl.textSize, "8")) + yText += 2; + if (highlightSamples && node->ident->name && hashLookup(highlightSamples, node->ident->name)) + hvGfxBox(hvg, leftLabelX, yBox, leftLabelWidth, pxPerHap, + MAKECOLOR_32(170, 255, 255)); + hvGfxLine(hvg, x, yLine, x+branchW, yLine, color); int textX = x + branchW + 3; if (pxPerHap >= tl.fontHeight+1 && textX < labelEnd) - hvGfxText(hvg, textX, y, MG_BLACK, font, node->ident->name); + hvGfxText(hvg, textX, yText, MG_BLACK, font, node->ident->name); } } static void drawPhyloTreeInLabelArea(struct phyloTree *tree, struct hvGfx *hvg, int yOff, int clipHeight, int gtHapCount, - MgFont *font, boolean drawRectangle, - unsigned int *leafOrderToHapOrderStart, - unsigned int *leafOrderToHapOrderEnd) + MgFont *font, struct hash *highlightSamples) { struct hvGfx *hvgLL = (hvgSide != NULL) ? hvgSide : hvg; int clipXBak, clipYBak, clipWidthBak, clipHeightBak; hvGfxGetClip(hvgLL, &clipXBak, &clipYBak, &clipWidthBak, &clipHeightBak); hvGfxUnclip(hvgLL); hvGfxSetClip(hvgLL, leftLabelX, yOff, leftLabelWidth, clipHeight); -// Figure out rank (vertical position) and depth (horizontal position) of every node in tree: -phyloTreeAddNodeCoords(tree, leafOrderToHapOrderStart, leafOrderToHapOrderEnd, 0); // Draw the tree: int x = leftLabelX; double pxPerHap = (double)clipHeight / gtHapCount; -rDrawPhyloTreeInLabelArea(tree, hvgLL, x, yOff, pxPerHap, font, drawRectangle); +rDrawPhyloTreeInLabelArea(tree, hvgLL, x, yOff, pxPerHap, font, highlightSamples); // Restore the prior clipping: hvGfxUnclip(hvgLL); hvGfxSetClip(hvgLL, clipXBak, clipYBak, clipWidthBak, clipHeightBak); } +static void rHighlightSampleRows(struct phyloTree *node, struct hvGfx *hvg, int yOff, + double pxPerHap, struct hash *highlightSamples) +/* For each leaf node, if it is in highlightSamples then draw a highlight box for it. */ +{ +if (node->numEdges > 0) + { + int ix; + for (ix = 0; ix < node->numEdges; ix++) + { + struct phyloTree *child = node->edges[ix]; + rHighlightSampleRows(child, hvg, yOff, pxPerHap, highlightSamples); + } + } +else + { + // leaf node; highlight if it's in highlightSamples + if (node->ident->name && hashLookup(highlightSamples, node->ident->name)) + { + struct nodeCoords *coords = node->priv; + int y = yOff + (coords->rank * pxPerHap); + hvGfxBox(hvg, insideX, y, insideWidth, pxPerHap, MAKECOLOR_32(170, 255, 255)); + } + } +} + +struct hash *getHighlightSamples(struct trackDb *tdb) +/* Return a hash of node IDs to highlight in the phylo tree display, or NULL if none specified. */ +{ +struct hash *highlightSamples = NULL; +char *setting = cartOrTdbString(cart, tdb, "highlightIds", NULL); +if (isNotEmpty(setting)) + { + struct slName *list = slNameListFromComma(setting); + highlightSamples = hashFromSlNameList(list); + } +return highlightSamples; +} + static void vcfGtHapTreeFileDraw(struct track *tg, int seqStart, int seqEnd, struct hvGfx *hvg, int xOff, int yOff, int width, MgFont *font, Color color, enum trackVisibility vis) /* Draw rows in the same fashion as vcfHapClusterDraw, but instead of clustering, use the * order in which samples appear in the VCF file. */ { struct vcfFile *vcff = tg->extraUiData; enum hapColorMode colorMode; struct seqWindow *gSeqWin; struct txInfo *txiList; if (!vcfHapClusterDrawInit(tg, vcff, hvg, &colorMode, &gSeqWin, &txiList)) return; struct phyloTree *tree = getTreeFromFile(tg->tdb); int gtHapCount; unsigned int *leafOrderToHapOrderStart, *leafOrderToHapOrderEnd; unsigned int *gtHapOrder = gtHapOrderFromTree(vcff, tree, &leafOrderToHapOrderStart, &leafOrderToHapOrderEnd, >HapCount); +// Figure out rank (vertical position) and depth (horizontal position) of every node in tree: +phyloTreeAddNodeCoords(tree, leafOrderToHapOrderStart, leafOrderToHapOrderEnd, 0); +int extraPixel = (colorMode == altOnlyMode || colorMode == functionMode) ? 1 : 0; +int hapHeight = tg->height - CLIP_PAD - 2*extraPixel; +struct hash *highlightSamples = getHighlightSamples(tg->tdb); +if (highlightSamples) + { + double pxPerHap = (double)hapHeight / gtHapCount; + rHighlightSampleRows(tree, hvg, yOff+extraPixel, pxPerHap, highlightSamples); + } struct vcfRecord *rec; for (rec = vcff->records; rec != NULL; rec = rec->next) { enum soTerm funcTerm = soUnknown; if (colorMode == functionMode) funcTerm = functionForRecord(rec, gSeqWin, txiList); drawOneRec(rec, gtHapOrder, gtHapCount, tg, hvg, xOff, yOff, width, FALSE, FALSE, colorMode, funcTerm); } -int extraPixel = (colorMode == altOnlyMode || colorMode == functionMode) ? 1 : 0; -int hapHeight = tg->height - CLIP_PAD - 2*extraPixel; -char *treeAngle = cartOrTdbString(cart, tg->tdb, VCF_HAP_TREEANGLE_VAR, VCF_DEFAULT_HAP_TREEANGLE); -boolean drawRectangle = sameString(treeAngle, VCF_HAP_TREEANGLE_RECTANGLE); -drawPhyloTreeInLabelArea(tree, hvg, yOff+extraPixel, hapHeight, gtHapCount, font, - drawRectangle, leafOrderToHapOrderStart, leafOrderToHapOrderEnd); +drawPhyloTreeInLabelArea(tree, hvg, yOff+extraPixel, hapHeight, gtHapCount, font, highlightSamples); drawSampleTitles(vcff, yOff+extraPixel, hapHeight, gtHapOrder, gtHapCount, tg->track); } static int vcfHapClusterTotalHeight(struct track *tg, enum trackVisibility vis) /* Return height of haplotype graph (2 * #samples * lineHeight); * 2 because we're assuming diploid genomes here, no XXY, tetraploid etc. */ { const struct vcfFile *vcff = tg->extraUiData; if (vcff->records == NULL) return 0; int ploidy = sameString(chromName, "chrY") ? 1 : 2; int simpleHeight = ploidy * vcff->genotypeCount * tg->lineHeight; int defaultHeight = min(simpleHeight, VCF_DEFAULT_HAP_HEIGHT); char *tdbHeight = trackDbSettingOrDefault(tg->tdb, VCF_HAP_HEIGHT_VAR, NULL); if (isNotEmpty(tdbHeight))