98572a57d651f8256c0d21e690f5df23fc1ce3c7 braney Tue Jun 23 16:05:31 2026 -0700 hgTracks: show lifted (target) coordinates in quickLift bigBed mouseOvers, refs #37615 Under quickLift the bedRow used for mouseOver substitution is loaded from the source-assembly interval, so $chrom/${chromStart}/${chromEnd} reported the pre-lift position while the position bar showed the lifted position. Overwrite those three coordinate fields with the lifted bed's target coordinates before substituting, so the tooltip matches where the item is drawn. Replaces the earlier disclaimer note that flagged the discrepancy. diff --git src/hg/hgTracks/bigBedTrack.c src/hg/hgTracks/bigBedTrack.c index d48d1f4519b..4431139ac4b 100644 --- src/hg/hgTracks/bigBedTrack.c +++ src/hg/hgTracks/bigBedTrack.c @@ -653,30 +653,44 @@ { struct bigBedFilter *highlight; char *cartHighlightColor = cartOrTdbString(cart, tdb, HIGHLIGHT_COLOR_CART_VAR, HIGHLIGHT_COLOR_DEFAULT); for (highlight = highlights; highlight != NULL; highlight = highlight->next) { if (bigBedFilterOne(highlight, bedRow, bbi)) { unsigned rgb = bedParseColor(cartHighlightColor); Color color = bedColorToGfxColor(rgb); lf->highlightColor = color; lf->highlightMode = highlightBackground; } } } +static void quickLiftSetCoordFields(char **bedRow, struct bed *liftedBed, + char *startBuf, char *endBuf, int bufSize) +/* Overwrite the chrom/chromStart/chromEnd entries (the first three bigBed fields) of bedRow + * with the lifted, target-assembly coordinates from liftedBed. Under quickLift bedRow is + * loaded from the source-assembly interval, so without this a $chrom/${chromStart}/${chromEnd} + * mouseOver substitution would report the pre-lift position instead of where the item is drawn. */ +{ +safef(startBuf, bufSize, "%u", liftedBed->chromStart); +safef(endBuf, bufSize, "%u", liftedBed->chromEnd); +bedRow[0] = liftedBed->chrom; +bedRow[1] = startBuf; +bedRow[2] = endBuf; +} + void bigBedAddLinkedFeaturesFromExt(struct track *track, char *chrom, int start, int end, int scoreMin, int scoreMax, boolean useItemRgb, int fieldCount, struct linkedFeatures **pLfList, int maxItems) /* Read in items in chrom:start-end from bigBed file named in track->bbiFileName, convert * them to linkedFeatures, and add to head of list. */ { struct lm *lm = lmInit(0); struct trackDb *tdb = track->tdb; char *mouseOverField = cartOrTdbString(cart, track->tdb, "mouseOverField", NULL); // check if this track can merge large items, this setting must be allowed in the trackDb // stanza for the track, but can be enabled/disabled via trackUi/right click menu so // we also need to check the cart for the current status int mergeCount = 0; boolean doWindowSizeFilter = trackDbSettingOn(track->tdb, MERGESPAN_TDB_SETTING); @@ -765,55 +779,41 @@ // mouseOvers can be built constructed via trackDb settings instead // of embedded directly in bigBed char *mouseOverPattern = NULL; char **fieldNames = NULL; if (!mouseOverIdx) { mouseOverPattern = cartOrTdbString(cart, track->tdb, "mouseOver", NULL); if (mouseOverPattern) { AllocArray(fieldNames, bbi->fieldCount); struct slName *field = NULL, *fields = bbFieldNames(bbi); int i = 0; for (field = fields; field != NULL; field = field->next) fieldNames[i++] = field->name; - - // Under quickLift, $chrom/${chromStart}/${chromEnd} substitutions resolve - // against the source assembly's row, so the tooltip will disagree with - // the position bar. Append a note so the discrepancy isn't surprising. - if (quickLiftFile && strstr(mouseOverPattern, "$chrom")) - { - char *sourceDb = trackDbSetting(track->tdb, "quickLiftDb"); - struct dyString *dy = dyStringNew(0); - dyStringAppend(dy, mouseOverPattern); - if (sourceDb) - dyStringPrintf(dy, "
(coordinates from source assembly %s)", sourceDb); - else - dyStringAppend(dy, "
(coordinates from source assembly)"); - mouseOverPattern = dyStringCannibalize(&dy); - } } } // a fake item that is the union of the items that span the current window struct linkedFeatures *spannedLf = NULL; unsigned filtered = 0; struct bed *bed = NULL, *bedCopy = NULL; for (bb = bbList; bb != NULL; bb = bb->next) { struct linkedFeatures *lf = NULL; + bedCopy = NULL; char *bedRow[bbi->fieldCount]; if (sameString(track->tdb->type, "bigPsl")) { // fill out bedRow to support mouseOver pattern replacements char startBuf[16], endBuf[16]; bigBedIntervalToRow(bb, chromName, startBuf, endBuf, bedRow, ArraySize(bedRow)); char *seq, *cds; struct psl *psl = pslFromBigPsl(chromName, bb, seqTypeField, &seq, &cds); int sizeMul = pslIsProtein(psl) ? 3 : 1; boolean isXeno = 0; // just affects grayIx boolean nameGetsPos = FALSE; // we want the name to stay the name if (sizeMul == 3) { // these tags are not currently supported by the drawing engine for protein psl @@ -907,31 +907,36 @@ int b = currColor.b + round((itemColor.b - currColor.b) / mergeCount); spannedLf->filterColor = MAKECOLOR_32(r,g,b); } } else { // setting the label here protects against the case when only one item would // have been merged. When this happens we warn in the track longLabel that // nothing happened and essentially make the spanned item be what the actual // item would be. If multiple items are merged then the labels and mouseOvers // will get fixed up later tmp->label = bigBedMakeLabel(track->tdb, track->labelColumns, bb, chromName); if (mouseOverIdx > 0) tmp->mouseOver = restField(bb, mouseOverIdx); else if (mouseOverPattern) + { + char qStartBuf[16], qEndBuf[16]; + if (quickLiftFile && bedCopy) + quickLiftSetCoordFields(bedRow, bedCopy, qStartBuf, qEndBuf, sizeof qStartBuf); tmp->mouseOver = replaceFieldInPattern(mouseOverPattern, bbi->fieldCount, fieldNames, bedRow); + } slAddHead(&spannedLf, tmp); } continue; // lf will be NULL, but these items aren't "filtered", they're merged } } if (lf == NULL) { filtered++; continue; } if (lf->label == NULL) lf->label = bigBedMakeLabel(track->tdb, track->labelColumns, bb, chromName); if (startsWith("bigGenePred", track->tdb->type) || startsWith("genePred", track->tdb->type)) @@ -950,32 +955,37 @@ { // Clone bb so that we'll have access the to extra fields contents. This is used in // alternate display modes for bigBeds (so far just "heatmap", but more are likely to come). struct bigBedInterval *bbCopy = CloneVar(bb); if (bbCopy->rest) bbCopy->rest = cloneMem(bbCopy->rest, strlen(bbCopy->rest)+1); bbCopy->next = NULL; lf->original = bbCopy; } if (lf->mouseOver == NULL) { if (mouseOverIdx > 0) lf->mouseOver = restField(bb, mouseOverIdx); else if (mouseOverPattern) + { + char qStartBuf[16], qEndBuf[16]; + if (quickLiftFile && bedCopy) + quickLiftSetCoordFields(bedRow, bedCopy, qStartBuf, qEndBuf, sizeof qStartBuf); lf->mouseOver = replaceFieldInPattern(mouseOverPattern, bbi->fieldCount, fieldNames, bedRow); } + } slAddHead(pLfList, lf); } if (filtered) labelTrackAsFilteredNumber(track, filtered); if (doWindowSizeFilter) // add the number of merged items to the track longLabel { char labelBuf[256]; if (mergeCount > 1) safef(labelBuf, sizeof(labelBuf), " (Merged %d items)", mergeCount); else safef(labelBuf, sizeof(labelBuf), " (No Items Merged in window)"); track->longLabel = catTwoStrings(track->longLabel, labelBuf);