From 91241539cfb826d3ff9ba93b65d7b49778096b99 Mon Sep 17 00:00:00 2001 From: David Huynh Date: Thu, 18 Mar 2010 20:45:52 +0000 Subject: [PATCH] Switched to a canvas-based implementation of histograms. git-svn-id: http://google-refine.googlecode.com/svn/trunk@319 7d457c2a-affb-35e4-300a-418c747d4874 --- src/main/webapp/project.html | 2 +- .../scripts/dialogs/clustering-dialog.js | 34 ++--- src/main/webapp/scripts/facets/range-facet.js | 37 ++--- .../webapp/scripts/util/histogram-widget.js | 126 ++++++++++++++++++ src/main/webapp/styles/project/browsing.css | 5 - .../webapp/styles/util/histogram-widget.css | 9 ++ 6 files changed, 154 insertions(+), 59 deletions(-) create mode 100644 src/main/webapp/scripts/util/histogram-widget.js create mode 100644 src/main/webapp/styles/util/histogram-widget.css diff --git a/src/main/webapp/project.html b/src/main/webapp/project.html index 0bf7b5f26..51ed45cf1 100644 --- a/src/main/webapp/project.html +++ b/src/main/webapp/project.html @@ -1 +1 @@ - Gridworks
starting up ...
\ No newline at end of file + Gridworks
starting up ...
\ No newline at end of file diff --git a/src/main/webapp/scripts/dialogs/clustering-dialog.js b/src/main/webapp/scripts/dialogs/clustering-dialog.js index d3257bb93..216a71d99 100644 --- a/src/main/webapp/scripts/dialogs/clustering-dialog.js +++ b/src/main/webapp/scripts/dialogs/clustering-dialog.js @@ -477,6 +477,9 @@ ClusteringDialog.Facet = function(dialog, title, property, elmt, clusters) { ).appendTo(elmt); this._elmts = DOM.bind(html); + + this._histogram = new HistogramWidget(this._elmts.histogramContainer, { binColors: [ "#aaaaff", "#000088" ] }); + this._elmts.slider.slider({ min: this._min, max: this._max, @@ -517,30 +520,13 @@ ClusteringDialog.Facet.prototype.update = function(clusters) { } var bins = this._computeDistribution(clusters); - - var max = 0; - for (var i = 0; i < this._baseBins.length; i++) { - max = Math.max(max, this._baseBins[i]); - } - - var values = []; - var diffs = []; - for (var i = 0; i < this._baseBins.length; i++) { - var v = Math.ceil(100 * bins[i] / max); - var diff = Math.ceil(100 * this._baseBins[i] / max) - v; - - values.push(v == 0 ? 0 : Math.max(2, v)); // use min 2 to make sure something shows up - diffs.push(diff == 0 ? 0 : Math.max(2, diff)); - } - - this._elmts.histogramContainer.empty(); - $('').attr("src", - "http://chart.apis.google.com/chart?" + [ - "chs=" + this._elmts.histogramContainer[0].offsetWidth + "x50", - "cht=bvs&chbh=r,0&chco=000088,aaaaff", - "chd=t:" + values.join(",") + "|" + diffs.join(",") - ].join("&") - ).appendTo(this._elmts.histogramContainer); + + this._histogram.update( + this._min, + this._max, + this._step, + [ this._baseBins, bins ] + ); }; ClusteringDialog.Facet.prototype._setRangeIndicators = function() { diff --git a/src/main/webapp/scripts/facets/range-facet.js b/src/main/webapp/scripts/facets/range-facet.js index e44629f4d..7b337f2b4 100644 --- a/src/main/webapp/scripts/facets/range-facet.js +++ b/src/main/webapp/scripts/facets/range-facet.js @@ -144,6 +144,8 @@ RangeFacet.prototype._initializeUI = function() { this._statusDiv = $('
').addClass("facet-range-status").appendTo(bodyDiv); this._otherChoicesDiv = $('
').addClass("facet-range-other-choices").appendTo(bodyDiv); + this._histogram = new HistogramWidget(this._histogramDiv, { binColors: [ "#aaaaff", "#000088" ] }); + var onSlide = function(event, ui) { switch (self._config.mode) { case "min": @@ -327,35 +329,12 @@ RangeFacet.prototype.render = function() { this._sliderDiv.slider("option", "max", this._config.max); this._sliderDiv.slider("option", "step", this._config.step); - var max = 0; - for (var i = 0; i < this._baseBins.length; i++) { - max = Math.max(max, this._baseBins[i]); - } - - if (max == 0) { - this._histogramDiv.hide(); - } else { - var values = []; - var diffs = []; - - for (var i = 0; i < this._baseBins.length; i++) { - var v = Math.ceil(100 * this._bins[i] / max); - var diff = Math.ceil(100 * this._baseBins[i] / max) - v; - - values.push(v == 0 ? 0 : Math.max(2, v)); // use min 2 to make sure something shows up - diffs.push(diff == 0 ? 0 : Math.max(2, diff)); - } - - this._histogramDiv.empty().show(); - $('').attr("src", - "http://chart.apis.google.com/chart?" + [ - "chs=" + this._histogramDiv[0].offsetWidth + "x50", - //"cht=ls&chm=o,ff6600,0,-1,3&chls=0", // use for line plot - "cht=bvs&chbh=r,0&chco=000088,aaaaff", // use for histogram - "chd=t:" + values.join(",") + "|" + diffs.join(",") - ].join("&") - ).appendTo(this._histogramDiv); - } + this._histogram.update( + this._config.min, + this._config.max, + this._config.step, + [ this._baseBins, this._bins ] + ); this._setRangeIndicators(); this._renderOtherChoices(); diff --git a/src/main/webapp/scripts/util/histogram-widget.js b/src/main/webapp/scripts/util/histogram-widget.js new file mode 100644 index 000000000..906ab1c3e --- /dev/null +++ b/src/main/webapp/scripts/util/histogram-widget.js @@ -0,0 +1,126 @@ +function HistogramWidget(elmt, options) { + this._elmt = elmt; + this._options = options; + + this._range = null; + this._binMatrix = null; + this._highlight = null; + + this._initializeUI(); +} + +HistogramWidget.prototype.highlight = function(from, to) { + this._highlight = { from: from, to: to }; + this._update(); +}; + +HistogramWidget.prototype.update = function(min, max, step, binMatrix) { + if (typeof min == "undefined" || typeof binMatrix == "undefined" || binMatrix.length === 0 || binMatrix[0].length === 0) { + this._range = null; + this._binMatrix = null; + this._highlight = null; + + this._elmt.hide(); + } else { + this._range = { min: min, max: max, step: step }; + this._binMatrix = binMatrix; + + this._peak = 0; + for (var r = 0; r < binMatrix.length; r++) { + var row = binMatrix[r]; + for (var c = 0; c < row.length; c++) { + this._peak = Math.max(this._peak, row[c]); + } + } + + this._update(); + } +}; + +HistogramWidget.prototype._update = function() { + if (this._binMatrix != null) { + if (this._highlight != null) { + this._highlight.from = Math.max(this._highlight.from, min); + this._highlight.to = Math.min(this._highlight.to, max); + } + + this._elmt.show(); + this._resize(); + this._render(); + } +}; + +HistogramWidget.prototype._initializeUI = function() { + this._elmt + .empty() + .hide() + .addClass("histogram-widget") + .html( + '' + ); + + this._elmts = DOM.bind(this._elmt); +}; + +HistogramWidget.prototype._resize = function() { + this._elmts.canvas.attr("height", "height" in this._options ? this._options.height : 50); + this._elmts.canvas.attr("width", this._elmts.canvas.width()); +}; + +HistogramWidget.prototype._render = function() { + var self = this; + var options = this._options; + + var canvas = this._elmts.canvas[0]; + var ctx = canvas.getContext('2d'); + ctx.clearRect(0, 0, canvas.width, canvas.height); + + ctx.save(); + ctx.translate(0, canvas.height); + ctx.scale(1, -1); + + /* + * Draw axis + */ + ctx.save(); + ctx.strokeStyle = "emptyBinColor" in options ? options.emptyBinColor : "#faa"; + ctx.lineWidth = 1; + ctx.moveTo(0, 0); + ctx.lineTo(canvas.width, 0); + ctx.stroke(); + ctx.restore(); + + /* + * Draw bins + */ + var stepPixels = canvas.width / this._binMatrix[0].length; + var makeColor = function(i) { + var n = Math.floor(15 * (self._binMatrix.length - i) / self._binMatrix.length); + var h = n.toString(16); + return "#" + h + h + h; + }; + var renderRow = function(row, color) { + ctx.save(); + ctx.lineWidth = 0; + ctx.fillStyle = color; + for (var c = 0; c < row.length; c++) { + var value = row[c]; + if (value > 0) { + var left = c * stepPixels; + var height = Math.ceil(value * canvas.height / self._peak); + ctx.fillRect(left, 0, Math.ceil(stepPixels), height); + } + } + ctx.restore(); + }; + for (var r = 0; r < this._binMatrix.length; r++) { + renderRow( + this._binMatrix[r], + "binColors" in options && r < options.binColors.length ? + options.binColors[r] : + makeColor(r) + ); + } + + ctx.restore(); +}; diff --git a/src/main/webapp/styles/project/browsing.css b/src/main/webapp/styles/project/browsing.css index 5ed1fdc24..69b7efca3 100644 --- a/src/main/webapp/styles/project/browsing.css +++ b/src/main/webapp/styles/project/browsing.css @@ -128,11 +128,6 @@ img.facet-choice-link { margin: 10px 4px; overflow: hidden; } -.facet-range-histogram img { - height: 50px; - position: relative; - left: -2px; -} .facet-range-slider { } .facet-range-status { diff --git a/src/main/webapp/styles/util/histogram-widget.css b/src/main/webapp/styles/util/histogram-widget.css new file mode 100644 index 000000000..f225171e7 --- /dev/null +++ b/src/main/webapp/styles/util/histogram-widget.css @@ -0,0 +1,9 @@ +.histogram-widget { + margin: 0; + padding: 0; + position: relative; +} + +.histogram-widget canvas { + width: 100%; +}