skeleton code for scatterfacet
git-svn-id: http://google-refine.googlecode.com/svn/trunk@453 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
parent
e154a7e02d
commit
7ab1acd801
@ -13,6 +13,7 @@ import com.metaweb.gridworks.Jsonizable;
|
||||
import com.metaweb.gridworks.browsing.facets.Facet;
|
||||
import com.metaweb.gridworks.browsing.facets.ListFacet;
|
||||
import com.metaweb.gridworks.browsing.facets.RangeFacet;
|
||||
import com.metaweb.gridworks.browsing.facets.ScatterplotFacet;
|
||||
import com.metaweb.gridworks.browsing.facets.TextSearchFacet;
|
||||
import com.metaweb.gridworks.browsing.filters.RowFilter;
|
||||
import com.metaweb.gridworks.model.Project;
|
||||
@ -66,6 +67,8 @@ public class Engine implements Jsonizable {
|
||||
facet = new ListFacet();
|
||||
} else if ("range".equals(type)) {
|
||||
facet = new RangeFacet();
|
||||
} else if ("scatterplot".equals(type)) {
|
||||
facet = new ScatterplotFacet();
|
||||
} else if ("text".equals(type)) {
|
||||
facet = new TextSearchFacet();
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ public class ScatterplotCharter {
|
||||
private static final double px = 0.5f;
|
||||
|
||||
boolean process = true;
|
||||
boolean smoothed = false;
|
||||
|
||||
int width = 50;
|
||||
int height = 50;
|
||||
@ -68,7 +69,7 @@ public class ScatterplotCharter {
|
||||
|
||||
BufferedImage image;
|
||||
Graphics2D g2;
|
||||
|
||||
|
||||
public DrawingRowVisitor(Project project, JSONObject o) throws JSONException {
|
||||
String col_x_name = o.getString("cx");
|
||||
Column column_x = project.columnModel.getColumnByName(col_x_name);
|
||||
@ -135,7 +136,7 @@ public class ScatterplotCharter {
|
||||
{
|
||||
double xv = ((Number) cellx.value).doubleValue();
|
||||
double yv = ((Number) celly.value).doubleValue();
|
||||
|
||||
|
||||
double x = (xv - min_x) * w / max_x;
|
||||
double y = (yv - min_y) * h / max_y;
|
||||
g2.fill(new Rectangle2D.Double(x, y, px, px));
|
||||
|
@ -0,0 +1,234 @@
|
||||
package com.metaweb.gridworks.browsing.facets;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONWriter;
|
||||
|
||||
import com.metaweb.gridworks.browsing.FilteredRows;
|
||||
import com.metaweb.gridworks.browsing.filters.ExpressionNumberComparisonRowFilter;
|
||||
import com.metaweb.gridworks.browsing.filters.RowFilter;
|
||||
import com.metaweb.gridworks.expr.Evaluable;
|
||||
import com.metaweb.gridworks.expr.MetaParser;
|
||||
import com.metaweb.gridworks.expr.ParsingException;
|
||||
import com.metaweb.gridworks.model.Column;
|
||||
import com.metaweb.gridworks.model.Project;
|
||||
import com.metaweb.gridworks.util.JSONUtilities;
|
||||
|
||||
public class ScatterplotFacet implements Facet {
|
||||
|
||||
/*
|
||||
* Configuration, from the client side
|
||||
*/
|
||||
protected String _name; // name of facet
|
||||
protected String _expression; // expression to compute numeric value(s) per row
|
||||
protected String _columnName; // column to base expression on, if any
|
||||
protected String _mode; // "range", MIN, MAX
|
||||
|
||||
protected double _from; // the numeric selection
|
||||
protected double _to;
|
||||
|
||||
protected boolean _selectNumeric; // whether the numeric selection applies, default true
|
||||
protected boolean _selectNonNumeric;
|
||||
protected boolean _selectBlank;
|
||||
protected boolean _selectError;
|
||||
|
||||
/*
|
||||
* Derived configuration data
|
||||
*/
|
||||
protected int _cellIndex;
|
||||
protected Evaluable _eval;
|
||||
protected String _errorMessage;
|
||||
protected boolean _selected; // false if we're certain that all rows will match
|
||||
// and there isn't any filtering to do
|
||||
|
||||
/*
|
||||
* Computed data, to return to the client side
|
||||
*/
|
||||
protected double _min;
|
||||
protected double _max;
|
||||
protected double _step;
|
||||
protected int[] _baseBins;
|
||||
protected int[] _bins;
|
||||
|
||||
protected int _numericCount;
|
||||
protected int _nonNumericCount;
|
||||
protected int _blankCount;
|
||||
protected int _errorCount;
|
||||
|
||||
public ScatterplotFacet() {
|
||||
}
|
||||
|
||||
private static final String MIN = "min";
|
||||
private static final String MAX = "max";
|
||||
private static final String TO = "to";
|
||||
private static final String FROM = "from";
|
||||
|
||||
public void write(JSONWriter writer, Properties options)
|
||||
throws JSONException {
|
||||
|
||||
writer.object();
|
||||
writer.key("name"); writer.value(_name);
|
||||
writer.key("expression"); writer.value(_expression);
|
||||
writer.key("columnName"); writer.value(_columnName);
|
||||
writer.key("mode"); writer.value(_mode);
|
||||
|
||||
if (_errorMessage != null) {
|
||||
writer.key("error"); writer.value(_errorMessage);
|
||||
} else {
|
||||
if (!Double.isInfinite(_min) && !Double.isInfinite(_max)) {
|
||||
writer.key(MIN); writer.value(_min);
|
||||
writer.key(MAX); writer.value(_max);
|
||||
writer.key("step"); writer.value(_step);
|
||||
|
||||
writer.key("bins"); writer.array();
|
||||
for (int b : _bins) {
|
||||
writer.value(b);
|
||||
}
|
||||
writer.endArray();
|
||||
|
||||
writer.key("baseBins"); writer.array();
|
||||
for (int b : _baseBins) {
|
||||
writer.value(b);
|
||||
}
|
||||
writer.endArray();
|
||||
|
||||
if (MIN.equals(_mode)) {
|
||||
writer.key(FROM); writer.value(_from);
|
||||
} else if (MAX.equals(_mode)) {
|
||||
writer.key(TO); writer.value(_to);
|
||||
} else {
|
||||
writer.key(FROM); writer.value(_from);
|
||||
writer.key(TO); writer.value(_to);
|
||||
}
|
||||
}
|
||||
|
||||
writer.key("numericCount"); writer.value(_numericCount);
|
||||
writer.key("nonNumericCount"); writer.value(_nonNumericCount);
|
||||
writer.key("blankCount"); writer.value(_blankCount);
|
||||
writer.key("errorCount"); writer.value(_errorCount);
|
||||
}
|
||||
writer.endObject();
|
||||
}
|
||||
|
||||
public void initializeFromJSON(Project project, JSONObject o) throws Exception {
|
||||
_name = o.getString("name");
|
||||
_expression = o.getString("expression");
|
||||
_columnName = o.getString("columnName");
|
||||
|
||||
if (_columnName.length() > 0) {
|
||||
Column column = project.columnModel.getColumnByName(_columnName);
|
||||
if (column != null) {
|
||||
_cellIndex = column.getCellIndex();
|
||||
} else {
|
||||
_errorMessage = "No column named " + _columnName;
|
||||
}
|
||||
} else {
|
||||
_cellIndex = -1;
|
||||
}
|
||||
|
||||
try {
|
||||
_eval = MetaParser.parse(_expression);
|
||||
} catch (ParsingException e) {
|
||||
_errorMessage = e.getMessage();
|
||||
}
|
||||
|
||||
_mode = o.getString("mode");
|
||||
if (MIN.equals(_mode)) {
|
||||
if (o.has(FROM)) {
|
||||
_from = o.getDouble(FROM);
|
||||
_selected = true;
|
||||
}
|
||||
} else if (MAX.equals(_mode)) {
|
||||
if (o.has(TO)) {
|
||||
_to = o.getDouble(TO);
|
||||
_selected = true;
|
||||
}
|
||||
} else {
|
||||
if (o.has(FROM) && o.has(TO)) {
|
||||
_from = o.getDouble(FROM);
|
||||
_to = o.getDouble(TO);
|
||||
_selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
_selectNumeric = JSONUtilities.getBoolean(o, "selectNumeric", true);
|
||||
_selectNonNumeric = JSONUtilities.getBoolean(o, "selectNonNumeric", true);
|
||||
_selectBlank = JSONUtilities.getBoolean(o, "selectBlank", true);
|
||||
_selectError = JSONUtilities.getBoolean(o, "selectError", true);
|
||||
|
||||
if (!_selectNumeric || !_selectNonNumeric || !_selectBlank || !_selectError) {
|
||||
_selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
public RowFilter getRowFilter() {
|
||||
if (_eval != null && _errorMessage == null && _selected) {
|
||||
if (MIN.equals(_mode)) {
|
||||
return new ExpressionNumberComparisonRowFilter(
|
||||
_eval, _columnName, _cellIndex, _selectNumeric, _selectNonNumeric, _selectBlank, _selectError) {
|
||||
|
||||
protected boolean checkValue(double d) {
|
||||
return d >= _from;
|
||||
};
|
||||
};
|
||||
} else if (MAX.equals(_mode)) {
|
||||
return new ExpressionNumberComparisonRowFilter(
|
||||
_eval, _columnName, _cellIndex, _selectNumeric, _selectNonNumeric, _selectBlank, _selectError) {
|
||||
|
||||
protected boolean checkValue(double d) {
|
||||
return d < _to;
|
||||
};
|
||||
};
|
||||
} else {
|
||||
return new ExpressionNumberComparisonRowFilter(
|
||||
_eval, _columnName, _cellIndex, _selectNumeric, _selectNonNumeric, _selectBlank, _selectError) {
|
||||
|
||||
protected boolean checkValue(double d) {
|
||||
return d >= _from && d < _to;
|
||||
};
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void computeChoices(Project project, FilteredRows filteredRows) {
|
||||
if (_eval != null && _errorMessage == null) {
|
||||
Column column = project.columnModel.getColumnByCellIndex(_cellIndex);
|
||||
|
||||
String key = "numeric-bin:" + _expression;
|
||||
NumericBinIndex index = (NumericBinIndex) column.getPrecompute(key);
|
||||
if (index == null) {
|
||||
index = new NumericBinIndex(project, _columnName, _cellIndex, _eval);
|
||||
column.setPrecompute(key, index);
|
||||
}
|
||||
|
||||
_min = index.getMin();
|
||||
_max = index.getMax();
|
||||
_step = index.getStep();
|
||||
_baseBins = index.getBins();
|
||||
|
||||
if (_selected) {
|
||||
_from = Math.max(_from, _min);
|
||||
_to = Math.min(_to, _max);
|
||||
} else {
|
||||
_from = _min;
|
||||
_to = _max;
|
||||
}
|
||||
|
||||
ExpressionNumericRowBinner binner =
|
||||
new ExpressionNumericRowBinner(_eval, _columnName, _cellIndex, index);
|
||||
|
||||
filteredRows.accept(project, binner);
|
||||
|
||||
_bins = binner.bins;
|
||||
_numericCount = binner.numericCount;
|
||||
_nonNumericCount = binner.nonNumericCount;
|
||||
_blankCount = binner.blankCount;
|
||||
_errorCount = binner.errorCount;
|
||||
}
|
||||
}
|
||||
}
|
@ -40,6 +40,7 @@ public class GetScatterplotCommand extends Command {
|
||||
|
||||
//Gridworks.log("drawn scatterplot in " + (System.currentTimeMillis() - start) + "ms");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
respondException(response, e);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -46,17 +46,37 @@ ScatterplotDialog.prototype._renderMatrix = function(columns) {
|
||||
for (var i = 0; i < columns.length; i++) {
|
||||
var tr = table.insertRow(table.rows.length);
|
||||
for (var j = 0; j < i; j++) {
|
||||
var url = "/command/get-scatterplot?" + $.param({
|
||||
var cx = columns[i];
|
||||
var cy = columns[j];
|
||||
var plotter_params = {
|
||||
'cx' : cx.name,
|
||||
'cy' : cy.name,
|
||||
'w' : 20,
|
||||
'h' : 20
|
||||
};
|
||||
var params = {
|
||||
project: theProject.id,
|
||||
engine: JSON.stringify(ui.browsingEngine.getJSON()),
|
||||
plotter: JSON.stringify({
|
||||
'cx' : columns[i].name,
|
||||
'cy' : columns[j].name,
|
||||
'w' : 20,
|
||||
'h' : 20
|
||||
})
|
||||
plotter: JSON.stringify(plotter_params)
|
||||
}
|
||||
var url = "/command/get-scatterplot?" + $.param(params);
|
||||
var name = cx.name + '(x) vs. ' + cy.name + '(y)';
|
||||
var cell = $(tr.insertCell(j));
|
||||
var link = $('<a href="javascript:{}"></a>').attr("title",name).click(function() {
|
||||
ui.browsingEngine.addFacet(
|
||||
"scatterplot",
|
||||
{
|
||||
"name" : name,
|
||||
"x_column" : cx.name,
|
||||
"y_column" : cy.name,
|
||||
"expression" : "value",
|
||||
"mode" : "scatterplot"
|
||||
}
|
||||
);
|
||||
//self._dismiss();
|
||||
});
|
||||
$(tr.insertCell(j)).html('<img class="scatterplot" title="' + columns[i].name + ' vs. ' + columns[j].name + '" src="' + url + '" />');
|
||||
var plot = $('<img src="' + url + '" />').addClass("scatterplot").appendTo(link);
|
||||
link.appendTo(cell);
|
||||
}
|
||||
$(tr.insertCell(i)).text(columns[i]);
|
||||
for (var j = i + 1; j < columns.length; j++) {
|
||||
|
156
src/main/webapp/scripts/facets/scatterplot-facet.js
Normal file
156
src/main/webapp/scripts/facets/scatterplot-facet.js
Normal file
@ -0,0 +1,156 @@
|
||||
function ScatterplotFacet(div, config, options) {
|
||||
this._div = div;
|
||||
this._config = config;
|
||||
this._options = options;
|
||||
|
||||
this._from_x = ("from_x" in this._config) ? this._config.from_x : null;
|
||||
this._to_x = ("to_x" in this._config) ? this._config.to_x : null;
|
||||
this._from_y = ("from_y" in this._config) ? this._config.from_y : null;
|
||||
this._to_y = ("to_y" in this._config) ? this._config.to_y : null;
|
||||
|
||||
this._error = false;
|
||||
this._initializedUI = false;
|
||||
}
|
||||
|
||||
ScatterplotFacet.prototype.reset = function() {
|
||||
// TODO
|
||||
};
|
||||
|
||||
ScatterplotFacet.reconstruct = function(div, uiState) {
|
||||
return new ScatterplotFacet(div, uiState.c, uiState.o);
|
||||
};
|
||||
|
||||
ScatterplotFacet.prototype.getUIState = function() {
|
||||
var json = {
|
||||
c: this.getJSON(),
|
||||
o: this._options
|
||||
};
|
||||
|
||||
return json;
|
||||
};
|
||||
|
||||
|
||||
ScatterplotFacet.prototype.getJSON = function() {
|
||||
var o = {
|
||||
type: "scatterplot",
|
||||
name: this._config.name,
|
||||
mode: this._config.mode,
|
||||
expression: this._config.expression,
|
||||
x_column : this._config.x_column,
|
||||
y_column : this._config.y_column,
|
||||
};
|
||||
|
||||
return o;
|
||||
};
|
||||
|
||||
ScatterplotFacet.prototype.hasSelection = function() {
|
||||
// TODO
|
||||
};
|
||||
|
||||
ScatterplotFacet.prototype._initializeUI = function() {
|
||||
var self = this;
|
||||
var container = this._div.empty();
|
||||
|
||||
var headerDiv = $('<div></div>').addClass("facet-title").appendTo(container);
|
||||
$('<span></span>').text(this._config.name).appendTo(headerDiv);
|
||||
|
||||
var resetButton = $('<a href="javascript:{}"></a>').addClass("facet-choice-link").text("reset").click(function() {
|
||||
self.reset();
|
||||
self._updateRest();
|
||||
}).prependTo(headerDiv);
|
||||
|
||||
var removeButton = $('<img>')
|
||||
.attr("src", "images/close.png")
|
||||
.attr("title", "Remove this facet")
|
||||
.addClass("facet-choice-link")
|
||||
.click(function() {
|
||||
self._remove();
|
||||
}).prependTo(headerDiv);
|
||||
|
||||
var bodyDiv = $('<div></div>').addClass("facet-scatterplot-body").appendTo(container);
|
||||
|
||||
this._messageDiv = $('<div>').text("Loading...").addClass("facet-scatterplot-message").appendTo(bodyDiv);
|
||||
this._plotDiv = $('<div>').addClass("facet-scatterplot-plot").appendTo(bodyDiv);
|
||||
this._statusDiv = $('<div>').addClass("facet-scatterplot-status").appendTo(bodyDiv);
|
||||
|
||||
this._plot = new ScatterplotWidget(this._plotDiv, { binColors: [ "#ccccff", "#6666ff" ] });
|
||||
};
|
||||
|
||||
ScatterplotFacet.prototype.updateState = function(data) {
|
||||
if ("min" in data && "max" in data) {
|
||||
this._error = false;
|
||||
|
||||
this._config.min = data.min;
|
||||
this._config.max = data.max;
|
||||
this._config.step = data.step;
|
||||
this._baseBins = data.baseBins;
|
||||
this._bins = data.bins;
|
||||
|
||||
switch (this._config.mode) {
|
||||
case "min":
|
||||
this._from = Math.max(data.from, this._config.min);
|
||||
break;
|
||||
case "max":
|
||||
this._to = Math.min(data.to, this._config.max);
|
||||
break;
|
||||
default:
|
||||
this._from = Math.max(data.from, this._config.min);
|
||||
if ("to" in data) {
|
||||
this._to = Math.min(data.to, this._config.max);
|
||||
} else {
|
||||
this._to = data.max;
|
||||
}
|
||||
}
|
||||
|
||||
this._numericCount = data.numericCount;
|
||||
this._nonNumericCount = data.nonNumericCount;
|
||||
this._blankCount = data.blankCount;
|
||||
this._errorCount = data.errorCount;
|
||||
} else {
|
||||
this._error = true;
|
||||
this._errorMessage = "error" in data ? data.error : "Unknown error.";
|
||||
}
|
||||
|
||||
this.render();
|
||||
};
|
||||
|
||||
ScatterplotFacet.prototype.render = function() {
|
||||
if (!this._initializedUI) {
|
||||
this._initializeUI();
|
||||
this._initializedUI = true;
|
||||
}
|
||||
|
||||
if (this._error) {
|
||||
this._messageDiv.text(this._errorMessage).show();
|
||||
this._sliderDiv.hide();
|
||||
this._histogramDiv.hide();
|
||||
this._statusDiv.hide();
|
||||
this._otherChoicesDiv.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
this._messageDiv.hide();
|
||||
this._plotDiv.show();
|
||||
this._statusDiv.show();
|
||||
|
||||
this._plot.update(
|
||||
this._config.min,
|
||||
this._config.max,
|
||||
this._config.step,
|
||||
[ this._baseBins, this._bins ],
|
||||
this._from,
|
||||
this._to
|
||||
);
|
||||
};
|
||||
|
||||
ScatterplotFacet.prototype._remove = function() {
|
||||
ui.browsingEngine.removeFacet(this);
|
||||
|
||||
this._div = null;
|
||||
this._config = null;
|
||||
this._data = null;
|
||||
};
|
||||
|
||||
ScatterplotFacet.prototype._updateRest = function() {
|
||||
Gridworks.update({ engineChanged: true });
|
||||
};
|
@ -15,6 +15,9 @@ function BrowsingEngine(div, facetConfigs) {
|
||||
case "range":
|
||||
facet = RangeFacet.reconstruct(elmt, facetConfig);
|
||||
break;
|
||||
case "scatterplot":
|
||||
facet = ScatterplotFacet.reconstruct(elmt, facetConfig);
|
||||
break;
|
||||
case "text":
|
||||
facet = TextSearchFacet.reconstruct(elmt, facetConfig);
|
||||
break;
|
||||
@ -111,6 +114,9 @@ BrowsingEngine.prototype.addFacet = function(type, config, options) {
|
||||
case "range":
|
||||
facet = new RangeFacet(elmt, config, options);
|
||||
break;
|
||||
case "scatterplot":
|
||||
facet = new ScatterplotFacet(elmt, config, options);
|
||||
break;
|
||||
case "text":
|
||||
facet = new TextSearchFacet(elmt, config, options);
|
||||
break;
|
||||
|
160
src/main/webapp/scripts/widgets/scatterplot-widget.js
Normal file
160
src/main/webapp/scripts/widgets/scatterplot-widget.js
Normal file
@ -0,0 +1,160 @@
|
||||
function ScatterplotWidget(elmt, options) {
|
||||
this._elmt = elmt;
|
||||
this._options = options;
|
||||
|
||||
this._range = null;
|
||||
this._binMatrix = null;
|
||||
this._highlight = null;
|
||||
|
||||
this._initializeUI();
|
||||
}
|
||||
|
||||
ScatterplotWidget.prototype.highlight = function(from, to) {
|
||||
this._highlight = { from: from, to: to };
|
||||
this._update();
|
||||
};
|
||||
|
||||
ScatterplotWidget.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;
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof from != "undefined" && typeof to != "undefined") {
|
||||
this._highlight = { from: from, to: to };
|
||||
}
|
||||
|
||||
this._update();
|
||||
}
|
||||
};
|
||||
|
||||
ScatterplotWidget.prototype._update = function() {
|
||||
if (this._binMatrix !== null) {
|
||||
if (this._highlight !== null) {
|
||||
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();
|
||||
this._resize();
|
||||
this._render();
|
||||
}
|
||||
};
|
||||
|
||||
ScatterplotWidget.prototype._initializeUI = function() {
|
||||
this._elmt
|
||||
.empty()
|
||||
.hide()
|
||||
.addClass("scatterplot-widget")
|
||||
.html(
|
||||
'<canvas bind="canvas"></canvas>'
|
||||
);
|
||||
|
||||
this._elmts = DOM.bind(this._elmt);
|
||||
};
|
||||
|
||||
ScatterplotWidget.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());
|
||||
};
|
||||
|
||||
ScatterplotWidget.prototype._render = function() {
|
||||
var self = this;
|
||||
var options = this._options;
|
||||
|
||||
var canvas = this._elmts.canvas[0];
|
||||
var ctx = canvas.getContext('2d');
|
||||
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
|
||||
*/
|
||||
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 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 x = self._range.min + c * self._range.step;
|
||||
var y = row[c];
|
||||
if (y > 0) {
|
||||
var left = c * stepPixels;
|
||||
var width = Math.ceil(stepPixels);
|
||||
var height = Math.ceil(y * canvas.height / self._peak);
|
||||
|
||||
ctx.fillRect(left, 0, width, 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)
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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();
|
||||
};
|
9
src/main/webapp/styles/widgets/scatterplot-widget.css
Normal file
9
src/main/webapp/styles/widgets/scatterplot-widget.css
Normal file
@ -0,0 +1,9 @@
|
||||
.scatterplot-widget {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.scatterplot-widget canvas {
|
||||
width: 100%;
|
||||
}
|
Loading…
Reference in New Issue
Block a user