Advertisement
Guest User

Untitled

a guest
Mar 30th, 2017
79
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 24.91 KB | None | 0 0
  1. (function () {
  2.  
  3. if (!mstrmojo.plugins.D3BoxPlot) {
  4. mstrmojo.plugins.D3BoxPlot = {};
  5. }
  6.  
  7. mstrmojo.requiresCls(
  8. "mstrmojo.CustomVisBase",
  9. "mstrmojo.models.template.DataInterface"
  10. );
  11.  
  12.  
  13. mstrmojo.plugins.D3BoxPlot.D3BoxPlot = mstrmojo.declare(
  14. mstrmojo.CustomVisBase,
  15. null, {
  16. scriptClass: "mstrmojo.plugins.D3BoxPlot.D3BoxPlot",
  17. cssClass: "d3boxplot",
  18. errorMessage: "Either there is not enough data to display the visualization or the visualization configuration is incomplete.",
  19. errorDetails: "This visualization requires one or more attributes and one metric.",
  20. externalLibraries: [{
  21. url: "//code.jquery.com/jquery-3.1.1.slim.min.js"
  22. }, {
  23. url: "//d3js.org/d3.v3.min.js"
  24. }],
  25. useRichTooltip: false,
  26. reuseDOMNode: false,
  27. supportNEE: true, // indicate the widget supports PDF exporting by New Export Engine
  28. plot: function () {
  29. /**
  30. * Box Plot created by Darren Holmblad on 12/15/2015.
  31. * Version 1.0
  32. * This code is dependent on the D3 Library
  33. */
  34.  
  35. //defines the width of the individual box plot
  36. var boxPlotWidth = 20;
  37. var margin = {
  38. top: 20,
  39. left: 80,
  40. bottom: 65
  41. };
  42.  
  43. var width = parseInt(this.width, 10) - margin.left;
  44. var height = parseInt(this.height, 10) - (margin.top * 2) - margin.bottom;
  45. var inf = Infinity;
  46. //flag to decide if outliers should be removed from the box plot
  47.  
  48. var titleFont;
  49. var titleColor;
  50. var axisFont;
  51. var backgroundColor;
  52. var cnst;
  53. var maxVal = 0;
  54. var remvoveOutliers = false;
  55. var omitOutliers = false;
  56.  
  57. var applyVIFormatting = function (fmt) {
  58.  
  59.  
  60. backgroundColor = fmt["background-color"];
  61. axisFont = fmt.ttl.font.substring(fmt.ttl.font.indexOf(" "), fmt.ttl.font.length);
  62. ;
  63. titleFont = fmt.ttl.font;
  64. titleColor = fmt.ttl.color;
  65. };
  66.  
  67. /*
  68. * Function used to alter numerical value to have no special characters, and a maximum of two decimal points
  69. */
  70. var metricPretty = function (val) {
  71. return val.toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  72. }
  73.  
  74.  
  75. d3.selection.prototype.position = function () {
  76. var el = this.node();
  77. var elPos = el.getBoundingClientRect();
  78. var vpPos = getVpPos(el);
  79.  
  80. function getVpPos(el) {
  81. if (el.parentNode.nodeName === 'svg') {
  82. return el.parentNode.getBoundingClientRect();
  83. }
  84. return getVpPos(el.parentNode);
  85. }
  86.  
  87. return {
  88. top: elPos.top - vpPos.top,
  89. left: elPos.left - vpPos.left,
  90. width: elPos.width,
  91. bottom: elPos.bottom - vpPos.top,
  92. height: elPos.height,
  93. right: elPos.right - vpPos.left
  94. };
  95.  
  96. };
  97.  
  98. /*
  99. * Function used to find ancestor by the class name
  100. */
  101. var findAncestor = function(el, cls) {
  102. while ((el = el.parentElement) && !el.classList.contains(cls));
  103. return el;
  104. }
  105.  
  106. var tFormatter = function (val) {
  107. return val > 999 ? (val / 1000).toFixed(1) + 'k' : val;
  108. }
  109.  
  110. /*
  111. * This function takes the raw data from the MicroStrategy DataInterface API and processes it into a known flat structure
  112. */
  113. var processData = function (data) {
  114. var result = [];
  115. var rawChildren = data.children;
  116. for (var i = 0; i < rawChildren.length; i++) {
  117. var attributeNm = rawChildren[i].name;
  118. for (var z = 0; z < rawChildren[i].children.length; z++) {
  119. //set max value for the y-asix range
  120. if (rawChildren[i].children[z].value > maxVal) maxVal = rawChildren[i].children[z].value;
  121.  
  122. if (result.length === 0) {
  123. var metric = [];
  124. metric.push(rawChildren[i].children[z].value);
  125. result.push({
  126. att: rawChildren[i].name,
  127. sel: rawChildren[i].attributeSelector,
  128. d: metric
  129. });
  130. } else if (result.length != 0 && attributeNm != result[result.length - 1].att) {
  131. var metric = [];
  132. metric.push(rawChildren[i].children[z].value);
  133. result.push({
  134. att: rawChildren[i].name,
  135. sel: rawChildren[i].attributeSelector,
  136. d: metric
  137. });
  138. } else {
  139. result[result.length - 1].d.push(rawChildren[i].children[z].value);
  140. }
  141. }
  142. }
  143. return result;
  144. };
  145.  
  146. /*
  147. * This function takes the processed data and outputs calculated values for each box plot
  148. * Minimum Value
  149. * First Quartile
  150. * Median Value
  151. * Last Quartile
  152. * Maximum Value
  153. * Outliers, which are calculated as the IRQ, which is the distance between Q1 and Q2. And any outlier is greater or less than (IRQ x1.5)
  154. */
  155. var processDataToBoxPlot = function (data) {
  156.  
  157. var result = [];
  158.  
  159. for (var i = 0; i < data.length; i++) {
  160.  
  161. var dataArry = sortDataArrayAsc(data[i].d);
  162.  
  163. var m = findMedian(dataArry);
  164. var leftHalf;
  165.  
  166.  
  167. //break arrays
  168. if (dataArry.length == 1) {
  169. leftHalf = dataArry; // special case:
  170. // if array is single element, let min/max and first/median/third all be equal
  171. } else if (dataArry.length % 2) {
  172. //odd number, remove median
  173. leftHalf = dataArry.splice(0, Math.floor(dataArry.length / 2) + 1);
  174. } else {
  175. //even number split in half
  176. leftHalf = dataArry.splice(0, Math.floor(dataArry.length / 2));
  177. }
  178.  
  179. var f = findMedian(leftHalf);
  180. var t = findMedian(dataArry);
  181.  
  182. var o = [];
  183. var minPos = 0;
  184. var maxPos = dataArry.length - 1;
  185.  
  186. if (remvoveOutliers || omitOutliers) {
  187. //outlierDiff is irq(box range) times 1.5
  188.  
  189. var outlierDiff = (t - f) * 1.5;
  190.  
  191. if ((f - outlierDiff) > 0) {
  192. for (var j = 0; j < leftHalf.length; j++) {
  193. //check if outlier
  194. if (leftHalf[j] < (f - outlierDiff)) {
  195. o.push(leftHalf[j]);
  196. } else {
  197. minPos = j;
  198. break;
  199. }
  200. }
  201. }
  202.  
  203. for (var p = dataArry.length - 1; p > 0; p--) {
  204. //check if outlier
  205. if (dataArry[p] > (t + outlierDiff)) {
  206. o.push(dataArry[p]);
  207. } else {
  208. maxPos = p;
  209. break;
  210. }
  211. }
  212. }
  213.  
  214. result.push({
  215. attribute: data[i].att,
  216. min: leftHalf[minPos],
  217. first: f,
  218. median: m,
  219. third: t,
  220. max: dataArry[maxPos],
  221. outliers: o,
  222. sel: data[i].sel
  223. });
  224.  
  225. }
  226.  
  227. return result;
  228. };
  229.  
  230. var sortDataArrayAsc = function (data) {
  231. return data.sort(function (a, b) {
  232. return a - b;
  233. });
  234. };
  235.  
  236. var findMedian = function (data) {
  237. var half = Math.floor(data.length / 2);
  238. if (data.length % 2) {
  239. return data[half];
  240. } else {
  241. return (data[half - 1] + data[half]) / 2.0;
  242. }
  243. };
  244.  
  245.  
  246. $('.custom-vis-layout').css("overflow", "scroll");
  247.  
  248. var rawData = this.dataInterface.getRawData(mstrmojo.models.template.DataInterface.ENUM_RAW_DATA_FORMAT.ADV, {
  249. hasSelection: true
  250. });
  251. var outlierTip = this.zonesModel.getDropZoneObjectsByName("Display Outliers");
  252. var omitOutlierDrop = this.zonesModel.getDropZoneObjectsByName("Omit Outliers");
  253.  
  254.  
  255. if (omitOutlierDrop.length > 0) omitOutliers = true;
  256. else omitOutliers = false;
  257.  
  258. if (outlierTip.length > 0) remvoveOutliers = true;
  259. else remvoveOutliers = false;
  260.  
  261. cnst = this;
  262.  
  263. this.addUseAsFilterMenuItem();
  264. //Obtains the metric name to be used as the y-axis label
  265. var yaxisHeader = this.dataInterface.getColHeaders(0).getHeader(0).getName();
  266.  
  267. //Obtains the first attribute name to be used as the x-axis label
  268. var xaxisHeader = this.dataInterface.getRowTitles().titles[0].n;
  269.  
  270. //load style information from VI apis
  271. applyVIFormatting(this.defn.fmts);
  272.  
  273. var parsedData = processDataToBoxPlot(processData(rawData));
  274.  
  275. if (this.width > 600 && parsedData.length < 5) boxPlotWidth = 60;
  276.  
  277. width = parsedData.length * (boxPlotWidth * 2);
  278.  
  279.  
  280. if (width < this.width) {
  281. width = this.width;
  282. width = width - margin.left;
  283. }
  284.  
  285.  
  286. var svgParent = d3.select(this.domNode).select("svg");
  287.  
  288.  
  289. if (svgParent.empty()) {
  290. //define graph container
  291. var svgParent = d3.select(this.domNode).append("svg")
  292. .attr("width", width + margin.left)
  293. .attr("height", height + margin.top + margin.bottom)
  294. .attr("class", "chartBoxPlot")
  295. .on("click", function (d) {
  296. if (!event.target.classList.contains('box')) {
  297. $('.box').css("opacity", ".5");
  298. cnst.clearSelections();
  299. cnst.endSelections();
  300. } else {
  301. return true;
  302. }
  303. });
  304. } else {
  305. $(".chartBoxPlot").empty();
  306. }
  307.  
  308. /* Tooltip div for outlier circles */
  309. var outlierToolTip = d3.select(this.domNode)
  310. .append("div")
  311. .attr("class", "tool")
  312. .attr("id", "outlierToolTip")
  313. .style("position", "relative")
  314. .style("z-index", "10")
  315. .style("visibility", "hidden")
  316. .text("a");
  317. /* Tooltip div for boxplot */
  318. var tooltip = d3.select(this.domNode)
  319. .append("div")
  320. .attr("class", "tool")
  321. .attr("id", "toolTip")
  322. .style("position", "relative")
  323. .style("z-index", "10")
  324. .style("visibility", "hidden")
  325. .text("a");
  326. /* Tooltip div for boxplot if position is too far right */
  327. var overFlowTooltip = d3.select(this.domNode)
  328. .append("div")
  329. .attr("class", "tool")
  330. .attr("id", "toolTipOver")
  331. .style("position", "relative")
  332. .style("z-index", "10")
  333. .style("visibility", "hidden")
  334. .text("a");
  335.  
  336. var chartAndAxis = svgParent.append("g")
  337. .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
  338. .attr("class", "chart-and-axis");
  339.  
  340.  
  341. var chart = chartAndAxis.append("g")
  342. .attr("transform", "translate(0, 0)")
  343. .attr("class", "chart")
  344. .style("overflow", "scroll");
  345.  
  346. var x = d3.scale.ordinal()
  347. .domain(parsedData.map(function (d) {
  348. return d.attribute;
  349. }))
  350. .rangePoints([0, width], 0.6);
  351.  
  352.  
  353. var xAxis = d3.svg.axis()
  354. .scale(x)
  355. .orient("bottom");
  356.  
  357.  
  358. //append x axis
  359. chartAndAxis.append("g")
  360. .attr("transform", "translate(0, " + (height) + " )")
  361. .attr("class", "x axis")
  362. .call(xAxis)
  363. .selectAll("text")
  364. .style("text-anchor", "end")
  365. .style("font", axisFont)
  366. .attr("dx", "-.8em")
  367. .attr("dy", ".15em")
  368. .attr("transform", function (d) {
  369. return "rotate(-45)"
  370. })
  371. ;
  372.  
  373.  
  374. //y-asix
  375. var y = d3.scale.linear()
  376. .domain([0, maxVal + 50])
  377. .range([height, 0]);
  378. var yAxis = d3.svg.axis().scale(y).orient("left");
  379.  
  380. //append y axis
  381. chartAndAxis.append("g")
  382. .attr("class", "y axis")
  383. .style("font", axisFont)
  384. .call(yAxis)
  385. .append("text")
  386. .style("font", axisFont)
  387. .attr("transform", "rotate(-90)")
  388. .attr("y", 20 - margin.left)
  389. .attr("x", -height / 2)
  390. .text(yaxisHeader);
  391.  
  392. chart.insert("g", ".grid")
  393. .attr("class", "grid vertical")
  394. .attr("transform", "translate(0," + (height) + ")")
  395. .style("font", axisFont)
  396. .call(d3.svg.axis().scale(x)
  397. .orient("bottom")
  398. .tickSize(-(height), 0, 0)
  399. .tickFormat("")
  400. );
  401.  
  402. chart.insert("g", ".grid")
  403. .attr("class", "grid horizontal")
  404. .call(d3.svg.axis().scale(y)
  405. .orient("left")
  406. .tickSize(-(width), 0, 0)
  407. .tickFormat(""));
  408.  
  409.  
  410. //create box plot
  411. var boxplot = chart.selectAll("boxplot")
  412. .data(parsedData)
  413. .enter()
  414. .append("g")
  415. .attr("class", "boxplot").each(function (d, i) {
  416. if (!omitOutliers) {
  417. var outlierCircles = d3.select(this)
  418. .selectAll("circle")
  419. .data(d.outliers);
  420.  
  421. outlierCircles.enter()
  422. .append("circle")
  423. .attr("class", "outlier")
  424. .attr("r", 4)
  425. .attr("cx", x(d.attribute))
  426. .attr("cy", function (outlier) {
  427. return y(outlier);
  428. })
  429. .attr("fill", function (outlier) {
  430. return "#" + Math.floor(Math.random() * 16777215).toString(16)
  431. })
  432. .on('mouseover', function (outlier) {
  433. //outlierToolTip
  434.  
  435. var rectPos = d3.select(this).position();
  436. var curY = rectPos.top;
  437. var curX = rectPos.right;
  438.  
  439.  
  440. // debugger;
  441. outlierToolTip.html("<div>" + metricPretty(outlier) + "</div>");
  442. outlierToolTip.style("top", (curY - 33) + "px").style("left", (curX - 42) + "px");
  443. outlierToolTip.style("visibility", "visible");
  444. outlierToolTip.style("position", "relative");
  445. var cir = d3.select(this);
  446. cir.transition()
  447. .duration(50)
  448. .attr('stroke-width', 2);
  449. })
  450. .on('mouseout', function (outlier) {
  451. $(".tool").css("visibility", "hidden");
  452. d3.select(this)
  453. .transition()
  454. .duration(50)
  455. .attr('stroke-width', 1);
  456. })
  457. }
  458. })
  459. .on("click", function (d) {
  460. $('.box').css("opacity", ".5");
  461. var b = this.getElementsByClassName('box');
  462. $(b[0]).css("opacity", "1");
  463. cnst.applySelection(d.sel);
  464. });
  465.  
  466. //min line
  467. var minLine = boxplot.append("line")
  468. .attr("y1", function (d) {
  469. return y(d.min);
  470. })
  471. .attr("x1", function (d) {
  472. return x(d.attribute) - boxPlotWidth / 2;
  473. })
  474. .attr("y2", function (d) {
  475. return y(d.min);
  476. })
  477. .attr("x2", function (d) {
  478. return x(d.attribute) + boxPlotWidth / 2;
  479. })
  480. .attr("class", "line min-line");
  481.  
  482.  
  483. //min whisker
  484. var minWhisker = boxplot.append("line")
  485. .attr("x1", function (d) {
  486. return x(d.attribute);
  487. })
  488. .attr("y1", function (d) {
  489. return y(d.min);
  490. })
  491. .attr("x2", function (d) {
  492. return x(d.attribute);
  493. })
  494. .attr("y2", function (d) {
  495. return y(d.first);
  496. })
  497. .attr("class", "dotted-line min-line");
  498.  
  499. //first & third box
  500. var rect = boxplot.append("rect")
  501. .attr("class", "box")
  502. .attr("x", function (d) {
  503. return x(d.attribute) - boxPlotWidth / 2;
  504. })
  505. .attr("y", function (d) {
  506. return y(d.third);
  507. })
  508. .attr("width", boxPlotWidth)
  509. .attr("height", function (d) {
  510. return y(d.first) - y(d.third);
  511. })
  512. .on("mouseover", function (d) {
  513. var desiredTip;
  514. var rectPos = d3.select(this).position();
  515. var curY = rectPos.top;
  516. var curX = rectPos.right;
  517. if ((curX + $('#toolTip').width() + parseInt($('#toolTip').css('padding-left').replace(/[^-\d\.]/g, ''))) > $(window).width()) {
  518. //to far to the right to render tooltip flip it around
  519. curX = curX - 210;
  520. desiredTip = overFlowTooltip;
  521. } else {
  522. desiredTip = tooltip;
  523. }
  524.  
  525. /* Define the tooltip area*/
  526. desiredTip.html("<div><div id='toolHeader'><strong>Interquartile Range for " + d.attribute + "</strong></div>" + "<div class='left'>Maximum </div> <div class='right'>" + metricPretty(d.max) + "</div>" + "<div class='left'>Third Quartile</div><div class='right'> " + metricPretty(d.third) + "</div>" + "<div class='left'>Median</div> <div class='right'>" + metricPretty(d.median) + "</div>" + "<div class='left'>First Quartile</div> <div class='right'>" + metricPretty(d.first) + "</div>" + "<div class='left'>Minimum </div><div class='right'> " + metricPretty(d.min) + "</div>" + "</div>");
  527.  
  528. desiredTip.style("top", (curY - 55) + "px").style("left", (curX + 10) + "px");
  529. desiredTip.style("visibility", "visible")
  530. desiredTip.style("position", "relative");
  531. return true;
  532. })
  533. .on("mouseout", function () {
  534. return $(".tool").css("visibility", "hidden");
  535. });
  536.  
  537.  
  538. //median
  539. boxplot.append("line")
  540. .attr("y1", function (d) {
  541. return y(d.median);
  542. })
  543. .attr("x1", function (d) {
  544. return x(d.attribute) - boxPlotWidth / 2;
  545. })
  546. .attr("y2", function (d) {
  547. return y(d.median);
  548. })
  549. .attr("x2", function (d) {
  550. return x(d.attribute) + boxPlotWidth / 2;
  551. })
  552. .attr("class", "line median-line");
  553.  
  554.  
  555. //max line
  556. boxplot.append("line")
  557. .attr("y1", function (d) {
  558. return y(d.max);
  559. })
  560. .attr("x1", function (d) {
  561. return x(d.attribute) - boxPlotWidth / 2;
  562. })
  563. .attr("y2", function (d) {
  564. return y(d.max);
  565. })
  566. .attr("x2", function (d) {
  567. return x(d.attribute) + boxPlotWidth / 2;
  568. })
  569. .attr("class", "line max-line");
  570.  
  571.  
  572. //max whisker to box
  573. var maxWhisker = boxplot.append("line")
  574. .attr("x1", function (d) {
  575. return x(d.attribute);
  576. })
  577. .attr("y1", function (d) {
  578. return y(d.max);
  579. })
  580. .attr("x2", function (d) {
  581. return x(d.attribute);
  582. })
  583. .attr("y2", function (d) {
  584. return y(d.third);
  585. })
  586. .attr("class", "dotted-line max-line");
  587.  
  588.  
  589. //perform some formatting
  590. var xline = $('.grid.horizontal .tick').first().find('line');
  591. $(xline).css("stroke", "#727476");
  592. $(xline).css("stroke-width", "1px");
  593. $(xline).css("shapeRedndering", "crispEdges");
  594. $(xline).css("opacity", "1");
  595.  
  596. $('.chartBoxPlot text').css("font-family", axisFont);
  597. $('.chartBoxPlot text').css("font-size", "10px");
  598.  
  599. // raise event for New Export Engine
  600. this.raiseEvent({
  601. name: 'renderFinished',
  602. id: this.k
  603. });
  604. }
  605. })
  606. }());
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement