b33425c626414803bbddc702caecc256c5022277 max Fri Mar 20 09:10:59 2026 -0700 defining detailsHistogram tdb statements and an example histogram drawing code for it, used in the trexplorer track, refs #37273 diff --git src/hg/js/detailsHistogram.js src/hg/js/detailsHistogram.js new file mode 100644 index 00000000000..7044d99a075 --- /dev/null +++ src/hg/js/detailsHistogram.js @@ -0,0 +1,132 @@ +// detailsHistogram.js - Draw SVG histograms on hgc details pages from bigBed field data. +// Called automatically via the detailsJs trackDb mechanism. +// Expects bedDetails object with: track, chrom, start, end, fields, args +// args.histograms is an array of {binsField, valuesField, title} + +function detailsHistogram(bedDetails) { + if (!bedDetails || !bedDetails.args || !bedDetails.args.histograms) + return; + if (!bedDetails.fields) + return; + + var histograms = bedDetails.args.histograms; + for (var ci = 0; ci < histograms.length; ci++) { + var hist = histograms[ci]; + var binsStr = bedDetails.fields[hist.binsField]; + var valuesStr = bedDetails.fields[hist.valuesField]; + if (!binsStr || !valuesStr) + continue; + + var bins = binsStr.split(","); + var values = valuesStr.split(",").map(Number); + if (bins.length !== values.length || bins.length === 0) + continue; + + var svg = buildHistogramSvg(bins, values, hist.title); + + // Find the bins row by id attribute set by hgc and replace its value cell + var binsRow = document.getElementById("bfld_" + hist.binsField); + var valuesRow = document.getElementById("bfld_" + hist.valuesField); + if (binsRow) { + var valCell = binsRow.cells[1]; + if (valCell) + valCell.innerHTML = svg; + // Update the label to the histogram title + var labelCell = binsRow.cells[0]; + if (labelCell) + labelCell.innerHTML = hist.title || "Distribution"; + } + // Remove the values row since data is now visualized + if (valuesRow) + valuesRow.remove(); + } +} + +function buildHistogramSvg(bins, values, title) { + var maxVal = Math.max.apply(null, values); + if (maxVal === 0) + return "No data"; + + // Chart dimensions + var barWidth = Math.max(14, Math.min(36, Math.floor(600 / bins.length))); + var gap = Math.max(1, Math.floor(barWidth / 8)); + var chartHeight = 160; + var labelHeight = 30; + var topPad = 5; + var leftPad = 50; // room for y-axis labels + var svgWidth = leftPad + bins.length * barWidth + 10; + var svgHeight = chartHeight + labelHeight + topPad; + + var lines = []; + lines.push(''); + + // Y-axis: a few tick marks + var ticks = calcHistTicks(maxVal); + for (var ti = 0; ti < ticks.length; ti++) { + var tickVal = ticks[ti]; + var y = topPad + chartHeight - (tickVal / maxVal) * chartHeight; + lines.push(''); + lines.push('' + formatHistNumber(tickVal) + ''); + // Subtle grid line + lines.push(''); + } + + // Bars + for (var i = 0; i < bins.length; i++) { + var barHeight = (values[i] / maxVal) * chartHeight; + var bx = leftPad + i * barWidth + gap; + var by = topPad + chartHeight - barHeight; + var bw = barWidth - 2 * gap; + + lines.push(''); + lines.push('' + bins[i] + ': ' + values[i] + ''); + lines.push(''); + + // X-axis label (skip some if too crowded) + var labelEvery = Math.ceil(bins.length / (svgWidth / 30)); + if (i % labelEvery === 0 || bins.length <= 30) { + lines.push('' + bins[i] + ''); + } + } + + // Axis lines + lines.push(''); + lines.push(''); + + // X-axis label + lines.push('Allele size (repeat copies)'); + + lines.push(''); + return lines.join("\n"); +} + +function calcHistTicks(maxVal) { + // Return 3-5 nice tick values for the y-axis + if (maxVal <= 5) + return [1, 2, 3, 4, 5].filter(function(v) { return v <= maxVal; }); + var magnitude = Math.pow(10, Math.floor(Math.log10(maxVal))); + var step = magnitude; + if (maxVal / step < 3) + step = magnitude / 2; + else if (maxVal / step > 6) + step = magnitude * 2; + var ticks = []; + for (var v = step; v <= maxVal; v += step) + ticks.push(Math.round(v)); + return ticks; +} + +function formatHistNumber(n) { + if (n >= 1000000) return (n / 1000000).toFixed(1) + "M"; + if (n >= 1000) return (n / 1000).toFixed(1) + "K"; + return "" + n; +}