/* * RHQ Management Platform * Copyright (C) 2005-2013 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.coregui.client.inventory.groups.detail.monitoring.table; import org.rhq.core.domain.common.EntityContext; /** * D3 rendition of group composite graphs for single metric multiple resources. * * @author Mike Thompson */ public class CompositeGroupD3MultiLineGraph extends CompositeGroupD3GraphListView { public CompositeGroupD3MultiLineGraph(EntityContext context, int defId) { super(context, defId); } @Override public native void drawJsniChart() /*-{ //console.log("Draw d3 MultiLine jsni chart"); var MultiLineChartContext = function (chartId, chartHeight, metricsData, xAxisLabel, chartTitle, yAxisUnits, minChartTitle, avgChartTitle, peakChartTitle, dateLabel, timeLabel, chartHoverTimeFormat, chartHoverDateFormat, isPortalGraph, portalId, buttonBarDateTimeFormat, chartXaxisTimeFormatHours, chartXaxisTimeFormatHoursMinutes) { "use strict"; if (!(this instanceof MultiLineChartContext)) { throw new Error("MultiLineChartContext function cannot be called as a function.") } this.chartId = chartId; this.chartHeight = chartHeight; if(typeof metricsData !== 'undefined' && metricsData.length > 0){ this.data = $wnd.jQuery.parseJSON(metricsData); } this.xAxisLabel = xAxisLabel; this.chartTitle = chartTitle; this.yAxisUnits = yAxisUnits; this.minChartTitle = minChartTitle; this.avgChartTitle = avgChartTitle; this.peakChartTitle = peakChartTitle; this.dateLabel = dateLabel; this.timeLabel = timeLabel; this.chartHoverTimeFormat = chartHoverTimeFormat; this.chartHoverDateFormat = chartHoverDateFormat; this.chartHandle = "mChart-" + chartId; this.chartSelection = this.chartHandle + " svg"; this.buttonBarDateTimeFormat = buttonBarDateTimeFormat; this.chartXaxisTimeFormatHours = chartXaxisTimeFormatHours; this.chartXaxisTimeFormatHoursMinutes = chartXaxisTimeFormatHoursMinutes; }, global = this; // create a chartContext object (from rhq.js) with the data required to render to a chart // this same data could be passed to different chart types // This way, we are decoupled from the dependency on globals and JSNI and kept all the java interaction right here. chartContext = new MultiLineChartContext(global.@org.rhq.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getChartId()(), global.@org.rhq.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getChartHeight()(), global.@org.rhq.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getJsonMetrics()(), global.@org.rhq.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getXAxisTitle()(), global.@org.rhq.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getChartTitle()(), global.@org.rhq.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getYAxisUnits()(), global.@org.rhq.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getChartTitleMinLabel()(), global.@org.rhq.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getChartTitleAvgLabel()(), global.@org.rhq.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getChartTitlePeakLabel()(), global.@org.rhq.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getChartDateLabel()(), global.@org.rhq.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getChartTimeLabel()(), global.@org.rhq.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getChartHoverTimeFormat()(), global.@org.rhq.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getChartHoverDateFormat()(), global.@org.rhq.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getButtonBarDateTimeFormat()(), global.@org.rhq.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getXAxisTimeFormatHours()(), global.@org.rhq.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getXAxisTimeFormatHoursMinutes()() ); // Define the Stacked Bar Graph function using the module pattern var multiLineGraph = function () { "use strict"; // privates var margin = {top: 10, right: 5, bottom: 5, left: 50}, width = 750 - margin.left - margin.right, adjustedChartHeight = chartContext.chartHeight - 50, height = adjustedChartHeight - margin.top - margin.bottom, titleHeight = 30, titleSpace = 10, yScale, yAxis, timeScale, xAxis, colorScale = $wnd.d3.scale.category20(), chart, svg; function determineScale() { var xTicks, xTickSubDivide; //console.log("DetermineScale for # resources: "+ chartContext.data.length); if (chartContext.data.length > 0) { xTicks = 8; xTickSubDivide = 5; var myExtent = getExtentFromNestedValues(chartContext.data); yScale = $wnd.d3.scale.linear() .clamp(true) .rangeRound([height, 0]) .domain([myExtent[0],myExtent[1]]); yAxis = $wnd.d3.svg.axis() .scale(yScale) .tickSubdivide(1) .ticks(5) .tickSize(4, 4, 0) .orient("left"); var firstDataset = chartContext.data[0].value; timeScale = $wnd.d3.time.scale() .range([0, width]) .domain($wnd.d3.extent(firstDataset, function(d){ return d.x; })); xAxis = $wnd.d3.svg.axis() .scale(timeScale) .ticks(xTicks) .tickSubdivide(xTickSubDivide) .tickSize(4, 4, 0) .orient("bottom"); // create the actual chart group chart = $wnd.d3.select("#" + chartContext.chartSelection); svg = chart.append("g") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top - titleHeight - titleSpace + margin.bottom) .attr("transform", "translate(" + margin.left + "," + (+titleHeight + titleSpace + margin.top) + ")"); } } function getExtentFromNestedValues(data){ var tempArray = [], mergedArray = [], resultArray = [], max = 0, min = 0; for(var i=0; i< data.length;i++){ tempArray.push(data[i].value); } mergedArray = $wnd.d3.merge(tempArray, function(d){ return d.y;}); max = $wnd.d3.max(mergedArray, function(d){ return d.y}); min = $wnd.d3.min(mergedArray, function(d){ return d.y}); resultArray.push(min); resultArray.push(max); return resultArray; } function createYAxisGridLines() { // create the y axis grid lines svg.append("g").classed("grid y_grid", true) .call($wnd.d3.svg.axis() .scale(yScale) .orient("left") .ticks(10) .tickSize(-width, 0, 0) .tickFormat("") ); } function createXandYAxes() { //xAxis.tickFormat($wnd.rhqCommon.getD3CustomTimeFormat(chartContext.chartXaxisTimeFormatHours, chartContext.chartXaxisTimeFormatHoursMinutes)); // create x-axis svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .attr("letter-spacing", "3") .style("text-anchor", "end") .call(xAxis); // create y-axis svg.append("g") .attr("class", "y axis") .call(yAxis) .append("text") .attr("transform", "rotate(-90),translate( -60,0)") .attr("y", -30) .attr("letter-spacing", "3") .style("text-anchor", "end") .text(chartContext.yAxisUnits === "NONE" ? "" : chartContext.yAxisUnits); } function createLegend() { // add legend var legend = svg.append("g") .attr("class", "legend") .attr("x", width + 100) .attr("y", 70) .attr("height", 240) .attr("width", 150); legend.selectAll('g').data(chartContext.data) .enter() .append('g') .each(function (d, i) { var g = $wnd.d3.select(this); g.append("rect") .attr("x", width + 10) .attr("y", (i * 15) - 8) .attr("width", 10) .attr("height", 10) .style("fill", function(){return colorScale(i);}); g.append("text") .attr("x", width + 30) .attr("y", i * 15) .attr("height", 10) .attr("width", 135) .style("font-size", "10px") .style("font-family", "Arial, Helvetica, sans-serif") .style("fill", "#50505A") .text(function (d) { return d.key; }); }); } function createHeader(titleName) { var title = chart.append("g").append("rect") .attr("class", "title") .attr("x", 10) .attr("y", margin.top) .attr("height", titleHeight) .attr("width", width + 30 + margin.left) .attr("fill", "none"); chart.append("text") .attr("class", "titleName") .attr("x", 40) .attr("y", 37) .attr("font-size", "12") .attr("font-weight", "bold") .attr("text-anchor", "left") .text(titleName) .attr("fill", "#003168"); return title; } function createMultiLines(chartContext) { var graphLine = $wnd.d3.svg.line() .interpolate("linear") .x(function (d) { return timeScale(d.x); }) .y(function (d) { return yScale(d.y); }); chartContext.data.sort(function(a,b){return ((a.key < b.key) ? -1 : ((a.key > b.key) ? 1 : 0));}); svg.selectAll(".multiLine") .data(chartContext.data) .enter() .append('path') .attr("class", "multiLine") .attr("fill", "none") .attr("stroke", function(d,i){ return colorScale(i);}) .attr("stroke-width", "2") .attr("stroke-opacity", ".9") .attr("d", function(d) { return graphLine( d.value.filter(function(d) { return ('nodata' in d)? !d.nodata : true; }) );}); for (var i=0;i<chartContext.data.length;++i) { svg.selectAll("dot") .data(chartContext.data[i].value) .enter() .append("circle") .filter(function(d) { return ('nodata' in d)? !d.nodata : true; }) .attr("stroke", function(){ return colorScale(i);}) .attr("stroke-width", "1") .attr("stroke-opacity", ".9") .attr("fill", function(){ return colorScale(i);}) .attr("r", 2) .attr("cx", function(d) { return timeScale(d.x); }) .attr("cy", function(d) { return yScale(d.y); }); } } return { // Public API draw: function (chartContext) { "use strict"; // Guard condition that can occur when a portlet has not been configured yet //console.log("multi-resource chart handle:" + chartContext.chartHandle); if (chartContext.data.length > 0) { //console.log("Creating MultiLine Chart: " + chartContext.chartSelection + " --> " + chartContext.chartTitle); determineScale(); createHeader(chartContext.chartTitle); createYAxisGridLines(); createMultiLines(chartContext); createXandYAxes(); createLegend(); //console.log("finished drawing multi-line graph"); } } }; // end public closure }(); if (typeof chartContext.data !== 'undefined' && chartContext.data.length > 0) { multiLineGraph.draw(chartContext); } }-*/; }