progress but still no worky on the scatterfacet

git-svn-id: http://google-refine.googlecode.com/svn/trunk@457 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
Stefano Mazzocchi 2010-04-13 03:44:42 +00:00
parent ba85f50e39
commit e232a90a73
6 changed files with 374 additions and 382 deletions

View File

@ -7,14 +7,13 @@ 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.DualExpressionsNumberComparisonRowFilter;
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 {
@ -22,213 +21,184 @@ 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 String _x_expression; // expression to compute the x numeric value(s) per row
protected String _y_expression; // expression to compute the y numeric value(s) per row
protected String _x_columnName; // column to base the x expression on, if any
protected String _y_columnName; // column to base the y expression on, if any
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;
protected double _x_from; // the numeric selection for the x axis
protected double _x_to;
protected double _y_from; // the numeric selection for the y axis
protected double _y_to;
protected double _x_min;
protected double _x_max;
protected double _y_min;
protected double _y_max;
/*
* Derived configuration data
*/
protected int _cellIndex;
protected Evaluable _eval;
protected String _errorMessage;
protected int _x_cellIndex;
protected int _y_cellIndex;
protected Evaluable _x_eval;
protected Evaluable _y_eval;
protected String _x_errorMessage;
protected String _y_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";
private static final String X_MIN = "x_min";
private static final String X_MAX = "x_max";
private static final String X_TO = "x_to";
private static final String X_FROM = "x_from";
private static final String Y_MIN = "y_min";
private static final String Y_MAX = "y_max";
private static final String Y_TO = "y_to";
private static final String Y_FROM = "y_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);
writer.key("x_expression"); writer.value(_x_expression);
writer.key("x_columnName"); writer.value(_x_columnName);
if (_errorMessage != null) {
writer.key("error"); writer.value(_errorMessage);
if (_x_errorMessage != null) {
writer.key("x_error"); writer.value(_x_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);
}
if (!Double.isInfinite(_x_min) && !Double.isInfinite(_x_max)) {
writer.key(X_MIN); writer.value(_x_min);
writer.key(X_MAX); writer.value(_x_max);
writer.key(X_FROM); writer.value(_x_from);
writer.key(X_TO); writer.value(_x_to);
}
if (!Double.isInfinite(_y_min) && !Double.isInfinite(_y_max)) {
writer.key(Y_MIN); writer.value(_y_min);
writer.key(Y_MAX); writer.value(_y_max);
writer.key(Y_FROM); writer.value(_y_from);
writer.key(Y_TO); writer.value(_y_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");
_x_expression = o.getString("x_expression");
_x_columnName = o.getString("x_columnName");
if (_columnName.length() > 0) {
Column column = project.columnModel.getColumnByName(_columnName);
if (column != null) {
_cellIndex = column.getCellIndex();
if (_x_columnName.length() > 0) {
Column x_column = project.columnModel.getColumnByName(_x_columnName);
if (x_column != null) {
_x_cellIndex = x_column.getCellIndex();
} else {
_errorMessage = "No column named " + _columnName;
_x_errorMessage = "No column named " + _x_columnName;
}
} else {
_cellIndex = -1;
_x_cellIndex = -1;
}
try {
_eval = MetaParser.parse(_expression);
_x_eval = MetaParser.parse(_x_expression);
} catch (ParsingException e) {
_errorMessage = e.getMessage();
_x_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;
if (o.has(X_FROM) && o.has(X_TO)) {
_x_from = o.getDouble(X_FROM);
_x_to = o.getDouble(X_TO);
_selected = true;
}
_y_expression = o.getString("y_expression");
_y_columnName = o.getString("y_columnName");
if (_y_columnName.length() > 0) {
Column y_column = project.columnModel.getColumnByName(_y_columnName);
if (y_column != null) {
_y_cellIndex = y_column.getCellIndex();
} else {
_y_errorMessage = "No column named " + _y_columnName;
}
} else {
if (o.has(FROM) && o.has(TO)) {
_from = o.getDouble(FROM);
_to = o.getDouble(TO);
_selected = true;
}
_y_cellIndex = -1;
}
_selectNumeric = JSONUtilities.getBoolean(o, "selectNumeric", true);
_selectNonNumeric = JSONUtilities.getBoolean(o, "selectNonNumeric", true);
_selectBlank = JSONUtilities.getBoolean(o, "selectBlank", true);
_selectError = JSONUtilities.getBoolean(o, "selectError", true);
try {
_y_eval = MetaParser.parse(_y_expression);
} catch (ParsingException e) {
_y_errorMessage = e.getMessage();
}
if (!_selectNumeric || !_selectNonNumeric || !_selectBlank || !_selectError) {
if (o.has(Y_FROM) && o.has(Y_TO)) {
_y_from = o.getDouble(Y_FROM);
_y_to = o.getDouble(Y_TO);
_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;
};
if (_selected &&
_x_eval != null && _x_errorMessage == null &&
_y_eval != null && _y_errorMessage == null)
{
return new DualExpressionsNumberComparisonRowFilter(_x_eval, _x_columnName, _x_cellIndex, _y_eval, _y_columnName, _y_cellIndex) {
protected boolean checkValues(double x, double y) {
return x >= _x_from && x < _x_to && y >= _y_from && y < _y_to;
};
} 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);
if (_x_eval != null && _y_eval != null && _x_errorMessage == null && _y_errorMessage == null) {
Column column_x = project.columnModel.getColumnByCellIndex(_x_cellIndex);
String key_x = "numeric-bin:" + _x_expression;
NumericBinIndex index_x = (NumericBinIndex) column_x.getPrecompute(key_x);
if (index_x == null) {
index_x = new NumericBinIndex(project, _x_columnName, _x_cellIndex, _x_eval);
column_x.setPrecompute(key_x, index_x);
}
_min = index.getMin();
_max = index.getMax();
_step = index.getStep();
_baseBins = index.getBins();
_x_min = index_x.getMin();
_x_max = index_x.getMax();
if (_selected) {
_from = Math.max(_from, _min);
_to = Math.min(_to, _max);
_x_from = Math.max(_x_from, _x_min);
_x_to = Math.min(_x_to, _x_max);
} else {
_from = _min;
_to = _max;
_x_from = _x_min;
_x_to = _x_max;
}
ExpressionNumericRowBinner binner =
new ExpressionNumericRowBinner(_eval, _columnName, _cellIndex, index);
Column column_y = project.columnModel.getColumnByCellIndex(_y_cellIndex);
String key_y = "numeric-bin:" + _y_expression;
NumericBinIndex index_y = (NumericBinIndex) column_y.getPrecompute(key_y);
if (index_y == null) {
index_y = new NumericBinIndex(project, _y_columnName, _y_cellIndex, _y_eval);
column_y.setPrecompute(key_y, index_y);
}
filteredRows.accept(project, binner);
_y_min = index_y.getMin();
_y_max = index_y.getMax();
_bins = binner.bins;
_numericCount = binner.numericCount;
_nonNumericCount = binner.nonNumericCount;
_blankCount = binner.blankCount;
_errorCount = binner.errorCount;
if (_selected) {
_y_from = Math.max(_y_from, _y_min);
_y_to = Math.min(_y_to, _y_max);
} else {
_y_from = _y_min;
_y_to = _y_max;
}
}
}
}

View File

@ -0,0 +1,80 @@
package com.metaweb.gridworks.browsing.filters;
import java.util.Collection;
import java.util.Properties;
import com.metaweb.gridworks.expr.Evaluable;
import com.metaweb.gridworks.expr.ExpressionUtils;
import com.metaweb.gridworks.model.Cell;
import com.metaweb.gridworks.model.Project;
import com.metaweb.gridworks.model.Row;
/**
* Judge if a row matches by evaluating two given expressions on the row, based on two different columns
* and checking the results. It's a match if the result satisfies some numeric comparisons.
*/
abstract public class DualExpressionsNumberComparisonRowFilter implements RowFilter {
final protected Evaluable _x_evaluable;
final protected String _x_columnName;
final protected int _x_cellIndex;
final protected Evaluable _y_evaluable;
final protected String _y_columnName;
final protected int _y_cellIndex;
public DualExpressionsNumberComparisonRowFilter(
Evaluable x_evaluable,
String x_columnName,
int x_cellIndex,
Evaluable y_evaluable,
String y_columnName,
int y_cellIndex
) {
_x_evaluable = x_evaluable;
_x_columnName = x_columnName;
_x_cellIndex = x_cellIndex;
_y_evaluable = y_evaluable;
_y_columnName = y_columnName;
_y_cellIndex = y_cellIndex;
}
public boolean filterRow(Project project, int rowIndex, Row row) {
Cell x_cell = _x_cellIndex < 0 ? null : row.getCell(_x_cellIndex);
Properties x_bindings = ExpressionUtils.createBindings(project);
ExpressionUtils.bind(x_bindings, row, rowIndex, _x_columnName, x_cell);
Object x_value = _x_evaluable.evaluate(x_bindings);
Cell y_cell = _y_cellIndex < 0 ? null : row.getCell(_y_cellIndex);
Properties y_bindings = ExpressionUtils.createBindings(project);
ExpressionUtils.bind(y_bindings, row, rowIndex, _y_columnName, y_cell);
Object y_value = _y_evaluable.evaluate(y_bindings);
if (x_value != null && y_value != null) {
if (x_value.getClass().isArray() || y_value.getClass().isArray()) {
return false;
} else if (x_value instanceof Collection<?> || y_value instanceof Collection<?>) {
return false;
} // else, fall through
}
return checkValue(x_value,y_value);
}
protected boolean checkValue(Object vx, Object vy) {
if (ExpressionUtils.isError(vx) || ExpressionUtils.isError(vy)) {
return false;
} else if (ExpressionUtils.isNonBlankData(vx) && ExpressionUtils.isNonBlankData(vy)) {
if (vx instanceof Number && vy instanceof Number) {
double dx = ((Number) vx).doubleValue();
double dy = ((Number) vy).doubleValue();
return (!Double.isInfinite(dx) && !Double.isNaN(dx) && !Double.isInfinite(dy) && !Double.isNaN(dy) && checkValue(dx,dy));
} else {
return false;
}
} else {
return false;
}
}
abstract protected boolean checkValues(double dx, double dy);
}

View File

@ -92,14 +92,28 @@ ScatterplotDialog.prototype._renderMatrix = function() {
if (columns.length > 0) {
var table = $('<table></table>').addClass("scatterplot-matrix-table")[0];
var wrap = function(element,cx,cy) {
return element.click(function() {
var options = {
"name" : name,
"x_columnName" : cx,
"y_columnName" : cy,
"x_expression" : "value",
"y_expression" : "value",
};
ui.browsingEngine.addFacet("scatterplot",options);
//self._dismiss();
});
};
for (var i = 0; i < columns.length; i++) {
var tr = table.insertRow(table.rows.length);
for (var j = 0; j < i; j++) {
var cx = columns[i];
var cy = columns[j];
var cx = columns[i].name;
var cy = columns[j].name;
var plotter_params = {
'cx' : cx.name,
'cy' : cy.name,
'cx' : cx,
'cy' : cy,
'w' : self._plot_size,
'h' : self._plot_size,
'dot': self._dot_size,
@ -111,21 +125,9 @@ ScatterplotDialog.prototype._renderMatrix = function() {
plotter: JSON.stringify(plotter_params)
}
var url = "/command/get-scatterplot?" + $.param(params);
var name = cx.name + '(x) vs. ' + cy.name + '(y)';
var name = cx + ' (x) vs. ' + cy + ' (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();
});
var link = wrap($('<a href="javascript:{}"></a>').attr("title",name),cx,cy);
var plot = $('<img src="' + url + '" />').addClass("scatterplot").appendTo(link);
link.appendTo(cell);
}

View File

@ -29,15 +29,14 @@ ScatterplotFacet.prototype.getUIState = function() {
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,
x_columnName : this._config.x_columnName,
y_columnName : this._config.y_columnName,
x_expression: this._config.x_expression,
y_expression: this._config.y_expression,
};
return o;
@ -72,40 +71,33 @@ ScatterplotFacet.prototype._initializeUI = function() {
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" ] });
this._plot = new ScatterplotWidget(this._plotDiv, this._config);
};
ScatterplotFacet.prototype.updateState = function(data) {
if ("min" in data && "max" in data) {
if ("min_x" in data && "max_x" in data && "max_y" in data && "min_y" 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;
this._config.min_x = data.min_x;
this._config.max_x = data.max_x;
this._config.min_y = data.min_y;
this._config.max_y = data.max_y;
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._from_x = Math.max(data.from_x, this._config.min_x);
if ("to_x" in data) {
this._to_x = Math.min(data.to_x, this._config.max_x);
} else {
this._to_x = data.max_x;
}
this._from_y = Math.max(data.from_y, this._config.min_y);
if ("to_y" in data) {
this._to_y = Math.min(data.to_y, this._config.max_y);
} else {
this._to_y = data.max_y;
}
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.";
@ -122,10 +114,8 @@ ScatterplotFacet.prototype.render = function() {
if (this._error) {
this._messageDiv.text(this._errorMessage).show();
this._sliderDiv.hide();
this._histogramDiv.hide();
this._plotDiv.hide();
this._statusDiv.hide();
this._otherChoicesDiv.hide();
return;
}
@ -134,12 +124,14 @@ ScatterplotFacet.prototype.render = function() {
this._statusDiv.show();
this._plot.update(
this._config.min,
this._config.max,
this._config.step,
[ this._baseBins, this._bins ],
this._from,
this._to
this._config.x_min,
this._config.x_max,
this._x_from,
this._x_to,
this._config.y_min,
this._config.y_max,
this._y_from,
this._y_to
);
};
@ -148,7 +140,6 @@ ScatterplotFacet.prototype._remove = function() {
this._div = null;
this._config = null;
this._data = null;
};
ScatterplotFacet.prototype._updateRest = function() {

View File

@ -84,77 +84,77 @@ HistogramWidget.prototype._render = function() {
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();
var stepPixels = canvas.width / this._binMatrix[0].length;
var stepScale = stepPixels / this._range.step;
/*
* 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);
}
/*
* 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)
}
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
);
}
/*
* 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
);
}
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();
};

View File

@ -2,39 +2,37 @@ function ScatterplotWidget(elmt, options) {
this._elmt = elmt;
this._options = options;
this._plotter = {
'cx' : options.x_column,
'cy' : options.y_column,
'ye' : options.x_expression,
'ye' : options.x_expression,
};
this._range = null;
this._binMatrix = null;
this._highlight = null;
this._initializeUI();
}
ScatterplotWidget.prototype.highlight = function(from, to) {
this._highlight = { from: from, to: to };
ScatterplotWidget.prototype.highlight = function(from_x, to_x, from_y, to_y) {
this._highlight = { from_x: from_x, to_x: to_x, from_y: from_y, to_y: to_y };
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) {
ScatterplotWidget.prototype.update = function(x_min, x_max, x_from, x_to, y_min, y_max, y_from, y_to) {
if (typeof x_min == "undefined" || typeof y_min == "undefined") {
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._range = { x_min: x_min, x_max: x_max, y_min: y_min, y_max: y_max };
if (typeof from_x != "undefined" && typeof to_x != "undefined" &&
typeof from_y != "undefined" && typeof to_y != "undefined")
{
this._highlight = { from_x: from_x, to_x: to_x, from_y: from_y, to_y: to_y };
}
this._update();
@ -42,16 +40,16 @@ ScatterplotWidget.prototype.update = function(min, max, step, binMatrix, from, t
};
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();
if (this._highlight !== null) {
this._highlight.from_x = Math.max(this._highlight.from_x, this._range.min_x);
this._highlight.to_x = Math.min(this._highlight.to_x, this._range.max_x);
this._highlight.from_y = Math.max(this._highlight.from_y, this._range.min_y);
this._highlight.to_y = Math.min(this._highlight.to_y, this._range.max_y);
}
this._elmt.show();
this._resize();
this._render();
};
ScatterplotWidget.prototype._initializeUI = function() {
@ -59,16 +57,16 @@ ScatterplotWidget.prototype._initializeUI = function() {
.empty()
.hide()
.addClass("scatterplot-widget")
.html(
'<canvas bind="canvas"></canvas>'
);
.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());
this._plotter.w = this._elmts.canvas.width();
this._plotter.h = ("height" in this._options) ? this._options.height : w;
this._elmts.canvas.attr("width", this._plotter.w);
this._elmts.canvas.attr("height", this._plotter.h);
};
ScatterplotWidget.prototype._render = function() {
@ -79,82 +77,33 @@ ScatterplotWidget.prototype._render = function() {
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);
}
}
var img = new Image();
img.onload = function(){
ctx.drawImage(img,0,0);
var img2 = new Image();
img2.onload = function(){
ctx.drawImage(img2,0,0);
ctx.translate(0, canvas.height);
ctx.scale(1, -1);
// draw something else
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();
}
img2.src = self._get_image_url(self._plotter) + "&color=000088&dot=0.3";
}
img.src = self._get_image_url(self._plotter) + "&color=000000&dot=0.2";
};
ScatterplotWidget.prototype._get_image_url = function(o) {
var params = {
project: theProject.id,
engine: JSON.stringify(ui.browsingEngine.getJSON()),
plotter: JSON.stringify(o)
}
return "/command/get-scatterplot?" + $.param(params);
};