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('');
+ 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;
+}