I have a piece of code that creates a donut (i.e., d3.layout.pie()).
I used to load JSON data in the following way
d3.json(jsonFilePath,
function(json) {
//logic
....
})
My visualizations are interactive (i.e., the donut variates in size, both innerRadius and "d" attribute). So I correctly implemented the three cases, enter, remove and update and the attrTween function to transition the "d" attribute.
It worked until now. I decided to implement some sort of caching mechanism (i.e., I've a variable that holds the data so that data are pre-fetched once and re-used if needed again). I tested the behavior of my cache and it's working. Here's a snippet...
if(cache[name] == undefined) {
console.log('Loading data and create viz');
// Loading all the JSONs for this name
var remaining = revs.length;
// revs is an array w/ json file names to load (elements in the form r1, r4 etc.)
var current = {};
for (var i in revs) {
d3.json(revs[i] + ".json" , function(json) {
current["r"+json.revision] = json;
if (!--remaining) {
// Data loaded, here, hence setting cache
cache[appName] = current;
createViz(rev);
}
});
}
}
I e to the point. I replaced the way I used to load data with a simple access of my cache data containing the loaded JSON string, so now instead of the above code block I've something that looks like
var currentCache = cache[someName];
var json = currentCache[revisionName];
When I inspect the json variable the json is correcty loaded and looks fine. The point is that now the transition is no longer working and I got lot of messages such as
Error: Problem parsing d="M2.1431318985078682e-14,-350A350,350 0 1,1 NaN,NaNLNaN,NaNA230.68565589945686,230.68565589945686 0 1,0 1.412542250532388e-14,-230.68565589945686Z"
I already faced this messages before, but now the thing is that I dunno why this is happening. The code is unchanged, instead of using d3.json() I'm using the json string from a variable. Any suggestion?
I attach part of the code for reference...
// An array of objects such as {"name" : myObject, "calls" : 23}
var calls = json.children;
var donut = d3.layout.pie()
.value(function(d) { return d.calls; })
.sort(null)
var arcs = d3.select("#vizGroup")
.selectAll(".callSlice")
.data(donut(calls));
//update
arcs
.data(donut(calls))
.transition()
.duration(750)
.attrTween("d", arcTween)
.style("fill", function(d) { return somecolor(d.data.name); } )
//enter
arcs.enter()
.append("svg:path")
.style("fill", function(d) { return somecolor(d.data.name); } )
.attr("stroke", "#EEF")
.attr('class', 'callSlice')
.each(function(d) { this._current = d; })
.transition()
.duration(750)
.attrTween("d", arcTween)
//remove
arcs.exit().remove();
function arcTween(a) {
a.innerRadius = myScale(myVariable);
var i = d3.interpolate({
startAngle: this._current.startAngle,
endAngle: this._current.endAngle,
innerRadius: this._current.innerRadius}, a);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
I have a piece of code that creates a donut (i.e., d3.layout.pie()).
I used to load JSON data in the following way
d3.json(jsonFilePath,
function(json) {
//logic
....
})
My visualizations are interactive (i.e., the donut variates in size, both innerRadius and "d" attribute). So I correctly implemented the three cases, enter, remove and update and the attrTween function to transition the "d" attribute.
It worked until now. I decided to implement some sort of caching mechanism (i.e., I've a variable that holds the data so that data are pre-fetched once and re-used if needed again). I tested the behavior of my cache and it's working. Here's a snippet...
if(cache[name] == undefined) {
console.log('Loading data and create viz');
// Loading all the JSONs for this name
var remaining = revs.length;
// revs is an array w/ json file names to load (elements in the form r1, r4 etc.)
var current = {};
for (var i in revs) {
d3.json(revs[i] + ".json" , function(json) {
current["r"+json.revision] = json;
if (!--remaining) {
// Data loaded, here, hence setting cache
cache[appName] = current;
createViz(rev);
}
});
}
}
I e to the point. I replaced the way I used to load data with a simple access of my cache data containing the loaded JSON string, so now instead of the above code block I've something that looks like
var currentCache = cache[someName];
var json = currentCache[revisionName];
When I inspect the json variable the json is correcty loaded and looks fine. The point is that now the transition is no longer working and I got lot of messages such as
Error: Problem parsing d="M2.1431318985078682e-14,-350A350,350 0 1,1 NaN,NaNLNaN,NaNA230.68565589945686,230.68565589945686 0 1,0 1.412542250532388e-14,-230.68565589945686Z"
I already faced this messages before, but now the thing is that I dunno why this is happening. The code is unchanged, instead of using d3.json() I'm using the json string from a variable. Any suggestion?
I attach part of the code for reference...
// An array of objects such as {"name" : myObject, "calls" : 23}
var calls = json.children;
var donut = d3.layout.pie()
.value(function(d) { return d.calls; })
.sort(null)
var arcs = d3.select("#vizGroup")
.selectAll(".callSlice")
.data(donut(calls));
//update
arcs
.data(donut(calls))
.transition()
.duration(750)
.attrTween("d", arcTween)
.style("fill", function(d) { return somecolor(d.data.name); } )
//enter
arcs.enter()
.append("svg:path")
.style("fill", function(d) { return somecolor(d.data.name); } )
.attr("stroke", "#EEF")
.attr('class', 'callSlice')
.each(function(d) { this._current = d; })
.transition()
.duration(750)
.attrTween("d", arcTween)
//remove
arcs.exit().remove();
function arcTween(a) {
a.innerRadius = myScale(myVariable);
var i = d3.interpolate({
startAngle: this._current.startAngle,
endAngle: this._current.endAngle,
innerRadius: this._current.innerRadius}, a);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
Share
Improve this question
edited Jun 16, 2014 at 10:18
VividD
10.5k8 gold badges66 silver badges112 bronze badges
asked Apr 26, 2012 at 16:37
RMinelliRMinelli
1913 silver badges13 bronze badges
2 Answers
Reset to default 4Your snippet refers to an undefined variable rev
. Also, don't use a for-in loop with an array. I'm going to assume you meant something like this:
for (var i = 0, n = revs.length; i < n; ++i) {
d3.json(revs[i] + ".json", function(json) {
var rev = revs[i];
/* do something with rev here */
});
}
Or equivalently, something like this:
for (var i = 0, n = revs.length; i < n; ++i) {
var rev = revs[i];
d3.json(revs[i] + ".json", function(json) {
/* do something with rev here */
});
}
Neither of these will work as expected because the d3.json callback is asynchronous, and closures don't capture the value of rev
(or for that matter i
) in the current iteration of the for loop: they capture a reference, not a value, and so when the callback is invoked at a later point, all your callbacks will see the value in the last iteration of the for loop (revs.length
), not the value when each d3.json is called.
You can fix this by capturing the value with a closure. For example:
for (var i = 0, n = revs.length; i < n; ++i) {
fetch(revs[i]);
}
function fetch(rev) {
d3.json(rev + ".json", function(json) {
/* do something with rev here */
});
}
This works because each unique value of rev
is captured by the function fetch
. For more on closures, see:
- http://jibbering./faq/notes/closures/
I don't think you've included the code that is at fault, which I suspect is your JSON caching code. My guess is that you're still fetching JSON asynchronously, but you're trying to use it right away. d3.json()
takes a callback function that isn't invoked until the data is actually loaded - I'm not seeing any similar structure in your current code, so unless you're loading the JSON inline, rather than from a remote file, that's likely to be the issue.
Checking the console manually won't help in this case, because by the time you check the console, the data has been loaded - it's when your code is actually running that you have problems.