Repainting/Refreshing Graph in D3 - javascript

I currently have a dropdown and datepicker that when clicked, returns a new data source. A similar method is seen here.
This data source is used to create my graph and scale up/down the amount of data shown.
Generally, my source code follows this construct:
var updateGraphData = function(error, json) {
updateGraph(json);
}
// datepicker code goes here
// url param update code as seen in linked jsfiddle goes here
function MakeUrl() {
var urlSource = BaseURL + param1 + param2; /*these values are defined in the param code mentioned */
d3.json(urlSource, updateGraphData);
}
//set dimensions
var margin = {top: 30,right: 60,bottom: 30,left: 60
};
var width = 800 - margin.left - margin.right;
var height = 400 - margin.top - margin.bottom;
var color = d3.scale.category10();
function updateGraph(json_data) {
/* everything I want drawn goes here, i.e. axes, lines, points, labels, etc.*/
};
Now, my appended url source code works -- the graph does re-render with the proper data set. BUT ONLY when I define my SVG element inside the updateGraph function. When I do that, a new graph renders with the proper, BUT it pops a new graph below the old one. Which is not what I want. I want my new graph to replace the old one.
When I tried pulling my SVG element before the updateGraph function (so defined where the margin and dimensions are defined), the graph didn't repeat on dropdown change, but it layered the data in such a way that I was unable to see any useful data.
Basically, I am not able to remove all my graph elements on a new data request. How would you suggest going about this? I feel like it should be as easy as using a svg.selectAll("*").remove() in my updateGraphData() function, but that just stops any data from being rendered inside the SVG at all.
Any advice for me?
Thank you very much! Let me know if I'm not being clear.

Alright Team,
After hours and hours of searching for an answer, I finally found one. Hopefully, this will help someone else.
So like I said, I was getting new graphs to append below the old graph. I was having issues getting the previous data to clear out, and I couldn't seem to figure out how to specify a specific graph to clear without clearing all of my data (old and new) away.
The solution is simply adding the line:
d3.selectAll("g > *").remove()
In my updateGraph() function.
This deletes all of the "g" elements you previously created. That way, the svg container remains and you can populate with new data.

Related

How to scale a shape using d3 and x3dom?

I am trying to create a 3d "chart" using d3 data-binding and x3dom.
The data-binding seems to work correctly and I am able to generate HTML which looks correct, but x3dom does not show the expected scale (as in the size of the shape is not scaled at all).
The code is fairly simple. First, I bind data points to transforms:
var t = scene.selectAll('transform').data(data);
var transform = t.enter();
var shape = transform.append('transform')
.attr('translation', function (d, i) {
...
}).append('shape');
Within each transform element created for a data-point, I use this code to create a box:
shape.append('box').attr('size', '1, 1, 1');
Then, I expect to scale the boxes later using the data, using a transition:
t.transition().attr('scale', function (d) {
return "1.0 1.0 " + d.size;
});
This only works if I run the whole function that does the above twice, the second time in a setInterval() function!
I am a newbie, so I am certainly missing something here, but the code does generate what I expect and does work if run twice, for whatever reason. So, why doesn't it just work first time?
Note: I could of course just set the size directly instead of using an animation to do that later by scaling the shapes, but that also didn't work. It seems that the shapes never get the correct size using either technique, unless I run them twice.

Plot within a tooltip in d3.js

I would like to create a d3-based plot which graphs a plot within a tooltip. Unfortunately, I haven't found any examples on the web. Here is a sample JSON file.
[{"x":[0.4],
"y":[0.2],
"scatter.x":[0.54,0.9297,0.6024,-1.9224,2.2819],
"scatter.y":[0.4139,1.1298,-0.1119,2.3624,-1.1947]},
{"x":[0.1],
"y":[0.9],
"scatter.x":[-0.8566,-0.5806,-0.9326,0.8329,-0.5792],
"scatter.y":[-0.5462,-0.7054,1.0264,-3.4874,-1.0431]}]
The idea is to have a scatter plot for (x,y) coordinates first. However, when one mouses over a point, a different scatter plot within a tooltip appears based on [scatter.x, scatter.y] coordinates for that respective point.
I can do the scatter plots separately but have been struggling to put them together. Could anyone shed some light on this and/or provide a minimal example?
This was too long for a comment but I'm not certain if it's the answer you were looking for. One of the issues you might find is that your nested data is formatted differently-- one uses JSON objects with x and y, while the other uses two arrays of points.
My solution to this would be to create an extensible function:
function makeScatterPlot(elem, width, height, data, fill)
elem, width, height, and data are the core parameters: which element to attach the chart to, the size of the chart, and the data for the chart (in the JSON object format).
This function would generate all necessary items for the chart and add the chart to the provided element.
Then you want to bind to mouseover of your main chart, and in that function you'll have to do a bit of data modification to re-organize the two arrays into the JSON object structure.
function mainMouseover(d){
var newData = [];
for (var i = 0; i < d["scatter.x"].length; i++){
var t = {x: [0], y: [0]};
t.x[0] = d["scatter.x"][i];
t.y[0] = d["scatter.y"][i];
newData.push(t);
}
var newG = mainG.append("g").attr("transform", "translate(200,200)");
makeScatterPlot(newG, 100,100, newData, "red");
}
Of course, you would modify the translate to match wherever you want your tooltip to be.
Putting this all together you get the following (very crude) fiddle. Hover over either of the black dots to see the sub-chart. Obviously this needs quite a bit of work to be a solid example (i.e. remove the sub-chart on mouseout), but hopefully it will set you in the right direction.
If the tooltip chart is significantly different styling-wise compared to your main chart it may not be the best idea to use an extensible function, and you could just create another custom function instead.

