From b54f7162a87ea5deeaae5c1eca14d8d037b50f03 Mon Sep 17 00:00:00 2001 From: David Huynh Date: Thu, 18 Mar 2010 22:58:06 +0000 Subject: [PATCH] Made histogram widget capable of highlighting the selected range. Added value.log() common numeric facet. git-svn-id: http://google-refine.googlecode.com/svn/trunk@320 7d457c2a-affb-35e4-300a-418c747d4874 --- .../scripts/dialogs/clustering-dialog.js | 16 +++++- src/main/webapp/scripts/facets/range-facet.js | 7 ++- .../webapp/scripts/util/histogram-widget.js | 54 +++++++++++++++---- .../views/data-table-column-header-ui.js | 14 +++++ src/main/webapp/styles/common.css | 2 + .../styles/dialogs/clustering-dialog.css | 6 +-- src/main/webapp/styles/project/browsing.css | 10 +++- 7 files changed, 90 insertions(+), 19 deletions(-) diff --git a/src/main/webapp/scripts/dialogs/clustering-dialog.js b/src/main/webapp/scripts/dialogs/clustering-dialog.js index 216a71d99..1e47efdad 100644 --- a/src/main/webapp/scripts/dialogs/clustering-dialog.js +++ b/src/main/webapp/scripts/dialogs/clustering-dialog.js @@ -462,6 +462,10 @@ ClusteringDialog.Facet = function(dialog, title, property, elmt, clusters) { if (this._binCount > 100) { this._step *= 2; this._binCount = Math.round((1 + this._binCount) / 2); + } else if (this._binCount < 3) { + this._step /= 2; + this._binCount *= 2; + this._max = (Math.ceil(this._max / this._step) * this._step); } this._baseBins = this._computeDistribution(clusters); @@ -478,12 +482,17 @@ ClusteringDialog.Facet = function(dialog, title, property, elmt, clusters) { this._elmts = DOM.bind(html); - this._histogram = new HistogramWidget(this._elmts.histogramContainer, { binColors: [ "#aaaaff", "#000088" ] }); + this._histogram = new HistogramWidget(this._elmts.histogramContainer, { binColors: [ "#ccccff", "#6666ff" ] }); this._elmts.slider.slider({ min: this._min, max: this._max, values: [ this._from, this._to ], + slide: function(evt, ui) { + self._from = ui.values[0]; + self._to = ui.values[1]; + self._setRangeIndicators(); + }, stop: function(evt, ui) { self._from = ui.values[0]; self._to = ui.values[1]; @@ -525,11 +534,14 @@ ClusteringDialog.Facet.prototype.update = function(clusters) { this._min, this._max, this._step, - [ this._baseBins, bins ] + [ this._baseBins, bins ], + this._from, + this._to ); }; ClusteringDialog.Facet.prototype._setRangeIndicators = function() { + this._histogram.highlight(this._from, this._to); this._elmts.selectionContainer.text(this._from + " to " + this._to); }; diff --git a/src/main/webapp/scripts/facets/range-facet.js b/src/main/webapp/scripts/facets/range-facet.js index 7b337f2b4..ed92790e2 100644 --- a/src/main/webapp/scripts/facets/range-facet.js +++ b/src/main/webapp/scripts/facets/range-facet.js @@ -144,7 +144,7 @@ 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" ] }); + this._histogram = new HistogramWidget(this._histogramDiv, { binColors: [ "#ccccff", "#6666ff" ] }); var onSlide = function(event, ui) { switch (self._config.mode) { @@ -264,6 +264,7 @@ RangeFacet.prototype._setRangeIndicators = function() { } this._statusDiv.text(text); + this._histogram.highlight(this._from, this._to); }; RangeFacet.prototype.updateState = function(data) { @@ -333,7 +334,9 @@ RangeFacet.prototype.render = function() { this._config.min, this._config.max, this._config.step, - [ this._baseBins, this._bins ] + [ this._baseBins, this._bins ], + this._from, + this._to ); this._setRangeIndicators(); diff --git a/src/main/webapp/scripts/util/histogram-widget.js b/src/main/webapp/scripts/util/histogram-widget.js index 906ab1c3e..e9b54571e 100644 --- a/src/main/webapp/scripts/util/histogram-widget.js +++ b/src/main/webapp/scripts/util/histogram-widget.js @@ -14,7 +14,7 @@ HistogramWidget.prototype.highlight = function(from, to) { this._update(); }; -HistogramWidget.prototype.update = function(min, max, step, binMatrix) { +HistogramWidget.prototype.update = function(min, max, step, binMatrix, from, to) { if (typeof min == "undefined" || typeof binMatrix == "undefined" || binMatrix.length === 0 || binMatrix[0].length === 0) { this._range = null; this._binMatrix = null; @@ -33,6 +33,10 @@ HistogramWidget.prototype.update = function(min, max, step, binMatrix) { } } + if (typeof from != "undefined" && typeof to != "undefined") { + this._highlight = { from: from, to: to }; + } + this._update(); } }; @@ -40,8 +44,8 @@ HistogramWidget.prototype.update = function(min, max, step, binMatrix) { 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._highlight.from = Math.max(this._highlight.from, this._range.min); + this._highlight.to = Math.min(this._highlight.to, this._range.max); } this._elmt.show(); @@ -73,12 +77,16 @@ HistogramWidget.prototype._render = function() { var canvas = this._elmts.canvas[0]; var ctx = canvas.getContext('2d'); - ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.save(); ctx.translate(0, canvas.height); ctx.scale(1, -1); + var stepPixels = canvas.width / this._binMatrix[0].length; + var stepScale = stepPixels / this._range.step; + /* * Draw axis */ @@ -93,7 +101,6 @@ HistogramWidget.prototype._render = function() { /* * 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); @@ -104,11 +111,14 @@ HistogramWidget.prototype._render = function() { ctx.lineWidth = 0; ctx.fillStyle = color; for (var c = 0; c < row.length; c++) { - var value = row[c]; - if (value > 0) { + var x = self._range.min + c * self._range.step; + var y = row[c]; + if (y > 0) { var left = c * stepPixels; - var height = Math.ceil(value * canvas.height / self._peak); - ctx.fillRect(left, 0, Math.ceil(stepPixels), height); + var width = Math.ceil(stepPixels); + var height = Math.ceil(y * canvas.height / self._peak); + + ctx.fillRect(left, 0, width, height); } } ctx.restore(); @@ -121,6 +131,30 @@ HistogramWidget.prototype._render = function() { makeColor(r) ); } - + + /* + * Draw highlight + */ + if (this._highlight != null) { + ctx.fillStyle = "rgba(192,192,192, 0.5)"; + ctx.globalCompositeOperation = "source-over"; + if (this._highlight.from > this._range.min) { + ctx.fillRect( + 0, + 0, + (this._highlight.from - this._range.min) * stepScale, + canvas.height + ); + } + if (this._highlight.to < this._range.max) { + ctx.fillRect( + (this._highlight.to - this._range.min) * stepScale, + 0, + canvas.width - (this._highlight.to - this._range.min) * stepScale, + canvas.height + ); + } + } + ctx.restore(); }; diff --git a/src/main/webapp/scripts/views/data-table-column-header-ui.js b/src/main/webapp/scripts/views/data-table-column-header-ui.js index 313f33c9e..0636e0433 100644 --- a/src/main/webapp/scripts/views/data-table-column-header-ui.js +++ b/src/main/webapp/scripts/views/data-table-column-header-ui.js @@ -119,6 +119,20 @@ DataTableColumnHeaderUI.prototype._createMenuForColumnHeader = function(elmt) { { label: "Common Numeric Facets", submenu: [ + { + label: "Numeric Log Facet", + click: function() { + ui.browsingEngine.addFacet( + "range", + { + "name" : self._column.name + ": value.log()", + "columnName" : self._column.name, + "expression" : "value.log()", + "mode" : "range" + } + ); + } + }, { label: "Text Length Facet", click: function() { diff --git a/src/main/webapp/styles/common.css b/src/main/webapp/styles/common.css index 6a39f0c2d..68c1c121e 100644 --- a/src/main/webapp/styles/common.css +++ b/src/main/webapp/styles/common.css @@ -135,11 +135,13 @@ img { .ui-widget-content a.ui-state-default.ui-slider-handle { background: url(../images/slider-left-bracket.png) no-repeat bottom left; border: none; + margin-left: -12px; top: -10px; } .ui-widget-content a.ui-state-default.ui-slider-handle:last-child { background: url(../images/slider-right-bracket.png) no-repeat bottom right; border: none; + margin-left: -3px; top: -10px; } .ui-slider .ui-slider-handle { diff --git a/src/main/webapp/styles/dialogs/clustering-dialog.css b/src/main/webapp/styles/dialogs/clustering-dialog.css index d02d6a349..a9230efd6 100644 --- a/src/main/webapp/styles/dialogs/clustering-dialog.css +++ b/src/main/webapp/styles/dialogs/clustering-dialog.css @@ -64,11 +64,11 @@ table.clustering-dialog-entry-table a:hover { font-weight: bold; } .clustering-dialog-facet-histogram { - margin: 10px 8px 5px 8px; - height: 52px; + margin: 10px; + overflow: hidden; } .clustering-dialog-facet-slider { - margin: 5px; + margin: 5px 10px; } .clustering-dialog-facet-selection { text-align: center; diff --git a/src/main/webapp/styles/project/browsing.css b/src/main/webapp/styles/project/browsing.css index 69b7efca3..6aef30821 100644 --- a/src/main/webapp/styles/project/browsing.css +++ b/src/main/webapp/styles/project/browsing.css @@ -125,11 +125,17 @@ img.facet-choice-link { color: #f88; } .facet-range-histogram { - margin: 10px 4px; + margin: 10px 0px; overflow: hidden; } -.facet-range-slider { +.facet-range-slider.ui-corner-all { + border-bottom-left-radius: 0px 0px; + border-bottom-right-radius: 0px 0px; + border-top-left-radius: 0px 0px; + border-top-right-radius: 0px 0px; } + + .facet-range-status { margin: 10px 0; text-align: center;