I'm using http://d3pie.org/ to generate a pie chart for my web application. Now, I want to manipulate the value appearing on my chart. Currently, the values appearing have more than 4 decimals. For example, 10.35234234234 . I just want 10.3523 to appear.
Here's my current code:
function drawPie(id,from,to,C,formula){
var subArray = getSubArray(C.table,from,to);
if(Object.keys(subArray).length){
var partials = createPartials(subArray, C.attributes);
//console.log("partials -> ",partials);
formula = formula.slice(1,-1);
//console.log("formula ->" , formula);
formula = formula.split(",");
//console.log("formula ->" , formula);
var cloneFormula = formula.slice();
for( var i in partials){
if(i.substr(0,1) == "$"){
for(var j = 0; j< formula.length; j++){
formula[j] = replaceFunction(i,formula[j],partials[i]);
}
}
}
//console.log("replaced formula ->" , formula);
var data = [];
var isEmpty = true;
for(var j = 0; j< formula.length; j++){
var label = cloneFormula[j].split(/(?=[-+*\/])/)[0].split(".")[1];
var temp = {};
try{
temp.value = eval(formula[j]);
}catch(err){
temp.value = 0;
}
temp.label = partials[label + ".name"];
temp.color = partials[label + ".color"];
data.push(temp);
if(temp.value != 0){
isEmpty = false;
}
}
if(isEmpty){
return true;
}
//console.log("data ->" , data);
var pie = new d3pie(id, {
"size": {
"canvasHeight": 400,
"canvasWidth": 800, //see defect 1418
"pieOuterRadius": "88%"
},
"data": {
"content": data
},
"labels": {
"outer": {
"pieDistance": 100
},
"inner": {
"format": "value"
},
"mainLabel": {
"font": "verdana"
},
"percentage": {
"color": "#e1e1e1",
"font": "verdana",
"decimalPlaces": 0
},
"value": {
"color": "#e1e1e1",
"font": "verdana"
},
"lines": {
"enabled": true,
"color": "#cccccc"
},
"truncation": {
"enabled": true
}
},
"effects": {
"pullOutSegmentOnClick": {
"effect": "linear",
"speed": 400,
"size": 8
}
}
});
return false;
}
return true;
}
Can you help me try to modify the texts appearing inside my pie chart?
use the following to strip the number and then round it.
function strip(number) {
return (parseFloat(number).toPrecision(4));
}
example
var number = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679
function strip(number) {
return (parseFloat(number).toPrecision(6));
}
window.alert(strip(number));
For the generator you are using, go to the labels section, and under the label styles / Settings you will find the option for decimal places for percentage values. Enter the number of decimal places you would like to limit the labels to.
Related
I'm trying to display a multiple line chart of about the growth of the population on the different department of a country but when I get the JSON from PHP I can't iterate the array for getting the year and total of the population. Here's my code:
$(document).ready(function(){
$.ajax({
url: "../assets/api/stats.php",
data: "stat=birth&in=departement",
type: "GET",
success: function(data) {
console.log(data);
var departement = {
Zone: []
};
var year = {
Birth: []
};
var total = {
Birth: []
};
var len = data.length;
console.log(data.length);
var lctx = $('#line-chart- departement');
for (var j = 0; j < len; j++) {
departement.Zone.push(data[j][0].departement);
for (var i = 0; i < data.length; i++) {
annee.Naissance.push(departement.Zone[i].annee);
total.Naissance.push(departement.Zone[i].total);
}
var data = {
labels: annee.Naissance,
datasets: [{
label: data[j],
data: total.Naissance,
backgroundColor: getRandomColor(),
borderColor: "#3e95cd",
fill: false,
lineTension: 0,
pointRadiues: 5
}]
};
console.log();
var chart = new Chart(lctx, {
type: "line",
data: data,
options: {}
});
}
},error: function(data) {
//console.log(data)
}
});
function getRandomColor() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;}
});
And there is my array
[
{
"Nord-Est":
[
{
"annee": "1995",
"totalnaissance": "1"
}
]
},
{
"Ouest":
[
{
"annee": "1994",
"totalnaissance": "2"
},
{
"annee": "1995",
"totalnaissance": "1"
}
]
},
{
"Nippes":
[
{
"annee": "1951",
"totalnaissance": "1"
},
{
"annee": "1987",
"totalnaissance": "1"
},
{
"annee": "1986",
"totalnaissance": "1"
},
{
"annee": "1992",
"totalnaissance": "2"
}
]
},
{
"Sud-Est":
[
{
"annee": "1985",
"totalnaissance": "1"
}
]
}
]
That turned out to be 'more than meets the eye'.
Since we need all the years as x axis, we need to go through the data twice - once to get all the years, once to get the data for each year. If data is not available for that year, we have to enter null. Otherwise the x and y points get mismatched.
After all the years are fetched, they have to be sorted in ascending order, and we need to get the totalnaissance data in the same order.
You had given the borderColor a fixed value of #3e95cd, but for a line chart a border color IS the line color. So I've changed that. I've also made the backgroundColor 'transparent' and pointBackgroundColor equal to borderColor.
I've created a Pen.
<canvas id="myChart" width="500" height="500"></canvas>
<script>
var ctx = document.getElementById("myChart").getContext('2d');
var data = {
Nippes: [
{
annee: "1951",
totalnaissance: "1"
},
{
annee: "1986",
totalnaissance: "1"
},
{
annee: "1987",
totalnaissance: "1"
},
{
annee: "1992",
totalnaissance: "2"
}
],
"Nord-Est": [
{
annee: "1995",
totalnaissance: "1"
}
],
Ouest: [
{
annee: "1994",
totalnaissance: "2"
},
{
annee: "1995",
totalnaissance: "1"
}
],
"Sud-Est": [
{
annee: "1985",
totalnaissance: "1"
}
]
};
var departments = [];
var annees = [];
for (var department in data) {
if (data.hasOwnProperty(department)) {
var departmentData = data[department];
getYears(departmentData);
}
}
annees.sort();
for (var department in data) {
if (data.hasOwnProperty(department)) {
var departmentData = data[department];//getDataForDepartment(i);
var totalnaissanceData = getTotalNaissanceDataForDep(departmentData);
var departmentObject = prepareDepartmentDetails(department, totalnaissanceData);
departments.push(departmentObject);
}
}
var chartData = {
labels: annees,
datasets : departments
};
var chart = new Chart(ctx, {
type: "line",
data: chartData,
options: {}
});
function getDataForDepartment(index){
return data[i][Object.keys(data[i])[0]];
}
function getYears(departmentData){
for (var j = 0; j< departmentData.length; j++){
if (!annees.includes(departmentData[j].annee)){
annees.push(departmentData[j].annee);
}
}
}
function getTotalNaissanceDataForDep(departmentData){
var totalnaissanceData = [];
for (var j = 0; j < annees.length; j++){
var currentAnnee = annees[j];
var currentTotalNaissance = null;
for (var k = 0; k< departmentData.length; k++){
if (departmentData[k].annee === currentAnnee){
currentTotalNaissance = departmentData[k].totalnaissance;
break;
}
}
totalnaissanceData.push(currentTotalNaissance);
}
return totalnaissanceData;
}
function prepareDepartmentDetails(departmentName, totalnaissanceData){
var dataColor = getRandomColor();
return {
label : departmentName,
data : totalnaissanceData,
backgroundColor: 'transparent',
borderColor: dataColor,//"#3e95cd",
pointBackgroundColor : dataColor,
fill: false,
lineTension: 0,
pointRadius: 5
}
}
function getRandomColor() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
</script>
I am using stackByValue of amcharts to arranged a stack column chart . I would like to add a bullet point on each chart to check if they meet a certain target or not. Currently what happen is the bullet point is added to the stacked chart is there a way that I could do this without removing the stackByValue ?
Here is my JsFiddle: `http://jsfiddle.net/sky5rvdz/13/
$(document).ready(function() {
AmCharts.addInitHandler(function(chart) {
// Check if enabled
if (chart.valueAxes === undefined || chart.valueAxes.length === 0 || !chart.valueAxes[0].stackByValue)
return;
// Disable built-in stacking
chart.valueAxes[0].stackType = "none";
// Prepare all graphs
for (var i = 0; i < chart.graphs.length; i++) {
var graph = chart.graphs[i];
graph.originalValueField = graph.valueField;
graph.valueField = graph.originalValueField + "Close";
graph.openField = graph.originalValueField + "Open";
graph.clustered = false;
if (graph.labelText)
graph.labelText = graph.labelText.split("[[value]]").join("[[" + graph.originalValueField + "]]");
if (graph.balloonText)
graph.balloonText = graph.balloonText.split("[[value]]").join("[[" + graph.originalValueField + "]]");
}
// Go through each category and order values
for (var i = 0; i < chart.dataProvider.length; i++) {
// Assemble intermediate array of data point items
var dp = chart.dataProvider[i];
var items = [];
var sum = 0;
for (var x = 0; x < chart.graphs.length; x++) {
var graph = chart.graphs[x];
items.push({
"graph": graph,
"value": dp[graph.originalValueField]
});
}
var sortValue = 0;
// Order according to value
items.sort(function(a, b) {
if (sortValue == 0) {
return a.value - b.value;
} else {
return b.value - a.value;
}
});
// Calculate open and close fields
var offset = 0;
for (var x = 0; x < items.length; x++) {
var item = items[x];
dp[item.graph.openField] = offset;
dp[item.graph.valueField] = offset + dp[item.graph.originalValueField];
offset = dp[item.graph.valueField];
}
}
}, ["serial"]);
var response = [{
"name": "Jan",
"target": 2062186.74,
"USA": 0,
"MAN": 605873.95,
"PAN": 759763.5
}, {
"name": "Feb",
"target": 1492210.81,
"MAN": 499538.43,
"PAN": 559504.95,
"USA": 5850
}, {
"name": "Mar",
"target": 1455750,
"MAN": 403715.2,
"PAN": 694353.95,
"USA": 0
}, {
"name": "Apr",
"target": 2008623.96,
"USA": 0,
"MAN": 409993.3,
"PAN": 511030
}];
var graphs = Object.keys(response[0]).reduce(function(graphsArray, key) {
if (key !== "name" && key !== "target") {
graphsArray.push({
"balloonText": "<b>[[value]]</b>",
"balloonFunction": function(item, graph) {
var result = graph.balloonText;
for (var key in item.dataContext) {
if (item.dataContext.hasOwnProperty(key) && !isNaN(item.dataContext[key])) {
var formatted = AmCharts.formatNumber(item.dataContext[key], {
precision: chart.precision,
decimalSeparator: chart.decimalSeparator,
thousandsSeparator: chart.thousandsSeparator
}, 2);
result = result.replace("[[" + key + "]]", formatted);
}
}
return result;
},
"fillAlphas": 0.8,
"labelText": "[[title]]<br>",
"labelPosition": "middle",
"lineAlpha": 0.3,
"title": key,
"type": "column",
"color": "#000000",
//"showAllValueLabels": true,
"valueField": key
});
}
if (key === "target") {
graphsArray.push({
"balloonText": "<b>[[value]]</b>",
"balloonFunction": function(item, graph) {
var result = graph.balloonText;
for (var key in item.dataContext) {
if (item.dataContext.hasOwnProperty(key) && !isNaN(item.dataContext[key])) {
var formatted = AmCharts.formatNumber(item.dataContext[key], {
precision: chart.precision,
decimalSeparator: chart.decimalSeparator,
thousandsSeparator: chart.thousandsSeparator
}, 2);
result = result.replace("[[" + key + "]]", formatted);
}
}
return result;
},
"valueAxis": "v2",
"lineAlpha": 0,
"bullet": "round",
"bulletSize": 20,
"title": "target",
"type": "line",
"valueField": "target"
});
}
return graphsArray;
}, []);
var chart = AmCharts.makeChart("chartdiv", {
"type": "serial",
"theme": "light",
"legend": {
"horizontalGap": 10,
"maxColumns": 1,
"position": "right",
"useGraphSettings": true,
"markerSize": 10
},
"numberFormatter": {
"precision": 1,
"decimalSeparator": ".",
"thousandsSeparator": ","
},
"dataProvider": response,
"valueAxes": [{
"id": "v1",
"stackType": "regular",
/**
* A proprietary setting `stackByValue` which is not an
* official config option. It will be used by our custom
* plugin
*/
"stackByValue": true,
"axisAlpha": 0.3,
"gridAlpha": 0
}, , {
"id": "v2",
"axisAlpha": 0.3,
"gridAlpha": 0,
"position": "top",
"title": "Target"
}],
"gridAboveGraphs": true,
"startDuration": 0,
"graphs": graphs,
"categoryField": "name",
"categoryAxis": {
"gridPosition": "start",
"axisAlpha": 0,
"gridAlpha": 0,
"position": "left"
},
"export": {
"enabled": true
}
});
console.log(graphs);
console.log(response);
Object.keys(response[0]).forEach(key => {
console.log(key) // returns the keys in an object
// console.log(a[key]) // returns the appropriate value
})
});
The issue is that the sort by value plugin assumes that all graphs need to be sorted and modified to use the open/close fields to achieve this effect, which causes it to move your bullet to an incorrect location. Since you have multiple axes, you can modify the plugin to check if the graph belongs to the first axis and set a flag to be used to re-add the point correctly:
// Go through each category and order values
for (var i = 0; i < chart.dataProvider.length; i++) {
// ...
for (var x = 0; x < chart.graphs.length; x++) {
var graph = chart.graphs[x];
items.push({
"graph": graph,
// check if this graph's data points need to be omitted from the sorting process.
"ignoreSort": (graph.valueAxis && graph.valueAxis !== chart.valueAxes[0].id),
"value": dp[graph.originalValueField]
});
}
// ...
// Calculate open and close fields
var offset = 0;
for (var x = 0; x < items.length; x++) {
var item = items[x];
if (!item.ignoreSort) {
//process the pont as normal if it doesn't have the flag set with open/value fields
dp[item.graph.openField] = offset;
dp[item.graph.valueField] = offset + dp[item.graph.originalValueField];
offset = dp[item.graph.valueField];
} else {
//otherwise treat the point as a normal graph and use the value field
dp[item.graph.valueField] = dp[item.graph.originalValueField]
}
}
}
You'll also want to synchronize the axes' min/max values so that your target is correctly placed with respect to your stacked bars. You can achieve this using another custom plugin through addInitHandler:
//synchronizes axes' min/max values using a custom synchronizeValueAxes property
//(while synchronizeGrid exists, it doesn't work with this particular chart)
AmCharts.addInitHandler(function(chart) {
if (chart.synchronizeValueAxes) {
setTimeout(function() {
var max = chart.valueAxes.reduce(function(max, axis) {
if (!isNaN(axis.max)) {
return Math.max(max, axis.max);
} else {
return max;
}
}, Number.MIN_VALUE);
var min = chart.valueAxes.reduce(function(min, axis) {
if (!isNaN(axis.min)) {
return Math.min(min, axis.min);
} else {
return min;
}
}, Number.MAX_VALUE);
chart.valueAxes.forEach(function(axis) {
axis.maximum = max;
axis.minimum = min;
axis.strictMinMax = true;
});
chart.validateData();
}, 500);
}
}, ["serial"]);
Updated fiddle
Json Data :
{
"food": {
"rates": {
"defaultRates": {
"cardRate": 600,
"discountedRate": 500
},
"otherRates": [
{
"cardRate": 600,
"discountedRate": 400,
"fields": [
{
"name": "Quantity",
"min": 3,
"max": 6
}
],
"name": "3-6 Quantity"
},
{
"cardRate": 600,
"discountedRate": 300,
"fields": [
{
"name": "Quantity",
"min": 7
}
],
"name": "7+ Quantity"
}
]
},
"details" : {
"name" : "Something"
}
}
JS Code:
$scope.food = angular.copy(JSONObject); // Copied JsonObject
$scope.defaultRates = angular.copy($scope.food.rates.defaultRates);
$scope.otherRates = angular.copy($scope.food.rates.otherRates);
$scope.quantity = 4;
angular.forEach($scope.otherRates, function (otherRate) {
angular.forEach(otherRate.fields, function (field) {
if($scope.quantity >= field.min){
if(field.max) {
if($scope.quantity <= field.max){
$scope.discountedRate = angular.copy(otherRate.discountedRate);
}
} else {
$scope.discountedRate = angular.copy(otherRate.discountedRate);
}
} else {
$scope.discountedRate = angular.copy($scope.defaultRates.discountedRate);
}
})
});
If I console the $scope.discountedRate I'm getting the 7+ quantity rate. I want to get the $scope.discountedRate according to the $scope.quantity I have given.
So, How to get the discounted rate from the json data by comparing the quantity of the food.
You need to iterate over otherRates as follows.
var qty = what ever the user selects
var matched = {};
angular.forEach(food.otherRates, function(otherRate){
angular.forEach(otherRate.fields, function(field){
if(field.min <= qty && field.max >= qty)
matched = otherRate;
else if(field.min <= qty)
matched = otherRate;
});
});
console.log(matched);
//matched is the other Rate
I've stuck for this for 2 days, tried so many ways still couldn't get it right. I can't change the API data so I have to deal with front end handling. I really need some help.
$scope.stationary = [{
"name": "Pen",
"data": [{
"date": "1-10-2017",
"inventory": 25
}, {
"date": "2-10-2017",
"inventory": 21
}]
}, {
"name": "Color Pencil",
"data": [{
"date": "1-10-2017",
"inventory": 3
}, {
"date": "2-10-2017",
"inventory": 0
}]
}, {
"name": "Color Pencil Special",
"data": [{
"date": "1-10-2017",
"inventory": 2
}, {
"date": "2-10-2017",
"inventory": 1 // make this view to '-' since inventory of color pencil is zero
}]
}]
http://jsfiddle.net/op2zd2vr/
The case is if color pencil's inventory is zero, should display '-' on color pencil special column.
Try this:
Updated Answer:
myApp.filter('customFilter', function() {
return function(items) {
for (var i = 0; i < items.length; i++) {
for (var k = 0; k < items[i].data.length; k++) {
if (i != 0 && items[i - 1].data[k].inventory == 0) {
items[i].data[k]['isZero'] = true;
} else {
items[i].data[k]['isZero'] = false;
}
}
}
return items;
};
});
See Updated jsfiddle link.
It's working for me.
Guess you have to manipulate the datas itselves, you cannot simply use a filter for this particular case.
Here's my fiddle: http://jsfiddle.net/op2zd2vr/2/
Solution used (depending ont the datas' names):
var positions = [];
$scope.stationary.forEach(function (s) {
if(s.name === 'Color Pencil'){
for(var i = 0; i < s.data.length; i++){
if(s.data[i].inventory === 0){
positions.push(i);
}
}
}
if(s.name === 'Color Pencil Special'){
for(var i = 0; i < s.data.length; i++){
if(positions.indexOf(i) >= 0){
s.data[i].inventory = '-';
}
}
}
});
I have data.json in this format:
{
"Users": [
{
"userName": "Herbie",
"weigh-in data": [
{ "date": "2016.01.04", "weight": "114.3" },
{ "date": "2016.01.05", "weight": "114.6" },
{ "date": "2016.01.06", "weight": "114.9" }
]
},
{
"userName": "Wayne",
"weigh-in data": [
{ "date": "2016.02.01", "weight": "120.3" },
{ "date": "2016.02.05", "weight": "123.6" },
{ "date": "2016.02.06", "weight": "123.9" }
]
}
]
}
// etc., more user objects
In my application: a selection of a user is made, an ajax call gets this data, I loop thru it to get only the selected user's weigh-in data, and I'm successfully rendering this data to a table.
Now I'm trying to use the same result in a Dimple chart but Dimple evidently doesn't like my chartData array:
var dataObj = JSON.parse(jsonData);
var usersArray = dataObj.Users;
var chartData = [];
// etc. SNIP
for (var obj of usersArray) {
if (obj.userName === selUser) { // weigh-ins of the selected user only
dataRows.innerHTML = "";
for (var i = 0, j = selUserData.length; i < j; i++) {
var dataDate = selUserData[i].date;
var dataWeight = selUserData[i].weight;
chartData.push('{ "User"' + ': ' + '"'+selUser+'", ' + '"Date":' + ' ' + '"'+dataDate+'", ' + '"Weight":' + ' ' + '"'+dataWeight+'" }');
// SNIP: build rows from the data, load up the table, no problem
dataRows.innerHTML += row;
} // selUserData loop
var svg = dimple.newSvg("#chartContainer", 800, 600);
var chart = new dimple.chart(svg, chartData);
chart.setBounds(60, 30, 505, 305);
var x = chart.addCategoryAxis("x", "Date");
x.addOrderRule("Dates");
var y = chart.addCategoryAxis("y", "Weight");
y.addOrderRule("Weights");
var s = chart.addSeries("weigh-ins", dimple.plot.line);
chart.draw();
... which results in a non-chart. However, if I console.log or alert(chartData) and set the result as chartData, i.e.:
var chartData = [
{ "User": "Wayne", "Date": "2016.02.01", "Weight": "180.3" },
{ "User": "Wayne", "Date": "2016.02.05", "Weight": "123.6" },
{ "User": "Wayne", "Date": "2016.02.06", "Weight": "153.9" }
]
... then I get a chart, ergo my confusion.
Any insight greatly appreciated,
Whiskey
You're pushing the JSON into your array as strings not objects, it should be:
chartData.push({ "User": selUser, "Date": dataDate, "Weight": dataWeight });
Which has the added benefit of being much easier to read!