How to update chart elements after changing time domain

First of all, let me be clear on this: i'm a d3 newbie!
So, i have a chart based on utc time domain. the domain is one of this: 1 day | 3 days, and I can switch from one to the other with a user action.
Refreshing / transitioning the x to represent 24hs to 72hs is working properly, but I'm having difficulties to update the elements that are already in the chart. Some how it seems that after changing the domain .data() .enter() won't actually enter (so my attr don't get updated).
This is a jsbin with the full example of my problem. Any clues?
You need to follow this series of examples very carefully.
Here is the problem code corrected ...
group.enter()
.append('g')
.attr('class', 'flight');
group.attr('transform', function (trip) {
return 'translate(' + hourDomain( Math.min.apply(null, _.map(trip.legs, 'departureDateTime'))) + ', 60)';
});
The problem is that you are applying the positioning to the enter selection only: only new nodes. After the first call, the enter selection is always empty because there are no new nodes. The linked examples will explain it.

problems with latest cluster force layout example

Based on this work: http://bl.ocks.org/mbostock/7882658
If I substitute the automatic nodes creation by a JSON.stringify() output of the automatically generated data like this...
var nodes = [
{"cluster":2,"radius":1.6180680659922448},
{"cluster":0,"radius":3.3575295077569},
{"cluster":1,"radius":0.9569281165554346},
{"cluster":3,"radius":10.7245554165012}
];
...I get an exception "cannot read property x of undefined" on the line:
var x = d.x - cluster.x,
This is inside the cluster(alpha) function. So, apparently the d3.map function that automatically generates the data is putting something in the structure that the JSON stringification has not caught? Maybe I am just overlooking something simple...help is appreciated. Thanks! Here is a fiddle to help out: http://jsfiddle.net/Nivaldo/FJ3qq/1/
I commented out the code that is not working. Also, another detail, it does not seem like the original code as i left it (except that i reduced the count of clusters and nodes) is actually handling the right number of distinct clusters. It should paint 4 different ones but is only painting with 3 colors.
The problem is that nodes is not the only data structure that needs to be initialised -- clusters needs to be as well. In particular, specific nodes are assigned to specific cluster indices. If you don't do that, things will break.
To fix, do something like
nodes.forEach(function(d) { clusters[d.cluster] = d; });
Complete jsfiddle here.

d3.js Update circle on line chart

I'm posting there because I couldn't find something on other post or on google.
http://jsfiddle.net/CUQaN/9/
As you can see on the JS fiddle, I have a line chart with circle on each point.
I want to update this chart with new data. The problem is that I can receive less or more point than I already have on the graph. For example, I can have 8 point on my line chart and then when I'm updating the chart, I can have just 4, or even 15 point. And my circle are not updating properly because I'm just changing the value of the circle which already exist.
But I really don't know how to update them properly.
I can have that data sometimes :
var data = [
{"date":"4-May-12","close":Math.random()*568.13,"open":Math.random()*35.12},
{"date":"3-May-12","close":Math.random()*568.13,"open":Math.random()*35.12},
{"date":"2-May-12","close":Math.random()*568.13,"open":Math.random()*35.12},
{"date":"1-May-12","close":Math.random()*568.13,"open":Math.random()*35.12},
{"date":"30-Apr-12","close":Math.random()*354.98,"open":Math.random()*424.56},
{"date":"27-Apr-12","close":Math.random()*24.00,"open":Math.random()*253.89},
{"date":"26-Apr-12","close":Math.random()*490.70,"open":Math.random()*215.54},
{"date":"25-Apr-12","close":Math.random()*42.00,"open":Math.random()*351.23},
{"date":"24-Apr-12","close":Math.random()*210.28,"open":Math.random()*20.23},
{"date":"23-Apr-12","close":Math.random()*20.70,"open":Math.random()*368.34},
{"date":"20-Apr-12","close":Math.random()*412.98,"open":Math.random()*42},
{"date":"19-Apr-12","close":Math.random()*26.44,"open":Math.random()*20.56},
{"date":"18-Apr-12","close":Math.random()*48.34,"open":Math.random()*356.45},
{"date":"17-Apr-12","close":Math.random()*26.44,"open":Math.random()*20.56},
{"date":"15-Apr-12","close":Math.random()*48.34,"open":Math.random()*356.45},
];
And just that data other times : (more or less)
var data = [
{"date":"4-May-12","close":Math.random()*568.13,"open":Math.random()*35.12},
{"date":"3-May-12","close":Math.random()*568.13,"open":Math.random()*35.12},
{"date":"2-May-12","close":Math.random()*568.13,"open":Math.random()*35.12},
{"date":"1-May-12","close":Math.random()*568.13,"open":Math.random()*35.12},
{"date":"30-Apr-12","close":Math.random()*354.98,"open":Math.random()*424.56},
];
Is someone can help me please ?
Thanks a lot !
It might be better to remove the old points and add the new ones rather than trying to move them. To do this you can use an id function to make the points unique - see here
A small example:
svg.selectAll("circle")
.data(myData, function(d) { return d.x; })
.enter()
.append("circle");
The important part here is that points are identified by their x-value, rather than their index in the data point array. Infact this might help in your current situation as the points will only move up/down and not side to side.

Categories