Create drilldown fusion chart on the same page - javascript
You will get results on the fusioncharts website if you search up what I asked, but it is not exactly what I am looking for.
I am querying data from a MySQL database, and putting this data into a fusion chart to display on my webpage. I want there to be 2 graphs on the same page, and when you click on one of the datapoints on the parent graph, the child graph will display the "drilled down" graph. How can I do this? As of right now I can press on the parent graph and it will open the child graph on a new webpage. This is the code for the home page with the parent graph. The file is named "dept.php".
<?php
/*Include the `fusioncharts.php` file that contains functions
to embed the charts.
*/
include("includes/fusioncharts.php");
// Establish a connection to the database. Variables defined before
$dbhandle = new mysqli($hostdb, $userdb, $passdb, $namedb);
// Render an error message, to avoid abrupt failure, if the database connection parameters are incorrect
if ($dbhandle->connect_error) {
exit("There was an error with your connection: ".$dbhandle->connect_error);
}
?>
<html>
<head>
<title>FusionCharts XT - Column 2D Chart - Data from a database</title>
<link rel="stylesheet" type="text/css" href="css/style.css" />
<!-- Include the `fusioncharts.js` file. This file is needed to render the chart. Ensure that the path to this JS file is correct. Otherwise, it may lead to JavaScript errors. -->
<script src="fusioncharts/js/fusioncharts.js"></script>
</head>
<body>
<?php
// Form the SQL query that returns the top 10 most populous countries
$strQuery = "SELECT Department, SUM(Quantity) AS Quantity FROM Scrap GROUP BY Department ORDER BY Department";
// Execute the query, or else return the error message.
$result = $dbhandle->query($strQuery) or exit("Error code ({$dbhandle->errno}): {$dbhandle->error}");
// If the query returns a valid response, prepare the JSON string
if ($result) {
// The `$arrData` array holds the chart attributes and data
$arrData = array(
"chart" => array(
"caption" => "Sample Chart",
"paletteColors" => "#0075c2",
"bgColor" => "#ffffff",
"borderAlpha"=> "20",
"canvasBorderAlpha"=> "0",
"usePlotGradientColor"=> "0",
"plotBorderAlpha"=> "10",
"showXAxisLine"=> "1",
"xAxisLineColor" => "#999999",
"showValues"=> "0",
"divlineColor" => "#999999",
"divLineIsDashed" => "1",
"showAlternateHGridColor" => "0"
)
);
$arrData["data"] = array();
// Push the data into the array
while($row = mysqli_fetch_array($result)) {
array_push($arrData["data"], array(
"label" => $row["Department"],
"value" => $row["Quantity"],
"link" => "deptDrillDown.php?Department=".$row["Department"]
)
);
}
/*JSON Encode the data to retrieve the string containing the JSON representation of the data in the array. */
$jsonEncodedData = json_encode($arrData);
/*Create an object for the column chart. Initialize this object using the FusionCharts PHP class constructor. The constructor is used to initialize
the chart type, chart id, width, height, the div id of the chart container, the data format, and the data source. */
$columnChart = new FusionCharts("column2D", "myFirstChart" , 600, 300, "chart-1", "json", $jsonEncodedData);
// Render the chart
$columnChart->render();
// Close the database connection
$dbhandle->close();
}
?>
<div id="chart-1"><!-- Fusion Charts will render here--></div>
</body>
</html>
And then here is the other page that contains the child graph. The file is named "deptDrillDown.php".
<?php
/* Include the `includes/fusioncharts.php` file that contains functions to embed the charts.*/
include("includes/fusioncharts.php");
// Establish a connection to the database. Variables defined earlier
$dbhandle = new mysqli($hostdb, $userdb, $passdb, $namedb);
/*Render an error message, to avoid abrupt failure, if the database connection parameters are incorrect */
if ($dbhandle->connect_error) {
exit("There was an error with your connection: ".$dbhandle->connect_error);
}
?>
<html>
<head>
<title>FusionCharts XT - Column 2D Chart</title>
<link rel="stylesheet" type="text/css" href="css/style.css" />
<!-- Include the `fusioncharts.js` file. This file is needed to render the chart. Ensure that the path to this JS file is correct. Otherwise, it may lead to JavaScript errors. -->
<script src="fusioncharts/js/fusioncharts.js"></script>
</head>
<body>
<?php
// Get the country code from the GET parameter
$countryCode = $_GET["Department"];
// Form the SQL query that returns the top 10 most populous cities in the selected country
$cityQuery = "SELECT ScrapDate, SUM(Quantity) AS Quantity FROM Scrap WHERE Department = ? GROUP BY ScrapDate ORDER BY ScrapDate";
// Prepare the query statement
$cityPrepStmt = $dbhandle->prepare($cityQuery);
// If there is an error in the statement, exit with an error message
if($cityPrepStmt === false) {
exit("Error while preparing the query to fetch data from City Table. ".$dbhandle->error);
}
// Bind the parameters to the query prepared
$cityPrepStmt->bind_param("s", $countryCode);
// Execute the query
$cityPrepStmt->execute();
// Get the results from the query executed
$cityResult = $cityPrepStmt->get_result();
// If the query returns a valid response, prepare the JSON string
if ($cityResult) {
/* Form the SQL query that will return the country name based on the country code. The result of the above query contains only the country code.
The country name is needed to be rendered as a caption for the chart that shows the 10 most populous cities */
$countryNameQuery = "SELECT ScrapDate FROM Scrap WHERE Department = ?";
// Prepare the query statement
$countryPrepStmt = $dbhandle->prepare($countryNameQuery);
// If there is an error in the statement, exit with an error message
if($countryPrepStmt === false) {
exit("Error while preparing the query to fetch data from Country Table. ".$dbhandle->error);
}
// Bind the parameters to the query prepared
$countryPrepStmt->bind_param("s", $countryCode);
// Execute the query
$countryPrepStmt->execute();
// Bind the country name to the variable `$countryName`
$countryPrepStmt->bind_result($countryName);
// Fetch the result from prepared statement
$countryPrepStmt->fetch();
// The `$arrData` array holds the chart attributes and data
$arrData = array(
"chart" => array(
"caption" => "Top 10 Most Populous Cities in ".$countryName,
"paletteColors" => "#0075c2",
"bgColor" => "#ffffff",
"borderAlpha"=> "20",
"canvasBorderAlpha"=> "0",
"usePlotGradientColor"=> "0",
"plotBorderAlpha"=> "10",
"showXAxisLine"=> "1",
"xAxisLineColor" => "#999999",
"showValues"=> "0",
"divlineColor" => "#999999",
"divLineIsDashed" => "1",
"showAlternateHGridColor" => "0"
)
);
$arrData["data"] = array();
// Push the data into the array
while($row = $cityResult->fetch_array()) {
array_push($arrData["data"], array(
"label" => $row["ScrapDate"],
"value" => $row["Quantity"]
)
);
}
/*JSON Encode the data to retrieve the string containing the JSON representation of the data in the array. */
$jsonEncodedData = json_encode($arrData);
/*Create an object for the column chart using the FusionCharts PHP class constructor. Syntax for the constructor is `FusionCharts("type of chart",
"unique chart id", "width of chart", "height of chart", "div id to render the chart", "data format", "data source")`.*/
$columnChart = new FusionCharts("column2D", "myFirstChart" , 600, 300, "chart-1", "json", $jsonEncodedData);
// Render the chart
$columnChart->render();
// Close the database connection
$dbhandle->close();
}
?>
Back
<div id="chart-1"><!-- Fusion Charts will render here--></div>
</body>
</html>
n number of charts can be rendered in a single page using FusionCharts.
Store their chart references, e.g. in an associative array.
Use the dataplotClick event to capture the event being generated by clicking on a data.
Inside the callback, use the setJSONData to update the child chart, one wanna update.
A dummy code for this would be:
FusionCharts.ready(function () {
var chart1 = new FusionCharts({
type: 'msstackedcolumn2d',
renderAt: 'chart-container1',
width: '550',
height: '350',
dataFormat: 'json',
dataSource: {
// enter the json data here
},
"events": {
"dataplotClick": function(eventObj, dataObj) {
/* so every time a dataClickEvent is being triggered from the data plot,
a new json `json2` is fetched from a sql query and
chart2 is updated with it.*/
chart2.setJSONData(json2);
}
}
}
}).render();
});
Couple of days back I created this fiddle, hope this becomes useful here too. Instead of doing a SQL query, here we have a generalised data, every time a click is made, it internally makes a function call, and creates a data dynamically out of it. Lot of function calls for making it entirely dynamic might make the code look complex. But the basic philosophy I shared in the dummy code avobe is the same here.
The snippet version for the code for a quick reference.Better to run the result in full page to check whats exactly happening.
function getData() {
var arr = [{
seriesname: "Book A",
data: [{
"label": "Paper",
"value": 100
}, {
"label": "Promotion",
"value": 150
}, {
"label": "Transportation",
"value": 175
}, {
"label": "Royality",
"value": 200
}, {
"label": "Printing",
"value": 250
}, {
"label": "Binding",
"value": 275
}]
}, {
seriesname: "Book B",
data: [{
"label": "Paper",
"value": 130
}, {
"label": "Promotion",
"value": 110
}, {
"label": "Transportation",
"value": 155
}, {
"label": "Royality",
"value": 250
}, {
"label": "Printing",
"value": 210
}, {
"label": "Binding",
"value": 215
}]
}, {
seriesname: "Book C",
data: [{
"label": "Paper",
"value": 70
}, {
"label": "Promotion",
"value": 180
}, {
"label": "Transportation",
"value": 125
}, {
"label": "Royality",
"value": 150
}, {
"label": "Printing",
"value": 290
}, {
"label": "Binding",
"value": 245
}]
}, {
seriesname: "Book D",
data: [{
"label": "Paper",
"value": 150
}, {
"label": "Promotion",
"value": 100
}, {
"label": "Transportation",
"value": 105
}, {
"label": "Royality",
"value": 125
}, {
"label": "Printing",
"value": 278
}, {
"label": "Binding",
"value": 235
}]
}, {
seriesname: "Book E",
data: [{
"label": "Paper",
"value": 60
}, {
"label": "Promotion",
"value": 250
}, {
"label": "Transportation",
"value": 115
}, {
"label": "Royality",
"value": 189
}, {
"label": "Printing",
"value": 190
}, {
"label": "Binding",
"value": 285
}]
}, {
seriesname: "Book F",
data: [{
"label": "Paper",
"value": 190
}, {
"label": "Promotion",
"value": 200
}, {
"label": "Transportation",
"value": 160
}, {
"label": "Royality",
"value": 148
}, {
"label": "Printing",
"value": 178
}, {
"label": "Binding",
"value": 295
}]
}];
return arr;
}
function getValues(componentName) {
var i,
j,
arr = getData(),
valueArr = [],
len1;
for (i = 0, len = arr.length; i < len; i += 1) {
for (j = 0, len1 = arr[i].data.length; j < len1; j += 1) {
if (arr[i].data[j].label === componentName) {
valueArr.push({
value: arr[i].data[j].value
});
break;
}
}
}
return [{
seriesname: componentName,
data: valueArr
}];
}
function getProducts(componentName) {
var arr = getData(),
productArr = [];
for (i = 0, len = arr.length; i < len; i += 1) {
for (j = 0; j < arr[i].data.length; j += 1) {
if (arr[i].data[j].label === componentName) {
productArr.push({
"label": arr[i].seriesname,
"value": arr[i].data[j].value
});
break;
}
}
}
return productArr;
}
function getComponents(label, value) {
var arr = getData(),
sum,
i,
j,
len,
len1,
obj =
componentArr = [];
if (label === undefined) {
label = true;
}
if (value === undefined) {
value = true;
}
for (i = 0, len = arr[0].data.length; i < len; i += 1) {
sum = 0;
obj = {};
for (j = 0, len1 = arr.length; j < len1; j += 1) {
sum += arr[j].data[i].value;
}
if (label) {
obj.label = arr[0].data[i].label;
}
if (value) {
obj.value = sum;
}
componentArr.push(obj);
}
return componentArr;
}
function getSeriesNames() {
var arr = getData(),
seriesName = [];
for (i = 0, len = arr.length; i < len; i += 1) {
seriesName.push({
"label": arr[i].seriesname
});
}
return seriesName;
}
function getMode() {
var e = document.getElementById("interaction");
return e.options[e.selectedIndex].value;
}
FusionCharts.ready(function() {
var lastClickedId = true;
var pieChart = new FusionCharts({
type: 'pie2d',
renderAt: 'pieContainer',
width: '600',
height: '400',
dataFormat: 'json',
dataSource: {
"chart": {
"caption": "Expenditures Incurred in Publishing a Book",
"subCaption": "Component-wise BreakUp",
"enableMultiSlicing": "0",
"bgcolor": "FFFFFF",
"showvalues": "1",
"showpercentvalues": "1",
"showborder": "0",
"showplotborder": "0",
"showlegend": "1",
"legendborder": "0",
"legendposition": "bottom",
"enablesmartlabels": "1",
"use3dlighting": "0",
"showshadow": "0",
"legendbgcolor": "#CCCCCC",
"legendbgalpha": "20",
"legendborderalpha": "0",
"legendshadow": "0",
"legendnumcolumns": "3",
"palettecolors": "#f8bd19,#e44a00,#008ee4,#33bdda,#6baa01,#583e78"
},
"data": getComponents()
},
"events": {
"dataplotClick": function(eventObj, dataObj) {
if (getMode() === 'pie') {
var json = stackedChart.getJSONData(),
categoryLabel = dataObj.categoryLabel;
json.chart.subCaption = "BreakUp of " + categoryLabel + " in different product";
json.categories[0].category = getSeriesNames();
json.dataset = getValues(dataObj.categoryLabel);
stackedChart.setJSONData(json);
}
}
}
}).render();
var stackedChart = new FusionCharts({
type: 'stackedBar2D',
renderAt: 'barContainer',
width: '600',
height: '400',
dataFormat: 'json',
dataSource: {
"chart": {
"bgcolor": "FFFFFF",
"outcnvbasefontcolor": "666666",
"caption": "Expenditures Incurred in Publishing a Book",
"subCaption": "Product-wise BreakUp",
"xaxisname": "Expenditures Cost",
"yaxisname": "Cost",
"numberprefix": "$",
"showvalues": "0",
"numvdivlines": "10",
"showalternatevgridcolor": "1",
"alternatevgridcolor": "e1f5ff",
"divlinecolor": "e1f5ff",
"vdivlinecolor": "e1f5ff",
"basefontcolor": "666666",
"tooltipbgcolor": "F3F3F3",
"tooltipbordercolor": "666666",
"canvasbordercolor": "666666",
"canvasborderthickness": "1",
"showplotborder": "1",
"plotfillalpha": "80",
"showborder": "0",
"legendbgcolor": "#CCCCCC",
"legendbgalpha": "20",
"legendborderalpha": "0",
"legendshadow": "0",
"legendnumcolumns": "3"
},
"categories": [{
"category": getComponents(true, false)
}],
"dataset": getData()
},
"events": {
"dataplotClick": function(eventObj, dataObj) {
if (getMode() === 'stackedBar') {
var JSON = pieChart.getJSONData(),
categoryLabel = dataObj.categoryLabel;
JSON.chart.subCaption = "BreakUp of " + categoryLabel + " in different product";
JSON.data = getProducts(categoryLabel);
pieChart.setJSONData(JSON);
pieChart.slicePlotItem(dataObj.datasetIndex);
}
}
}
}).render();
function resetFN() {
var json = pieChart.getJSONData();
json.chart.subCaption = "Component-wise BreakUp";
json.data = getComponents();
pieChart.setJSONData(json);
json = stackedChart.getJSONData();
json.chart.subCaption = "Product-wise BreakUp";
json.categories[0].category = getComponents(true, false);
json.dataset = getData();
stackedChart.setJSONData(json);
}
document.getElementById('reset').addEventListener('click', resetFN);
document.getElementById('interaction').addEventListener('change', resetFN);
});
h4 {
font-size: 20px;
margin-bottom: 10px
}
.intro {
margin: 0 auto;
background-color: #fff280;
padding: 15px
}
em {
font-style: italic
}
#interactionWrapper {
margin: 5px 10px;
}
button {
border: 1px solid #0b77bc;
background-color: #0d83ce;
color: #ffffff;
margin: 10px 0 0 15px;
padding: 5px 10px;
font-size: 14px;
cursor: pointer
}
.centerAlign {
text-align: center;
}
<script src="http://static.fusioncharts.com/code/latest/fusioncharts.js"></script>
<script src="http://static.fusioncharts.com/code/latest/themes/fusioncharts.theme.fint.js"></script>
<div class="intro">
<h4>Expenditures incurred while publishing books</h4>
<p><em>A company has 6 books to publish for this quater. The stacked chart shows component prices stacked as per different books. While the pie chart, shows the cumilative component price.</em></p>
<p>
<em>There are two interaction modes - namely "Interact in stacked chart" and "Interact in pie chart".On clicking in any plot on stacked chart, it shows the book-wise distribution of that component in the pie chart. Whereas on clicking the pie chart, for a component being clicked, it shows the book-wise distribution in the bar chart</em>
</p>
</div>
<div id="interactionWrapper">
<span>Interaction Mode:</span>
<span>
<select id="interaction">
<option value="stackedBar">Interact in stacked bar</option>
<option value="pie">Interact in the pie chart</option>
</select>
</span>
</div>
<div class="centerAlign">
<span id="barContainer">FusionCharts XT will load here!</span>
<span id="pieContainer">FusionCharts XT will load here!</span>
</div>
<button id="reset">Reset</button>
Related
AmChart : hide event
I'm using amChart with the plugin dataLoader to load data and eventData so I have something like this : var defaultStockChartData = { "dataLoader": { "url": "urlToMyFile.csv", "format": "csv", "delimiter": ",", "useColumnNames": true }, /** * data loader for events data */ "eventDataLoader": { "url": urlToMyFile.scv, "async": true, "postProcess": function(data) { for (var x in data) { var color = "#85CDE6"; switch (data[x].Type) { case 'A': color = "#85CDE6"; break; case 'B': color = "#85C56E6"; break; default: color = "#cccccc"; break; } data[x] = { "type": "pin", "graph": "g1", "backgroundColor": color, "date": data[x].Date, "text": data[x].Type, "description": data[x].Description }; } return data; } } ... } Now what I need to do is a checkbox that shows all the event on the graph with the type 'A' when it's checked and hide all the event with the type 'A' when it's unchecked. How I can I access the events of my chart and hide them follow their type?
A better way to do this is to modify the event data directly and toggle each element's graph property to null or the original graph to show/hide them so you don't have to hack around the charts' bullet handling on zoom and other events. For example: HTML: <div> <label>Hide Event A <input type="checkbox" class="hide-event" data-event="A"></label> <label>Hide Event B <input type="checkbox" class="hide-event" data-event="B"></label> </div> JS: //show/hide events based on selected checkbox Array.prototype.forEach.call( document.querySelectorAll('.hide-event'), function(checkbox) { checkbox.addEventListener('change', function() { var event = checkbox.dataset.event; chart.dataSets[0].stockEvents.forEach(function(eventItem) { if (eventItem.text === event) { if (checkbox.checked) { //copy graph reference to a dummy value and null out the original graph eventItem.oldGraph = eventItem.graph; eventItem.graph = null; } else { //restore original graph and null out copy/dummy reference eventItem.graph = eventItem.oldGraph; eventItem.oldGraph = null; } } }); chart.validateData(); //redraw the chart }); } ); Full demo below: var chartData = []; var eventData = []; generateChartData(); //show/hide events based on selected checkbox Array.prototype.forEach.call( document.querySelectorAll('.hide-event'), function(checkbox) { checkbox.addEventListener('change', function() { var event = checkbox.dataset.event; chart.dataSets[0].stockEvents.forEach(function(eventItem) { if (eventItem.text === event) { if (checkbox.checked) { //copy graph reference to a dummy value and null out the original graph eventItem.oldGraph = eventItem.graph; eventItem.graph = null; } else { //restore original graph and null out copy/dummy reference eventItem.graph = eventItem.oldGraph; eventItem.oldGraph = null; } } }); chart.validateData(); //redraw the chart }); } ); function generateChartData() { var firstDate = new Date( 2012, 0, 1 ); firstDate.setDate( firstDate.getDate() - 500 ); firstDate.setHours( 0, 0, 0, 0 ); for ( var i = 0; i < 500; i++ ) { var newDate = new Date( firstDate ); newDate.setDate( newDate.getDate() + i ); var a = Math.round( Math.random() * ( 40 + i ) ) + 100 + i; var b = Math.round( Math.random() * 100000000 ); chartData.push( { "date": newDate, "value": a, "volume": b } ); if ((i + 1) % 8 === 0) { eventData.push({ "date": newDate, "type": "sign", "backgroundColor": "#85CDE6", "graph": "g1", "text": (i + 1) % 5 == 0 ? "B" : "A", "description": "Event " + ((i + 1) % 5 == 0 ? "B" : "A") + " at index " + i }) } } } var chart = AmCharts.makeChart( "chartdiv", { "type": "stock", "theme": "light", "dataSets": [ { "color": "#b0de09", "fieldMappings": [ { "fromField": "value", "toField": "value" }, { "fromField": "volume", "toField": "volume" } ], "dataProvider": chartData, "categoryField": "date", // EVENTS "stockEvents": eventData } ], "panels": [ { "title": "Value", "stockGraphs": [ { "id": "g1", "valueField": "value" } ], "stockLegend": { "valueTextRegular": " ", "markerType": "none" } } ], "chartScrollbarSettings": { "graph": "g1" }, "chartCursorSettings": { "valueBalloonsEnabled": true, "graphBulletSize": 1, "valueLineBalloonEnabled": true, "valueLineEnabled": true, "valueLineAlpha": 0.5 } } ); #chartdiv { width: 100%; height: 500px; } <script src="https://www.amcharts.com/lib/3/amcharts.js"></script> <script src="https://www.amcharts.com/lib/3/serial.js"></script> <script src="https://www.amcharts.com/lib/3/amstock.js"></script> <script src="https://www.amcharts.com/lib/3/themes/light.js"></script> <div> <label>Hide Event A <input type="checkbox" class="hide-event" data-event="A"></label> <label>Hide Event B <input type="checkbox" class="hide-event" data-event="B"></label> </div> <div id="chartdiv"></div>
I didn't find a way to do it with AmChart so I did it in javascript (not by type but just need to check the value) First I need this function : function hideShowGraphEvent() { if($("#chxEventA").prop("checked")) { $("g.amcharts-graph-bullet").show(); } else { $("g.amcharts-graph-bullet").hide(); } } And call it each time you check or uncheck the box : $("#chxEventA").change(function() { hideShowGraphEvent(); }); But if you use zoom (like me) it will stop working when you zoom so you need to call the function each time you zoom : "listeners" : [ { "event": "zoomed", "method": function() { hideShowGraphEvent(); } }, ... ]
Auto-truncate long category axis labels but display the full label in legend?
I am using amchart for a graph. Below is the code, var chart = AmCharts.makeChart("chartdiv", { "theme": "light", "type": "serial", "startDuration": 2, "dataProvider": [{ "country": "This is Sample Data with long label", "visits": 4025, "color": "#FF0F00" }, { "country": "This is Sample Data with long label1", "visits": 1882, "color": "#FF6600" }, { "country": "This is Sample Data with long label2", "visits": 1809, "color": "#FF9E01" }, { "country": "This is Sample Data with long label3", "visits": 1322, "color": "#FCD202" }, { "country": "This is Sample Data with long label4", "visits": 1122, "color": "#F8FF01" }, { "country": "This is Sample Data with long label5", "visits": 1114, "color": "#B0DE09" }, { "country": "This is Sample Data with long label6", "visits": 984, "color": "#04D215" }, { "country": "This is Sample Data with long label7", "visits": 711, "color": "#0D8ECF" }, { "country": "This is Sample Data with long label8", "visits": 665, "color": "#0D52D1" }, { "country": "This is Sample Data with long label9", "visits": 580, "color": "#2A0CD0" }, { "country": "This is Sample Data with long label10", "visits": 443, "color": "#8A0CCF" }, { "country": "This is Sample Data with long label11", "visits": 441, "color": "#CD0D74" }, { "country": "This is Sample Data with long label12", "visits": 395, "color": "#754DEB" }, { "country": "This is Sample Data with long label13", "visits": 386, "color": "#DDDDDD" }, { "country": "This is Sample Data with long label14", "visits": 338, "color": "#333333" }], "valueAxes": [{ "position": "left", "axisAlpha":0, "gridAlpha":0 }], "graphs": [{ "balloonText": "[[category]]: <b>[[value]]</b>", "colorField": "color", "fillAlphas": 0.85, "lineAlpha": 0.1, "type": "column", "topRadius":1, "valueField": "visits" }], "depth3D": 40, "angle": 30, "chartCursor": { "categoryBalloonEnabled": false, "cursorAlpha": 0, "zoomable": false }, "categoryField": "country", "categoryAxis": { "gridPosition": "start", "axisAlpha":0, "gridAlpha":0 }, "labelFunction": function(label, item, axis) { var chart = axis.chart; if ( (chart.realWidth <= 300 ) && ( label.length > 5 ) ) return label.substr(0, 5) + '...'; if ( (chart.realWidth <= 500 ) && ( label.length > 10 ) ) return label.substr(0, 10) + '...'; return label; }, "legend": { "useGraphSettings": true }, "export": { "enabled": true } }, 0); However the Xaxis label is very lenghy, I wanted to auto truncate the long category axis labels like this example and also enable legend. But enabling legend doesn't work, also auto truncating doesn't seem to work. Could someone help me out here? Thanks in advance. Here is the link to codepen [1]. [1] https://codepen.io/gknathkumar/pen/OxKGev
As others have stated, the labelFunction is part of the categoryAxis, so it needs to go in there. I'm partial to the method in kuzyn's implementation, but pick whichever you want. As for the legend, it is generated by graph objects by design. Since there's one graph object, there's only one marker. Adding a marker for each column requires you add custom code that modifies the legend's data array to generate customized markers. AmCharts has a knowledge base article for generating markers for each column. Relevant code below: /* Plugin to generate legend markers based on category and fillColor/lineColor/color field from the chart data by using the legend's custom data array. Also allows for toggling markers by completely removing/adding columns from the chart The plugin assumes there is only one graph object. */ AmCharts.addInitHandler(function(chart) { //method to handle removing/adding columns when the marker is toggled function handleCustomMarkerToggle(legendEvent) { var dataProvider = legendEvent.chart.dataProvider; var itemIndex; //store the location of the removed item //Set a custom flag so that the dataUpdated event doesn't fire infinitely, in case you have //a dataUpdated event of your own legendEvent.chart.toggleLegend = true; // The following toggles the markers on and off. // The only way to "hide" a column and reserved space on the axis is to remove it // completely from the dataProvider. You'll want to use the hidden flag as a means // to store/retrieve the object as needed and then sort it back to its original location // on the chart using the dataIdx property in the init handler if (undefined !== legendEvent.dataItem.hidden && legendEvent.dataItem.hidden) { legendEvent.dataItem.hidden = false; dataProvider.push(legendEvent.dataItem.storedObj); legendEvent.dataItem.storedObj = undefined; //re-sort the array by dataIdx so it comes back in the right order. dataProvider.sort(function(lhs, rhs) { return lhs.dataIdx - rhs.dataIdx; }); } else { // toggle the marker off legendEvent.dataItem.hidden = true; //get the index of the data item from the data provider, using the //dataIdx property. for (var i = 0; i < dataProvider.length; ++i) { if (dataProvider[i].dataIdx === legendEvent.dataItem.dataIdx) { itemIndex = i; break; } } //store the object into the dataItem legendEvent.dataItem.storedObj = dataProvider[itemIndex]; //remove it dataProvider.splice(itemIndex, 1); } legendEvent.chart.validateData(); //redraw the chart } //check if legend is enabled and custom generateFromData property //is set before running if (!chart.legend || !chart.legend.enabled || !chart.legend.generateFromData) { return; } var categoryField = chart.categoryField; var colorField = chart.graphs[0].lineColorField || chart.graphs[0].fillColorsField || chart.graphs[0].colorField; var legendData = chart.dataProvider.map(function(data, idx) { var markerData = { "title": data[categoryField] + ": " + data[chart.graphs[0].valueField], "color": data[colorField], "dataIdx": idx //store a copy of the index of where this appears in the dataProvider array for ease of removal/re-insertion }; if (!markerData.color) { markerData.color = chart.graphs[0].lineColor; } data.dataIdx = idx; //also store it in the dataProvider object itself return markerData; }); chart.legend.data = legendData; //make the markers toggleable chart.legend.switchable = true; chart.legend.addListener("clickMarker", handleCustomMarkerToggle); }, ["serial"]); This plugin requires that you set a custom generateFromData flag to true in your legend and nothing else (useGraphSettings is not compatible): "legend": { "generateFromData": true //custom property for the plugin }, Here's a demo that leverages kuzyn's trim method and the aforementioned plugin: /* Plugin to generate legend markers based on category and fillColor/lineColor/color field from the chart data by using the legend's custom data array. Also allows for toggling markers by completely removing/adding columns from the chart The plugin assumes there is only one graph object. */ AmCharts.addInitHandler(function(chart) { //method to handle removing/adding columns when the marker is toggled function handleCustomMarkerToggle(legendEvent) { var dataProvider = legendEvent.chart.dataProvider; var itemIndex; //store the location of the removed item //Set a custom flag so that the dataUpdated event doesn't fire infinitely, in case you have //a dataUpdated event of your own legendEvent.chart.toggleLegend = true; // The following toggles the markers on and off. // The only way to "hide" a column and reserved space on the axis is to remove it // completely from the dataProvider. You'll want to use the hidden flag as a means // to store/retrieve the object as needed and then sort it back to its original location // on the chart using the dataIdx property in the init handler if (undefined !== legendEvent.dataItem.hidden && legendEvent.dataItem.hidden) { legendEvent.dataItem.hidden = false; dataProvider.push(legendEvent.dataItem.storedObj); legendEvent.dataItem.storedObj = undefined; //re-sort the array by dataIdx so it comes back in the right order. dataProvider.sort(function(lhs, rhs) { return lhs.dataIdx - rhs.dataIdx; }); } else { // toggle the marker off legendEvent.dataItem.hidden = true; //get the index of the data item from the data provider, using the //dataIdx property. for (var i = 0; i < dataProvider.length; ++i) { if (dataProvider[i].dataIdx === legendEvent.dataItem.dataIdx) { itemIndex = i; break; } } //store the object into the dataItem legendEvent.dataItem.storedObj = dataProvider[itemIndex]; //remove it dataProvider.splice(itemIndex, 1); } legendEvent.chart.validateData(); //redraw the chart } //check if legend is enabled and custom generateFromData property //is set before running if (!chart.legend || !chart.legend.enabled || !chart.legend.generateFromData) { return; } var categoryField = chart.categoryField; var colorField = chart.graphs[0].lineColorField || chart.graphs[0].fillColorsField || chart.graphs[0].colorField; var legendData = chart.dataProvider.map(function(data, idx) { var markerData = { "title": data[categoryField] + ": " + data[chart.graphs[0].valueField], "color": data[colorField], "dataIdx": idx //store a copy of the index of where this appears in the dataProvider array for ease of removal/re-insertion }; if (!markerData.color) { markerData.color = chart.graphs[0].lineColor; } data.dataIdx = idx; //also store it in the dataProvider object itself return markerData; }); chart.legend.data = legendData; //make the markers toggleable chart.legend.switchable = true; chart.legend.addListener("clickMarker", handleCustomMarkerToggle); }, ["serial"]); // keep the data object separate from the call var dataProvider = [ { country: "This is Sample Data with long label", visits: 4025, color: "#FF0F00" }, { country: "This is Sample Data with long label1", visits: 1882, color: "#FF6600" }, { country: "This is Sample Data with long label2", visits: 1809, color: "#FF9E01" }, { country: "This is Sample Data with long label3", visits: 1322, color: "#FCD202" } ]; var chart = AmCharts.makeChart( "chartdiv", { theme: "light", type: "serial", startDuration: 2, dataProvider: dataProvider, valueAxes: [ { position: "left", axisAlpha: 0, gridAlpha: 0 } ], graphs: [ { balloonText: "[[category]]: <b>[[value]]</b>", colorField: "color", fillAlphas: 0.85, lineAlpha: 0.1, type: "column", topRadius: 1, valueField: "visits" } ], depth3D: 40, angle: 30, chartCursor: { categoryBalloonEnabled: false, cursorAlpha: 0, zoomable: false }, categoryField: "country", categoryAxis: { gridPosition: "start", axisAlpha: 0, gridAlpha: 0, labelFunction: trimLabel, }, legend: { generateFromData: true //custom property for the plugin }, export: { enabled: true } }, 0 ); // function to trim the labels function trimLabel(label, item, axis) { var chartWidth = axis.chart.realWidth; var maxLabelLength = 15; // not counting the dots... // trim when the width of the chart is smalled than 300px if (chartWidth <= 300 && label.length > 5) return label.substr(0, 5) + "..."; // trim when the width of the chart is smalled than 500px if (chartWidth <= 500 && label.length > 10) return label.substr(0, 10) + "..."; // trim when label is longer than maxLabelLength regardless of chart width return label.length >= 15 ? label.substr(0, 14) + "...": label; } #chartdiv { width: 990px; height: 365px; border-radius: 3px; margin: 0px; border: 1px dotted #728FCE; } <script src="https://www.amcharts.com/lib/3/amcharts.js"></script> <script src="https://www.amcharts.com/lib/3/serial.js"></script> <script src="https://www.amcharts.com/lib/3/plugins/export/export.min.js"></script> <link rel="stylesheet" href="https://www.amcharts.com/lib/3/plugins/export/export.css" type="text/css" media="all" /> <script src="https://www.amcharts.com/lib/3/themes/light.js"></script> <input type="button" value="Set width to 300px" onclick="document.getElementById('chartdiv').style.width='300px';" /> <input type="button" value="Set width to 500px" onclick="document.getElementById('chartdiv').style.width='500px';" /> <input type="button" value="Set width to 700px" onclick="document.getElementById('chartdiv').style.width='700px';" /> <div id="chartdiv"></div> Note that if you want the labels on the markers trimmed, you'll have to call trim when creating the markers' titles in the initHandler as well.
make labelFunction like below: "labelFunction": function(label, item, axis) { var chart = axis.chart; console.log("CHART:", chart.realWidth, label.length, label ); if ( ( label.length > 5 ) ){ console.log("CHARTLABEL:", label.substr(0, 5) + '...'); return label.substr(0, 7) + '...'; } if ( ( label.length > 10 ) ){ return label.substr(0, 10) + '...'; } return label; }, And your code was not working because you have to put label function inside categoryAxis Final working solution : https://codepen.io/anon/pen/aLerBZ?editors=0010
There are a couple of small mistake in your code: labelFunction is not in categoryAxis the size of your chart never drops below 500px, hence the labels were never trimmed like in the example some of the code could have been put in variables to make is easier to debug I've separated some of the code, and added a maximum length (15 characters) for labels regardless of the chart width View the full example on Codepen // keep the data object separate from the call var dataProvider = [ { country: "This is Sample Data with long label", visits: 4025, color: "#FF0F00" }, { country: "This is Sample Data with long label1", visits: 1882, color: "#FF6600" }, { country: "This is Sample Data with long label2", visits: 1809, color: "#FF9E01" }, { country: "This is Sample Data with long label3", visits: 1322, color: "#FCD202" } ]; var chart = AmCharts.makeChart( "chartdiv", { theme: "light", type: "serial", startDuration: 2, dataProvider: dataProvider, valueAxes: [ { position: "left", axisAlpha: 0, gridAlpha: 0 } ], graphs: [ { balloonText: "[[category]]: <b>[[value]]</b>", colorField: "color", fillAlphas: 0.85, lineAlpha: 0.1, type: "column", topRadius: 1, valueField: "visits" } ], depth3D: 40, angle: 30, chartCursor: { categoryBalloonEnabled: false, cursorAlpha: 0, zoomable: false }, categoryField: "country", categoryAxis: { gridPosition: "start", axisAlpha: 0, gridAlpha: 0, labelFunction: trimLabel, }, legend: { useGraphSettings: true }, export: { enabled: true } }, 0 ); // function to trim the labels function trimLabel(label, item, axis) { var chartWidth = axis.chart.realWidth; var maxLabelLength = 15; // not counting the dots... // trim when the width of the chart is smalled than 300px if (chartWidth <= 300 && label.length > 5) return label.substr(0, 5) + "..."; // trim when the width of the chart is smalled than 500px if (chartWidth <= 500 && label.length > 10) return label.substr(0, 10) + "..."; // trim when label is longer than maxLabelLength regardless of chart width return label.length >= 15 ? label.substr(0, 14) + "...": label; }
Displaying data from socket io using angularJs
I'm trying to get data from socket io and display the data on a graph using angularJs. I'm getting the correct data from the server but when I'm trying to display it I'm getting:"No Data To Display". I tried to debug my controller and i saw that I'm getting data but some how angular on my HTML doesn't show it. i also tried using Async.js but it still doesn't work. my controller code: $scope.graph2,data2 = []; socketFactory.emit('getTemplateStatistics', null); socketFactory.on('sendTemplateStatistics', function(result) { for (var i=0; i < result.length; i++) { var temp = {label: result[i]._id, value: ""+result[i].total}; data2.push(temp); } $scope.graph2 = { chart: { caption: "Age profile of website visitors", subcaption: "Last Year", startingangle: "120", showlabels: "0", showlegend: "1", enablemultislicing: "0", slicingdistance: "15", showpercentvalues: "1", showpercentintooltip: "0", plottooltext: "Age group : $label Total visit : $datavalue", theme: "fint" }, data2 }; }); my HTML code: <div class="statistics view indent"> <div class="container" style="margin-top:10px"> <header> <h3>Statistics Preview Displayer</h3> </header> <div> <fusioncharts width="600" height="400" type="pie3d" datasource="{{ graph2 }}" ></fusioncharts> </div> Go back </div> </div>
I dont think this is a socket issue. Probably '$scope.graph2' is getting assigned before the for loop completes execution and so 'data2' has no value. Try changing the code like this $scope.graph2,data2 = []; socketFactory.emit('getTemplateStatistics', null); socketFactory.on('sendTemplateStatistics', function(result) { for (var i=0; i < result.length; i++) { var temp = {label: result[i]._id, value: ""+result[i].total}; data2.push(temp); if(i == result.length - 1) { // To ensure $scope.graph2 is assigned only after for loop completes and data has all values $scope.graph2 = { chart: { caption: "Age profile of website visitors", subcaption: "Last Year", startingangle: "120", showlabels: "0", showlegend: "1", enablemultislicing: "0", slicingdistance: "15", showpercentvalues: "1", showpercentintooltip: "0", plottooltext: "Age group : $label Total visit : $datavalue", theme: "fint" }, data2 }; } } }); This way $scope.graph2 gets assigned only after completion of for loop and so data2 wont be null
d3pie error: no data supplied on d3pie.js
I am using rails, and gathering some data to make pie charts. I am just using ruby objects (so no JSON), and using d3pie. First, I make a helper function. Then I make a javascript function using that helper, and pass it in the dom. Here's my code; helper.rb def options_data_to_d3(options_data) d3_data = [] options_data.each do |key, value| d3_data.push( { label: key.option.as_json, value: value.as_json } ) end return JSON.pretty_generate(d3_data) end this takes the ruby hash, and makes it into json js function function dataPieChart(id, data) { var config = { "header": { "title": { "text": "Quiz Questions", "fontSize": 18, "font": "verdana" }, "size": { "canvasHeight": 400, "canvasWidth": 500 }, "data": { "content": data }, "labels": { "outer": { "pieDistance": 32 } } } } var pie = new d3pie(id, config); } passing into the view <div id="quizQuestionOptionPie<%= question.id %>"></div> <script type="text/javascript">dataPieChart("quizQuestionOptionPie<%= question.id %>", <%= raw options_data_to_d3(data[:options]) %>); </script> when I call a console log in the javascript function to see what data is, I get the correct output that both d3pie and d3 are looking for, yet I am still getting the error d3pie error: no data supplied. does anyone see something wrong with my code, or something I am missing? any help is appreciated.
You config option is bracketed incorrectly (everything is a child of "header"). You really meant: var config = { "header": { "title": { "text": "Quiz Questions", "fontSize": 18, "font": "verdana" }, }, "size": { "canvasHeight": 400, "canvasWidth": 500 }, "data": { "content": data }, "labels": { "outer": { "pieDistance": 32 } } };
Dynamically create multi leveled drilldowns in Amcharts
I need to create a multi leveled that is dynamic because I have to drill down to hundreds or even thousands of data providers that will load from the database. The flow goes like this: I have 1, 2, 3, 4, and 5 years that will drill down to 16 Departments each and will drill down to Courses 10 or more courses. Doing it manually is tedious and I need it to be dynamic. Please help me. The variables: var ccs_final_data = AmCharts.loadJSON("<?php echo base_url();?>index.php/osa/final_ccs_data"); //VAR CCS AVERAGE_FINAL var drill_down_to_ccs_courses_average_final = AmCharts.loadJSON("<?php echo base_url();?>index.php/osa/ccs_courses_data_average_final"); var drill_down_to_ccs_sections_BSIT_average_final = AmCharts.loadJSON("<?php echo base_url();?>index.php/osa/ccs_sections_data_BSIT_average_final"); var drill_down_to_ccs_sections_ACT_average_final = AmCharts.loadJSON("<?php echo base_url();?>index.php/osa/ccs_sections_data_ACT_average_final"); var drill_down_to_ccs_sections_BSCS_average_final = AmCharts.loadJSON("<?php echo base_url();?>index.php/osa/ccs_sections_data_BSCS_average_final"); The graph: var chart2 = AmCharts.makeChart( "ccs2", { "theme": "light", type: "serial", pathToImages: "http://cdn.amcharts.com/lib/3/images/", dataProvider: ccs_final_data, categoryField: "category", categoryAxis: { labelRotation: 0, gridPosition: "start" }, valueAxes: [ { title: "CCS FINAL TERM - Passing" } ], graphs: [ { valueField: "value", colorField: "color", type: "column", lineAlpha: 100, fillAlphas: 1 } ], chartScrollbar: { "updateOnReleaseOnly": true }, chartCursor: { bulletsEnabled: "enabled", bulletSize: 15, cursorAlpha: 100, cursorColor: "#CC0000", zoomable: true, categoryBalloonEnabled: true }, export: { enabled: true } } ); Here's the drill down stuff: chart2.addListener("clickGraphItem", function (event) { if(event.item.category == "Average"){ event.chart.dataProvider = drill_down_to_ccs_courses_average_final; event.chart.validateData(); chart2.addListener("clickGraphItem", function (event) { if(event.item.category == "BSIT"){ event.chart.dataProvider = drill_down_to_ccs_sections_BSIT_average_final; event.chart.validateData(); } else if(event.item.category == "ACT"){ event.chart.dataProvider = drill_down_to_ccs_sections_ACT_average_final; event.chart.validateData(); } else if(event.item.category == "BSCS"){ event.chart.dataProvider = drill_down_to_ccs_sections_BSCS_average_final; event.chart.validateData(); } }); }
I'd say the best way to make it dynamic is to include some custom field for each data point in your data that would be passed in to server-side script so it knows which data to load. I'm assuming your data looks like this now: [ { "category": "BSIT", "value": 100 }, { "category": "ACT", "value": 200 }, { "category": "BSCS", "value": 150 } ] You could easily add a third field to hold the information for drill-down data load: [ { "category": "BSIT", "value": 100, "drill": "ccs_sections_data_BSIT_average_final" }, { "category": "ACT", "value": 200, "drill": "ccs_sections_data_ACT_average_final" }, { "category": "BSCS", "value": 150, "drill": "ccs_sections_data_BSCS_average_final" } ] Then, when clickGraphItem event occurs, you could just take that info and pass it to load script dynamically: chart2.addListener( "clickGraphItem", function( event ) { if ( event.item.dataContext.drill !== undefined ) { event.chart.dataProvider = AmCharts.loadJSON( "<?php echo base_url();?>index.php/osa/" + event.item.dataContext.drill ); event.chart.validateData(); } } ); This way, you could have any number of drill-down levels, with each level data containing info about where to look for the data for the next level. Also, I'm not sure as you haven't posted the code for it, so I'm assuming that AmCharts.loadJSON method is synchronous. If it's not (for example if you are using method from Data Loader), you will need to assign the chart's dataProvider after the data is loaded.