- updgraded commons-coded to the last version (needed for base64 encoding of data: uris)

- added the ability to embed the scatterplot inside the returned json data with data: uris (although it doesn't seem to work well)
- connected the selection logic to the scatterfacets (although it doesn't seem to filter the rows... and I'm puzzled as why)
- reduced cut/paste and code overlap between the scatterplot generator and the scatterplot facet


git-svn-id: http://google-refine.googlecode.com/svn/trunk@490 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
Stefano Mazzocchi 2010-04-17 03:00:38 +00:00
parent 8085208cf0
commit 7a716a4a1b
20 changed files with 1317 additions and 526 deletions

View File

@ -8,7 +8,7 @@
<classpathentry kind="lib" path="lib/jetty-6.1.22.jar" sourcepath="lib-src/jetty-6.1.22-sources.jar"/>
<classpathentry kind="lib" path="lib/jetty-util-6.1.22.jar" sourcepath="lib-src/jetty-util-6.1.22-sources.jar"/>
<classpathentry kind="lib" path="lib/log4j-1.2.15.jar" sourcepath="lib-src/log4j-1.2.15-sources.jar"/>
<classpathentry kind="lib" path="lib/commons-codec-1.3.jar" sourcepath="lib-src/commons-codec-1.3-sources.jar"/>
<classpathentry kind="lib" path="lib/commons-codec-1.4.jar" sourcepath="lib-src/commons-codec-1.4-sources.jar"/>
<classpathentry kind="lib" path="lib/commons-lang-2.5.jar" sourcepath="lib-src/commons-lang-2.5-sources.jar"/>
<classpathentry kind="lib" path="lib/commons-fileupload-1.2.1.jar" sourcepath="lib-src/commons-fileupload-1.2.1-sources.jar"/>
<classpathentry kind="lib" path="lib/json-20100208.jar" sourcepath="lib-src/json-20100208-sources.jar"/>

View File

@ -0,0 +1,3 @@
#Fri Apr 16 12:46:04 PDT 2010
eclipse.preferences.version=1
encoding//src/main/webapp/scripts/dialogs/scatterplot-dialog.js=UTF-8

Binary file not shown.

Binary file not shown.

BIN
lib/commons-codec-1.4.jar Normal file

Binary file not shown.

View File

@ -39,9 +39,11 @@ public class Engine implements Jsonizable {
public FilteredRows getFilteredRows(Facet except, boolean includeContextual) {
ConjunctiveFilteredRows cfr = new ConjunctiveFilteredRows(includeContextual, _includeDependent);
for (Facet facet : _facets) {
System.out.println("facet: " + facet);
if (facet != except) {
RowFilter rowFilter = facet.getRowFilter();
if (rowFilter != null) {
System.out.println(" rowFilter: " + rowFilter);
cfr.add(rowFilter);
}
}

View File

@ -1,202 +0,0 @@
package com.metaweb.gridworks.browsing.charting;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.IOException;
import java.io.OutputStream;
import javax.imageio.ImageIO;
import org.json.JSONException;
import org.json.JSONObject;
import com.metaweb.gridworks.browsing.Engine;
import com.metaweb.gridworks.browsing.FilteredRows;
import com.metaweb.gridworks.browsing.RowVisitor;
import com.metaweb.gridworks.browsing.facets.NumericBinIndex;
import com.metaweb.gridworks.expr.Evaluable;
import com.metaweb.gridworks.expr.MetaParser;
import com.metaweb.gridworks.expr.ParsingException;
import com.metaweb.gridworks.model.Cell;
import com.metaweb.gridworks.model.Column;
import com.metaweb.gridworks.model.Project;
import com.metaweb.gridworks.model.Row;
public class ScatterplotCharter {
private static final int LIN = 0;
private static final int LOG = 1;
private static final int POLAR = 2;
private static int getAxisDim(String type) {
if ("log".equals(type)) {
return LOG;
} else if ("pol".equals(type) || "polar".equals(type)) {
return POLAR;
} else {
return LIN;
}
}
public String getContentType() {
return "image/png";
}
public void draw(OutputStream output, Project project, Engine engine, JSONObject options) throws IOException, JSONException {
DrawingRowVisitor drawingVisitor = new DrawingRowVisitor(project, options);
FilteredRows filteredRows = engine.getAllFilteredRows(false);
filteredRows.accept(project, drawingVisitor);
ImageIO.write(drawingVisitor.getImage(), "png", output);
}
class DrawingRowVisitor implements RowVisitor {
boolean process = true;
int width;
int height;
int col_x;
int col_y;
int dim;
double w;
double h;
double min_x;
double min_y;
double max_x;
double max_y;
double delta_x;
double delta_y;
double log_delta_x;
double log_delta_y;
double rx;
double ry;
double dot;
Color color;
NumericBinIndex index_x;
NumericBinIndex index_y;
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);
if (column_x != null) {
col_x = column_x.getCellIndex();
index_x = getBinIndex(project, column_x);
min_x = index_x.getMin();
max_x = index_x.getMax();
}
String col_y_name = o.getString("cy");
Column column_y = project.columnModel.getColumnByName(col_y_name);
if (column_y != null) {
col_y = column_y.getCellIndex();
index_y = getBinIndex(project, column_y);
min_y = index_y.getMin();
max_y = index_y.getMax();
}
width = (o.has("w")) ? o.getInt("w") : 50;
height = (o.has("h")) ? o.getInt("h") : 50;
dot = (o.has("dot")) ? o.getDouble("dot") : 0.2d;
dim = (o.has("dim")) ? getAxisDim(o.getString("dim")) : LIN;
delta_x = max_x - min_x;
delta_y = max_y - min_y;
if (dim == POLAR) {
rx = (o.has("rx")) ? o.getDouble("rx") : 0.0d;
ry = (o.has("ry")) ? o.getDouble("ry") : 0.0d;
} else if (dim == LOG) {
log_delta_x = Math.log10(delta_x);
log_delta_y = Math.log10(delta_y);
}
String color_str = (o.has("color")) ? o.getString("color") : "000000";
color = new Color(Integer.parseInt(color_str,16));
w = (double) width;
h = (double) height;
if (index_x.isNumeric() && index_y.isNumeric()) {
image = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
g2 = (Graphics2D) image.getGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(1.0f));
AffineTransform t = AffineTransform.getTranslateInstance(0,h);
t.concatenate(AffineTransform.getScaleInstance(1.0d, -1.0d));
g2.setTransform(t);
g2.setColor(color);
g2.setPaint(color);
} else {
image = new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR);
process = false;
}
}
private NumericBinIndex getBinIndex(Project project, Column column) {
String expression = "value";
String key = "numeric-bin:" + expression;
Evaluable eval = null;
try {
eval = MetaParser.parse(expression);
} catch (ParsingException e) {
// this should never happen
}
NumericBinIndex index = (NumericBinIndex) column.getPrecompute(key);
if (index == null) {
index = new NumericBinIndex(project, column.getName(), column.getCellIndex(), eval);
column.setPrecompute(key, index);
}
return index;
}
public boolean visit(Project project, int rowIndex, Row row, boolean includeContextual, boolean includeDependent) {
if (process) {
Cell cellx = row.getCell(col_x);
Cell celly = row.getCell(col_y);
if ((cellx != null && cellx.value != null && cellx.value instanceof Number) &&
(celly != null && celly.value != null && celly.value instanceof Number))
{
double xv = ((Number) cellx.value).doubleValue();
double yv = ((Number) celly.value).doubleValue();
double x;
double y;
if (dim == LOG) {
x = Math.log10(xv - min_x) * w / log_delta_x - dot / 2;
y = Math.log10(yv - min_y) * h / log_delta_y - dot / 2;
} else if (dim == POLAR) {
x = (xv - min_x) * w / delta_x - dot / 2;
y = (yv - min_y) * h / delta_y - dot / 2;
} else {
x = (xv - min_x) * w / delta_x - dot / 2;
y = (yv - min_y) * h / delta_y - dot / 2;
}
g2.fill(new Rectangle2D.Double(x, y, dot, dot));
}
}
return false;
}
public RenderedImage getImage() {
return image;
}
}
}

