annotate src/DML/MainVisBundle/Resources/assets/marionette/modules/GraphicsRenderingModule/GraphicsRenderingModule.20-Renderer.histogram.js @ 1:f38015048f48 tip

Added GPL
author Daniel Wolff
date Sat, 13 Feb 2016 20:43:38 +0100
parents 493bcb69166c
children
rev   line source
Daniel@0 1 "use strict";
Daniel@0 2
Daniel@0 3 App.module("GraphicsRenderingModule", function(GraphicsRenderingModule, App, Backbone, Marionette, $, _, Logger) {
Daniel@0 4
Daniel@0 5 GraphicsRenderingModule.addInitializer(function(options){
Daniel@0 6
Daniel@0 7 GraphicsRenderingModule.registerRenderer({
Daniel@0 8 id: "histogram",
Daniel@0 9 inherit: "_",
Daniel@0 10
Daniel@0 11 defaultVegaConfig: {
Daniel@0 12 comparisonMode: null,
Daniel@0 13 dataDefinition: null, // forXs, forBars, forFloats, forXMean, forXStdDev, forceXsAreEdges
Daniel@0 14
Daniel@0 15 colorForFlats: "#666",
Daniel@0 16 colorForBars: 0,
Daniel@0 17
Daniel@0 18 ylabelDX: 3,
Daniel@0 19 xlabelDY: -3,
Daniel@0 20 paddingWhenAxisLabelsAreShown: {"top": 5, "left": 40, "bottom": 20, "right": 10},
Daniel@0 21 paddingWhenAxisLabelsAreHidden: {"top": 5, "left": 10, "bottom": 1, "right": 10},
Daniel@0 22 },
Daniel@0 23
Daniel@0 24
Daniel@0 25 _formVC: function(vc, data) {
Daniel@0 26 vc.enoughSpaceForAxisLabels = vc.totalWidth > 200;
Daniel@0 27 vc.padding = vc.enoughSpaceForAxisLabels ? vc.paddingWhenAxisLabelsAreShown : vc.paddingWhenAxisLabelsAreHidden;
Daniel@0 28 vc.width = vc.totalWidth - vc.padding.left - vc.padding.right;
Daniel@0 29 vc.height = vc.totalHeight - vc.padding.top - vc.padding.bottom;
Daniel@0 30
Daniel@0 31 var xs;
Daniel@0 32 var valuesForBars0 = [];
Daniel@0 33 var valuesForBars1 = [];
Daniel@0 34 var valuesForBars2 = [];
Daniel@0 35 var valuesForFlats0 = [];
Daniel@0 36 var valuesForFlats1 = [];
Daniel@0 37 var valuesForFlats2 = [];
Daniel@0 38 var valueForHoris0 = null;
Daniel@0 39 var valuesForOverlays0 = [];
Daniel@0 40 var xsAreEdges = false;
Daniel@0 41 var xsAreCategories = false;
Daniel@0 42
Daniel@0 43 if (vc.comparisonMode) {
Daniel@0 44 xs = _.isFunction(vc.dataDefinition.forXs) ? vc.dataDefinition.forXs(data.left) : data.left[vc.dataDefinition.forXs];
Daniel@0 45 } else {
Daniel@0 46 xs = _.isFunction(vc.dataDefinition.forXs) ? vc.dataDefinition.forXs(data.self) : data.self[vc.dataDefinition.forXs];
Daniel@0 47 }
Daniel@0 48
Daniel@0 49 if (vc.comparisonMode) {
Daniel@0 50 valuesForBars1 = (_.isFunction(vc.dataDefinition.forBars) ? vc.dataDefinition.forBars (data.left) : data.left [vc.dataDefinition.forBars] );
Daniel@0 51 valuesForBars2 = (_.isFunction(vc.dataDefinition.forBars) ? vc.dataDefinition.forBars (data.right) : data.right[vc.dataDefinition.forBars] );
Daniel@0 52 if (vc.dataDefinition.forFlats) {
Daniel@0 53 valuesForFlats1 = (_.isFunction(vc.dataDefinition.forFlats) ? vc.dataDefinition.forFlats(data.left) : data.left [vc.dataDefinition.forFlats]);
Daniel@0 54 valuesForFlats2 = (_.isFunction(vc.dataDefinition.forFlats) ? vc.dataDefinition.forFlats(data.right) : data.right[vc.dataDefinition.forFlats]);
Daniel@0 55 }
Daniel@0 56 } else {
Daniel@0 57 valuesForBars0 = (_.isFunction(vc.dataDefinition.forBars) ? vc.dataDefinition.forBars (data.self) : data.self[vc.dataDefinition.forBars ]);
Daniel@0 58 if (vc.dataDefinition.forFlats) {
Daniel@0 59 valuesForFlats0 = (_.isFunction(vc.dataDefinition.forFlats) ? vc.dataDefinition.forFlats(data.self) : data.self[vc.dataDefinition.forFlats]);
Daniel@0 60 }
Daniel@0 61 }
Daniel@0 62
Daniel@0 63 if (vc.comparisonMode == "direct") {
Daniel@0 64 for (var i = 0; i < valuesForBars1.length; i++) {
Daniel@0 65 valuesForBars0.push(valuesForBars2[i] - valuesForBars1[i]);
Daniel@0 66 }
Daniel@0 67 for (var i = 0; i < valuesForFlats1.length; i++) {
Daniel@0 68 valuesForFlats0.push(valuesForFlats2[i] - valuesForFlats1[i]);
Daniel@0 69 }
Daniel@0 70 valuesForBars1 = [];
Daniel@0 71 valuesForFlats1 = [];
Daniel@0 72 valuesForBars2 = [];
Daniel@0 73 valuesForFlats2 = [];
Daniel@0 74 }
Daniel@0 75
Daniel@0 76 try {
Daniel@0 77 if (valuesForBars0.length == xs.length - 1) {
Daniel@0 78 xsAreEdges = true;
Daniel@0 79 }
Daniel@0 80 if (valuesForBars1.length == xs.length - 1) {
Daniel@0 81 xsAreEdges = true;
Daniel@0 82 }
Daniel@0 83 if (vc.dataDefinition.forceXsAreEdges) {
Daniel@0 84 xsAreEdges = true;
Daniel@0 85 }
Daniel@0 86 } catch (e) {
Daniel@0 87 throw new App.RepresentationModule.Error({type: "ok-count-0"});
Daniel@0 88 }
Daniel@0 89
Daniel@0 90 if (_.isString(xs[0])) {
Daniel@0 91 xsAreCategories = true;
Daniel@0 92 }
Daniel@0 93
Daniel@0 94 var xMean = undefined;
Daniel@0 95 if (vc.dataDefinition.forXMean && data.self) {
Daniel@0 96 xMean = _.isFunction(vc.dataDefinition.forXMean) ? vc.dataDefinition.forXMean (data.self) : data.self [vc.dataDefinition.forXMean];
Daniel@0 97 }
Daniel@0 98 var xStdDev = undefined;
Daniel@0 99 if (vc.dataDefinition.forXStdDev && data.self) {
Daniel@0 100 xStdDev = _.isFunction(vc.dataDefinition.forXStdDev) ? vc.dataDefinition.forXStdDev (data.self) : data.self [vc.dataDefinition.forXStdDev];
Daniel@0 101 }
Daniel@0 102
Daniel@0 103 //var vegaDataByName = {}
Daniel@0 104 var generateVegaData = function(name, values, remarkPrefix, remarkSuffix) {
Daniel@0 105 var vegaValues = [];
Daniel@0 106 var tooltipType = xsAreEdges ? "tooltip_range" : "tooltip_point";
Daniel@0 107 var maxI = values.length;
Daniel@0 108 for (var i = 0; i < maxI; i++) {
Daniel@0 109 var nextX = xs[i+1];
Daniel@0 110 if (nextX !== undefined || xsAreCategories) {
Daniel@0 111 vegaValues.push([tooltipType, values[i], xs[i], nextX, remarkPrefix, remarkSuffix]);
Daniel@0 112 }
Daniel@0 113 }
Daniel@0 114 //console.log(vegaValues);
Daniel@0 115 //vegaDataByName[name] = result;
Daniel@0 116 return {
Daniel@0 117 "name": name,
Daniel@0 118 "values": vegaValues
Daniel@0 119 };
Daniel@0 120 };
Daniel@0 121
Daniel@0 122 var generateVegaDataForHoris = function(name, values) {
Daniel@0 123 return {
Daniel@0 124 "name": name,
Daniel@0 125 "values": ["tooltip_horis", value]
Daniel@0 126 };
Daniel@0 127 };
Daniel@0 128
Daniel@0 129 if (valuesForBars0 .length) {vc.data.push(generateVegaData("bars0", valuesForBars0));}
Daniel@0 130 if (valuesForBars1 .length) {vc.data.push(generateVegaData("bars1", valuesForBars1, "left: "));}
Daniel@0 131 if (valuesForBars2 .length) {vc.data.push(generateVegaData("bars2", valuesForBars2, "right: "));}
Daniel@0 132
Daniel@0 133 if (valuesForFlats0 .length) {vc.data.push(generateVegaData("flats0", valuesForFlats0));}
Daniel@0 134 if (valuesForFlats1 .length) {vc.data.push(generateVegaData("flats1", valuesForFlats0, "left: "));}
Daniel@0 135 if (valuesForFlats2 .length) {vc.data.push(generateVegaData("flats2", valuesForFlats0, "right: "));}
Daniel@0 136
Daniel@0 137 if (valueForHoris0 !== null) {vc.data.push(generateVegaDataForHoris("horis0", valuesForOverlays0));}
Daniel@0 138
Daniel@0 139
Daniel@0 140 var scalex = {
Daniel@0 141 "name": "scalex",
Daniel@0 142 "range": "width",
Daniel@0 143 "zero": false,
Daniel@0 144 //"nice": true,
Daniel@0 145 "round": false,
Daniel@0 146 };
Daniel@0 147
Daniel@0 148 if (xsAreCategories) {
Daniel@0 149 scalex["domain"] = {"data": valuesForBars0.length ? "bars0" : "bars1", "field": "2"};
Daniel@0 150 scalex["type"] = "ordinal";
Daniel@0 151 scalex["padding"] = 0.2;
Daniel@0 152 } else if (xsAreEdges) {
Daniel@0 153 scalex["type"] = "linear";
Daniel@0 154 scalex["domain"] = [_.first(xs), _.last(xs)];
Daniel@0 155 scalex["points"] = true;
Daniel@0 156 } else {
Daniel@0 157 scalex["domain"] = {"data": valuesForBars0.length ? "bars0" : "bars1", "field": "2"};
Daniel@0 158 //scalex["type"] = "ordinal";
Daniel@0 159 //scalex["domain"] = xs;
Daniel@0 160 //scalex["points"] = true;
Daniel@0 161 scalex["domain"] = [_.first(xs), _.last(xs)];
Daniel@0 162 }
Daniel@0 163
Daniel@0 164 var scalexRound = _.clone(scalex);
Daniel@0 165 scalexRound.name = "scalex_round";
Daniel@0 166 scalexRound.round = true;
Daniel@0 167
Daniel@0 168 vc.scales.push(scalex);
Daniel@0 169 vc.scales.push(scalexRound);
Daniel@0 170
Daniel@0 171 var mult = 1.1;
Daniel@0 172 var generateVegaYScale = function(name, valuesForBars) {
Daniel@0 173 var min = _.min(valuesForBars);
Daniel@0 174 if (min < 0) {
Daniel@0 175 min = min * mult;
Daniel@0 176 }
Daniel@0 177 var max = _.max(valuesForBars);
Daniel@0 178 if (max > 0) {
Daniel@0 179 max = max * mult;
Daniel@0 180 }
Daniel@0 181 return {
Daniel@0 182 "name": name,
Daniel@0 183 "range": "height",
Daniel@0 184 "round": true,
Daniel@0 185 "nice": false,
Daniel@0 186 "domain": [Math.min(0, min), Math.max(0, max)]
Daniel@0 187 };
Daniel@0 188
Daniel@0 189 };
Daniel@0 190
Daniel@0 191 if (valuesForBars0 .length) {vc.scales.push(generateVegaYScale("scale0", valuesForBars0));}
Daniel@0 192 if (valuesForBars1 .length) {vc.scales.push(generateVegaYScale("scale1", valuesForBars1));}
Daniel@0 193 if (valuesForBars2 .length) {vc.scales.push(generateVegaYScale("scale2", valuesForBars2));}
Daniel@0 194
Daniel@0 195 //console.log(vegaScales);
Daniel@0 196 //return;
Daniel@0 197
Daniel@0 198 var barWidth = (vc.width / xs.length) + 0.5;
Daniel@0 199 var offsetForXRight = .5;
Daniel@0 200 // if (vegaConfig.width / xs.length > 1) {
Daniel@0 201 // offsetForXRight = 0.5;
Daniel@0 202 // }
Daniel@0 203 var generateVegaMark = function(dataName, scale, type, color, opacity) {
Daniel@0 204 var markProperties = {
Daniel@0 205 //"fill": {"value": color},
Daniel@0 206 "opacity": {"value": opacity},
Daniel@0 207 "y": {"scale": scale, "field": "1"},
Daniel@0 208 };
Daniel@0 209
Daniel@0 210 if (type == "bar") {
Daniel@0 211 markProperties["y2"] = {"scale": scale, "value": 0};
Daniel@0 212 } else {
Daniel@0 213 markProperties["height"] = {"value": 2};
Daniel@0 214 }
Daniel@0 215 if (xsAreCategories) {
Daniel@0 216 markProperties["x"] = {"scale": "scalex", "field": "2"};
Daniel@0 217 markProperties["width"] = {"value": 1};
Daniel@0 218 markProperties["width"] = {"scale": "scalex", "band": true};
Daniel@0 219 } else if (xsAreEdges) {
Daniel@0 220 markProperties["x"] = {"scale": "scalex", "field": "2"};
Daniel@0 221 markProperties["x2"] = {"scale": "scalex", "field": "3", "offset": offsetForXRight};
Daniel@0 222 } else {
Daniel@0 223 markProperties["x"] = {"scale": "scalex", "field": "2", "offset": offsetForXRight / 2};
Daniel@0 224 //markProperties["x"] = {"scale": "scalex", "field": "2"};
Daniel@0 225 markProperties["width"] = {"value": barWidth};
Daniel@0 226 //markProperties["width"] = {"scale": "scalex", "band": true};
Daniel@0 227 }
Daniel@0 228 return {
Daniel@0 229 "type": "rect",
Daniel@0 230 "from": {"data": dataName},
Daniel@0 231 "properties": {
Daniel@0 232 "enter": markProperties,
Daniel@0 233 "update": {"fill": {"value": color}},
Daniel@0 234 "hover": {"fill": {"value": "black"}}
Daniel@0 235 }
Daniel@0 236 };
Daniel@0 237 };
Daniel@0 238
Daniel@0 239 // background (to catch mouse events and remove tooltips)
Daniel@0 240 vc.marks.push({
Daniel@0 241 "type": "rect",
Daniel@0 242 "properties": {
Daniel@0 243 "enter": {
Daniel@0 244 "x": {"value": -vc.padding.left},
Daniel@0 245 "y": {"value": -vc.padding.top},
Daniel@0 246 "fill": {"value": "#fff"},
Daniel@0 247 "y2": {"field": {"group": "height"}, "offset": vc.padding.left + vc.padding.right},
Daniel@0 248 "x2": {"field": {"group": "width"}, "offset": vc.padding.top + vc.padding.bottom},
Daniel@0 249 }
Daniel@0 250 }
Daniel@0 251 });
Daniel@0 252
Daniel@0 253
Daniel@0 254 // Marks
Daniel@0 255 if (valuesForBars0 .length) {vc.marks.push(generateVegaMark("bars0", "scale0", "bar", vc.colorForBars, 1));}
Daniel@0 256 if (valuesForBars1 .length) {vc.marks.push(generateVegaMark("bars1", "scale1", "bar", vc.colorForBars, .5));}
Daniel@0 257 if (valuesForBars2 .length) {vc.marks.push(generateVegaMark("bars2", "scale2", "bar", vc.colorForBars, .5));}
Daniel@0 258
Daniel@0 259 if (valuesForFlats0.length) {vc.marks.push(generateVegaMark("flats0", "scale0", "flat", vc.colorForFlats, 1));}
Daniel@0 260 if (valuesForFlats1.length) {vc.marks.push(generateVegaMark("flats1", "scale1", "flat", vc.colorForFlats, .5));}
Daniel@0 261 if (valuesForFlats2.length) {vc.marks.push(generateVegaMark("flats2", "scale2", "flat", vc.colorForFlats, .5));}
Daniel@0 262
Daniel@0 263 // y axis
Daniel@0 264 vc.marks.push({
Daniel@0 265 "type": "rect",
Daniel@0 266 "properties": {
Daniel@0 267 "enter": {
Daniel@0 268 "fill": {"value": vc.colorForAxes},
Daniel@0 269 "x": {"value": 0},
Daniel@0 270 "x2": {"field": {"group": "width"}},
Daniel@0 271 "height": {"value": 1},
Daniel@0 272 "y": {"scale": valuesForBars0.length ? "scale0" : "scale1", "value": 0},
Daniel@0 273 }
Daniel@0 274 }
Daniel@0 275 });
Daniel@0 276
Daniel@0 277 var generateVegaVerticalMark = function(x, color, dataName, strokeDash) {
Daniel@0 278 return {
Daniel@0 279 "type": "rect",
Daniel@0 280 "from": {
Daniel@0 281 "data": dataName,
Daniel@0 282 },
Daniel@0 283 "properties": {
Daniel@0 284 "enter": {
Daniel@0 285 "stroke": {"value": vc.colorForAxisLabels},
Daniel@0 286 "strokeWidth": {"value": 1},
Daniel@0 287 "y": {"value": -99},
Daniel@0 288 "y2": {"field": {"group": "height"}},
Daniel@0 289 "x": {"scale": "scalex_round", "value": x},
Daniel@0 290 //"x2": {"scale": "scalex", "value": x, "offset": ""},
Daniel@0 291 "width": {"value": 1},
Daniel@0 292 "strokeDash": {"value": strokeDash}
Daniel@0 293 }
Daniel@0 294 }
Daniel@0 295 };
Daniel@0 296 };
Daniel@0 297
Daniel@0 298 // x mean, x std dev
Daniel@0 299 if (xStdDev !== undefined && xMean !== undefined) {
Daniel@0 300 vc.data.push({
Daniel@0 301 "name": "stdDev",
Daniel@0 302 "values": [["tooltip_value", xStdDev, 0, 0, "standard deviation: "]]
Daniel@0 303 });
Daniel@0 304 vc.marks.push(generateVegaVerticalMark(xMean - xStdDev, "#ccc", "stdDev", [1, 1]));
Daniel@0 305 vc.marks.push(generateVegaVerticalMark(xMean + xStdDev, "#ccc", "stdDev", [1, 1]));
Daniel@0 306 }
Daniel@0 307 if (xMean !== undefined) {
Daniel@0 308 vc.data.push({
Daniel@0 309 "name": "mean",
Daniel@0 310 "values": [["tooltip_value", xMean, 0, 0, "mean: ", xStdDev === 0 ? " (standard deviation is zero)" : undefined]]
Daniel@0 311 });
Daniel@0 312 vc.marks.push(generateVegaVerticalMark(xMean, "#666", "mean", 0));
Daniel@0 313 }
Daniel@0 314
Daniel@0 315
Daniel@0 316 //Axes
Daniel@0 317 var axisOpacity = vc.enoughSpaceForAxisLabels ? {"value" : 1} : {"value" : 0};
Daniel@0 318
Daniel@0 319 vc.axes.push({"type": "x", "scale": "scalex", /*"ticks": 8,*/
Daniel@0 320 "properties": {
Daniel@0 321 "axis": {
Daniel@0 322 "stroke": {"value": "#fff"},
Daniel@0 323 "strokeWidth": {"value": 0}
Daniel@0 324 },
Daniel@0 325 "grid": {
Daniel@0 326 "stroke": {"value": "#fff"},
Daniel@0 327 "strokeOpacity": {"value": 0.4},
Daniel@0 328 "strokeWidth": {"value": 1}
Daniel@0 329 },
Daniel@0 330 "ticks": {
Daniel@0 331 "stroke": {"value": "#fff"},
Daniel@0 332 "strokeWidth": {"value": 0},
Daniel@0 333 "opacity": axisOpacity
Daniel@0 334 },
Daniel@0 335 "labels": {
Daniel@0 336 "fill": {"value": vc.colorForAxisLabels},
Daniel@0 337 "dy": {"value": vc.xlabelDY},
Daniel@0 338 "font": {"value": vc.fontFace},
Daniel@0 339 "fontSize": {"value": vc.fontSizeForLabelsInAxis},
Daniel@0 340 "opacity": axisOpacity
Daniel@0 341 }
Daniel@0 342 }
Daniel@0 343 });
Daniel@0 344 if (valuesForBars0.length) {
Daniel@0 345 vc.axes.push({"type": "y", "scale": "scale0", /*"ticks": 5,*/ "grid": vc.enoughSpaceForAxisLabels, "zero": false,
Daniel@0 346 "properties": {
Daniel@0 347 "axis": {
Daniel@0 348 "stroke": {"value": "#fff"},
Daniel@0 349 "strokeWidth": {"value": 0}
Daniel@0 350 },
Daniel@0 351 "grid": {
Daniel@0 352 "stroke": {"value": "#fff"},
Daniel@0 353 "strokeOpacity": {"value": 0.4},
Daniel@0 354 "opacity": {"value": 1}
Daniel@0 355 },
Daniel@0 356 "ticks": {
Daniel@0 357 "stroke": {"value": "#fff"},
Daniel@0 358 "strokeWidth": {"value": 0},
Daniel@0 359 "opacity": axisOpacity
Daniel@0 360 },
Daniel@0 361 "labels": {
Daniel@0 362 "fill": {"value": vc.colorForAxisLabels},
Daniel@0 363 "fontSize": {"value": vc.fontSizeForLabelsInAxis},
Daniel@0 364 "dx": {"value": vc.ylabelDX},
Daniel@0 365 "font": {"value": vc.fontFace},
Daniel@0 366 "opacity": axisOpacity
Daniel@0 367 }
Daniel@0 368 }
Daniel@0 369 });
Daniel@0 370 };
Daniel@0 371 },
Daniel@0 372 });
Daniel@0 373 });
Daniel@0 374 }, Logger);