first step at scatterplot facet selector
git-svn-id: http://google-refine.googlecode.com/svn/trunk@442 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
parent
ed0778f18d
commit
81fb2f1740
@ -18,18 +18,18 @@ import com.metaweb.gridworks.commands.edit.AnnotateRowsCommand;
|
||||
import com.metaweb.gridworks.commands.edit.ApplyOperationsCommand;
|
||||
import com.metaweb.gridworks.commands.edit.CreateProjectCommand;
|
||||
import com.metaweb.gridworks.commands.edit.DeleteProjectCommand;
|
||||
import com.metaweb.gridworks.commands.edit.EditOneCellCommand;
|
||||
import com.metaweb.gridworks.commands.edit.ExportProjectCommand;
|
||||
import com.metaweb.gridworks.commands.edit.ExtendDataCommand;
|
||||
import com.metaweb.gridworks.commands.edit.ImportProjectCommand;
|
||||
import com.metaweb.gridworks.commands.edit.JoinMultiValueCellsCommand;
|
||||
import com.metaweb.gridworks.commands.edit.MassEditCommand;
|
||||
import com.metaweb.gridworks.commands.edit.RemoveColumnCommand;
|
||||
import com.metaweb.gridworks.commands.edit.RemoveRowsCommand;
|
||||
import com.metaweb.gridworks.commands.edit.RenameColumnCommand;
|
||||
import com.metaweb.gridworks.commands.edit.TextTransformCommand;
|
||||
import com.metaweb.gridworks.commands.edit.EditOneCellCommand;
|
||||
import com.metaweb.gridworks.commands.edit.MassEditCommand;
|
||||
import com.metaweb.gridworks.commands.edit.JoinMultiValueCellsCommand;
|
||||
import com.metaweb.gridworks.commands.edit.RemoveColumnCommand;
|
||||
import com.metaweb.gridworks.commands.edit.SaveProtographCommand;
|
||||
import com.metaweb.gridworks.commands.edit.SplitMultiValueCellsCommand;
|
||||
import com.metaweb.gridworks.commands.edit.TextTransformCommand;
|
||||
import com.metaweb.gridworks.commands.edit.UndoRedoCommand;
|
||||
import com.metaweb.gridworks.commands.info.ComputeClustersCommand;
|
||||
import com.metaweb.gridworks.commands.info.ComputeFacetsCommand;
|
||||
@ -42,6 +42,7 @@ import com.metaweb.gridworks.commands.info.GetOperationsCommand;
|
||||
import com.metaweb.gridworks.commands.info.GetProcessesCommand;
|
||||
import com.metaweb.gridworks.commands.info.GetProjectMetadataCommand;
|
||||
import com.metaweb.gridworks.commands.info.GetRowsCommand;
|
||||
import com.metaweb.gridworks.commands.info.GetScatterplotCommand;
|
||||
import com.metaweb.gridworks.commands.recon.ReconDiscardJudgmentsCommand;
|
||||
import com.metaweb.gridworks.commands.recon.ReconJudgeOneCellCommand;
|
||||
import com.metaweb.gridworks.commands.recon.ReconJudgeSimilarCellsCommand;
|
||||
@ -82,6 +83,7 @@ public class GridworksServlet extends HttpServlet {
|
||||
_commands.put("get-processes", new GetProcessesCommand());
|
||||
_commands.put("get-history", new GetHistoryCommand());
|
||||
_commands.put("get-operations", new GetOperationsCommand());
|
||||
_commands.put("get-scatterplot", new GetScatterplotCommand());
|
||||
|
||||
_commands.put("undo-redo", new UndoRedoCommand());
|
||||
_commands.put("apply-operations", new ApplyOperationsCommand());
|
||||
|
@ -0,0 +1,152 @@
|
||||
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 Color COLOR = Color.black;
|
||||
|
||||
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 {
|
||||
|
||||
private static final double px = 0.5f;
|
||||
|
||||
boolean process = true;
|
||||
|
||||
int width = 50;
|
||||
int height = 50;
|
||||
|
||||
int col_x;
|
||||
int col_y;
|
||||
double w;
|
||||
double h;
|
||||
double min_x;
|
||||
double min_y;
|
||||
double max_x;
|
||||
double max_y;
|
||||
|
||||
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() * 1.1d;
|
||||
max_x = index_x.getMax() * 1.1d;
|
||||
}
|
||||
|
||||
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() * 1.1d;
|
||||
max_y = index_y.getMax() * 1.1d;
|
||||
}
|
||||
|
||||
width = o.getInt("w");
|
||||
height = o.getInt("h");
|
||||
|
||||
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 key = "numeric-bin:value";
|
||||
Evaluable eval = null;
|
||||
try {
|
||||
eval = MetaParser.parse("value");
|
||||
} 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 = (xv - min_x) * w / max_x;
|
||||
double y = (yv - min_y) * h / max_y;
|
||||
g2.fill(new Rectangle2D.Double(x, y, px, px));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public RenderedImage getImage() {
|
||||
return image;
|
||||
}
|
||||
}
|
||||
}
|
@ -22,6 +22,9 @@ import com.metaweb.gridworks.model.Row;
|
||||
* as the user interacts with the facet.
|
||||
*/
|
||||
public class NumericBinIndex {
|
||||
|
||||
private int _total_count;
|
||||
private int _number_count;
|
||||
private double _min;
|
||||
private double _max;
|
||||
private double _step;
|
||||
@ -45,22 +48,29 @@ public class NumericBinIndex {
|
||||
if (value.getClass().isArray()) {
|
||||
Object[] a = (Object[]) value;
|
||||
for (Object v : a) {
|
||||
_total_count++;
|
||||
if (v instanceof Number) {
|
||||
processValue(((Number) v).doubleValue(), allValues);
|
||||
}
|
||||
}
|
||||
} else if (value instanceof Collection<?>) {
|
||||
for (Object v : ExpressionUtils.toObjectCollection(value)) {
|
||||
_total_count++;
|
||||
if (v instanceof Number) {
|
||||
processValue(((Number) v).doubleValue(), allValues);
|
||||
}
|
||||
}
|
||||
} else if (value instanceof Number) {
|
||||
processValue(((Number) value).doubleValue(), allValues);
|
||||
} else {
|
||||
_total_count++;
|
||||
if (value instanceof Number) {
|
||||
processValue(((Number) value).doubleValue(), allValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_number_count = allValues.size();
|
||||
|
||||
if (_min >= _max) {
|
||||
_step = 1;
|
||||
_min = 0;
|
||||
@ -105,6 +115,14 @@ public class NumericBinIndex {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isNumeric() {
|
||||
return _number_count > _total_count / 2;
|
||||
}
|
||||
|
||||
public int getNumberCount() {
|
||||
return _number_count;
|
||||
}
|
||||
|
||||
public double getMin() {
|
||||
return _min;
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
package com.metaweb.gridworks.commands.info;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.metaweb.gridworks.browsing.Engine;
|
||||
import com.metaweb.gridworks.browsing.charting.ScatterplotCharter;
|
||||
import com.metaweb.gridworks.commands.Command;
|
||||
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();
|
||||
Project project = getProject(request);
|
||||
Engine engine = getEngine(request, project);
|
||||
JSONObject conf = getJsonParameter(request,"plotter");
|
||||
|
||||
response.setHeader("Content-Type", charter.getContentType());
|
||||
|
||||
ServletOutputStream sos = null;
|
||||
|
||||
try {
|
||||
sos = response.getOutputStream();
|
||||
charter.draw(sos, project, engine, conf);
|
||||
} finally {
|
||||
sos.close();
|
||||
}
|
||||
|
||||
//Gridworks.log("drawn scatterplot in " + (System.currentTimeMillis() - start) + "ms");
|
||||
} catch (Exception e) {
|
||||
respondException(response, e);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
80
src/main/webapp/scripts/dialogs/scatterplot-dialog.js
Normal file
80
src/main/webapp/scripts/dialogs/scatterplot-dialog.js
Normal file
@ -0,0 +1,80 @@
|
||||
function ScatterplotDialog() {
|
||||
this._createDialog();
|
||||
}
|
||||
|
||||
ScatterplotDialog.prototype._createDialog = function() {
|
||||
var self = this;
|
||||
var frame = DialogSystem.createDialog();
|
||||
frame.width("900px");
|
||||
|
||||
var header = $('<div></div>').addClass("dialog-header").text('Scatterplot Matrix').appendTo(frame);
|
||||
var body = $('<div></div>').addClass("dialog-body").appendTo(frame);
|
||||
var footer = $(
|
||||
'<div class="dialog-footer">' +
|
||||
'<table width="100%"><tr>' +
|
||||
'<td class="left" style="text-align: left"></td>' +
|
||||
'<td class="right" style="text-align: right"></td>' +
|
||||
'</tr></table>' +
|
||||
'</div>'
|
||||
).appendTo(frame);
|
||||
|
||||
var html = $(
|
||||
'<div class="grid-layout layout-normal">' +
|
||||
'<div bind="tableContainer" class="scatterplot-dialog-table-container"></div>' +
|
||||
'</div>'
|
||||
).appendTo(body);
|
||||
|
||||
this._elmts = DOM.bind(html);
|
||||
|
||||
var left_footer = footer.find(".left");
|
||||
|
||||
var right_footer = footer.find(".right");
|
||||
$('<button></button>').text("Close").click(function() { self._dismiss(); }).appendTo(right_footer);
|
||||
|
||||
this._renderMatrix(theProject.columnModel.columns);
|
||||
this._level = DialogSystem.showDialog(frame);
|
||||
};
|
||||
|
||||
ScatterplotDialog.prototype._renderMatrix = function(columns) {
|
||||
var self = this;
|
||||
|
||||
var container = this._elmts.tableContainer;
|
||||
|
||||
if (columns.length > 0) {
|
||||
var table = $('<table></table>').addClass("scatterplot-matrix-table")[0];
|
||||
|
||||
for (var i = 0; i < columns.length; i++) {
|
||||
var tr = table.insertRow(table.rows.length);
|
||||
for (var j = 0; j < i; j++) {
|
||||
var url = "/command/get-scatterplot?" + $.param({
|
||||
project: theProject.id,
|
||||
engine: JSON.stringify(ui.browsingEngine.getJSON()),
|
||||
plotter: JSON.stringify({
|
||||
'cx' : columns[i].name,
|
||||
'cy' : columns[j].name,
|
||||
'w' : 20,
|
||||
'h' : 20
|
||||
})
|
||||
});
|
||||
$(tr.insertCell(j)).html('<img class="scatterplot" title="' + columns[i].name + ' vs. ' + columns[j].name + '" src="' + url + '" />');
|
||||
}
|
||||
$(tr.insertCell(i)).text(columns[i]);
|
||||
for (var j = i + 1; j < columns.length; j++) {
|
||||
$(tr.insertCell(j)).text(" ");
|
||||
}
|
||||
}
|
||||
|
||||
container.empty().append(table);
|
||||
|
||||
} else {
|
||||
container.html(
|
||||
'<div style="margin: 2em;"><div style="font-size: 130%; color: #333;">There are no columns in this dataset</div></div>'
|
||||
);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
ScatterplotDialog.prototype._dismiss = function() {
|
||||
DialogSystem.dismissUntil(this._level - 1);
|
||||
};
|
||||
|
@ -62,6 +62,12 @@ MenuBar.prototype._initializeUI = function() {
|
||||
click: function() {}
|
||||
}
|
||||
]);
|
||||
this._createTopLevelMenuItem("Scatterplots", [
|
||||
{
|
||||
label: "Show scatterplot matrix ...",
|
||||
click: function() { self._showScatterplotMatrix(); }
|
||||
}
|
||||
]);
|
||||
|
||||
this._wireAllMenuItemsInactive();
|
||||
};
|
||||
@ -217,3 +223,7 @@ MenuBar.prototype._doAutoSchemaAlignment = function() {
|
||||
MenuBar.prototype._doEditSchemaAlignment = function(reset) {
|
||||
new SchemaAlignmentDialog(reset ? null : theProject.protograph, function(newProtograph) {});
|
||||
};
|
||||
|
||||
MenuBar.prototype._showScatterplotMatrix = function() {
|
||||
new ScatterplotDialog();
|
||||
};
|
||||
|
19
src/main/webapp/styles/dialogs/scatterplot-dialog.css
Normal file
19
src/main/webapp/styles/dialogs/scatterplot-dialog.css
Normal file
@ -0,0 +1,19 @@
|
||||
.scatterplot-dialog-table-container {
|
||||
height: 500px;
|
||||
overflow: auto;
|
||||
border: 1px solid #aaa;
|
||||
}
|
||||
|
||||
table.scatterplot-matrix-table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table.scatterplot-matrix-table > tbody > tr > td {
|
||||
padding: 2px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table.scatterplot-matrix-table img.scatterplot {
|
||||
border: 1px solid #eee;
|
||||
}
|
Loading…
Reference in New Issue
Block a user