View File

@ -0,0 +1,103 @@
package com.metaweb.gridworks.browsing.facets;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import com.metaweb.gridworks.browsing.RowVisitor;
import com.metaweb.gridworks.model.Cell;
import com.metaweb.gridworks.model.Project;
import com.metaweb.gridworks.model.Row;
public class ScatterplotDrawingRowVisitor implements RowVisitor {
int col_x;
int col_y;
int size;
int dim_x;
int dim_y;
int rotation;
double l;
double dot;
double from_x;
double from_y;
double to_x;
double to_y;
double min_x;
double max_x;
double min_y;
double max_y;
Color color;
BufferedImage image;
Graphics2D g2;
public ScatterplotDrawingRowVisitor(
int col_x, int col_y, double min_x, double max_x, double min_y, double max_y,
int size, int dim_x, int dim_y, int rotation, double dot, Color color,
double from_x, double from_y, double to_x, double to_y)
{
this.col_x = col_x;
this.col_y = col_y;
this.min_x = min_x;
this.min_y = min_y;
this.max_x = max_x;
this.max_y = max_y;
this.size = size;
this.dot = dot;
this.color = color;
this.dim_x = dim_x;
this.dim_y = dim_y;
this.rotation = rotation;
this.from_x = from_x;
this.from_y = from_y;
this.to_x = to_x;
this.to_y = to_y;
l = (double) size;
image = new BufferedImage(size, size, BufferedImage.TYPE_4BYTE_ABGR);
g2 = (Graphics2D) image.getGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(1.0f));
AffineTransform t = AffineTransform.getTranslateInstance(0,l);
t.concatenate(AffineTransform.getScaleInstance(1.0d, -1.0d));
g2.setTransform(t);
g2.setColor(color);
g2.setPaint(color);
}
public boolean visit(Project project, int rowIndex, Row row, boolean includeContextual, boolean includeDependent) {
Cell cellx = row.getCell(col_x);
Cell celly = row.getCell(col_y);
if ((cellx != null && cellx.value != null && cellx.value instanceof Number) &&
(celly != null && celly.value != null && celly.value instanceof Number))
{
double xv = ((Number) cellx.value).doubleValue();
double yv = ((Number) celly.value).doubleValue();
Point2D.Double p = new Point2D.Double(xv,yv);
p = ScatterplotFacet.translateCoordinates(p, dim_x, dim_y, rotation, l, min_x, max_x, min_y, max_y);
g2.fill(new Rectangle2D.Double(p.x - dot / 2, p.y - dot / 2, dot, dot));
}
return false;
}
public RenderedImage getImage() {
return image;
}
}

View File

