nvd3 chart only displaying after resize - javascript

I am having a problem where my chart will only render after a resize event has been called by the browser.
After the resize event it looks exactly how I want it to.
I am using d3.csv to load the data I suspect this may have something to do with it but I have no idea really.
I am using d3 3.5.6 and nvd3 1.8.1
I am locally serving with python -m SimpleHTTPServer
Here is the javascript that I am using to create the chart:
nv.addGraph(function () {
var chart = nv.models.lineChart()
.useInteractiveGuideline(true);
chart.duration(0)
.margin({
left: 100,
bottom: 100
})
.x(function (d) {
return d.financial_year
})
.y(function (d) {
return d.real_expenditure_millions
})
.forceY([0]);
chart.xAxis.axisLabel("Financial Year ");
chart.yAxis.axisLabel('Expenditure (millions)');
chart.showXAxis(true);
myData = getData();
d3.select(".chart")
.datum(myData)
.attr("id", function (d) { console.log(d); })
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
Here is my getData function which also serves to update a table on the page:
function getData() {
var myData = [];
d3.csv("data.csv", function (data) {
var table = d3.select("table");
var row = table.append("thead").append("tr")
row.append("th").text("financial year");
row.append("th").text("Real Expenditure (millions)");
table = table.append("tbody");
for (d in data) {
var year = data[d].financial_year;
var expenditure = data[d].real_expenditure_millions;
row = table.append("tr");
row.append("td").text(year);
row.append("td").text(expenditure);
myData.push({
financial_year: +(year.substr(0, 4)),
real_expenditure_millions: +expenditure
});
}
});
return [{
values: myData,
key: "Federal Government Dental Expenditure in NSW",
color: "#ff7f0e"
}];
}
data.csv looks like
financial_year,real_expenditure_millions
1997-98,60
1998-99,115
1999-00,144
2000-01,177
2001-02,182
2002-03,175
2003-04,169
2004-05,177
2005-06,183
2006-07,189
2007-08,280
2008-09,479
2009-10,578
2010-11,613
2011-12,660
The chart only displays after a window.dispatchEvent(new Event('resize')); in the browser console.

I ended up having to load the data by calling my get data function outside the nv.addGraph

Related

Call Json API on bar chart NVD3.js

I am trying to do an drill-down on bar graph using NVD3.js library. Chart has been made successfully. now I want to call an Json API by clicking on a particular bar so that new chart can be generated as per the parameter.
nv.addGraph(function() {
var chart = nv.models.discreteBarChart()
.x(function(d) { return d.label })
.y(function(d) { return d.value })
.showValues(true)
.duration(1000)
.showLegend(true)
;
d3.select('#chart1 svg')
.datum(data)
.transition().duration(500)
.call(chart);
chart.discretebar.dispatch.on("elementClick", function(e) {
alert("You've clicked " + e.data.label);
});
d3.select('#chart1 svg').datum(historicalBarChart).call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
I get the alert with the label value of the bar on which i clicked. Now I want to call an Json API instead of alert.
Kindly help
Thanks
Refer to http://learnjsdata.com/read_data.html
d3.json("/data/employees.json", function(data) {
console.log(data[0]);
});

"Error: <path> attribute d: Expected number"

I'm trying to create a cartogram using cartogram.js and d3.js. I've used the examples found in the cartogram.js repo and here to put together a script that generates a world map inside an SVG using the d3.geo.mercator() projection and now I'm trying to distort the map using the cartogram.js library however I'm getting the following error:
d3.js:8756 Error: <path> attribute d: Expected number, "MNaN,NaNLNaN,NaNL…".
(anonymous function) # d3.js:8756
tick # d3.js:8956
(anonymous function) # d3.js:8936
d3_timer_mark # d3.js:2166
d3_timer_step # d3.js:2147
Here's my code I'm using to distort the map:
var dataLoaded = new Event("dataLoaded"),
svg = d3.select("svg"),
proj = d3.geo.mercator(),
path = d3.geo.path()
.projection(proj),
countries = svg.append("g")
.attr("id", "countries")
.selectAll("path"),
carto = d3.cartogram()
.projection(proj)
.properties(function(d) {
return d.properties
}),
mapData = d3.map(),
geometries,
topology
function init() {
d3.csv("data/data.csv", function(data) {
data.forEach(function (d) {
mapData.set(d.COUNTRY, d.VALUE)
})
})
d3.json("data/world.json", function(data) {
topology = data
geometries = topology.objects.countries
var features = carto.features(topology, geometries)
countries = countries
.data(features)
.enter()
.append("path")
.attr("fill", function (e) {
return "#000000"
})
.attr("d", path)
document.dispatchEvent(dataLoaded)
})
}
document.addEventListener("dataLoaded", function() {
$("#container").css("visibility", "visible").hide().fadeIn("fast")
$("header").css("visibility", "visible").hide().fadeIn("slow")
carto.value(function(d) {
return +mapData.get(d.properties.name)
})
countries.data(carto(topology, geometries).features)
countries.transition()
.duration(750)
.attr("d", carto.path);
})
init()
and the CSV file containing the data I want to use to distort the map:
COUNTRY,VALUE
Afghanistan,90
Albania,390
Algeria,90
Andora,110
Angola,10
Antigua,2400
Argentina,320
Armenia,40
Australia,6600
Austria,1300
Axerbaijan,0
Bahamas,1900
Bahrain,90
Bangladesh,50
Barbados,8100
Belarus,20
Belgium,260
Belize,480
Benin,0
Bhutan,170
Bolivia,90
Bosnia,70
Botswana,110
Brazil,1300
Brunei,40
Bulgaria,3600
Burkina Faso,0
Burundi,0
Cabo Verde,0
Cambodia,720
Cameroon,10
Canada,4400
Central African Republic,0
Chad,10
Chile,320
China,1600
Combodia,0
Comoros,10
Congo,20
Costa Rica,2900
Cote d'Ivoire,0
Croatia,9900
Cuba,14800
Cyprus,8100
Czech Republic,70
Denmark,320
Dijbouti,0
Dominica,0
Dominican Republic,4400
Ecuador,90
Egypt,6600
El Salvador,10
Equatorial Guinea,0
Eritrea,10
Estonia,110
Ethiopia,70
Fiji,1900
Finland,720
France,2900
Gabon,10
Gambia,2400
Georgia,70
Germany,880
Ghana,210
Greece,14800
Grenada,720
Guatemala,40
Guinea,0
Guinea - Bissau,0
Guyana,50
Haiti,90
Honduras,110
Hungary,170
Iceland,8100
India,2900
Indonesia,390
Iran,390
Iraq,140
Ireland,1900
Israel,590
Italy,9900
Jamaica,6600
Japan,3600
Jordan,480
Kazakhstan,40
Kenya,1000
Kiribati,10
Kosovo,10
Kuwait,40
Kyrgyzstan,10
Laos,70
Latvia,110
Lebanon,70
Lesotho,0
Liberia,10
Libya,30
Liechtenstein,10
Lithuania,70
Luxembourg,50
Macedonia,70
Madagascar,0
Malawi,40
Malaysia,1300
Maldives,12100
Mali,40
Malta,12100
Marshall Islands,10
Mauritania,10
Mauritius,6600
Mexico,18100
Micronesia,20
Moldova,20
Monaco,590
Mongolia,110
Montenegro,880
Morocco,4400
Mozambique,90
Myanmar,90
Namibia,210
Nauru,10
Nepal,0
Netherlands,50
New Zealand,1900
Nicaragua,50
Niger,10
Nigeria,90
North Korea,390
Norway,1600
Oman,590
Pakistan,110
Palau,50
Palestine,10
Panama,210
Papua New Guinea,40
Paraguay,10
Peru,1000
Philippines,590
Poland,880
Portugal,12100
Qatar,210
Romania,320
Russia,480
Rwanda,20
Saint Kitts and Nevis,0
Saint Lucia,90
Saint Vincent and the Grenadines,0
Samoa,90
San Marino,70
Sao Tome and Principe,10
Saudi Arabia,110
Senegal,70
Serbia,50
Seychelles,1600
Sierra Leone,20
Singapore,880
Slovakia,70
Slovenia,390
Solomon Islands,10
Somalia,70
South Africa,1900
South Korea,140
South Sudan ,0
Spain,14800
Sri Lanka,3600
Sudan,20
Suriname,10
Sweden,720
Switzerland,1300
Syria,590
Taiwan,50
Tajikistan,10
Tanzania,260
Thailand,14800
Timor-Leste,0
Togo,10
Tonga,50
Trinidad and Tobago,140
Tunisia,4400
Turkey,9900
Turkmenistan,10
Tuvalu,30
Uganda,50
Ukraine,70
United Arab Emirates,20
United Kingdom,50
United States of America,3600
Uruguay,50
Uzbekistan,30
Vanuatu,30
Vatican City,30
Venezuela,170
Vietnam,2400
Yemen,20
Zambia,90
Zimbabwe,70
I don't have any experience using d3.js prior to this project so I would appreciate any feedback/guidance you can give me.
I'm using version 3.5.17 of d3, fyi.
Thanks.
UPDATE - 9/8/2016 15:22 BST
As per #Mark's suggestion, I've implemented d3-queue, although the problem still persists. If I've done anything wrong with this implementation, however, I'd be grateful for any insight anyone can give me! :)
var svg = d3.select("svg"),
proj = d3.geo.mercator(),
path = d3.geo.path()
.projection(proj),
countries = svg.append("g")
.attr("id", "countries")
.selectAll("path"),
carto = d3.cartogram()
.projection(proj)
.properties(function(d) {
return d.properties
}),
queue = d3.queue()
.defer(csv)
.defer(json)
.awaitAll(ready),
mapData = d3.map(),
geometries,
topology
function json(callback) {
d3.json("data/world.json", function(data) {
topology = data
geometries = topology.objects.countries
var features = carto.features(topology, geometries)
countries = countries
.data(features)
.enter()
.append("path")
.attr("fill", function (e) {
return "#000000"
})
.attr("d", path)
callback()
})
}
function csv(callback) {
d3.csv("data/data.csv", function(data) {
data.forEach(function (d) {
mapData.set(d.COUNTRY, +d.VALUE)
})
callback()
})
}
function ready() {
$("#container").css("visibility", "visible").hide().fadeIn("fast")
$("header").css("visibility", "visible").hide().fadeIn("slow")
carto.value(function(d) {
if (mapData.has(d.properties.name)) {
return +mapData.get(d.properties.name)
}
})
countries.data(carto(topology, geometries).features)
countries.transition()
.duration(750)
.attr("d", carto.path);
}
UPDATE 2 - 9/8/2016 18:05 BST
Here is the latest version of the script on Plunker which can be used for testing, courtesy of #Mark: http://plnkr.co/edit/iK9EZSIfwIXjIEBHhlep?p=preview
It seems my initial error has been fixed although the resulting cartogram isn't displaying correctly.
UPDATE 3 - 10/8/2016 20:45 BST
#Mark's answer helped clarify a lot of my issues and I had a partially functioning cartogram as a result however to fix the issue detailed here, I regenerated my map file using the --stitch-poles false parameter and and after doing this I am once again receiving the following error:
d3.js:8756 Error: <path> attribute d: Expected number, "MNaN,NaNLNaN,NaNL…".
#Mark's initial fix for this error is still in place therefore I'm quite confused as to why this has resurfaced. You can see my latest code here and my new map topojson file here. Thanks again.
Okay, I'm making progress. It turns out that after fixing your .value function the reason you don't get a catrogram is the your values are too disparate. Why this throws off cartogram.js, I'm unsure, but the problem can be easily solved by introducing a scale.
With your data:
s = d3.scale.linear().range([1,100]).domain(d3.extent(data, function(d){ return +d.VALUE}));
And then in your .value accessor:
carto.value(function(d,i) {
if (mapData.has(d.properties.name)) {
return s(mapData.get(d.properties.name));
} else {
return 1;
}
});
Alas, though, all your problems aren't fixed. It seems that countries that "wrap" the projection (ie Russia and Fiji) get distorted by the paths generated by cartogram.js. Here's a fix though, discussed at length here
Regardless of that, here's what we've got so far.

D3.js Problems Filtering topojson data

I've setup an example to demonstrate the issue's I've encountered:
To be brief, I'm using d3 to render a map of the united states. I'm appending relevant data attributes to handle click events and identify which state was clicked.
On click events the following is preformed:
I'm grabbing the US County topojson file (which contains ALL US
Counties).
Removing irrelevant counties from the data and rendering them on the
map of the state that was clicked.
I can't seem to figure out what is going on behind the scenes that is causing some of the counties to be drawn while others are ignored.
When I log the data that is returned from the filtered list, I'm displaying the accurate number of counties, but they are only partially drawn on the map. Some states don't return any. Pennsylvania and Texas partially work.
I've checked the data and the comparison operations, but I'm thinking this may have to do with arcs properties being mismatched.
If I utilize the Counties JSON file to render the entire map of the united states they are all present.
If anyone can help shed some light on what might be happening that would be great.
svg {
fill:#cccccc;
height:100%;
width:100%;
}
.subunit{
outline:#000000;
stroke:#FFFFFF;
stroke-width: 1px;
}
.subunit:hover{
fill:#ffffff;
stroke:#FFFFFF;
stroke-width="10";
}
<body>
<script src="http://www.cleanandgreenfuels.org/jquery-1.11.3.min.js"></script>
<script src="http://www.cleanandgreenfuels.org/d3.v3.min.js"></script>
<script src="http://www.cleanandgreenfuels.org/topojson.v1.min.js"></script>
<script>
var width = window.innerWidth;
height = window.innerHeight;
var projection = d3.geo.albers()
.scale(1500)
.translate([width / 2, height / 2]);
//d3.geo.transverseMercator()
//.rotate([77 + 45 / 60, -39 - 20 / 60]);
//.rotate([77+45/60,-40-10/60])
//.scale(500)
//.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("body").append("svg")
.attr("width", width+"px")
.attr("height", height+"px");
d3.json("http://www.cleanandgreenfuels.org/usstates2.json.php", function(error, us, init) {
//svg.append("path")
// .datum(topojson.feature(us, us.objects.counties))
//.attr("d", path);
function init(){
$( document ).ready(function() {
$('.subunit').on('click',function(){
var stateid = $(this).attr("data-stateid");
function clearStates(stateid){
d3.json("http://www.cleanandgreenfuels.org/uscounties2.json.php", function(error, us) {
console.log(us);
console.log("DATA CLICKED ID "+stateid);
test = jQuery.grep(us.objects.counties.geometries, function(n){
return (n.properties.stateid == stateid);
});
us.objects.counties.geometries = test;
console.log(test.length);
console.log(us.objects.counties.geometries.length);
var test = topojson.feature(us, us.objects.counties).features;
console.log(test);
console.log(test.length);
svg.selectAll(".subunit")
.data(test)
.enter().append("path")
.attr("class", function(d) { return "subunit"; })
.attr("d", path)
.attr("data-countyid", function(r){ return r.id; });
});
}
clearStates(stateid);
});
});
}
svg.selectAll(".subunit")
.data(topojson.feature(us, us.objects.us).features)
.enter().append("path")
.attr("class", function(d) { return "subunit"; })
.attr("d", path)
.attr("data-stateid", function(r){ return r.id; });
init();
});
</script>
</body>
It appears as if I was attempting to utilize some outdated features, using topojson.mesh and .datum() to add the new data has resolved this issue, but has introduced a new error.
Now it appears as if the polygons that are rendered must be in sequence to be drawn properly this way.
I think the data going in should be cleaned up to optimize the way d3 is designed to function, but I'd still like to know more about how it is rendering this information that is obtained from the dataset.
function clearStates(stateid){
d3.json("http://www.cleanandgreenfuels.org/uscounties2.json.php", function(error, us) {
console.log(us);
console.log("DATA CLICKED ID "+stateid);
test = jQuery.grep(us.objects.counties.geometries, function(n){
return (n.properties.stateid == stateid);
});
us.objects.counties.geometries = test;
console.log(test.length);
console.log(us.objects.counties.geometries.length);
**var test = topojson.mesh(us, us.objects.counties);**
console.log(test);
console.log(test.length);
**svg.append("path")
.datum(test)
.attr("class", function(d) { return "subunit"; })
.attr("d", path)
.attr("data-countyid", function(r){ return r.id; });**
});
}

Legend toggling for d3.js pie chart

I'm working on a d3.js application.
In this example I am trying to toggle the slices when the user clicks on the legend components. It will initially take the complete data as its source, but if there is a previous manipulated data source will use that as a base. I've tried to hook into the toggling functionality as the legend is manipulated. I would prefer to separate the functionality - but wasn't sure how else to know if the slice is to be active or not.
Its not working as expected though, especially when trying to handle multiple active/non active slices.
http://jsfiddle.net/Qh9X5/3282/
onLegendClick: function(dt, i){
//_toggle rectangle in legend
var completeData = jQuery.extend(true, [], methods.currentDataSet);
newDataSet = completeData;
if(methods.manipulatedData){
newDataSet = methods.manipulatedData;
}
d3.selectAll('rect')
.data([dt], function(d) {
return d.data.label;
})
.style("fill-opacity", function(d, j) {
var isActive = Math.abs(1-d3.select(this).style("fill-opacity"));
if(isActive){
newDataSet[j].total = completeData[j].total;
}else{
newDataSet[j].total = 0;
}
return isActive;
});
//animate slices
methods.animateSlices(newDataSet);
//stash manipulated data
methods.manipulatedData = newDataSet;
}
Here is the onLegendClick function.
I am toggling the opacity of the inner fill on the rectangle when the user clicks.
I've tried to modify the value of the data accordingly - although it is not handling multiple toggles.
http://jsfiddle.net/Qh9X5/3324/
Ideally if the user tries to deactivate all slices, I would want it to reset the chart and restore all the slices in the process. Would be keen to streamline the code and maybe separate the logic and presentation layer for the styling of the rectangles in the legend.
line 234
onLegendClick: function(dt, i){
//_toggle rectangle in legend
var completeData = jQuery.extend(true, [], methods.currentDataSet);
newDataSet = completeData;
if(methods.manipulatedData){
newDataSet = methods.manipulatedData;
}
d3.selectAll('rect')
.data([dt], function(d) {
return d.data.label;
})
.style("fill-opacity", function(d, j) {
var isActive = Math.abs(1-d3.select(this).style("fill-opacity"));
if(isActive){
newDataSet[j].total = completeData[j].total;
}else{
newDataSet[j].total = 0;
}
return isActive;
});
//animate slices
methods.animateSlices(newDataSet);
//stash manipulated data
methods.manipulatedData = newDataSet;
}

Add Unique Links to all d3.js Data Points in Graph

I'm using nvd3.js to create a line graph that displays ratings that I have calculated over time. I have more information for each individual data point (rating) and would like to have each data point on the graph link to a unique page with more information about that specific data point.
For example: I would like to be able to hover over the first data point on the graph (x: 1345457533, y: -0.0126262626263) and click on it to go to a specific page (http://www.example.com/info?id=1) that provides more information about that rating or data point. Each data point has a unique id and unique url that I would like to link to.
Here is the code that I am using to generate the graph:
nv.addGraph(function() {
var chart = nv.models.lineChart();
chart.xAxis
.axisLabel('Time')
.tickFormat(d3.format('r'));
chart.yAxis
.axisLabel('Rating')
.tickFormat(d3.format('.2f'));
d3.select('#chart svg')
.datum(data())
.transition().duration(500)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
function data() {
var data = [ { x: 1345457533, y: -0.0126262626263 },
{ x: 1345457409, y: 0.0224089635854 },
{ x: 1345457288, y: 0.0270935960591 },
{ x: 1345457168, y: -0.0378151260504 },
{ x: 1345457046, y: -0.115789473684 } ]
return [
{
values: data,
key: "Sample1",
color: "#232066"
}
];
}
The HTML:
<div id="chart">
<svg></svg>
</div>
And here is a working example.
Here is an working solution http://jsfiddle.net/66hAj/7/
$('#chart svg').on('click', function(e){
var elem = $(e.target),
currentItem, currentUrl;
if(elem.parent('.nv-point-paths').length) {
currentItem = e.target.getAttribute('class').match(/\d+/)[0];
currentUrl = _data[0].urls[ currentItem ];
$('#log').text(currentUrl);
//window.location = currentUrl
}
})
I've used jQuery to bind a click handler on the canvas and then get the data based on the element clicked on the graph.
currentItem gives you the id of the current item that you clicked on
currentUrl gives the url related to the currently clicked item.
You can see the url change in the div below the chart as you click on each point over the graph.

Categories

Resources