In one of my ealrier posts, Custom AngularJS directive to plot chart using D3.js, i talked about how to go about creating custom directive for simple chart. In this post I am going to post some code that I have put together as building blocks for reusable charts using D3.js. The sample shows how to build grouped bar charts with line charts.
The data structure this chart is as below.
[
{id=112, supply=20000, demand=18000, price=1100},
{id=113, supply=25000, demand=22000, price=1000}
]
The data is grouped on id. The values for supply and demand properties are combined to create series data for each "id" group. The line charts super imposed on bar charts uses data from price property value.
The implementation shows how to use some of D3 concepts to configure padding, offsets etc. for data groups and series.
var defaultSettings = {
showValues: true,
dimensions: { width: 250, height: 500, useContainer: true },
margin: { top: 20, right: 40, bottom: 30, left: 40 },
axis: { showX: false, showY: true },
padding: { left:10, right:10, series: 0, group: 10 }
};
The code also shows how to draw markers on line chart.
Following code shows how reusable angularjs service has been built.
angular.module('ui.byteblocks.charts', []).
controller('ChartController',
['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
var width, height;
var chartContainer;
var defaultSettings = {
showValues: true,
dimensions: { width: 250, height: 500, useContainer: true },
margin: { top: 20, right: 40, bottom: 30, left: 40 },
axis: { showX: false, showY: true },
padding: { left:10, right:10, series: 0, group: 10 }
};
$scope.colors = d3.scale.category20();
$scope.container = function() { return chartContainer; };
$scope.dimensions = function() { return { width: width, height: height }; };
$scope.getSettings = function(settings) {
return $.extend(true, defaultSettings, settings ? settings : {});
};
$scope.initialize = function(selector) {
calculateDimensions();
createChart(selector);
};
$scope.renderXAxis = function(scale, orientation, position) {
var xAxis = d3.svg.axis()
.scale(scale)
.orient(orientation);
$scope.container().append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + position + ")")
.call(xAxis);
};
$scope.renderYAxis = function(label, scale, orientation, position, labelPosition) {
var yAxis = d3.svg.axis()
.scale(scale)
.orient(orientation)
.tickFormat(d3.format(".2s"));
$scope.container().append("g")
.attr("class", "y axis")
.call(yAxis)
.attr("transform", "translate(" + position + ")")
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", labelPosition)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text(label);
};
var createChart = function(selector) {
chartContainer = d3.select(selector)
.attr("width",
width + $scope.chartConfig.margin.left + $scope.chartConfig.margin.right)
.attr("height",
height + $scope.chartConfig.margin.top + $scope.chartConfig.margin.bottom)
.append("g")
.attr("transform",
"translate(" + $scope.chartConfig.margin.left + "," + $scope.chartConfig.margin.top + ")");
return chartContainer;
};
var calculateDimensions = function() {
if ($scope.chartConfig.dimensions.useContainer) {
width = $($element).width();
} else {
width = $scope.chartConfig.dimensions.width;
}
width = width - $scope.chartConfig.margin.left - $scope.chartConfig.margin.right;
height = $scope.chartConfig.dimensions.height -
$scope.chartConfig.margin.top - $scope.chartConfig.margin.bottom;
};
}]);
WebPortal.controller('ReportsController',
[
'$scope', 'reportsApi', 'diagnostics',
function ($scope, reportsApi, diagnostics) {
$scope.supplyDemand = {};
var getSupplyDemandData = function() {
reportsApi.getSupplyDemand().
then(
function(response) {
$scope.supplyDemand = response.data.Data;
},
function(response) {
}
);
};
getSupplyDemandData();
}
]);
WebPortal.directive('supplyDemandChart', [
function () {
var directiveObj = {
restrict: 'E',
scope: {
supplyDemand: '=',
config: '='
},
controller: 'ChartController',
transclude: true,
template: "<svg id='supplyDemandReportChart' class='chart'></svg>",
link: function (scope, element) {
scope.chartConfig = scope.getSettings(scope.config);
var width = 0;
var height = 0;
var columnWidth = 0;
var sY, sX, sX1, pY, pX;
var testScale;
scope.$watch('supplyDemand', function (newVal, oldVal) {
update();
});
var update = function () {
if (!scope.supplyDemand || !angular.isArray(scope.supplyDemand)) return;
width = scope.dimensions().width;
height = scope.dimensions().height;
calculateXYScales();
calculateBarWidths();
var line = d3.svg.line()
.x(function (d) {
return sX(d.Number) + columnWidth;
})
.y(function (d) {
return pY(d.Price);
});
if (scope.chartConfig.axis.showX) {
scope.renderXAxis(sX, "bottom", height);
}
if (scope.chartConfig.axis.showY) {
scope.renderYAxis("Demand/Supply", sY, "left", 0, -35);
}
teGroups = scope.container().selectAll(".group").data(scope.supplyDemand)
.enter().append("g")
.attr("class", "g")
.attr("transform", function (d, i) {
return "translate(" + sX(d.Number) + ",0)";
});
teGroups.selectAll("rect")
.data(function (d) { return [d.Supply, d.Demand]; })
.enter().append("rect")
.attr("x", function (d, i) {
return sX1(i);
})
.attr("y", function (d) {
return sY(d);
})
.attr("width", sX1.rangeBand())
.attr("height", function (d, i) {
return height - sY(d);
})
.style("fill", function (d, i) { return scope.colors(i); });
if (scope.chartConfig.axis.showY) {
scope.renderYAxis("Price", pY, "right", width, 30);
}
scope.container().append("path")
.datum(scope.supplyDemand)
.attr("class", "line")
.attr("d", line);
scope.container().selectAll("circle").data(scope.supplyDemand).enter().append("circle")
.attr("r", 5).attr("cx", function (d) {
return sX(d.Number) + columnWidth;
}).attr("cy", function (d) {
return pY(d.Price);
});
};
var calculateBarWidths = function () {
columnWidth = sX1.rangeBand();
};
var calculateXYScales = function () {
sY = d3.scale.linear().range([height, 0]);
pY = d3.scale.linear().range([height, 0]);
sY.domain([0, d3.max(scope.supplyDemand, function (d) {
return d.Supply;
})]);
pY.domain([0, d3.max(scope.supplyDemand, function (d) {
return d.Price;
})]);
sX = d3.scale.ordinal();
sX.rangeRoundBands([0, width], scope.chartConfig.padding.group / 100, scope.chartConfig.padding.left / 100);
sX.domain(scope.supplyDemand.map(function (d) {
return d.Number;
}));
sX1 = d3.scale.ordinal();
sX1.domain([0, 1]).rangeRoundBands([0, sX.rangeBand()]);
pX = d3.scale.ordinal().rangeRoundBands([0, width]);
pX.domain(scope.supplyDemand.map(function (d) {
return d.Number;
}));
};
scope.initialize("#supplyDemandReportChart");
update();
}
};
return directiveObj;
}
]);
Include the service in your application's module definition.
var WebPortal = angular.module("AngularWebPortal",
['ngRoute', 'ui.byteblocks.qtip', 'ui.byteblocks.charts']);
HTML code for this sample looks as below.
<div ng-controller="ReportsController">
<div class="row">
<div class="col-xs-12 text-center">
<h3>Supply Demand</h3>
</div>
</div>
<div class="row" style="border:1px solid #eee;">
<div class="col-xs-12" style="height:500px;">
<supply-demand-chart data-supply-demand="supplyDemand"
config="{}"></supply-demand-chart>
</div>
</div>
</div>
How to plan CCSP Exam preparation
Develop a MongoDB pipeline to transform data into time buckets
Alert and Confirm pop up using BootBox in AngularJS
AngularJS Grouped Bar Chart and Line Chart using D3
How to lock and unlock account in Asp.Net Identity provider
2024 © Byteblocks, ALL Rights Reserved. Privacy Policy | Terms of Use