@ -1,11 +1,21 @@
package com.metaweb.gridworks.browsing.facets;
import java.awt.Color;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Properties;
import javax.imageio.ImageIO;
import org.apache.commons.codec.binary.Base64;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONWriter;
import com.metaweb.gridworks.Gridworks;
import com.metaweb.gridworks.browsing.FilteredRows;
import com.metaweb.gridworks.browsing.filters.DualExpressionsNumberComparisonRowFilter;
import com.metaweb.gridworks.browsing.filters.RowFilter;
@ -17,144 +27,221 @@ import com.metaweb.gridworks.model.Project;
public class ScatterplotFacet implements Facet {
public static final int LIN = 0;
public static final int LOG = 1;
public static final int NO_ROTATION = 0;
public static final int ROTATE_CW = 1;
public static final int ROTATE_CCW = 2;
/*
* Configuration, from the client side
*/
protected String _name; // name of facet
protected String name; // name of facet
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 String expression_x; // expression to compute the x numeric value(s) per row
protected String expression_y; // expression to compute the y numeric value(s) per row
protected String columnName_x; // column to base the x expression on, if any
protected String columnName_y; // column to base the y expression on, if any
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 from_x; // the numeric selection for the x axis
protected double to_x;
protected double from_y; // the numeric selection for the y axis
protected double to_y;
protected double _x_min;
protected double _x_max;
protected double _y_min;
protected double _y_max;
protected double min_x;
protected double max_x;
protected double min_y;
protected double max_y;
protected int size;
protected int dim_x;
protected int dim_y;
protected int rotation;
protected double l;
protected double dot;
protected String image;
protected String color_str;
protected Color color;
/*
* Derived configuration data
*/
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 int columnIndex_x;
protected int columnIndex_y;
protected Evaluable eval_x;
protected Evaluable eval_y;
protected String errorMessage_x;
protected String errorMessage_y;
protected boolean _selected; // false if we're certain that all rows will match
// and there isn't any filtering to do
protected boolean selected; // false if we're certain that all rows will match
// and there isn't any filtering to do
public ScatterplotFacet() {
}
public static final String NAME = "name";
public static final String IMAGE = "image";
public static final String COLOR = "color";
public static final String SIZE = "l";
public static final String ROTATION = "r";
public static final String DOT = "dot";
public static final String DIM_X = "dim_x";
public static final String DIM_Y = "dim_y";
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 static final String X_COLUMN_NAME = "cx";
public static final String X_EXPRESSION = "ex";
public static final String MIN_X = "min_x";
public static final String MAX_X = "max_x";
public static final String TO_X = "to_x";
public static final String FROM_X = "from_x";
public static final String ERROR_X = "error_x";
public void write(JSONWriter writer, Properties options)
throws JSONException {
public static final String Y_COLUMN_NAME = "cy";
public static final String Y_EXPRESSION = "ey";
public static final String MIN_Y = "min_y";
public static final String MAX_Y = "max_y";
public static final String TO_Y = "to_y";
public static final String FROM_Y = "from_y";
public static final String ERROR_Y = "error_y";
private static final boolean IMAGE_URI = false;
public static String EMPTY_IMAGE;
static {
try {
EMPTY_IMAGE = serializeImage(new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR));
} catch (IOException e) {
EMPTY_IMAGE = "";
}
}
public void write(JSONWriter writer, Properties options) throws JSONException {
writer.object();
writer.key("name"); writer.value(_name);
writer.key("x_expression"); writer.value(_x_expression);
writer.key("x_columnName"); writer.value(_x_columnName);
writer.key("y_expression"); writer.value(_y_expression);
writer.key("y_columnName"); writer.value(_y_columnName);
if (_x_errorMessage != null) {
writer.key("x_error"); writer.value(_x_errorMessage);
writer.key(NAME); writer.value(name);
writer.key(X_COLUMN_NAME); writer.value(columnName_x);
writer.key(X_EXPRESSION); writer.value(expression_x);
writer.key(Y_COLUMN_NAME); writer.value(columnName_y);
writer.key(Y_EXPRESSION); writer.value(expression_y);
writer.key(SIZE); writer.value(size);
writer.key(DOT); writer.value(dot);
writer.key(ROTATION); writer.value(rotation);
writer.key(DIM_X); writer.value(dim_x);
writer.key(DIM_Y); writer.value(dim_y);
writer.key(COLOR); writer.value(color_str);
if (IMAGE_URI) {
writer.key(IMAGE); writer.value(image);
}
if (errorMessage_x != null) {
writer.key(ERROR_X); writer.value(errorMessage_x);
} else {
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);
if (!Double.isInfinite(min_x) && !Double.isInfinite(max_x)) {
writer.key(MIN_X); writer.value(min_x);
writer.key(MAX_X); writer.value(max_x);
writer.key(FROM_X); writer.value(from_x);
writer.key(TO_X); writer.value(to_x);
}
}
if (errorMessage_y != null) {
writer.key(ERROR_Y); writer.value(errorMessage_y);
} else {
if (!Double.isInfinite(min_y) && !Double.isInfinite(max_y)) {
writer.key(MIN_Y); writer.value(min_y);
writer.key(MAX_Y); writer.value(max_y);
writer.key(FROM_Y); writer.value(from_y);
writer.key(TO_Y); writer.value(to_y);
}
}
writer.endObject();
}
public void initializeFromJSON(Project project, JSONObject o) throws Exception {
_name = o.getString("name");
name = o.getString(NAME);
_x_expression = o.getString("x_expression");
_x_columnName = o.getString("x_columnName");
size = (o.has(SIZE)) ? o.getInt(SIZE) : 100;
dot = (o.has(DOT)) ? o.getInt(DOT) : 0.5d;
if (_x_columnName.length() > 0) {
Column x_column = project.columnModel.getColumnByName(_x_columnName);
dim_x = (o.has(DIM_X)) ? getAxisDim(o.getString(DIM_X)) : LIN;
dim_y = (o.has(DIM_Y)) ? getAxisDim(o.getString(DIM_Y)) : LIN;
rotation = (o.has(ROTATION)) ? getRotation(o.getString(ROTATION)) : NO_ROTATION;
color_str = (o.has(COLOR)) ? o.getString(COLOR) : "000000";
color = new Color(Integer.parseInt(color_str,16));
columnName_x = o.getString(X_COLUMN_NAME);
expression_x = o.getString(X_EXPRESSION);
if (columnName_x.length() > 0) {
Column x_column = project.columnModel.getColumnByName(columnName_x);
if (x_column != null) {
_x_cellIndex = x_column.getCellIndex();
columnIndex_x = x_column.getCellIndex();
} else {
_x_errorMessage = "No column named " + _x_columnName;
errorMessage_x = "No column named " + columnName_x;
}
} else {
_x_cellIndex = -1;
columnIndex_x = -1;
}
try {
_x_eval = MetaParser.parse(_x_expression);
eval_x = MetaParser.parse(expression_x);
} catch (ParsingException e) {
_x_errorMessage = e.getMessage();
errorMessage_x = e.getMessage();
}
if (o.has(X_FROM) && o.has(X_TO)) {
_x_from = o.getDouble(X_FROM);
_x_to = o.getDouble(X_TO);
_selected = true;
if (o.has(FROM_X) && o.has(TO_X)) {
from_x = o.getDouble(FROM_X);
to_x = o.getDouble(TO_X);
selected = true;
}
_y_expression = o.getString("y_expression");
_y_columnName = o.getString("y_columnName");
columnName_y = o.getString(Y_COLUMN_NAME);
expression_y = o.getString(Y_EXPRESSION);
if (_y_columnName.length() > 0) {
Column y_column = project.columnModel.getColumnByName(_y_columnName);
if (columnName_y.length() > 0) {
Column y_column = project.columnModel.getColumnByName(columnName_y);
if (y_column != null) {
_y_cellIndex = y_column.getCellIndex();
columnIndex_y = y_column.getCellIndex();
} else {
_y_errorMessage = "No column named " + _y_columnName;
errorMessage_y = "No column named " + columnName_y;
}
} else {
_y_cellIndex = -1;
columnIndex_y = -1;
}
try {
_y_eval = MetaParser.parse(_y_expression);
eval_y = MetaParser.parse(expression_y);
} catch (ParsingException e) {
_y_errorMessage = e.getMessage();
errorMessage_y = e.getMessage();
}
if (o.has(Y_FROM) && o.has(Y_TO)) {
_y_from = o.getDouble(Y_FROM);
_y_to = o.getDouble(Y_TO);
_selected = true;
if (o.has(FROM_Y) && o.has(TO_Y)) {
from_y = o.getDouble(FROM_Y);
to_y = o.getDouble(TO_Y);
selected = true;
}
}
public RowFilter getRowFilter() {
if (_selected &&
_x_eval != null && _x_errorMessage == null &&
_y_eval != null && _y_errorMessage == null)
if (selected &&
eval_x != null && errorMessage_x == null &&
eval_y != null && errorMessage_y == null)
{
return new DualExpressionsNumberComparisonRowFilter(_x_eval, _x_columnName, _x_cellIndex, _y_eval, _y_columnName, _y_cellIndex) {
return new DualExpressionsNumberComparisonRowFilter(eval_x, columnName_x, columnIndex_x, eval_y, columnName_y, columnIndex_y) {
protected boolean checkValues(double x, double y) {
return x >= _x_from && x < _x_to && y >= _y_from && y < _y_to;
Point2D.Double p = new Point2D.Double(x,y);
p = translateCoordinates(p, dim_x, dim_y, rotation, l, min_x, max_x, min_y, max_y);
boolean value = p.x >= from_x && p.x < to_x && p.y >= from_y && p.y < to_y;
System.out.println(p + " " + value);
return value;
};
};
} else {
@ -163,44 +250,129 @@ public class ScatterplotFacet implements Facet {
}
public void computeChoices(Project project, FilteredRows filteredRows) {
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);
}
if (eval_x != null && eval_y != null && errorMessage_x == null && errorMessage_y == null) {
Column column_x = project.columnModel.getColumnByCellIndex(columnIndex_x);
NumericBinIndex index_x = getBinIndex(project, column_x, eval_x, expression_x);
_x_min = index_x.getMin();
_x_max = index_x.getMax();
min_x = index_x.getMin();
max_x = index_x.getMax();
if (_selected) {
_x_from = Math.max(_x_from, _x_min);
_x_to = Math.min(_x_to, _x_max);
if (selected) {
from_x = Math.max(from_x, min_x);
to_x = Math.min(to_x, max_x);
} else {
_x_from = _x_min;
_x_to = _x_max;
from_x = min_x;
to_x = max_x;
}
Column column_y = project.columnModel.getColumnByCellIndex(columnIndex_y);
NumericBinIndex index_y = getBinIndex(project, column_y, eval_y, expression_y);
min_y = index_y.getMin();
max_y = index_y.getMax();
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);
}
_y_min = index_y.getMin();
_y_max = index_y.getMax();
if (_selected) {
_y_from = Math.max(_y_from, _y_min);
_y_to = Math.min(_y_to, _y_max);
if (selected) {
from_y = Math.max(from_y, min_y);
to_y = Math.min(to_y, max_y);
} else {
_y_from = _y_min;
_y_to = _y_max;
from_y = min_y;
to_y = max_y;
}
if (IMAGE_URI) {
if (index_x.isNumeric() && index_y.isNumeric()) {
ScatterplotDrawingRowVisitor drawer = new ScatterplotDrawingRowVisitor(
columnIndex_x, columnIndex_y, min_x, max_x, min_y, max_y,
size, dim_x, dim_y, rotation, dot, color,
from_x, from_y, to_x, to_y
);
filteredRows.accept(project, drawer);
try {
image = serializeImage(drawer.getImage());
} catch (IOException e) {
Gridworks.warn("Exception caught while generating the image", e);
}
} else {
image = EMPTY_IMAGE;
}
}
}
}
public static String serializeImage(RenderedImage image) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream(4096);
ImageIO.write(image, "png", output);
output.close();
String encoded = Base64.encodeBase64String(output.toByteArray());
String url = "data:image/png;base64," + encoded;
return url;
}
public static int getAxisDim(String type) {
return ("log".equals(type.toLowerCase())) ? LOG : LIN;
}
public static int getRotation(String rotation) {
rotation = rotation.toLowerCase();
if ("cw".equals(rotation) || "right".equals(rotation)) {
return ScatterplotFacet.ROTATE_CW;
} else if ("ccw".equals(rotation) || "left".equals(rotation)) {
return ScatterplotFacet.ROTATE_CCW;
} else {
return NO_ROTATION;
}
}
public static NumericBinIndex getBinIndex(Project project, Column column, Evaluable eval, String expression) {
String key = "numeric-bin:" + expression;
if (eval == null) {
try {
eval = MetaParser.parse(expression);
} catch (ParsingException e) {
Gridworks.warn("Error parsing expression",e);
}
}
NumericBinIndex index = (NumericBinIndex) column.getPrecompute(key);
if (index == null) {
index = new NumericBinIndex(project, column.getName(), column.getCellIndex(), eval);
column.setPrecompute(key, index);
}
return index;
}
public static Point2D.Double translateCoordinates(Point2D.Double p, int dim_x, int dim_y, int rotation, double l, double min_x, double max_x, double min_y, double max_y) {
double x = p.x;
double y = p.y;
if (dim_x == ScatterplotFacet.LOG) {
x = Math.log10(p.x - min_x) * l / Math.log10(max_x - min_x);
} else {
x = (p.x - min_x) * l / (max_x - min_x);
}
if (dim_y == ScatterplotFacet.LOG) {
y = Math.log10(p.y - min_y) * l / Math.log10(max_y - min_y);
} else {
y = (p.y - min_y) * l / (max_y - min_y);
}
if (rotation == ScatterplotFacet.ROTATE_CW) {
double x1 = (x + y) / 2;
double y1 = (l - x + y) / 2;
x = x1;
y = y1;
} else if (rotation == ScatterplotFacet.ROTATE_CCW) {
double x1 = (l - y + x) / 2;
double y1 = (y + x) / 2;
x = x1;
y = y1;
}
p.x = x;
p.y = y;
return p;
}
}

View File

@ -22,7 +22,7 @@ abstract public class DualExpressionsNumberComparisonRowFilter implements RowFil
final protected String _y_columnName;
final protected int _y_cellIndex;
public DualExpressionsNumberComparisonRowFilter(
public DualExpressionsNumberComparisonRowFilter (
Evaluable x_evaluable,
String x_columnName,
int x_cellIndex,

View File

@ -1,48 +1,173 @@
package com.metaweb.gridworks.commands.info;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.JSONException;
import org.json.JSONObject;
import com.metaweb.gridworks.Gridworks;
import com.metaweb.gridworks.browsing.Engine;
import com.metaweb.gridworks.browsing.charting.ScatterplotCharter;
import com.metaweb.gridworks.browsing.FilteredRows;
import com.metaweb.gridworks.browsing.facets.NumericBinIndex;
import com.metaweb.gridworks.browsing.facets.ScatterplotDrawingRowVisitor;
import com.metaweb.gridworks.browsing.facets.ScatterplotFacet;
import com.metaweb.gridworks.commands.Command;
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;
public class GetScatterplotCommand extends Command {
final private ScatterplotCharter charter = new ScatterplotCharter();
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
//long start = System.currentTimeMillis();
long start = System.currentTimeMillis();
Project project = getProject(request);
Engine engine = getEngine(request, project);
JSONObject conf = getJsonParameter(request,"plotter");
response.setHeader("Content-Type", charter.getContentType());
response.setHeader("Content-Type", "image/png");
ServletOutputStream sos = null;
try {
sos = response.getOutputStream();
charter.draw(sos, project, engine, conf);
draw(sos, project, engine, conf);
} finally {
sos.close();
}
//Gridworks.log("Drawn scatterplot in " + (System.currentTimeMillis() - start) + "ms");
Gridworks.log("Drawn scatterplot in " + (System.currentTimeMillis() - start) + "ms");
} catch (Exception e) {
e.printStackTrace();
respondException(response, e);
}
}
public void draw(OutputStream output, Project project, Engine engine, JSONObject o) throws IOException, JSONException {
double min_x = 0;
double min_y = 0;
double max_x = 0;
double max_y = 0;
double from_x = 0;
double to_x = 0;
double from_y = 0;
double to_y = 0;
int columnIndex_x = 0;
int columnIndex_y = 0;
Evaluable eval_x = null;
Evaluable eval_y = null;
int size = (o.has(ScatterplotFacet.SIZE)) ? o.getInt(ScatterplotFacet.SIZE) : 100;
double dot = (o.has(ScatterplotFacet.DOT)) ? o.getDouble(ScatterplotFacet.DOT) : 100;
int dim_x = (o.has(ScatterplotFacet.DIM_X)) ? ScatterplotFacet.getAxisDim(o.getString(ScatterplotFacet.DIM_X)) : ScatterplotFacet.LIN;
int dim_y = (o.has(ScatterplotFacet.DIM_Y)) ? ScatterplotFacet.getAxisDim(o.getString(ScatterplotFacet.DIM_Y)) : ScatterplotFacet.LIN;
int rotation = (o.has(ScatterplotFacet.ROTATION)) ? ScatterplotFacet.getRotation(o.getString(ScatterplotFacet.ROTATION)) : ScatterplotFacet.NO_ROTATION;
String color_str = (o.has(ScatterplotFacet.COLOR)) ? o.getString(ScatterplotFacet.COLOR) : "000000";
Color color = new Color(Integer.parseInt(color_str,16));
String columnName_x = o.getString(ScatterplotFacet.X_COLUMN_NAME);
String expression_x = (o.has(ScatterplotFacet.X_EXPRESSION)) ? o.getString(ScatterplotFacet.X_EXPRESSION) : "value";
if (columnName_x.length() > 0) {
Column x_column = project.columnModel.getColumnByName(columnName_x);
if (x_column != null) {
columnIndex_x = x_column.getCellIndex();
}
} else {
columnIndex_x = -1;
}
try {
eval_x = MetaParser.parse(expression_x);
} catch (ParsingException e) {
Gridworks.warn("error parsing expression", e);
}
if (o.has(ScatterplotFacet.FROM_X) && o.has(ScatterplotFacet.TO_X)) {
from_x = o.getDouble(ScatterplotFacet.FROM_X);
to_x = o.getDouble(ScatterplotFacet.TO_X);
}
String columnName_y = o.getString(ScatterplotFacet.Y_COLUMN_NAME);
String expression_y = (o.has(ScatterplotFacet.Y_EXPRESSION)) ? o.getString(ScatterplotFacet.Y_EXPRESSION) : "value";
if (columnName_y.length() > 0) {
Column y_column = project.columnModel.getColumnByName(columnName_y);
if (y_column != null) {
columnIndex_y = y_column.getCellIndex();
}
} else {
columnIndex_y = -1;
}
try {
eval_y = MetaParser.parse(expression_y);
} catch (ParsingException e) {
Gridworks.warn("error parsing expression", e);
}
if (o.has(ScatterplotFacet.FROM_Y) && o.has(ScatterplotFacet.TO_Y)) {
from_y = o.getDouble(ScatterplotFacet.FROM_Y);
to_y = o.getDouble(ScatterplotFacet.TO_Y);
}
NumericBinIndex index_x = null;
NumericBinIndex index_y = null;
String col_x_name = o.getString(ScatterplotFacet.X_COLUMN_NAME);
Column column_x = project.columnModel.getColumnByName(col_x_name);
if (column_x != null) {
columnIndex_x = column_x.getCellIndex();
index_x = ScatterplotFacet.getBinIndex(project, column_x, eval_x, expression_x);
min_x = index_x.getMin();
max_x = index_x.getMax();
}
String col_y_name = o.getString(ScatterplotFacet.Y_COLUMN_NAME);
Column column_y = project.columnModel.getColumnByName(col_y_name);
if (column_y != null) {
columnIndex_y = column_y.getCellIndex();
index_y = ScatterplotFacet.getBinIndex(project, column_y, eval_y, expression_y);
min_y = index_y.getMin();
max_y = index_y.getMax();
}
if (index_x != null && index_y != null && index_x.isNumeric() && index_y.isNumeric()) {
ScatterplotDrawingRowVisitor drawer = new ScatterplotDrawingRowVisitor(
columnIndex_x, columnIndex_y, min_x, max_x, min_y, max_y,
size, dim_x, dim_y, rotation, dot, color,
from_x, from_y, to_x, to_y
);
FilteredRows filteredRows = engine.getAllFilteredRows(false);
filteredRows.accept(project, drawer);
ImageIO.write(drawer.getImage(), "png", output);
} else {
ImageIO.write(new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR), "png", output);
}
}
}

View File

@ -0,0 +1,713 @@
/*
* imgAreaSelect jQuery plugin
* version 0.9.2
*
* Copyright (c) 2008-2010 Michal Wojciechowski (odyniec.net)
*
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://odyniec.net/projects/imgareaselect/
*
*/
(function($) {
var abs = Math.abs,
max = Math.max,
min = Math.min,
round = Math.round;
function div() {
return $('<div/>');
}
$.imgAreaSelect = function (img, options) {
var
$img = $(img),
imgLoaded,
$box = div(),
$area = div(),
$border = div().add(div()).add(div()).add(div()),
$outer = div().add(div()).add(div()).add(div()),
$handles = $([]),
$areaOpera,
left, top,
imgOfs,
imgWidth, imgHeight,
$parent,
parOfs,
zIndex = 0,
position = 'absolute',
startX, startY,
scaleX, scaleY,
resizeMargin = 10,
resize,
minWidth, minHeight, maxWidth, maxHeight,
aspectRatio,
shown,
x1, y1, x2, y2,
selection = { x1: 0, y1: 0, x2: 0, y2: 0, width: 0, height: 0 },
docElem = document.documentElement,
$p, d, i, o, w, h, adjusted;
function viewX(x) {
return x + imgOfs.left - parOfs.left;
}
function viewY(y) {
return y + imgOfs.top - parOfs.top;
}
function selX(x) {
return x - imgOfs.left + parOfs.left;
}
function selY(y) {
return y - imgOfs.top + parOfs.top;
}
function evX(event) {
return event.pageX - parOfs.left;
}
function evY(event) {
return event.pageY - parOfs.top;
}
function getSelection(noScale) {
var sx = noScale || scaleX, sy = noScale || scaleY;
return { x1: round(selection.x1 * sx),
y1: round(selection.y1 * sy),
x2: round(selection.x2 * sx),
y2: round(selection.y2 * sy),
width: round(selection.x2 * sx) - round(selection.x1 * sx),
height: round(selection.y2 * sy) - round(selection.y1 * sy) };
}
function setSelection(x1, y1, x2, y2, noScale) {
var sx = noScale || scaleX, sy = noScale || scaleY;
selection = {
x1: round(x1 / sx),
y1: round(y1 / sy),
x2: round(x2 / sx),
y2: round(y2 / sy)
};
selection.width = selection.x2 - selection.x1;
selection.height = selection.y2 - selection.y1;
}
function adjust() {
if (!$img.width())
return;
imgOfs = { left: round($img.offset().left), top: round($img.offset().top) };
imgWidth = $img.width() + 1;
imgHeight = $img.height() + 1;
minWidth = options.minWidth || 0;
minHeight = options.minHeight || 0;
maxWidth = min(options.maxWidth || 1<<24, imgWidth);
maxHeight = min(options.maxHeight || 1<<24, imgHeight);
if ($().jquery == '1.3.2' && position == 'fixed' &&
!docElem['getBoundingClientRect'])
{
imgOfs.top += max(document.body.scrollTop, docElem.scrollTop);
imgOfs.left += max(document.body.scrollLeft, docElem.scrollLeft);
}
parOfs = $.inArray($parent.css('position'), ['absolute', 'relative']) + 1 ?
{ left: round($parent.offset().left) - $parent.scrollLeft(),
top: round($parent.offset().top) - $parent.scrollTop() } :
position == 'fixed' ?
{ left: $(document).scrollLeft(), top: $(document).scrollTop() } :
{ left: 0, top: 0 };
left = viewX(0);
top = viewY(0);
if (selection.x2 > imgWidth || selection.y2 > imgHeight)
doResize();
}
function update(resetKeyPress) {
if (!shown) return;
$box.css({ left: viewX(selection.x1), top: viewY(selection.y1) })
.add($area).width(w = selection.width).height(h = selection.height);
$area.add($border).add($handles).css({ left: 0, top: 0 });
$border
.width(max(w - $border.outerWidth() + $border.innerWidth(), 0))
.height(max(h - $border.outerHeight() + $border.innerHeight(), 0));
$($outer[0]).css({ left: left, top: top,
width: selection.x1, height: imgHeight });
$($outer[1]).css({ left: left + selection.x1, top: top,
width: w, height: selection.y1 });
$($outer[2]).css({ left: left + selection.x2, top: top,
width: imgWidth - selection.x2, height: imgHeight });
$($outer[3]).css({ left: left + selection.x1, top: top + selection.y2,
width: w, height: imgHeight - selection.y2 });
w -= $handles.outerWidth();
h -= $handles.outerHeight();
switch ($handles.length) {
case 8:
$($handles[4]).css({ left: w / 2 });
$($handles[5]).css({ left: w, top: h / 2 });
$($handles[6]).css({ left: w / 2, top: h });
$($handles[7]).css({ top: h / 2 });
case 4:
$handles.slice(1,3).css({ left: w });
$handles.slice(2,4).css({ top: h });
}
if (resetKeyPress !== false) {
if ($.imgAreaSelect.keyPress != docKeyPress)
$(document).unbind($.imgAreaSelect.keyPress,
$.imgAreaSelect.onKeyPress);
if (options.keys)
$(document)[$.imgAreaSelect.keyPress](
$.imgAreaSelect.onKeyPress = docKeyPress);
}
if ($.browser.msie && $border.outerWidth() - $border.innerWidth() == 2) {
$border.css('margin', 0);
setTimeout(function () { $border.css('margin', 'auto'); }, 0);
}
}
function doUpdate(resetKeyPress) {
adjust();
update(resetKeyPress);
x1 = viewX(selection.x1); y1 = viewY(selection.y1);
x2 = viewX(selection.x2); y2 = viewY(selection.y2);
}
function hide($elem, fn) {
options.fadeSpeed ? $elem.fadeOut(options.fadeSpeed, fn) : $elem.hide();
}
function areaMouseMove(event) {
var x = selX(evX(event)) - selection.x1,
y = selY(evY(event)) - selection.y1;
if (!adjusted) {
adjust();
adjusted = true;
$box.one('mouseout', function () { adjusted = false; });
}
resize = '';
if (options.resizable) {
if (y <= resizeMargin)
resize = 'n';
else if (y >= selection.height - resizeMargin)
resize = 's';
if (x <= resizeMargin)
resize += 'w';
else if (x >= selection.width - resizeMargin)
resize += 'e';
}
$box.css('cursor', resize ? resize + '-resize' :
options.movable ? 'move' : '');
if ($areaOpera)
$areaOpera.toggle();
}
function docMouseUp(event) {
$('body').css('cursor', '');
if (options.autoHide || selection.width * selection.height == 0)
hide($box.add($outer), function () { $(this).hide(); });
options.onSelectEnd(img, getSelection());
$(document).unbind('mousemove', selectingMouseMove);
$box.mousemove(areaMouseMove);
}
function areaMouseDown(event) {
if (event.which != 1) return false;
adjust();
if (resize) {
$('body').css('cursor', resize + '-resize');
x1 = viewX(selection[/w/.test(resize) ? 'x2' : 'x1']);
y1 = viewY(selection[/n/.test(resize) ? 'y2' : 'y1']);
$(document).mousemove(selectingMouseMove)
.one('mouseup', docMouseUp);
$box.unbind('mousemove', areaMouseMove);
}
else if (options.movable) {
startX = left + selection.x1 - evX(event);
startY = top + selection.y1 - evY(event);
$box.unbind('mousemove', areaMouseMove);
$(document).mousemove(movingMouseMove)
.one('mouseup', function () {
options.onSelectEnd(img, getSelection());
$(document).unbind('mousemove', movingMouseMove);
$box.mousemove(areaMouseMove);
});
}
else
$img.mousedown(event);
return false;
}
function fixAspectRatio(xFirst) {
if (aspectRatio)
if (xFirst) {
x2 = max(left, min(left + imgWidth,
x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1)));
y2 = round(max(top, min(top + imgHeight,
y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1))));
x2 = round(x2);
}
else {
y2 = max(top, min(top + imgHeight,
y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1)));
x2 = round(max(left, min(left + imgWidth,
x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1))));
y2 = round(y2);
}
}
function doResize() {
x1 = min(x1, left + imgWidth);
y1 = min(y1, top + imgHeight);
if (abs(x2 - x1) < minWidth) {
x2 = x1 - minWidth * (x2 < x1 || -1);
if (x2 < left)
x1 = left + minWidth;
else if (x2 > left + imgWidth)
x1 = left + imgWidth - minWidth;
}
if (abs(y2 - y1) < minHeight) {
y2 = y1 - minHeight * (y2 < y1 || -1);
if (y2 < top)
y1 = top + minHeight;
else if (y2 > top + imgHeight)
y1 = top + imgHeight - minHeight;
}
x2 = max(left, min(x2, left + imgWidth));
y2 = max(top, min(y2, top + imgHeight));
fixAspectRatio(abs(x2 - x1) < abs(y2 - y1) * aspectRatio);
if (abs(x2 - x1) > maxWidth) {
x2 = x1 - maxWidth * (x2 < x1 || -1);
fixAspectRatio();
}
if (abs(y2 - y1) > maxHeight) {
y2 = y1 - maxHeight * (y2 < y1 || -1);
fixAspectRatio(true);
}
selection = { x1: selX(min(x1, x2)), x2: selX(max(x1, x2)),
y1: selY(min(y1, y2)), y2: selY(max(y1, y2)),
width: abs(x2 - x1), height: abs(y2 - y1) };
update();
options.onSelectChange(img, getSelection());
}
function selectingMouseMove(event) {
x2 = resize == '' || /w|e/.test(resize) || aspectRatio ? evX(event) : viewX(selection.x2);
y2 = resize == '' || /n|s/.test(resize) || aspectRatio ? evY(event) : viewY(selection.y2);
doResize();
return false;
}
function doMove(newX1, newY1) {
x2 = (x1 = newX1) + selection.width;
y2 = (y1 = newY1) + selection.height;
$.extend(selection, { x1: selX(x1), y1: selY(y1), x2: selX(x2),
y2: selY(y2) });
update();
options.onSelectChange(img, getSelection());
}
function movingMouseMove(event) {
x1 = max(left, min(startX + evX(event), left + imgWidth - selection.width));
y1 = max(top, min(startY + evY(event), top + imgHeight - selection.height));
doMove(x1, y1);
event.preventDefault();
return false;
}
function startSelection() {
adjust();
x2 = x1;
y2 = y1;
doResize();
resize = '';
if ($outer.is(':not(:visible)'))
$box.add($outer).hide().fadeIn(options.fadeSpeed||0);
shown = true;
$(document).unbind('mouseup', cancelSelection)
.mousemove(selectingMouseMove).one('mouseup', docMouseUp);
$box.unbind('mousemove', areaMouseMove);
options.onSelectStart(img, getSelection());
}
function cancelSelection() {
$(document).unbind('mousemove', startSelection);
hide($box.add($outer));
selection = { x1: selX(x1), y1: selY(y1), x2: selX(x1), y2: selY(y1),
width: 0, height: 0 };
options.onSelectChange(img, getSelection());
options.onSelectEnd(img, getSelection());
}
function imgMouseDown(event) {
if (event.which != 1 || $outer.is(':animated')) return false;
adjust();
startX = x1 = evX(event);
startY = y1 = evY(event);
$(document).one('mousemove', startSelection)
.one('mouseup', cancelSelection);
return false;
}
function windowResize() {
doUpdate(false);
}
function imgLoad() {
imgLoaded = true;
setOptions(options = $.extend({
classPrefix: 'imgareaselect',
movable: true,
resizable: true,
parent: 'body',
onInit: function () {},
onSelectStart: function () {},
onSelectChange: function () {},
onSelectEnd: function () {}
}, options));
$box.add($outer).css({ visibility: '' });
if (options.show) {
shown = true;
adjust();
update();
$box.add($outer).hide().fadeIn(options.fadeSpeed||0);
}
setTimeout(function () { options.onInit(img, getSelection()); }, 0);
}
var docKeyPress = function(event) {
var k = options.keys, d, t, key = event.keyCode;
d = !isNaN(k.alt) && (event.altKey || event.originalEvent.altKey) ? k.alt :
!isNaN(k.ctrl) && event.ctrlKey ? k.ctrl :
!isNaN(k.shift) && event.shiftKey ? k.shift :
!isNaN(k.arrows) ? k.arrows : 10;
if (k.arrows == 'resize' || (k.shift == 'resize' && event.shiftKey) ||
(k.ctrl == 'resize' && event.ctrlKey) ||
(k.alt == 'resize' && (event.altKey || event.originalEvent.altKey)))
{
switch (key) {
case 37:
d = -d;
case 39:
t = max(x1, x2);
x1 = min(x1, x2);
x2 = max(t + d, x1);
fixAspectRatio();
break;
case 38:
d = -d;
case 40:
t = max(y1, y2);
y1 = min(y1, y2);
y2 = max(t + d, y1);
fixAspectRatio(true);
break;
default:
return;
}
doResize();
}
else {
x1 = min(x1, x2);
y1 = min(y1, y2);
switch (key) {
case 37:
doMove(max(x1 - d, left), y1);
break;
case 38:
doMove(x1, max(y1 - d, top));
break;
case 39:
doMove(x1 + min(d, imgWidth - selX(x2)), y1);
break;
case 40:
doMove(x1, y1 + min(d, imgHeight - selY(y2)));
break;
default:
return;
}
}
return false;
};
function styleOptions($elem, props) {
for (option in props)
if (options[option] !== undefined)
$elem.css(props[option], options[option]);
}
function setOptions(newOptions) {
if (newOptions.parent)
($parent = $(newOptions.parent)).append($box.add($outer));
$.extend(options, newOptions);
adjust();
if (newOptions.handles != null) {
$handles.remove();
$handles = $([]);
i = newOptions.handles ? newOptions.handles == 'corners' ? 4 : 8 : 0;
while (i--)
$handles = $handles.add(div());
$handles.addClass(options.classPrefix + '-handle').css({
position: 'absolute',
fontSize: 0,
zIndex: zIndex + 1 || 1
});
if (!parseInt($handles.css('width')))
$handles.width(5).height(5);
if (o = options.borderWidth)
$handles.css({ borderWidth: o, borderStyle: 'solid' });
styleOptions($handles, { borderColor1: 'border-color',
borderColor2: 'background-color',
borderOpacity: 'opacity' });
}
scaleX = options.imageWidth / imgWidth || 1;
scaleY = options.imageHeight / imgHeight || 1;
if (newOptions.x1 != null) {
setSelection(newOptions.x1, newOptions.y1, newOptions.x2,
newOptions.y2);
newOptions.show = !newOptions.hide;
}
if (newOptions.keys)
options.keys = $.extend({ shift: 1, ctrl: 'resize' },
newOptions.keys);
$outer.addClass(options.classPrefix + '-outer');
$area.addClass(options.classPrefix + '-selection');
for (i = 0; i++ < 4;)
$($border[i-1]).addClass(options.classPrefix + '-border' + i);
styleOptions($area, { selectionColor: 'background-color',
selectionOpacity: 'opacity' });
styleOptions($border, { borderOpacity: 'opacity',
borderWidth: 'border-width' });
styleOptions($outer, { outerColor: 'background-color',
outerOpacity: 'opacity' });
if (o = options.borderColor1)
$($border[0]).css({ borderStyle: 'solid', borderColor: o });
if (o = options.borderColor2)
$($border[1]).css({ borderStyle: 'dashed', borderColor: o });
$box.append($area.add($border).add($handles).add($areaOpera));
if ($.browser.msie) {
if (o = $outer.css('filter').match(/opacity=([0-9]+)/))
$outer.css('opacity', o[1]/100);
if (o = $border.css('filter').match(/opacity=([0-9]+)/))
$border.css('opacity', o[1]/100);
}
if (newOptions.hide)
hide($box.add($outer));
else if (newOptions.show && imgLoaded) {
shown = true;
$box.add($outer).fadeIn(options.fadeSpeed||0);
doUpdate();
}
aspectRatio = (d = (options.aspectRatio || '').split(/:/))[0] / d[1];
if (options.disable || options.enable === false) {
$box.unbind('mousemove', areaMouseMove).unbind('mousedown', areaMouseDown);
$img.add($outer).unbind('mousedown', imgMouseDown);
$(window).unbind('resize', windowResize);
}
else if (options.enable || options.disable === false) {
if (options.resizable || options.movable)
$box.mousemove(areaMouseMove).mousedown(areaMouseDown);
if (!options.persistent)
$img.add($outer).mousedown(imgMouseDown);
$(window).resize(windowResize);
}
options.enable = options.disable = undefined;
}
this.remove = function () {
$img.unbind('mousedown', imgMouseDown);
$box.add($outer).remove();
};
this.getOptions = function () { return options; };
this.setOptions = setOptions;
this.getSelection = getSelection;
this.setSelection = setSelection;
this.update = doUpdate;
$p = $img;
while ($p.length) {
zIndex = max(zIndex,
!isNaN($p.css('z-index')) ? $p.css('z-index') : zIndex);
if ($p.css('position') == 'fixed')
position = 'fixed';
$p = $p.parent(':not(body)');
}
zIndex = options.zIndex || zIndex;
if ($.browser.msie)
$img.attr('unselectable', 'on');
$.imgAreaSelect.keyPress = $.browser.msie ||
$.browser.safari ? 'keydown' : 'keypress';
if ($.browser.opera)
$areaOpera = div().css({ width: '100%', height: '100%',
position: 'absolute', zIndex: zIndex + 2 || 2 });
$box.add($outer).css({ visibility: 'hidden', position: position,
overflow: 'hidden', zIndex: zIndex || '0' });
$box.css({ zIndex: zIndex + 2 || 2 });
$area.add($border).css({ position: 'absolute', fontSize: 0 });
img.complete || img.readyState == 'complete' || !$img.is('img') ?
imgLoad() : $img.one('load', imgLoad);
};
$.fn.imgAreaSelect = function (options) {
options = options || {};
this.each(function () {
if ($(this).data('imgAreaSelect')) {
if (options.remove) {
$(this).data('imgAreaSelect').remove();
$(this).removeData('imgAreaSelect');
}
else
$(this).data('imgAreaSelect').setOptions(options);
}
else if (!options.remove) {
if (options.enable === undefined && options.disable === undefined)
options.enable = true;
$(this).data('imgAreaSelect', new $.imgAreaSelect(this, options));
}
});
if (options.instance)
return $(this).data('imgAreaSelect');
return this;
};
})(jQuery);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -25,11 +25,16 @@ ScatterplotDialog.prototype._createDialog = function() {
'<tr>' +
'<td>' +
'<span class="clustering-dialog-controls">Plot type: <select bind="plotSelector">' +
'<option selected="true">linear</option>' +
'<option>log-log</option>' +
'<option selected="true" value="lin">linear</option>' +
'<option value="log">log-log</option>' +
'</select></span>' +
'<span class="clustering-dialog-controls">Plot Size: <input bind="plotSize" type="test" size="2" value=""> px</span>' +
'<span class="clustering-dialog-controls">Dot Size: <input bind="dotSize" type="test" size="2" value=""> px</span>' +
'<span class="clustering-dialog-controls">Rotation: <select bind="rotationSelector">' +
'<option selected="true" value="none">none</option>' +
'<option value="cw">45° clockwise</option>' +
'<option value="ccw">45° counter-clockwise</option>' +
'</select></span>' +
'</td>' +
'</tr>' +
'<tr>' +
@ -44,15 +49,15 @@ ScatterplotDialog.prototype._createDialog = function() {
this._elmts = DOM.bind(html);
this._elmts.plotSelector.change(function() {
var selection = $(this).find("option:selected").text();
if (selection == 'linear') {
self._plot_method = "lin";
} else if (selection === 'log-log') {
self._plot_method = "log";
}
self._plot_method = $(this).find("option:selected").attr("value");
self._renderMatrix();
});
this._elmts.rotationSelector.change(function() {
self._rotation = $(this).find("option:selected").attr("value");
self._renderMatrix();
});
this._elmts.plotSize.change(function() {
try {
self._plot_size = parseInt($(this).val())
@ -119,10 +124,11 @@ ScatterplotDialog.prototype._renderMatrix = function() {
var plotter_params = {
'cx' : cx,
'cy' : cy,
'w' : self._plot_size * 3,
'h' : self._plot_size * 3,
'l' : self._plot_size,
'dot': self._dot_size,
'dim': self._plot_method
'dim_x': self._plot_method,
'dim_y': self._plot_method,
'r': self._rotation
};
var params = {
project: theProject.id,
@ -157,12 +163,15 @@ ScatterplotDialog.prototype._renderMatrix = function() {
container.find("a").click(function() {
var options = {
"name" : $(this).attr("title"),
"x_columnName" : $(this).attr("cx"),
"y_columnName" : $(this).attr("cy"),
"x_expression" : "value",
"y_expression" : "value",
"cx" : $(this).attr("cx"),
"cy" : $(this).attr("cy"),
"l" : 120,
"ex" : "value",
"ey" : "value",
"dot" : self._dot_size,
"dim" : self._plot_method
"dim_x" : self._plot_method,
"dim_y" : self._plot_method,
'r': self._rotation
};
ui.browsingEngine.addFacet("scatterplot", options);
//self._dismiss();

View File

@ -3,17 +3,16 @@ function ScatterplotFacet(div, config, options) {
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
delete this._config.from_x;
delete this._config.from_y;
delete this._config.to_x;
delete this._config.to_y;
this._plotImg.imgAreaSelect({ hide : true});
};
ScatterplotFacet.reconstruct = function(div, uiState) {
@ -30,18 +29,8 @@ ScatterplotFacet.prototype.getUIState = function() {
};
ScatterplotFacet.prototype.getJSON = function() {
var o = {
type: "scatterplot",
name: this._config.name,
x_columnName : this._config.x_columnName,
y_columnName : this._config.y_columnName,
x_expression: this._config.x_expression,
y_expression: this._config.y_expression,
dot: this._config.dot,
dim: this._config.dim,
};
return o;
this._config.type = "scatterplot";
return this._config;
};
ScatterplotFacet.prototype.hasSelection = function() {
@ -70,36 +59,60 @@ ScatterplotFacet.prototype._initializeUI = function() {
var bodyDiv = $('<div></div>').addClass("facet-scatterplot-body").appendTo(container);
var params = {
project: theProject.id,
engine: JSON.stringify(ui.browsingEngine.getJSON()),
plotter: JSON.stringify(this._config)
};
var url = "/command/get-scatterplot?" + $.param(params);
this._messageDiv = $('<div>').text("Loading...").addClass("facet-scatterplot-message").appendTo(bodyDiv);
this._plotDiv = $('<div>').addClass("facet-scatterplot-plot").appendTo(bodyDiv);
this._plotImg = $('<img>')
.addClass("facet-scatterplot-image")
.attr("src",url)
.attr("width", this._config.l)
.attr("height", this._config.l)
.imgAreaSelect({
handles: false,
fadeSpeed: 70,
onSelectEnd: function(elmt, selection) {
if (selection.height == 0 || selection.width == 0) {
self.reset();
} else {
self._config.from_x = selection.x1;
self._config.to_x = selection.x2 - 2;
self._config.from_y = self._config.l - selection.y2 + 2;
self._config.to_y = self._config.l - selection.y1 - 1;
}
self._updateRest();
}
}).appendTo(this._plotDiv);
this._statusDiv = $('<div>').addClass("facet-scatterplot-status").appendTo(bodyDiv);
this._plot = new ScatterplotWidget(this._plotDiv, this._config);
};
ScatterplotFacet.prototype.updateState = function(data) {
if ("x_min" in data && "x_max" in data && "x_max" in data && "x_min" in data) {
if ("min_x" in data && "max_x" in data && "max_x" in data && "min_x" in data) {
this._error = false;
this._config.x_min = data.x_min;
this._config.x_max = data.x_max;
this._config.y_min = data.y_min;
this._config.y_max = data.y_max;
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;
this._from_x = Math.max(data.from_x, this._config.x_min);
this._config.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.x_max);
this._config.to_x = Math.min(data.to_x, this._config.max_x);
} else {
this._to_x = data.x_max;
this._config.to_x = data.max_x;
}
this._from_y = Math.max(data.from_y, this._config.x_min);
this._config.from_y = Math.max(data.from_y, this._config.min_x);
if ("to_y" in data) {
this._to_y = Math.min(data.to_y, this._config.x_max);
this._config.to_y = Math.min(data.to_y, this._config.max_x);
} else {
this._to_y = data.x_max;
this._config.to_y = data.max_x;
}
} else {
this._error = true;
this._errorMessage = "error" in data ? data.error : "Unknown error.";
@ -124,17 +137,6 @@ ScatterplotFacet.prototype.render = function() {
this._messageDiv.hide();
this._plotDiv.show();
this._statusDiv.show();
this._plot.update(
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
);
};
ScatterplotFacet.prototype._remove = function() {

View File

@ -1,125 +0,0 @@
function ScatterplotWidget(elmt, options) {
this._elmt = elmt;
this._options = options;
this._plotter = {
'cx' : options.x_columnName,
'cy' : options.y_columnName,
'xe' : options.x_expression,
'ye' : options.y_expression,
'dot': options.dot,
'dim': options.dim
};
this._range = null;
this._highlight = null;
this._initializeUI();
}
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(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._highlight = null;
this._elmt.hide();
} else {
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();
}
};
ScatterplotWidget.prototype._update = function() {
if (this._highlight !== null) {
this._highlight.from_x = Math.max(this._highlight.from_x, this._range.x_min);
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.y_min);
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() {
self = this;
this._elmt
.empty()
.hide()
.addClass("scatterplot-widget")
.html('<canvas bind="canvas"></canvas>');
this._elmts = DOM.bind(this._elmt);
this._elmts.canvas.imgAreaSelect({
handles: false,
fadeSpeed: 70,
onSelectEnd: function(elmt, selection) {
self.highlight(
selection.x1,
selection.x2,
self._plotter.h - selection.y2,
self._plotter.h - selection.y1
);
}
});
};
ScatterplotWidget.prototype._resize = function() {
this._plotter.w = this._elmts.canvas.width();
this._plotter.h = ("height" in this._options) ? this._options.height : this._plotter.w;
this._elmts.canvas.attr("width", this._plotter.w);
this._elmts.canvas.attr("height", this._plotter.h);
};
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();
var img = new Image();
img.onload = function(){
ctx.drawImage(img,0,0);
if (self._highlight != null) {
var img2 = new Image();
img2.onload = function(){
ctx.drawImage(img2,0,0);
ctx.restore();
}
self._plotter.color = "000088";
console.log(self._plotter);
img2.src = self._get_image_url(self._plotter);
}
}
self._plotter.color = "000000";
console.log(self._plotter);
img.src = self._get_image_url(self._plotter);
};
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);
};

View File

@ -38,9 +38,6 @@ li.facet-container {
-moz-border-radius: 7px;
-webkit-border-radius: 7px;
border-radius: 7px 7px;
-moz-border-radius-bottomright: 0px;
-webkit-border-bottom-right-radius: 0px;
}
.facet-title {
@ -162,3 +159,8 @@ img.facet-choice-link {
border: 1px solid #ccc;
padding: 5px;
}
.facet-scatterplot-image {
border: 1px solid #f0f0f0;
margin: 0.5em 0.8em;
}

View File

@ -1,12 +0,0 @@
.scatterplot-widget {
margin: 0;
padding: 0;
position: relative;
text-align: left;
}
.scatterplot-widget canvas {
width: 120px;
border: 1px solid #f0f0f0;
margin: 0.5em 0.8em;
}