package water.util; import water.api.RequestBuilders; /** * Helper class to plot simple 2D scatter plots. * Input: x and y are two equal-sized float arrays with X and Y coordinates to be plotted. */ public class D3Plot { private float[] x; private float[] y; private String xaxislabel = "x axis"; private String yaxislabel = "y axis"; private String title = "Missing Title"; private boolean ordinal_interpolation = false; private boolean hide_toggle = true; // default values are usually fine - might want to add a formatting method later private String link = "Toggle view of plot"; private int width = 1000; private int height = 400; private int padding = 40; private int font_size = 11; public D3Plot(float[] x, float[] y, String xaxislabel, String yaxislabel, String title, boolean ordinal_interpolation, boolean hide_toggle) { this.yaxislabel = yaxislabel; this.xaxislabel = xaxislabel; this.title = title; this.x = x; this.y = y; this.link = "Toggle view of plot of " + title; assert(x.length == y.length); this.ordinal_interpolation = ordinal_interpolation; this.hide_toggle = hide_toggle; } public D3Plot(float[] x, float[] y, String xaxislabel, String yaxislabel, String title) { this.yaxislabel = yaxislabel; this.xaxislabel = xaxislabel; this.title = title; this.x = x; this.y = y; this.link = "Toggle view of plot of " + title; assert(x.length == y.length); } // populate the StringBuilder object with the Javascript code to display a 2D plot in a HTML page public void generate(StringBuilder sb) { final String plot = title.replaceAll(" ", ""); sb.append("<script type=\"text/javascript\" src='/h2o/js/d3.v3.min.js'></script>"); sb.append("<div>"); sb.append("<script>\n"); sb.append("$(document).on(\"click\", \"#pl" + plot + "\", function() { $(\"#plot" + plot + "\").toggleClass(\"hide\");});\n"); sb.append("</script>\n"); if (hide_toggle) { sb.append("<button class = 'btn btn-inverse btn-mini' id = \"pl" + plot +"\">" + link + "</button>\n"); sb.append("<div class=\"hide\" id=\"" + "plot" + plot + "\">"); } else { sb.append("<div id=\"" + "plot" + plot + "\">"); } sb.append("<style type=\"text/css\">"); sb.append(".axis path," + ".axis line {\n" + "fill: none;\n" + "stroke: black;\n" + "shape-rendering: crispEdges;\n" + "}\n" + ".axis text {\n" + "font-family: sans-serif;\n" + "font-size: " + font_size + "px;\n" + "}\n"); sb.append("</style>"); sb.append("<div id=\"" + "plot" + plot + "\" style=\"display:inline;\">"); sb.append("<script type=\"text/javascript\">"); sb.append("//Width and height\n"); sb.append("var w = " + width + ";\n"+ "var h = " + height + ";\n"+ "var padding = " + padding + ";\n" ); sb.append("var dataset = ["); for(int c = 0; c < x.length; c++) { if (c == 0) { sb.append("["+String.valueOf(x[c])+",").append(RequestBuilders.ElementBuilder.format(y[c])).append("]"); } sb.append(", ["+String.valueOf(x[c])+",").append(RequestBuilders.ElementBuilder.format(y[c])).append("]"); } sb.append("];"); sb.append( "//Create scale functions\n"+ "var xScale = d3.scale.linear()\n"+ ".domain([0, d3.max(dataset, function(d) { return d[0]; })])\n"+ ".range([padding, w - padding * 2]);\n"+ "var yScale = d3.scale.linear()"+ ".domain([0, d3.max(dataset, function(d) { return d[1]; })])\n"+ ".range([h - padding, padding]);\n"+ "var rScale = d3.scale.linear()"+ ".domain([0, d3.max(dataset, function(d) { return d[1]; })])\n"+ ".range([2, 5]);\n"+ "var lineFunction = d3.svg.line().interpolate(\"ordinal\")\n"+ ".x(function(d) {return xScale(d[0]); })\n"+ ".y(function(d) { return yScale(d[1]); });\n"+ "//Define X axis\n"+ "var xAxis = d3.svg.axis()\n"+ ".scale(xScale)\n"+ ".orient(\"bottom\")\n"+ ".ticks(5);\n"+ "//Define Y axis\n"+ "var yAxis = d3.svg.axis()\n"+ ".scale(yScale)\n"+ ".orient(\"left\")\n"+ ".ticks(5);\n"+ "//Create SVG element\n"+ "var svg = d3.select(\"#" + "plot" + plot + "\")\n"+ ".append(\"svg\")\n"+ ".attr(\"width\", w)\n"+ ".attr(\"height\", h);\n"+ "//Create circles\n"+ "svg.selectAll(\"circle\")\n"+ ".data(dataset)\n"+ ".enter()\n"+ ".append(\"circle\")\n"+ ".attr(\"cx\", function(d) {\n"+ "return xScale(d[0]);\n"+ "})\n"+ ".attr(\"cy\", function(d) {\n"+ "return yScale(d[1]);\n"+ "})\n"+ ".attr(\"r\", function(d) {\n"+ "return 2;\n"+//rScale(d[1]);\n"+ "});\n"+ "/*"+ "//Create labels\n"+ "svg.selectAll(\"text\")"+ ".data(dataset)"+ ".enter()"+ ".append(\"text\")"+ ".text(function(d) {"+ "return d[0] + \",\" + d[1];"+ "})"+ ".attr(\"x\", function(d) {"+ "return xScale(d[0]);"+ "})"+ ".attr(\"y\", function(d) {"+ "return yScale(d[1]);"+ "})"+ ".attr(\"font-family\", \"sans-serif\")"+ ".attr(\"font-size\", \"11px\")"+ ".attr(\"fill\", \"red\");"+ "*/\n"+ "//Create X axis\n"+ "svg.append(\"g\")"+ ".attr(\"class\", \"axis\")"+ ".attr(\"transform\", \"translate(0,\" + (h - padding) + \")\")"+ ".call(xAxis);\n"+ "//X axis label\n"+ "d3.select('#" + "plot" + plot + " svg')"+ ".append(\"text\")"+ ".attr(\"x\",w/2)"+ ".attr(\"y\",h - 5)"+ ".attr(\"text-anchor\", \"middle\")"+ ".text(\"" + xaxislabel + "\");\n"+ "//Create Y axis\n"+ "svg.append(\"g\")"+ ".attr(\"class\", \"axis\")"+ ".attr(\"transform\", \"translate(\" + padding + \",0)\")"+ ".call(yAxis);\n"+ "//Y axis label\n"+ "d3.select('#" + "plot" + plot + " svg')"+ ".append(\"text\")"+ ".attr(\"x\",150)"+ ".attr(\"y\",-2)"+ ".attr(\"transform\", \"rotate(90)\")"+ //".attr(\"transform\", \"translate(0,\" + (h - padding) + \")\")"+ ".attr(\"text-anchor\", \"middle\")"+ ".text(\"" + yaxislabel + "\");\n"+ "//Title\n"+ "d3.select('#" + "plot" + plot + " svg')"+ ".append(\"text\")"+ ".attr(\"x\",w/2)"+ ".attr(\"y\",padding - 20)"+ ".attr(\"text-anchor\", \"middle\")"+ ".text(\"" + title + "\");\n"); if (ordinal_interpolation) { sb.append("var linesGroup = svg.append(\"g\").attr(\"class\", \"line\");\n"+ "linesGroup.append(\"path\")\n"+ ".attr(\"d\", lineFunction(dataset))\n"+ ".attr(\"class\", \"lines\")\n"+ ".attr(\"fill\", \"none\")\n"+ ".attr(\"stroke\", function(d, i) {\n"+ "return linedata.color;\n"+ "});\n"); } sb.append("</script>"); sb.append("</div>"); sb.append("</script>"); sb.append("</div>"); sb.append("<style>"); sb.append(".line {\n" + " fill: none;\n" + " stroke: steelblue;\n" + " stroke-width: 1.5px;\n" + " }"); sb.append("</style>"); sb.append("</div>"); } }