Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 6 of 6
  1. #1
    New Coder
    Join Date
    Nov 2010
    Posts
    94
    Thanks
    23
    Thanked 0 Times in 0 Posts

    Reading json file - help

    So I'm trying to read a json file and use some of the data within it to populate a chart using charts.js.

    The json file can be found here - http://www.cems.uwe.ac.uk/~r4-george...outh_west/json

    Here's my script -

    Code:
    var colors = ["#0a7b83","#2aa876","#ffd265","#f19c65","#ce4d45","#56676e","#e8e2b2","#d95e42"];
    
    		$.ajax({
    			url: "http://www.cems.uwe.ac.uk/~r4-george/atwd/crimes/6-2013/south_west/json",
    			dataType: 'json' 
    			
    		}).done(function(data) {
    		
    			var chartData = [];
    			$.each(data.response.crimes.region.area, function(key,area){
    				chartData.push({value: area['@attributes'].total, color:colors[key],title:area['@attributes'].id});
    			});
    
    			//Get the context of the canvas element we want to select
    			var ctx = document.getElementById("myChart").getContext("2d");
    			var chart = new Chart(ctx).Doughnut(chartData,{});
    			$( this ).addClass( "done" );
    			
    
    
    		});
    The only thing I can think of is that the totals in json are within speech marks, e.g "id":"South West","total":"240027", whereas I assume charts.js would require it as "total": 240027 without speech marks?

    I'm completely stuck on this one, any help appreciated!

    Thanks

  • #2
    Senior Coder Arbitrator's Avatar
    Join Date
    Mar 2006
    Location
    Splendora, Texas, United States of America
    Posts
    3,302
    Thanks
    28
    Thanked 276 Times in 270 Posts
    Quote Originally Posted by swiltch View Post
    The only thing I can think of is that the totals in json are within speech marks, e.g "id":"South West","total":"240027", whereas I assume charts.js would require it as "total": 240027 without speech marks?
    Assuming that there are no cross-domain permissions issues, a type issue is indeed the problem. (Though it's worth noting that correct terminology for "speech marks" is "quotation marks" and the type that those quotation marks indicate is a "string".) To explicitly convert a string to a number, use the Number constructor: Number(area["@attributes"].total).

    Other Issues:
    • There's no need to use dataType: "json" which can be inferred from the content-type HTTP header sent by the server. I checked and that server is sending the correct header.
    • There's no reason to specify title properties as part of the chart data; there's no such property according to the Chart.js documentation, so that information simply gets discarded.
    • There's no need to specify the second, optional parameter of the Doughnut method.
    • $(this).addClass("done"); can be struck; it doesn't do anything useful.

    In case you're curious how to do this without the overhead of jQuery, here's a revision of your code that doesn't use it:

    Code:
    <!doctype html>
    <html lang="en">
    	<head>
    		<meta charset="utf-8">
    		<title>Demo</title>
    	</head>
    	<body>
    		<script src="charts.js"></script> <!-- Obtained from http://www.chartjs.org/. -->
    		<canvas id="myChart" width="500" height="500"></canvas>
    		<script>
    			(function () {
    				"use strict";
    				function drawChart(areaData) {
    					var context = document.getElementById("myChart").getContext("2d");
    					var chart = new Chart(context);
    					var areaIndex = 0;
    					var areas = areaData.response.crimes.region.area;
    					var chartItem = null;
    					var chartItemColors = ["#0a7b83", "#2aa876", "#ffd265", "#f19c65", "#ce4d45", "#56676e", "#e8e2b2", "#d95e42"];
    					var chartData = [];
    					while (areaIndex < areas.length) {
    						chartItem = Object.create(null);
    						chartItem.value = Number(areas[areaIndex]["@attributes"].total);
    						chartItem.color = chartItemColors[areaIndex];
    						chartData.push(chartItem);
    						areaIndex += 1;
    					}
    					chart.Doughnut(chartData);
    				}
    				function parseAreaData() {
    					var areaDataRequest = this;
    					var areaData = null;
    					if (areaDataRequest.readyState === XMLHttpRequest.DONE && areaDataRequest.statusText === "OK") {
    						areaDataRequest.removeEventListener("readystatechange", parseAreaData);
    						areaData = JSON.parse(areaDataRequest.responseText);
    						drawChart(areaData);
    					}
    				}
    				function getAreaData() {
    					var areaDataRequest = null;
    					areaDataRequest = new XMLHttpRequest();
    					areaDataRequest.addEventListener("readystatechange", parseAreaData);
    					areaDataRequest.open("get", "http://www.cems.uwe.ac.uk/~r4-george/atwd/crimes/6-2013/south_west/json", false);
    					areaDataRequest.send();
    				}
    				getAreaData(); // And draw a doughnut chart.
    			})();
    		</script>
    	</body>
    </html>
    For every complex problem, there is an answer that is clear, simple, and wrong.

  • Users who have thanked Arbitrator for this post:

    swiltch (01-25-2014)

  • #3
    New Coder
    Join Date
    Nov 2010
    Posts
    94
    Thanks
    23
    Thanked 0 Times in 0 Posts
    Quote Originally Posted by Arbitrator View Post
    Assuming that there are no cross-domain permissions issues, a type issue is indeed the problem. (Though it's worth noting that correct terminology for "speech marks" is "quotation marks" and the type that those quotation marks indicate is a "string".) To explicitly convert a string to a number, use the Number constructor: Number(area["@attributes"].total).

    Other Issues:
    • There's no need to use dataType: "json" which can be inferred from the content-type HTTP header sent by the server. I checked and that server is sending the correct header.
    • There's no reason to specify title properties as part of the chart data; there's no such property according to the Chart.js documentation, so that information simply gets discarded.
    • There's no need to specify the second, optional parameter of the Doughnut method.
    • $(this).addClass("done"); can be struck; it doesn't do anything useful.

    In case you're curious how to do this without the overhead of jQuery, here's a revision of your code that doesn't use it:

    Code:
    <!doctype html>
    <html lang="en">
    	<head>
    		<meta charset="utf-8">
    		<title>Demo</title>
    	</head>
    	<body>
    		<script src="charts.js"></script> <!-- Obtained from http://www.chartjs.org/. -->
    		<canvas id="myChart" width="500" height="500"></canvas>
    		<script>
    			(function () {
    				"use strict";
    				function drawChart(areaData) {
    					var context = document.getElementById("myChart").getContext("2d");
    					var chart = new Chart(context);
    					var areaIndex = 0;
    					var areas = areaData.response.crimes.region.area;
    					var chartItem = null;
    					var chartItemColors = ["#0a7b83", "#2aa876", "#ffd265", "#f19c65", "#ce4d45", "#56676e", "#e8e2b2", "#d95e42"];
    					var chartData = [];
    					while (areaIndex < areas.length) {
    						chartItem = Object.create(null);
    						chartItem.value = Number(areas[areaIndex]["@attributes"].total);
    						chartItem.color = chartItemColors[areaIndex];
    						chartData.push(chartItem);
    						areaIndex += 1;
    					}
    					chart.Doughnut(chartData);
    				}
    				function parseAreaData() {
    					var areaDataRequest = this;
    					var areaData = null;
    					if (areaDataRequest.readyState === XMLHttpRequest.DONE && areaDataRequest.statusText === "OK") {
    						areaDataRequest.removeEventListener("readystatechange", parseAreaData);
    						areaData = JSON.parse(areaDataRequest.responseText);
    						drawChart(areaData);
    					}
    				}
    				function getAreaData() {
    					var areaDataRequest = null;
    					areaDataRequest = new XMLHttpRequest();
    					areaDataRequest.addEventListener("readystatechange", parseAreaData);
    					areaDataRequest.open("get", "http://www.cems.uwe.ac.uk/~r4-george/atwd/crimes/6-2013/south_west/json", false);
    					areaDataRequest.send();
    				}
    				getAreaData(); // And draw a doughnut chart.
    			})();
    		</script>
    	</body>
    </html>
    Excellent! Thank you very much for taking the time to solve this and then explain it to me as well, that makes perfect sense from what you've highlighted.

    Just one more thing, this works great with the example URL I've given, (../south_west/json) but I'm also using a few files that don't have more than one area, e.g. - http://www.cems.uwe.ac.uk/~r4-george...ion_fraud/json

    I've tried changing the URL to the above but the graph does not show (I need it to produce a graph that is just one colour basically, sounds silly I know as there is nothing to compare it to).

    Thanks again!

  • #4
    Senior Coder Arbitrator's Avatar
    Join Date
    Mar 2006
    Location
    Splendora, Texas, United States of America
    Posts
    3,302
    Thanks
    28
    Thanked 276 Times in 270 Posts
    Quote Originally Posted by swiltch View Post
    Just one more thing, this works great with the example URL I've given, (../south_west/json) but I'm also using a few files that don't have more than one area, e.g. - http://www.cems.uwe.ac.uk/~r4-george...ion_fraud/json

    I've tried changing the URL to the above but the graph does not show (I need it to produce a graph that is just one colour basically, sounds silly I know as there is nothing to compare it to).
    Hmm... Looking closer at your JSON code, it looks like you're creating the arrays by redeclaring the same property (@attributes) multiple times. When it's only declared once, the parent property (area) doesn't get treated as an array and therefore no longer has a length property. The areaIndex < areas.length check then fails because areas.length is undefined.

    Google revealed an isArray method of the Array object. Following is revised code with changes highlighted:

    Code:
    <!doctype html>
    <html lang="en">
    	<head>
    		<meta charset="utf-8">
    		<title>Demo</title>
    	</head>
    	<body>
    		<script src="charts.js"></script> <!-- Obtained from http://www.chartjs.org/. -->
    		<canvas id="myChart" width="500" height="500"></canvas>
    		<script>
    			(function () {
    				"use strict";
    				function drawChart(areaData) {
    					var context = document.getElementById("myChart").getContext("2d");
    					var chart = new Chart(context);
    					var areaIndex = 0;
    					var areas = areaData.response.crimes.region.area;
    					var chartItem = null;
    					var chartItemColors = ["#0a7b83", "#2aa876", "#ffd265", "#f19c65", "#ce4d45", "#56676e", "#e8e2b2", "#d95e42"];
    					var chartData = [];
    					if (Array.isArray(areas)) { // areas.length > 1
    						while (areaIndex < areas.length) {
    							chartItem = Object.create(null);
    							chartItem.value = Number(areas[areaIndex]["@attributes"].total);
    							chartItem.color = chartItemColors[areaIndex];
    							chartData.push(chartItem);
    							areaIndex += 1;
    						}
    					}
    					else { // one area: typeof areas.length === "undefined"
    						chartItem = Object.create(null);
    						chartItem.value = Number(areas["@attributes"].total);
    						chartItem.color = chartItemColors[areaIndex];
    						chartData.push(chartItem);
    					}
    					chart.Doughnut(chartData);
    				}
    				function parseAreaData() {
    					var areaDataRequest = this;
    					var areaData = null;
    					if (areaDataRequest.readyState === XMLHttpRequest.DONE && areaDataRequest.statusText === "OK") {
    						areaDataRequest.removeEventListener("readystatechange", parseAreaData);
    						areaData = JSON.parse(areaDataRequest.responseText);
    						drawChart(areaData);
    					}
    				}
    				function getAreaData() {
    					var areaDataRequest = null;
    					areaDataRequest = new XMLHttpRequest();
    					areaDataRequest.addEventListener("readystatechange", parseAreaData);
    					areaDataRequest.open("get", "json.json", false);
    					areaDataRequest.send();
    				}
    				getAreaData(); // And draw a doughnut chart.
    			})();
    		</script>
    	</body>
    </html>
    For every complex problem, there is an answer that is clear, simple, and wrong.

  • Users who have thanked Arbitrator for this post:

    swiltch (01-25-2014)

  • #5
    New Coder
    Join Date
    Nov 2010
    Posts
    94
    Thanks
    23
    Thanked 0 Times in 0 Posts
    Quote Originally Posted by Arbitrator View Post
    Hmm... Looking closer at your JSON code, it looks like you're creating the arrays by redeclaring the same property (@attributes) multiple times. When it's only declared once, the parent property (area) doesn't get treated as an array and therefore no longer has a length property. The areaIndex < areas.length check then fails because areas.length is undefined.

    Google revealed an isArray method of the Array object. Following is revised code with changes highlighted:

    Code:
    <!doctype html>
    <html lang="en">
    	<head>
    		<meta charset="utf-8">
    		<title>Demo</title>
    	</head>
    	<body>
    		<script src="charts.js"></script> <!-- Obtained from http://www.chartjs.org/. -->
    		<canvas id="myChart" width="500" height="500"></canvas>
    		<script>
    			(function () {
    				"use strict";
    				function drawChart(areaData) {
    					var context = document.getElementById("myChart").getContext("2d");
    					var chart = new Chart(context);
    					var areaIndex = 0;
    					var areas = areaData.response.crimes.region.area;
    					var chartItem = null;
    					var chartItemColors = ["#0a7b83", "#2aa876", "#ffd265", "#f19c65", "#ce4d45", "#56676e", "#e8e2b2", "#d95e42"];
    					var chartData = [];
    					if (Array.isArray(areas)) { // areas.length > 1
    						while (areaIndex < areas.length) {
    							chartItem = Object.create(null);
    							chartItem.value = Number(areas[areaIndex]["@attributes"].total);
    							chartItem.color = chartItemColors[areaIndex];
    							chartData.push(chartItem);
    							areaIndex += 1;
    						}
    					}
    					else { // one area: typeof areas.length === "undefined"
    						chartItem = Object.create(null);
    						chartItem.value = Number(areas["@attributes"].total);
    						chartItem.color = chartItemColors[areaIndex];
    						chartData.push(chartItem);
    					}
    					chart.Doughnut(chartData);
    				}
    				function parseAreaData() {
    					var areaDataRequest = this;
    					var areaData = null;
    					if (areaDataRequest.readyState === XMLHttpRequest.DONE && areaDataRequest.statusText === "OK") {
    						areaDataRequest.removeEventListener("readystatechange", parseAreaData);
    						areaData = JSON.parse(areaDataRequest.responseText);
    						drawChart(areaData);
    					}
    				}
    				function getAreaData() {
    					var areaDataRequest = null;
    					areaDataRequest = new XMLHttpRequest();
    					areaDataRequest.addEventListener("readystatechange", parseAreaData);
    					areaDataRequest.open("get", "json.json", false);
    					areaDataRequest.send();
    				}
    				getAreaData(); // And draw a doughnut chart.
    			})();
    		</script>
    	</body>
    </html>
    Awesome, that works perfectly!

    Just one more thing (I know I said that last time), if I wanted to create a bar chart using the data instead of a doughnut (eventually I'd like to have both on the same page for each file), is it just changing the line -

    Code:
    					chart.Doughnut(chartData);
    To -

    Code:
    					chart.Bar(chartData);
    If I try that I get the same error that you mentioned above, that the property 'length' is undefined.

    Thanks again for your help!
    Last edited by swiltch; 01-25-2014 at 12:34 PM.

  • #6
    Senior Coder Arbitrator's Avatar
    Join Date
    Mar 2006
    Location
    Splendora, Texas, United States of America
    Posts
    3,302
    Thanks
    28
    Thanked 276 Times in 270 Posts
    Quote Originally Posted by swiltch View Post
    Just one more thing (I know I said that last time), if I wanted to create a bar chart using the data instead of a doughnut (eventually I'd like to have both on the same page for each file), is it just changing the line -

    Code:
    					chart.Doughnut(chartData);
    To -

    Code:
    					chart.Bar(chartData);
    If I try that I get the same error that you mentioned above, that the property 'length' is undefined.
    No, bar charts require that a different data object be passed which requires a rewrite of the function. My testing also indicates that Charts.js bar charts don't support passing a single data point in any useful way; you end up with a single, near-zero-height bar (that looks like a flat line) with a label beneath it.

    The error you're seeing when you invoke the Bar method instead of the Doughnut method originates from Charts.js (presumably from having invalid data passed to it). It's the same type of error I described in my previous post, but not the same error.

    Below is revised code that accommodates unlimited bar and doughnut charts with the caveats that the script will remove canvas elements for any bar charts with only one data point since there's no point in drawing such charts and only a single color scheme is supported for every chart of a given type on the page.

    The bar charts are kind of bland since Charts.js only supports a single color for each dataset. (There's a workaround if you're willing to scrap the labels, but that would make the bars meaningless for users.)

    Code:
    <!doctype html>
    <html lang="en">
    	<head>
    		<meta charset="utf-8">
    		<title>Demo</title>
    	</head>
    	<body>
    		<script src="charts.js"></script> <!-- Obtained from http://www.chartjs.org/. -->
    		<canvas id="barChart" width="500" height="500"></canvas>
    		<canvas id="doughnutChart" width="500" height="500"></canvas>
    		<script>
    			(function () {
    				"use strict";
    				var drawChart = null;
    				function drawBarChart(areaData, canvasID) {
    					var chartData = Object.create(null);
    					var chartDatasetItem = Object.create(null);
    					var chartItemFillColor = "#0a7b83";
    					var chartItemStrokeColor = "white";
    					var areas = areaData.response.crimes.region.area;
    					var areaIndex = 0;
    					var area = null;
    					var context = document.getElementById(canvasID).getContext("2d");
    					var chart = new Chart(context);
    					chartData.labels = [];
    					chartData.datasets = [chartDatasetItem];
    					chartDatasetItem.fillColor = chartItemFillColor;
    					chartDatasetItem.strokeColor = chartItemStrokeColor;
    					chartDatasetItem.data = [];
    					if (Array.isArray(areas)) { // areas.length > 1
    						while (areaIndex < areas.length) {
    							area = areas[areaIndex];
    							chartData.labels.push(area["@attributes"].id);
    							chartDatasetItem.data.push(Number(area["@attributes"].total));
    							areaIndex += 1;
    						}
    					}
    					else { // one area: typeof areas.length === "undefined"
    						context.canvas.parentNode.removeChild(context.canvas);
    					}
    					chart.Bar(chartData);
    				}
    				function drawDoughnutChart(areaData, canvasID) {
    					var areas = areaData.response.crimes.region.area;
    					var areaIndex = 0;
    					var chartItem = null;
    					var chartItemColors = ["#0a7b83", "#2aa876", "#ffd265", "#f19c65", "#ce4d45", "#56676e", "#e8e2b2", "#d95e42"];
    					var chartData = [];
    					var context = document.getElementById(canvasID).getContext("2d");
    					var chart = new Chart(context);
    					if (Array.isArray(areas)) { // areas.length > 1
    						while (areaIndex < areas.length) {
    							chartItem = Object.create(null);
    							chartItem.value = Number(areas[areaIndex]["@attributes"].total);
    							chartItem.color = chartItemColors[areaIndex];
    							chartData.push(chartItem);
    							areaIndex += 1;
    						}
    					}
    					else { // one area: typeof areas.length === "undefined"
    						chartItem = Object.create(null);
    						chartItem.value = Number(areas["@attributes"].total);
    						chartItem.color = chartItemColors[areaIndex];
    						chartData.push(chartItem);
    					}
    					chart.Doughnut(chartData);
    				}
    				function parseAreaData(chartType, canvasID) {
    					var areaDataRequest = this;
    					var areaData = null;
    					if (areaDataRequest.readyState === XMLHttpRequest.DONE && areaDataRequest.statusText === "OK") {
    						areaDataRequest.removeEventListener("readystatechange", parseAreaData);
    						areaData = JSON.parse(areaDataRequest.responseText);
    						if (chartType === "bar") {
    							drawBarChart(areaData, canvasID);
    						}
    						else if (chartType === "doughnut") {
    							drawDoughnutChart(areaData, canvasID);
    						}
    					}
    				}
    				drawChart = function getAreaData(chartType, canvasID, URI) {
    					var areaDataRequest = null;
    					areaDataRequest = new XMLHttpRequest();
    					areaDataRequest.addEventListener("readystatechange", parseAreaData.bind(areaDataRequest, chartType, canvasID));
    					areaDataRequest.open("get", URI, false);
    					areaDataRequest.send();
    				};
    				drawChart("bar", "barChart", "json.json");
    				drawChart("doughnut", "doughnutChart", "json.json");
    			})();
    		</script>
    	</body>
    </html>
    For every complex problem, there is an answer that is clear, simple, and wrong.

  • Users who have thanked Arbitrator for this post:

    swiltch (01-26-2014)


  •  

    LinkBacks (?)

    1. 07-08-2014, 02:25 PM

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •