I'm trying to adapt Mike Bostock's Wealth of Nations motion chart example to my own data set. Funny enough, I'm using GPS lata/long from train locomotives as the axes and datetimes instead of years.
Have a look at my jsfiddle (link).
This seems to be the key part to actually move the dots, but I'm not actually getting any motion.
// Add a dot per loco. Initialize the data at min(starttime), and set the colors.
var dot = svg.append("g")
.attr("class", "dots")
.selectAll(".dot")
.data(interpolateData(minDateInt))
.enter().append("circle")
.attr("class", "dot")
.style("fill", function(d) { return colorScale(color(d)); })
.call(position)
.sort(order);
// Positions the dots based on data.
function position(dot) {
dot .attr("cx", function(d) { return xScale(x(d)); })
.attr("cy", function(d) { return yScale(y(d)); })
.attr("r", function(d) { return radiusScale(radius(d)); });
Does anyone know what's wrong with my code?
Related
The following code should show a scatter plot but the when I check the elements in the developer tools of chrome I only see 1 circle. The full code can be seen here:
https://bl.ocks.org/centem/db547f011997e64ebf2544e9e22c33ce
I'm creating the circles like this:
svg.selectAll("circle")
.data(d3.values(data))
.enter()
.append("circle")
.attr("cx", function(d) { return x_scale(d["export_val_2010"]); })
.attr("cy", function(d) { return y_scale(d["export_val_2011"]); })
.attr("r", 5);
I want to show the city name and population related to the voronoi area hovered over. However, with how I made the voronoi areas, I had to either only send coordinate data and have all of the drawings work, or send more data and none of the voronoi areas are drawn (b/c it can't read the non-coordinate data, and I don't know how to specify within an array or object, at least when creating voronois). I can enter static or irrelevant data for the tooltip (as I did below), but not anything from the actual dataset.
var tooltip = d3.select("body")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.text("a simple tooltip");
var voronoi = d3.geom.voronoi()
.clipExtent([[0, 0], [w, h]]);
d3.csv("us-cities1.csv", function(d) {
return [projection([+d.lon, +d.lat])[0], projection([+d.lon, +d.lat])[1]];
}, function(error, rows) {
vertices = rows;
drawV(vertices);
}
);
function polygon(d) {
return "M" + d.join("L") + "Z";
}
function drawV(d) {
svg.append("g")
.selectAll("path")
.data(voronoi(d), polygon)
.enter().append("path")
.attr("class", "test")
.attr("d", polygon)
.attr("id", function(d, i){return i;})
.on("mouseover", function(){return tooltip.style("visibility", "visible");})
.on("mousemove", function(){return tooltip.style("top", (event.pageY-10)+"px").style("left",(event.pageX+10)+"px").text((this).id);})
.on("mouseout", function(){return tooltip.style("visibility", "hidden");});
svg.selectAll("circle")
.data(d)
.enter().append("circle")
.attr("class", "city")
.attr("transform", function(d) { return "translate(" + d + ")"; })
.attr("r", 2);
}
I've put together an example using your data to demonstrate what Lars mentions. I created a variable for Voronoi like this:
var voronoi = d3.geom.voronoi()
.x(function(d) { return (d.coords[0]); })
.y(function(d) { return (d.coords[1]); });
which was taken from this Bl.ock by Mike. This allows you to specify the array of coordinates and still have them connected to the descriptive data you want to display.
I then created the object to store all the data in a format that could be used in the Voronio polygons using:
cities.forEach(function (d,i) {
var element = {
coords: projection([+d.lon, +d.lat]),
place: d.place,
rank: d.rank,
population: d.population
};
locCities.push(element);
});
I could have specified the translation of the coordinates in the voronio variable and then just used the cities variable, but I didn't.
The title attribute was used for the tooltips, but this can be replaced with something more appropriate such as what you have in your code. The relevant code is :
.append("title") // using titles instead of tooltips
.text(function (d,i) { return d.point.place + " ranked " + d.point.rank; });
There were a few issues with the data. I had to use an older version of d3 (3.1.5) to get the geojson to render correctly. I know there have been a number of chnanges to the AlberUsa projection so beware there is an issue there.
The location of some of the cities seems wrong to me for instance San Fancisco appears somewhere in Florida (this caused me some confusion). So I checked the original csv file and the coordinates seem to be wrong in there and the data is rendering where it should (just not where I'd expect according to the labels).
Now putting it all together you can find it here
I am building an epidemic simulation using D3's force-directed diagram.
When a transmission event occurs, I want to move a circle from the transmitter to the newly infected individual.
PROBLEM: Only the first element is created and moved according to the bound data.
First, I gather the coordinates:
xyCoords = getPathogen_xyCoords(newInfections);
Where xyCoords looks like the following:
{receiverX: newInfections[i].x, receiverY: newInfections[i].y, transmitterX: newInfections[i].infectedBy.x, transmitterY: newInfections[i].infectedBy.y}
Then I create the circles and bind them to xyCoords:
d3.select(".svg").append("circle")
.attr("class", "pathogen")
d3.selectAll(".pathogen")
.data(xyCoords)
.attr("cx", function(d) { return d.transmitterX})
.attr("cy", function(d) { return d.transmitterY})
.attr("r", 4)
.style("fill", "green")
Finally, the circle is moved with a transition:
d3.selectAll(".pathogen")
.transition()
.duration(500)
.attr("cx", function(d) { return d.receiverX} )
.attr("cy", function(d) { return d.receiverY} );
EDIT: The game has been up for a few months now and doing quite well! Check it out at http://vax.herokuapp.com!
I've solved my problem...
At creation of the circles, I was not "entering" new circles to be associated with the newly bound data.
It was displaying only the first element on bind because there was only one circle to begin with.
Creation of the circles now looks like this:
xyCoords = getPathogen_xyCoords(newInfections);
var pathogen = svg.selectAll(".pathogen")
.data(xyCoords)
.enter()
.append("circle")
.attr("class", "pathogen")
.attr("cx", function(d) { return d.transmitterX })
.attr("cy", function(d) { return d.transmitterY })
.attr("r", 4)
.style("fill", "green")
So I'm using code based off of this..
http://bl.ocks.org/mbostock/3884955
Essentially, what I'm trying to do is at every data point I want to add a circle. Any help would be much appreciated seeing that I have no idea where to start.
This is my code so far: It worked when I was using single lines.
var circlegroup = focus.append("g")
circlegroup.attr("clip-path", "url(#clip)")
circlegroup.selectAll('.dot')
.data(data)
.enter().append("circle")
.attr('class', 'dot')
.attr("cx",function(d){ return x(d.date);})
.attr("cy", function(d){ return y(d.price);})
.attr("r", function(d){ return 4;})
.on('mouseover', function(d){ d3.select(this).attr('r', 8)})
.on('mouseout', function(d){ d3.select(this).attr('r', 4)});
You need nested selections for this. Assuming that data is a two-dimensional array, you would do something like this.
var groups = svg.selectAll("g").data(data).enter().append("g");
groups.data(function(d) { return d; })
.enter()
.append("circle")
// set attributes
My goal is to remove a data point from my bar chart.
It will then update itself:
Update X and Y axis
Update the actual bar chart
Update the legend
Issues I am having:
When I exit().remove() the rectangles in my graph, the code also gets rid of the rectangles in the legend. When I try to enter() the rectangles in my legend, they do not appear. I am not sure what is happening here, but I am not being successful in adding and removing elements due to data changes. Any help would be appreciated.
Code snippet of where I think I am having issues:
This is the part that also deletes the rectangles in the legend (I am not sure if I should do this here or in the enter/update/delete part of the legend)
The code below is executed right after the user clicks the "delete all" button. My intent here is to only delete one bar (the one named "ALL") and update the chart.
//Select rectangles
var bars = svg.selectAll("rect")
.data(dataset, function(d) { return d.State; });
//Enter rectangles
bars.enter()
.append("rect")
.style("fill", function(d,i) { return color(i) })
.attr("x", function(d) { return xScale(d.State); })
.attr("y", function(d) { return yScale(d.CustomerCount) })
.attr("width", xScale.rangeBand()) //returns rangeRoundBands width
.attr("height", function(d) { return h - yScale(d.CustomerCount) });
//Update rectangles
bars.transition()
.duration(1000)
.style("fill", function(d,i) { return color(i) })
.attr("x", function(d) { return xScale(d.State); })
.attr("y", function(d) { return yScale(d.CustomerCount) })
.attr("width", xScale.rangeBand()) //returns rangeRoundBands width
.attr("height", function(d) { return h - yScale(d.CustomerCount) });
//Exit rectangles
bars.exit()
.transition()
.duration(500)
.attr("x", w)
.remove();
Here is entire the code:
http://plnkr.co/edit/Nue5bocQsI4E6D5wfNSP
Here is the slightly smaller code:
I tried to reduce the code as much as possible but it is still pretty big.
http://jsfiddle.net/aNQWV/
In the part where you update the data you say:
var bars = svg.selectAll("rect")
.data(dataset, function(d) { return d.State; });
The problem is that at this point the svg contains two kind of rects. One kind corresponds to the bars, and the other ones are part of the legend. This means that you are joining the new data to this mix of rects, while actually you wanted to join the new data with the bars only.
So, you need a more specific selector that targets only the bar rects. The typical approach is to add a class to those rects when you create them:
svg.selectAll(".bar")
.data(input)
.enter().append("rect")
.attr("class", "bar");
Then in the part where you update the data you would say:
var bars = svg.selectAll(".bar")
.data(dataset, function(d) { return d.State; });