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 org.json.JSONWriter;
import com.metaweb.gridworks.browsing.FilteredRows; 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.browsing.filters.RowFilter;
import com.metaweb.gridworks.expr.Evaluable; import com.metaweb.gridworks.expr.Evaluable;
import com.metaweb.gridworks.expr.MetaParser; import com.metaweb.gridworks.expr.MetaParser;
import com.metaweb.gridworks.expr.ParsingException; import com.metaweb.gridworks.expr.ParsingException;
import com.metaweb.gridworks.model.Column; import com.metaweb.gridworks.model.Column;
import com.metaweb.gridworks.model.Project; import com.metaweb.gridworks.model.Project;
import com.metaweb.gridworks.util.JSONUtilities;
public class ScatterplotFacet implements Facet { public class ScatterplotFacet implements Facet {
@ -22,213 +21,184 @@ public class ScatterplotFacet implements Facet {
* Configuration, from the client side * Configuration, from the client side
*/ */
protected String _name; // name of facet 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 String _x_expression; // expression to compute the x numeric value(s) per row
protected double _to; 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 boolean _selectNumeric; // whether the numeric selection applies, default true protected double _x_from; // the numeric selection for the x axis
protected boolean _selectNonNumeric; protected double _x_to;
protected boolean _selectBlank; protected double _y_from; // the numeric selection for the y axis
protected boolean _selectError; protected double _y_to;
protected double _x_min;
protected double _x_max;
protected double _y_min;
protected double _y_max;
/* /*
* Derived configuration data * Derived configuration data
*/ */
protected int _cellIndex; protected int _x_cellIndex;
protected Evaluable _eval; protected int _y_cellIndex;
protected String _errorMessage; 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 protected boolean _selected; // false if we're certain that all rows will match
// and there isn't any filtering to do // 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() { public ScatterplotFacet() {
} }
private static final String MIN = "min"; private static final String X_MIN = "x_min";
private static final String MAX = "max"; private static final String X_MAX = "x_max";
private static final String TO = "to"; private static final String X_TO = "x_to";
private static final String FROM = "from"; 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) public void write(JSONWriter writer, Properties options)
throws JSONException { throws JSONException {
writer.object(); writer.object();
writer.key("name"); writer.value(_name); writer.key("name"); writer.value(_name);
writer.key("expression"); writer.value(_expression); writer.key("x_expression"); writer.value(_x_expression);
writer.key("columnName"); writer.value(_columnName); writer.key("x_columnName"); writer.value(_x_columnName);
writer.key("mode"); writer.value(_mode);
if (_errorMessage != null) { if (_x_errorMessage != null) {
writer.key("error"); writer.value(_errorMessage); writer.key("x_error"); writer.value(_x_errorMessage);
} else { } else {
if (!Double.isInfinite(_min) && !Double.isInfinite(_max)) { if (!Double.isInfinite(_x_min) && !Double.isInfinite(_x_max)) {
writer.key(MIN); writer.value(_min); writer.key(X_MIN); writer.value(_x_min);
writer.key(MAX); writer.value(_max); writer.key(X_MAX); writer.value(_x_max);
writer.key("step"); writer.value(_step); writer.key(X_FROM); writer.value(_x_from);
writer.key(X_TO); writer.value(_x_to);
writer.key("bins"); writer.array(); }
for (int b : _bins) { if (!Double.isInfinite(_y_min) && !Double.isInfinite(_y_max)) {
writer.value(b); writer.key(Y_MIN); writer.value(_y_min);
} writer.key(Y_MAX); writer.value(_y_max);
writer.endArray(); writer.key(Y_FROM); writer.value(_y_from);
writer.key(Y_TO); writer.value(_y_to);
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(); writer.endObject();
} }
public void initializeFromJSON(Project project, JSONObject o) throws Exception { public void initializeFromJSON(Project project, JSONObject o) throws Exception {
_name = o.getString("name"); _name = o.getString("name");
_expression = o.getString("expression");
_columnName = o.getString("columnName");
if (_columnName.length() > 0) { _x_expression = o.getString("x_expression");
Column column = project.columnModel.getColumnByName(_columnName); _x_columnName = o.getString("x_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 { } else {
_errorMessage = "No column named " + _columnName; _x_errorMessage = "No column named " + _x_columnName;
} }
} else { } else {
_cellIndex = -1; _x_cellIndex = -1;
} }
try { try {
_eval = MetaParser.parse(_expression); _x_eval = MetaParser.parse(_x_expression);
} catch (ParsingException e) { } catch (ParsingException e) {
_errorMessage = e.getMessage(); _x_errorMessage = e.getMessage();
} }
_mode = o.getString("mode"); if (o.has(X_FROM) && o.has(X_TO)) {
if (MIN.equals(_mode)) { _x_from = o.getDouble(X_FROM);
if (o.has(FROM)) { _x_to = o.getDouble(X_TO);
_from = o.getDouble(FROM); _selected = true;
_selected = true; }
}
} else if (MAX.equals(_mode)) { _y_expression = o.getString("y_expression");
if (o.has(TO)) { _y_columnName = o.getString("y_columnName");
_to = o.getDouble(TO);
_selected = true; 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 { } else {
if (o.has(FROM) && o.has(TO)) { _y_cellIndex = -1;
_from = o.getDouble(FROM);
_to = o.getDouble(TO);
_selected = true;
}
} }
_selectNumeric = JSONUtilities.getBoolean(o, "selectNumeric", true); try {
_selectNonNumeric = JSONUtilities.getBoolean(o, "selectNonNumeric", true); _y_eval = MetaParser.parse(_y_expression);
_selectBlank = JSONUtilities.getBoolean(o, "selectBlank", true); } catch (ParsingException e) {
_selectError = JSONUtilities.getBoolean(o, "selectError", true); _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; _selected = true;
} }
} }
public RowFilter getRowFilter() { public RowFilter getRowFilter() {
if (_eval != null && _errorMessage == null && _selected) { if (_selected &&
if (MIN.equals(_mode)) { _x_eval != null && _x_errorMessage == null &&
return new ExpressionNumberComparisonRowFilter( _y_eval != null && _y_errorMessage == null)
_eval, _columnName, _cellIndex, _selectNumeric, _selectNonNumeric, _selectBlank, _selectError) { {
return new DualExpressionsNumberComparisonRowFilter(_x_eval, _x_columnName, _x_cellIndex, _y_eval, _y_columnName, _y_cellIndex) {
protected boolean checkValue(double d) { protected boolean checkValues(double x, double y) {
return d >= _from; 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 { } else {
return null; return null;
} }
} }
public void computeChoices(Project project, FilteredRows filteredRows) { public void computeChoices(Project project, FilteredRows filteredRows) {
if (_eval != null && _errorMessage == null) { if (_x_eval != null && _y_eval != null && _x_errorMessage == null && _y_errorMessage == null) {
Column column = project.columnModel.getColumnByCellIndex(_cellIndex); Column column_x = project.columnModel.getColumnByCellIndex(_x_cellIndex);
String key_x = "numeric-bin:" + _x_expression;
String key = "numeric-bin:" + _expression; NumericBinIndex index_x = (NumericBinIndex) column_x.getPrecompute(key_x);
NumericBinIndex index = (NumericBinIndex) column.getPrecompute(key); if (index_x == null) {
if (index == null) { index_x = new NumericBinIndex(project, _x_columnName, _x_cellIndex, _x_eval);
index = new NumericBinIndex(project, _columnName, _cellIndex, _eval); column_x.setPrecompute(key_x, index_x);
column.setPrecompute(key, index);
} }
_min = index.getMin(); _x_min = index_x.getMin();
_max = index.getMax(); _x_max = index_x.getMax();
_step = index.getStep();
_baseBins = index.getBins();
if (_selected) { if (_selected) {
_from = Math.max(_from, _min); _x_from = Math.max(_x_from, _x_min);
_to = Math.min(_to, _max); _x_to = Math.min(_x_to, _x_max);
} else { } else {
_from = _min; _x_from = _x_min;
_to = _max; _x_to = _x_max;
} }
ExpressionNumericRowBinner binner = Column column_y = project.columnModel.getColumnByCellIndex(_y_cellIndex);
new ExpressionNumericRowBinner(_eval, _columnName, _cellIndex, index); 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; if (_selected) {
_numericCount = binner.numericCount; _y_from = Math.max(_y_from, _y_min);
_nonNumericCount = binner.nonNumericCount; _y_to = Math.min(_y_to, _y_max);
_blankCount = binner.blankCount; } else {
_errorCount = binner.errorCount; _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) { if (columns.length > 0) {
var table = $('<table></table>').addClass("scatterplot-matrix-table")[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++) { for (var i = 0; i < columns.length; i++) {
var tr = table.insertRow(table.rows.length); var tr = table.insertRow(table.rows.length);
for (var j = 0; j < i; j++) { for (var j = 0; j < i; j++) {
var cx = columns[i]; var cx = columns[i].name;
var cy = columns[j]; var cy = columns[j].name;
var plotter_params = { var plotter_params = {
'cx' : cx.name, 'cx' : cx,
'cy' : cy.name, 'cy' : cy,
'w' : self._plot_size, 'w' : self._plot_size,
'h' : self._plot_size, 'h' : self._plot_size,
'dot': self._dot_size, 'dot': self._dot_size,
@ -111,21 +125,9 @@ ScatterplotDialog.prototype._renderMatrix = function() {
plotter: JSON.stringify(plotter_params) plotter: JSON.stringify(plotter_params)
} }
var url = "/command/get-scatterplot?" + $.param(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 cell = $(tr.insertCell(j));
var link = $('<a href="javascript:{}"></a>').attr("title",name).click(function() { var link = wrap($('<a href="javascript:{}"></a>').attr("title",name),cx,cy);
ui.browsingEngine.addFacet(
"scatterplot",
{
"name" : name,
"x_column" : cx.name,
"y_column" : cy.name,
"expression" : "value",
"mode" : "scatterplot"
}
);
//self._dismiss();
});
var plot = $('<img src="' + url + '" />').addClass("scatterplot").appendTo(link); var plot = $('<img src="' + url + '" />').addClass("scatterplot").appendTo(link);
link.appendTo(cell); link.appendTo(cell);
} }

View File

@ -29,15 +29,14 @@ ScatterplotFacet.prototype.getUIState = function() {
return json; return json;
}; };
ScatterplotFacet.prototype.getJSON = function() { ScatterplotFacet.prototype.getJSON = function() {
var o = { var o = {
type: "scatterplot", type: "scatterplot",
name: this._config.name, name: this._config.name,
mode: this._config.mode, x_columnName : this._config.x_columnName,
expression: this._config.expression, y_columnName : this._config.y_columnName,
x_column : this._config.x_column, x_expression: this._config.x_expression,
y_column : this._config.y_column, y_expression: this._config.y_expression,
}; };
return o; return o;
@ -73,39 +72,32 @@ ScatterplotFacet.prototype._initializeUI = function() {
this._plotDiv = $('<div>').addClass("facet-scatterplot-plot").appendTo(bodyDiv); this._plotDiv = $('<div>').addClass("facet-scatterplot-plot").appendTo(bodyDiv);
this._statusDiv = $('<div>').addClass("facet-scatterplot-status").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) { 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._error = false;
this._config.min = data.min; this._config.min_x = data.min_x;
this._config.max = data.max; this._config.max_x = data.max_x;
this._config.step = data.step; this._config.min_y = data.min_y;
this._baseBins = data.baseBins; this._config.max_y = data.max_y;
this._bins = data.bins;
switch (this._config.mode) { this._from_x = Math.max(data.from_x, this._config.min_x);
case "min": if ("to_x" in data) {
this._from = Math.max(data.from, this._config.min); this._to_x = Math.min(data.to_x, this._config.max_x);
break; } else {
case "max": this._to_x = data.max_x;
this._to = Math.min(data.to, this._config.max); }
break;
default: this._from_y = Math.max(data.from_y, this._config.min_y);
this._from = Math.max(data.from, this._config.min); if ("to_y" in data) {
if ("to" in data) { this._to_y = Math.min(data.to_y, this._config.max_y);
this._to = Math.min(data.to, this._config.max); } else {
} else { this._to_y = data.max_y;
this._to = data.max;
}
} }
this._numericCount = data.numericCount;
this._nonNumericCount = data.nonNumericCount;
this._blankCount = data.blankCount;
this._errorCount = data.errorCount;
} else { } else {
this._error = true; this._error = true;
this._errorMessage = "error" in data ? data.error : "Unknown error."; this._errorMessage = "error" in data ? data.error : "Unknown error.";
@ -122,10 +114,8 @@ ScatterplotFacet.prototype.render = function() {
if (this._error) { if (this._error) {
this._messageDiv.text(this._errorMessage).show(); this._messageDiv.text(this._errorMessage).show();
this._sliderDiv.hide(); this._plotDiv.hide();
this._histogramDiv.hide();
this._statusDiv.hide(); this._statusDiv.hide();
this._otherChoicesDiv.hide();
return; return;
} }
@ -134,12 +124,14 @@ ScatterplotFacet.prototype.render = function() {
this._statusDiv.show(); this._statusDiv.show();
this._plot.update( this._plot.update(
this._config.min, this._config.x_min,
this._config.max, this._config.x_max,
this._config.step, this._x_from,
[ this._baseBins, this._bins ], this._x_to,
this._from, this._config.y_min,
this._to this._config.y_max,
this._y_from,
this._y_to
); );
}; };
@ -148,7 +140,6 @@ ScatterplotFacet.prototype._remove = function() {
this._div = null; this._div = null;
this._config = null; this._config = null;
this._data = null;
}; };
ScatterplotFacet.prototype._updateRest = function() { ScatterplotFacet.prototype._updateRest = function() {

View File

@ -84,77 +84,77 @@ HistogramWidget.prototype._render = function() {
ctx.translate(0, canvas.height); ctx.translate(0, canvas.height);
ctx.scale(1, -1); ctx.scale(1, -1);
var stepPixels = canvas.width / this._binMatrix[0].length; var stepPixels = canvas.width / this._binMatrix[0].length;
var stepScale = stepPixels / this._range.step; var stepScale = stepPixels / this._range.step;
/* /*
* Draw axis * 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.save();
ctx.strokeStyle = "emptyBinColor" in options ? options.emptyBinColor : "#faa"; ctx.lineWidth = 0;
ctx.lineWidth = 1; ctx.fillStyle = color;
ctx.moveTo(0, 0); for (var c = 0; c < row.length; c++) {
ctx.lineTo(canvas.width, 0); var x = self._range.min + c * self._range.step;
ctx.stroke(); var y = row[c];
ctx.restore(); 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 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(); }
}; ctx.restore();
for (var r = 0; r < this._binMatrix.length; r++) { };
renderRow( for (var r = 0; r < this._binMatrix.length; r++) {
this._binMatrix[r], renderRow(
"binColors" in options && r < options.binColors.length ? this._binMatrix[r],
options.binColors[r] : "binColors" in options && r < options.binColors.length ?
makeColor(r) 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(
* Draw highlight (this._highlight.to - this._range.min) * stepScale,
*/ 0,
if (this._highlight !== null) { canvas.width - (this._highlight.to - this._range.min) * stepScale,
ctx.fillStyle = "rgba(192,192,192, 0.5)"; canvas.height
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(); ctx.restore();
}; };

View File

@ -2,39 +2,37 @@ function ScatterplotWidget(elmt, options) {
this._elmt = elmt; this._elmt = elmt;
this._options = options; 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._range = null;
this._binMatrix = null;
this._highlight = null; this._highlight = null;
this._initializeUI(); this._initializeUI();
} }
ScatterplotWidget.prototype.highlight = function(from, to) { ScatterplotWidget.prototype.highlight = function(from_x, to_x, from_y, to_y) {
this._highlight = { from: from, to: to }; this._highlight = { from_x: from_x, to_x: to_x, from_y: from_y, to_y: to_y };
this._update(); this._update();
}; };
ScatterplotWidget.prototype.update = function(min, max, step, binMatrix, from, to) { ScatterplotWidget.prototype.update = function(x_min, x_max, x_from, x_to, y_min, y_max, y_from, y_to) {
if (typeof min == "undefined" || typeof binMatrix == "undefined" || binMatrix.length === 0 || binMatrix[0].length === 0) { if (typeof x_min == "undefined" || typeof y_min == "undefined") {
this._range = null; this._range = null;
this._binMatrix = null;
this._highlight = null; this._highlight = null;
this._elmt.hide(); this._elmt.hide();
} else { } else {
this._range = { min: min, max: max, step: step }; this._range = { x_min: x_min, x_max: x_max, y_min: y_min, y_max: y_max };
this._binMatrix = binMatrix;
this._peak = 0; if (typeof from_x != "undefined" && typeof to_x != "undefined" &&
for (var r = 0; r < binMatrix.length; r++) { typeof from_y != "undefined" && typeof to_y != "undefined")
var row = binMatrix[r]; {
for (var c = 0; c < row.length; c++) { this._highlight = { from_x: from_x, to_x: to_x, from_y: from_y, to_y: to_y };
this._peak = Math.max(this._peak, row[c]);
}
}
if (typeof from != "undefined" && typeof to != "undefined") {
this._highlight = { from: from, to: to };
} }
this._update(); this._update();
@ -42,16 +40,16 @@ ScatterplotWidget.prototype.update = function(min, max, step, binMatrix, from, t
}; };
ScatterplotWidget.prototype._update = function() { ScatterplotWidget.prototype._update = function() {
if (this._binMatrix !== null) { if (this._highlight !== null) {
if (this._highlight !== null) { this._highlight.from_x = Math.max(this._highlight.from_x, this._range.min_x);
this._highlight.from = Math.max(this._highlight.from, this._range.min); this._highlight.to_x = Math.min(this._highlight.to_x, this._range.max_x);
this._highlight.to = Math.min(this._highlight.to, this._range.max); 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();
} }
this._elmt.show();
this._resize();
this._render();
}; };
ScatterplotWidget.prototype._initializeUI = function() { ScatterplotWidget.prototype._initializeUI = function() {
@ -59,16 +57,16 @@ ScatterplotWidget.prototype._initializeUI = function() {
.empty() .empty()
.hide() .hide()
.addClass("scatterplot-widget") .addClass("scatterplot-widget")
.html( .html('<canvas bind="canvas"></canvas>');
'<canvas bind="canvas"></canvas>'
);
this._elmts = DOM.bind(this._elmt); this._elmts = DOM.bind(this._elmt);
}; };
ScatterplotWidget.prototype._resize = function() { ScatterplotWidget.prototype._resize = function() {
this._elmts.canvas.attr("height", "height" in this._options ? this._options.height : 50); this._plotter.w = this._elmts.canvas.width();
this._elmts.canvas.attr("width", 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() { ScatterplotWidget.prototype._render = function() {
@ -81,80 +79,31 @@ ScatterplotWidget.prototype._render = function() {
ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.save(); ctx.save();
ctx.translate(0, canvas.height);
ctx.scale(1, -1);
var stepPixels = canvas.width / this._binMatrix[0].length; var img = new Image();
var stepScale = stepPixels / this._range.step; 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);
* Draw axis ctx.scale(1, -1);
*/
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 something else
* 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(); 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)
);
} }
img2.src = self._get_image_url(self._plotter) + "&color=000088&dot=0.3";
/* }
* Draw highlight img.src = self._get_image_url(self._plotter) + "&color=000000&dot=0.2";
*/ };
if (this._highlight !== null) {
ctx.fillStyle = "rgba(192,192,192, 0.5)"; ScatterplotWidget.prototype._get_image_url = function(o) {
ctx.globalCompositeOperation = "source-over"; var params = {
if (this._highlight.from > this._range.min) { project: theProject.id,
ctx.fillRect( engine: JSON.stringify(ui.browsingEngine.getJSON()),
0, plotter: JSON.stringify(o)
0, }
(this._highlight.from - this._range.min) * stepScale, return "/command/get-scatterplot?" + $.param(params);
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();
}; };