Updating position of d3 generated circles with a timer - javascript

I'm new to d3 and fairly new to javascript in general and have encountered an error I can't explain with my current knowledge...
I've generated 6 circles using a 2d array (success) and have created a function to call in a timer to increment the x and y position by 1 each call. Currently the code I have generates the 6 circles but the timer infinitely creates more circles of "NaN" instead of updating the positions of the initial 6. My code is below;
<body>
<div id="svgDiv"></div>
<script src="~/scripts/d3/d3.min.js"></script>
<script src="~/scripts/App/test.js"></script>
</body>
and js;
var windowWidth = window.innerWidth;
var windowLength = window.innerHeight;
var pos =
[[50, 40],
[100, 80],
[150, 120],
[200, 160],
[250, 200],
[300, 240]];
var base = d3.select("#svgDiv").append("svg")
.attr("width", windowWidth)
.attr("height", windowLength);
function initSetup() {
windowWidth = window.innerWidth;;
windowLength = window.innerHeight;
base.attr("width", windowWidth)
.attr("height", windowLength);
document.body.style.overflow = 'hidden';
}
window.onload = initSetup;
function windowResize() {
windowWidth = window.innerWidth;;
windowLength = window.innerHeight;
base.attr("width", windowWidth)
.attr("height", windowLength);
};
window.addEventListener("resize", windowResize);
function svgDivClick() {
base.selectAll("circle")
.data(pos) // .data(pos, function (d) { return d; })
.enter()
.append("circle")
.attr("cx", function (d, i) {
return pos[i][0];
})
.attr("cy", function (d, i) {
return pos[i][1];
})
.attr("r", 0)
.style("fill", "00ACCD")
.transition()
.attr("r", 20)
.style("fill", "00ACCD")
.duration(500);
base.exit().transition()
.attr("r", 0)
.remove();
console.log("click works");
d3.timer(animate);
};
document.getElementById("svgDiv").addEventListener("click", svgDivClick);
function animate() {
base.selectAll("circle")
.data("circle", function (d) { return d; })
.enter()
.append("circle")
.attr("cx", function (d, i) {
return d.cx + 1;
})
.attr("cy", function (d, i) {
return d.cy + 1;
});
base.exit().transition()
.attr("r", 0)
.remove();
};
The end goal is to have the circles float about randomly but at this point I'm just trying to control the positions. EDIT: The error occurs in the animate function.
Any insights would be great, thanks in advance!

You have some problems here. The main one is that you are binding the same data to the DOM elements 60 times a second, which makes no sense. The second one is that you are not increasing the positions, but simply resetting them. A third one is that there is no cx or cy properties in the data. A last one is that you are not declaring your circles' selection.
I made a simple demo to show you how to use a d3.timer. Check how the position is retrieved and changed. Also, have in mind that in this example I removed all transitions: mixing transitions and timers is complicated and, often, unnecessary.
Here is the demo:
var windowWidth = 500;
var windowLength = 300;
var pos = [
[50, 40],
[100, 80],
[150, 120],
[200, 160],
[250, 200],
[300, 240]
];
var base = d3.select("body").append("svg")
.attr("width", windowWidth)
.attr("height", windowLength);
var circles = base.selectAll("charlesDarwin")
.data(pos)
.enter()
.append("circle")
.attr("cx", function(d, i) {
return pos[i][0];
})
.attr("cy", function(d, i) {
return pos[i][1];
})
.attr("r", 20)
.style("fill", "00ACCD");
var timer = d3.timer(animate);
function animate() {
circles.attr("cx", function() {
if (+d3.select(this).attr("cx") > 500) {
timer.stop()
}
return +d3.select(this).attr("cx") + 1
});
};
<script src="https://d3js.org/d3.v4.min.js"></script>

Related

d3 projection geoMercator returns none

I'm trying to plot some cordinates on an image using d3 v4 following this Link.When i'm trying to pass my co-ordinates to the projection function it returns NAN for some of the data points. I got some help from here that javascript follows the following latitude and longitude convention but not sure how it exacty works.
This is the format of my data:
{coordinates: [60, 84],coordinates: [204, 92.4],coordinates: [117, 132.72]}
D3 code :
var el = d3.select('.js-map'),
// 150 DPI image
width = 300,
// 150 DPI image
height = 300;
var thisObj = this;
var projection = d3.geoMercator()
.scale(1)
.translate([0, 0])
console.log('projection', projection);
var path = d3.geoPath()
.projection(projection);
var map = el.append('svg')
.attr('width', width)
.attr('height', height);
map.append('image')
.attr('xlink:href', this.floorMaps[0])
.attr('width', width)
.attr('height', height);
this.floorSensorInfo.forEach((data, index) => {
var lonlat = projection(data.coordinates);
console.log('Longitude Latitude', lonlat);
I can see my data output like [2.0420352248333655, NaN]and not sure what happened exactly.
and moreover if someone can explain following the first link which i realy don't understand it would be really helpful
Exported bounds of raster image
rasterBounds = [[-122.7895, 45.4394], [-122.5015, 45.6039]]
Update:
#Andrew suggested to plot normal co-ordinates because latitude and longitude apply only to world maps. So i had pasted my below working code version now which is plotting the points on the image now.
var svg = d3.select("body")
.append("svg")
.attr("width",960)
.attr("height",500)
// image width and height in pixels, we don't want to skew this or scale this (then image units aren't straight pixels)
var imageWidth = 300;
var imageHeight = 168;
var color_hash = { 0 : ["apple", "green"],
1 : ["mango", "orange"],
2 : ["cherry", "red"]
}
function scale(coords) {
return [coords[0] * imageWidth / 100, coords[1] * imageHeight / 100];
}
svg.append("image")
.attr("width",imageWidth)
.attr("height",imageHeight)
.attr("x", 0) // could be non-zero, but we would have to shift each circle that many pixels.
.attr("y", 0)
.attr("xlink:href", this.floorMaps[0])
var data = this.floorSensorInfo
// var dataNest = d3.nest()
// .key(function (d) { return d['sensor_name']; })
// .entries(data)
data.forEach(function (d, i) {
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) { return (d.value)[0]; })
.attr("cy", function(d) { return (d.value)[1]; })
.attr("r", 5)
.style("fill", function(d) {
var color = color_hash[data.indexOf(d)][1]
return color;
})
svg.append('text')
.attr("x", 20+(i)*100) // space legend
.attr("y", imageHeight+20)
// style the legend
.style("stroke", function () { // Add the colours dynamically
return d['color'] = color_hash[data.indexOf(d)][1];
})
//.attr("dy", ".35em")
.text( d.sensor_name);
//.text("jjjjjjj")
})}
var svg = d3.select("body")
.append("svg")
.attr("width",960)
.attr("height",500)
// image width and height in pixels, we don't want to skew this or scale this (then image units aren't straight pixels)
var imageWidth = 300;
var imageHeight = 168;
var color_hash = { 0 : ["apple", "green"],
1 : ["mango", "orange"],
2 : ["cherry", "red"]
}
function scale(coords) {
return [coords[0] * imageWidth / 100, coords[1] * imageHeight / 100];
}
svg.append("image")
.attr("width",imageWidth)
.attr("height",imageHeight)
.attr("x", 0) // could be non-zero, but we would have to shift each circle that many pixels.
.attr("y", 0)
.attr("xlink:href", this.floorMaps[0])
var data = this.floorSensorInfo
// var dataNest = d3.nest()
// .key(function (d) { return d['sensor_name']; })
// .entries(data)
data.forEach(function (d, i) {
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) { return (d.value)[0]; })
.attr("cy", function(d) { return (d.value)[1]; })
.attr("r", 5)
.style("fill", function(d) {
var color = color_hash[data.indexOf(d)][1]
return color;
})
svg.append('text')
.attr("x", 20+(i)*100) // space legend
.attr("y", imageHeight+20)
// style the legend
.style("stroke", function () { // Add the colours dynamically
return d['color'] = color_hash[data.indexOf(d)][1];
})
//.attr("dy", ".35em")
.text( d.sensor_name);
//.text("jjjjjjj")
})}
javascript d3.js

concentric emanating circles d3

I have an array of equally spaced values which I am using to draw concentric circles. I want to use an emanating effect, in essence, remove the outermost circle once its value exceeds the maximum value and add a new one at the center to compensate. I am unsure about manipulation on data set to remove and add new circle.
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("body").append("svg");
var w = window.innerWidth;
var h = window.innerHeight;
var separation = Math.min(50, w/12);
var n=Math.floor((w/2)/separation);
var ellipse=new Array(n);
for(var i=1;i<=n;i++){
ellipse[i-1]=(i*separation);
}
svg.attr("width", w).attr("height", h);
var g = svg.append("g");
var e=g.selectAll("ellipse")
.data(ellipse)
.enter()
.append("ellipse")
.attr("cx", w/2)
.attr("cy", h/2)
.attr("rx",0)
.attr("ry",0)
.transition()
.duration(5000)
.attr("rx", function(d,i){return ellipse[i];})
.attr("ry", function(d,i){return ellipse[i]/5;});
loop();
function loop(){
e.transition()
.attr("rx", function(d,i){
return ellipse[i]+separation;
})
.attr("ry", function(d,i){
return (ellipse[i]+separation)/5;
})
.on("end",loop());
}
</script>
You could approach it with a remove().exit() and enter().append() selection for each ring - but essentially you always have the same number of rings on the screen. Why not just recycle the same elements? When the size hits a threshold, reset it to zero, or some other starting value?
Something along the lines of:
var scale = d3.scaleLinear()
.range(["orange","steelblue","purple"])
.domain([0,60]);
var data = [0,10,20,30,40,50,60];
var width = 200;
var height = 200;
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height);
var circles = svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("r",function(d) { return d; })
.attr("transform","translate(80,80)")
.attr("fill","none")
.style("stroke-width","4")
.style("stroke",function(d) { return scale(d) });
function transition() {
// Update data, max d is 60:
data = data.map(function(d) { return d == 60 ? 0 : d + 10});
var i = 0;
// Grow circles
circles
.data(data)
.filter(function(d) { return d > 0 })
.transition()
.ease(d3.easeLinear)
.attr("r", function(d) { return d; })
.style("stroke", function(d) { return scale(d) })
.style("opacity",function(d) { return d == 60 ? 0 : 1 })
.duration(1000)
.on("end",function(){if(++i==circles.size()-1) { transition(); } });
// Reset circles where r == 0
circles
.filter(function(d) { return d == 0 })
.attr("r", 0)
.style("opacity",1)
.style("stroke",function(d) { return scale(d); });
}
transition();
<script src="https://d3js.org/d3.v4.min.js"></script>
Note that .on("end",... triggers on each element's transition end - this is why I count to see if all elements are done transitioning before running the transition function again.

Inserting data (bar charts and weather info) into SVG Circles

I'm trying to create a visual effect (using D3.js and json) where 4 small circles will pop up once I click on the big yellow circle. I want to make sure that ALL of the circles will present pieces of information that I'm trying to assign to each and every one of them. I plan to have:
-The 1st big yellow circle to present the weather (sunny, mostly cloudy, etc)
-2 small circles to present weather info by word
-2 small circles present a D3 bar chart that I made, along with some weather info by word
(I have all of the info separated by spaces and comments)
However, my problem is (for the lack of a better explanation) that I am just stumped! I have no idea how to make that happen. I would really appreciate it if I can get help from you guys. Here's my code (HTML and JS):
var h = 2000;
var w = 2000;
var xGrid = 300;
var yGrid = 300;
var radius = 300;
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var shape = svg.append("circle")
.attr("cx", xGrid)
.attr("cy", yGrid)
.attr("r", radius)
.style("fill", "yellow");
shape.on("click", function(){
var circle1 = svg.append("circle")
//.attr("cx", (xGrid-radius/2)+0)
//.attr("cy", (yGrid-radius/2)+0)
.attr("cx", xGrid - radius/2)
.attr("cy", yGrid - radius/2)
.attr("r", radius/2)
.style("fill", "red");
var circle2 = svg.append("circle")
//.attr("cx", (xGrid-radius/2)+10)
//.attr("cy", (yGrid-radius/2)+10)
.attr("cx", xGrid + radius/2)
.attr("cy", yGrid - radius/2)
.attr("r", radius/2)
.style("fill", "blue");
var circle3 = svg.append("circle")
// .attr("cx", (xGrid-radius/2)+20)
// .attr("cy", (yGrid-radius/2)+20)
.attr("cx", xGrid - radius/2)
.attr("cy", yGrid + radius/2)
.attr("r", radius/2)
.style("fill", "green");
var circle4 = svg.append("circle")
// .attr("cx", (xGrid-radius/2)+30)
//.attr("cy", (yGrid-radius/2)+30)
.attr("cx", xGrid + radius/2)
.attr("cy", yGrid + radius/2)
.attr("r", radius/2)
.style("fill", "purple");
});
<!-- ///////////////////////////////////////////////////////////////////////////// /////////////////
///////////////////////////////(END) Circle Pop-up (END)/////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////-->
</script>
<!--///////////////////////////////////////////////////////////////////////////// ////////////////////////
///////////////////////////(START) Info for circles to present (START)////////////////////////////////
///////////////////////////////////////////////////////////////////////////// //////////////////////////////-->
<!--/////Main Circle display/////////-->
<p id="w"></p><p id="main"></p>
<!--//////Circle 1 (upper left corner) display///////////-->
<p id="rh"></p><p id="c1a"></p>
<!--///////Circle 2 (upper right corner) display//////////-->
<p id="ws"></p><p id="c2a"></p>
<p id="wd"></p><p id="c2b"></p>
<p id="we"></p><p id="c2c"></p>
<p id="wm"></p><p id="c2d"></p>
<!--////////Circle 3 (lower left corner) display/////////-->
<p id="pti"></p><p id="c3a"></p>
<p id="ptc"></p><p id="c3b"></p>
<p id="df"></p><p id="c3c"></p>
<p id="dc"></p><p id="c3d"></p>
<!--///////Circle 4 (lower right corner) display//////////-->
<p id="hif"></p><p id="c4a"></p>
<p id="hic"></p><p id="c4b"></p>
<p id="sr"></p><p id="c4c"></p>
<p id="uv"></p><p id="c4d"></p>
<script type = "text/javascript">
var dataForMainCircle = '{"weather": "Mostly Cloudy"}';
var mcDis= JSON.parse(dataForMainCircle);
var weather = "weather: ";
document.getElementById("w").innerHTML = weather;
document.getElementById("main").innerHTML = mcDis.weather;
/////////////////////////////////////////////////////////////////////////////////
////////////Setup for display of 1st circle info///////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
var dataForCircle1b = '{"relative_humidity": "92%"}';
var relativeHum = "Relative Humidity: ";
var c1Dis = JSON.parse(dataForCircle1b);
d3.json("js/tempGraph.json", function (data) {
var canvas = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 500)
canvas.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("width", function (d) {return d.temp * 10; })
.attr("height", 48)
.attr("y", function (d,i) {return i * 50; })
.attr("fill", "red");
canvas.selectAll("text")
.data(data)
.enter()
.append("text")
.attr("fill", "white")
.attr("y", function (d,i) {return i * 50 + 24; })
.text(function (d) {return d.temp; })
})
document.getElementById("rh").innerHTML = relativeHum;
document.getElementById("c1a").innerHTML = c1Dis.relative_humidity;
/////////////////////////////////////////////////////////////////////////////////////////
/ ////////////////////Setup for display of 2nd circle info////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
var dataForCircle2 = '{"wind_string": "Calm", "wind_dir": "SW", "wind_degrees": 229, "wind_mph": 2}';
var c2Dis = JSON.parse(dataForCircle2);
var windCon = "Wind Condition: ";
var windDir = "Wind Direction: ";
var windDeg = "Wind Degree: ";
var windMph = "Wind (Miles Per Hour): "
document.getElementById("ws").innerHTML = windCon;
document.getElementById("c2a").innerHTML = c2Dis.wind_string;
document.getElementById("wd").innerHTML = windDir;
document.getElementById("c2b").innerHTML = c2Dis.wind_dir;
document.getElementById("we").innerHTML = windDeg;
document.getElementById("c2c").innerHTML = c2Dis.wind_degrees;
document.getElementById("wm").innerHTML = windMph;
document.getElementById("c2d").innerHTML = c2Dis.wind_mph;
///////////////////////////////////////////////////////////////////////////// //////////////////////////////////
//Setup for display of 3rd circle info/////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////// ////////////////////////////////
var dataForCircle3 = '{"precip_today_in": "0.00", "precip_today_metric": "0"}';
var c3Dis = JSON.parse(dataForCircle3);
var predate = "Today's Precipitation: ";
var prem = "Precipitation Metric: ";
//var dewF = "Dewpoint-F: ";
//var dewC = "Dewpoint-C: ";
document.getElementById("pti").innerHTML = predate;
document.getElementById("c3a").innerHTML = c3Dis.precip_today_in;
document.getElementById("ptc").innerHTML = prem;
document.getElementById("c3b").innerHTML = c3Dis.precip_today_metric;
//document.getElementById("df").innerHTML = dewF;
//document.getElementById("c3c").innerHTML = c3Dis.dewpoint_f;
d3.json("js/dewGraph.json", function (data) {
var canvas = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 500)
canvas.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("width", function (d) {return d.dewpoint * 10; })
.attr("height", 48)
.attr("y", function (d,i) {return i * 50; })
.attr("fill", "white");
canvas.selectAll("text")
.data(data)
.enter()
.append("text")
.attr("fill", "white")
.attr("y", function (d,i) {return i * 50 + 24; })
.text(function (d) {return d.dewpoint; })
})
//document.getElementById("dc").innerHTML = dewC;
//document.getElementById("c3d").innerHTML = c3Dis.dewpoint_c;
<!--//////////////Setup for display of 4th circle display////////////////////-->
var dataForCircle4 = '{"heat_index_f": "NA", "heat_index_c": "NA", "solarradiation": "--", "UV": "0"}';
var c4Dis = JSON.parse(dataForCircle4);
var heatF = "Heat Index-F: ";
var heatC = "Heat Index-C: ";
var sunR = "Solar Radiation: ";
var ultraV = "UV: ";
document.getElementById("hif").innerHTML = heatF;
document.getElementById("c4a").innerHTML = c4Dis.heat_index_f;
document.getElementById("hic").innerHTML = heatC;
document.getElementById("c4b").innerHTML = c4Dis.heat_index_c;
document.getElementById("sr").innerHTML = sunR;
document.getElementById("c4c").innerHTML = c4Dis.solarradiation;
document.getElementById("uv").innerHTML = ultraV;
document.getElementById("c4d").innerHTML = c4Dis.UV;
d3.json("js/tempGraph.json", function (data) {
var canvas1 = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 500)
canvas1.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("width", function (d) {return d.temp * 10; })
.attr("height", 48)
.attr("y", function (d,i) {return i * 50; })
.attr("fill", "red");
canvas1.selectAll("text")
.data(data)
.enter()
.append("text")
.attr("fill", "white")
.attr("y", function (d,i) {return i * 50 + 24; })
.text(function (d) {return d.temp; })
})
d3.json("js/dewGraph.json", function (data) {
var canvas2 = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 500)
canvas2.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("width", function (d) {return d.dewpoint * 10; })
.attr("height", 48)
.attr("y", function (d,i) {return i * 50; })
.attr("fill", "blue");
canvas2.selectAll("text")
.data(data)
.enter()
.append("text")
.attr("fill", "white")
.attr("y", function (d,i) {return i * 50 + 24; })
.text(function (d) {return d.dewpoint; })
})
</script>
</body>
</html>
Keep in mind, this program is sort of a prototype. My main concern is getting the info assigned for the circles INSIDE of them. If you guys found any errors/mix-ups in the code, feel free to notify me. Thank you!!
Ok, as promised, an example. It doesn't look super cool, but at least you will get the picture. If you have more questions, please shoot! Here goes:
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="donut.js" type="text/javascript"></script>
<style>
body{
background-color:black;
}
#container {
position:relative;
display:inline-block;
width:100%;
}
.row{
position:relative;
display:inline-block;
width:100%;
text-align:center;
}
#chart{
width:60%;
position:relative;
display:inline-block;
}
text{
font-size:1.4em;
}
</style>
</head>
<body>
<div id="container">
<div class="row">
<div id="chart">
</div>
</div>
</div>
<script>
var data = [
{"type":"weather", "status":"sunny"},
{"type":"humidity", "status":"high"},
{"type":"temperature", "status":"23°"},
{"type":"pressure", "status":"1040 hpa"},
{"type":"wind", "status":"East"},
];
var chartWidth = 500;
var chartHeight = 500;
/* Adding the svg drawing canvas by selecting the element that will contain the svg. put it in a var,
as it is a d3 selection. We can reuse this selection. Remember, when chaining, you will get returned the
element you last appended, in this case the svg.
*/
var svg = d3.select("#chart")
.append("svg")
.attr({ // you can put attributes in a javascript object, i like this notation better.
width:chartWidth,
height:chartHeight
});
/* Here I am going to add the circles. With SVG elements, you cannot draw inside other elements. There are only 2 elements
which can contain other elements (as far as I know) and that is the SVG element itself and the g element. So if you want
to give the impression that the elements are inside one another, you need to make them overlap. So if I add a circle and then
add text with coordinates that match the circle, the text will overlap the circle, giving the impression it is inside it.
*/
var circles = svg.selectAll("circle")
.data(data) //binding the data. I want 5 circles for my 5 pieces of data
.enter()
.append("circle")
.attr({
cx:function(d,i){ //when doing the append on a d3 selection, you are iteration over each element!
/* I will use the index (i) to 'identify' the pieces of data and to hardcode their x central point positions */
if(i == 0) {
return chartWidth/2;
}
if(i == 1) {
return chartWidth/5;
}
if(i == 2) {
return chartWidth *(2/7);
}
if(i == 3) {
return chartWidth *(5/7);
}
if(i == 4) {
return chartWidth * (4/5);
}
},
cy:function(d,i){ //when doing the append on a d3 selection, you are iteration over each element!
/* I will use the index (i) to 'identify' the pieces of data and to hardcode their y central point positions */
if(i == 0) {
return chartHeight/2;
}
if(i == 1) {
return chartHeight *(3/5);
}
if(i == 2) {
return chartHeight *(4/5);
}
if(i == 3) {
return chartHeight *(4/5);
}
if(i == 4) {
return chartHeight * (3/5);
}
},
r:function(d,i) { /* the radius is in function of the type. The first one (weather) should be the biggest one */
if(d.type === "weather") {
return 200;
}
else{
return 75;
}
},
fill:"yellow",
stroke:"black"
});
/* Now i'll append the text as last, so it overlaps nicely with the circles. For positioning, i ll have to reuse the x and y functions
from the circles. I want the text to be positioned at the center of the cirkels.
*/
var texts = svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(function(d){ return d.status})
.attr({
x:function(d,i){
/* So I used the same positioning for the text as for the center points of the circles.
you should realize that the text really starts at the middle of the circles. So you
should substract a bit from the x position to get them nicely in the middle. I aint going
for that trouble, its just an example.
*/
if(i == 0) {
return chartWidth/2;
}
if(i == 1) {
return chartWidth/5;
}
if(i == 2) {
return chartWidth *(2/7);
}
if(i == 3) {
return chartWidth *(5/7);
}
if(i == 4) {
return chartWidth * (4/5);
}
},
y:function(d,i){ //when doing the append on a d3 selection, you are iteration over each element!
/* I will use the index (i) to 'identify' the pieces of data and to hardcode their y central point positions */
if(i == 0) {
return chartHeight/2;
}
if(i == 1) {
return chartHeight *(3/5);
}
if(i == 2) {
return chartHeight *(4/5);
}
if(i == 3) {
return chartHeight *(4/5);
}
if(i == 4) {
return chartHeight * (3/5);
}
}
});
</script>
</body>
</html>
I did not add the function to click on the first circle to show the others. I could if you want me to though, but I felt like this example already has enough stuff in it to learn from.

D3 chart transition either gets right number or width, but not both

I just picked up D3 and started playing around with bar chart animations. I have 5 bars that transition to 3 and back when I click. I can either:
Get the 5 bars to 3 and back to 5 WITHOUT bar width dynamically changing, or
Have dynamically changing bar width but only 3 bars (by commenting out .enter() and .append()).
Would appreciate any help on how to get both! The full file is here:
https://github.com/datapress/learningD3/blob/master/chart.html
var sortOrder = false;
d3.selectAll("rect")
.on("click", function() {
sortOrder = !sortOrder
var rectID = d3.select(this).attr("id");
var dataset0 = [1, 2, 3, 4, 5];
var dataset1 = [5, 1, 1];
var dataset2 = [1, 5, 1];
var dataset3 = [1, 1, 5];
var dataset4 = [5, 5, 1];
var dataset5 = [1, 5, 5];
if (sortOrder) {
if (rectID == 0) { dataset = dataset1 };
if (rectID == 1) { dataset = dataset2 };
if (rectID == 2) { dataset = dataset3 };
if (rectID == 3) { dataset = dataset4 };
if (rectID == 4) { dataset = dataset5 };
} else {
dataset = dataset0
}
xScale.domain(d3.range(dataset.length))
yScale.domain([0, d3.max(dataset, function(d) { return d; })])
svg.selectAll("rect")
.data(dataset)
.exit()
.remove()
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.transition()
.duration(1000)
.attr({
x: function(d, i) { return xScale(i); },
y: function(d) { return h - yScale(d); },
width: xScale.rangeBand(),
height: function(d) { return yScale(d); },
fill: function(d) { return "rgb(0, 0, " + (255 - Math.round(d) * 36) + ")"; }
});
});
It's blunt but here is your code fixed to update both:
http://jsfiddle.net/TheMcMurder/r0ptsfLr/
Note: You weren't following d3's enter, update, exit methodology. You had your enter and your exit, but you didn't have the implicit update anywhere for the bars that existed on the page but needed to be changed with the new data.
Here's a simplified example:
var data =[45, 10]
var data2 =[45, 10, 20, 25, 30, 45]
/*The purpose of this JSfiddle is to show how enter, update, and exit works in d3js. The data values above are the data. The image had three manually created black circles. My update will resize them to the appropriate size (based on data) and change their color to blue. All newly drawn objects will be turned green and any object that is exiting will be turned red.
The transitions are delayed to make it easier to see. No delay is needed for this to work.
*/
//creating the svg so I can draw objects on it
var svg = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 5000)
//creating initial circle objects
var circle1 = svg.append("circle")
.attr("cx", 100)
.attr("cy", 100)
.attr("r", 25)
// another circle object
var circle2 = svg.append("circle")
.attr("cx", 100)
.attr("cy", 200)
.attr("r", 25)
// another circle object
var circle3 = svg.append("circle")
.attr("cx", 100)
.attr("cy", 300)
.attr("r", 25)
setTimeout( function(){
enter_update_exit(data)
}, 2500 )
setTimeout( function(){
enter_update_exit(data2)
}, 10000 )
setTimeout( function(){
enter_update_exit(data)
}, 15000 )
function enter_update_exit (data){
var circle_array = svg.selectAll("circle")
.data(data);
//**********************************************************************************
// Enter: all pieces of data that do not have a node to bind to. In this case where
// there are already three circles ('nodes') there would have to be more than 3
// data points in our dataset to have enter run at all;
//**********************************************************************************
circle_array.enter()
.append("circle")
.attr("cx", 100)
.attr("cy", function(d, i){
return (i + 1)*100
})
.attr("r", 0)
.attr("fill", "#78AB46")
.transition()
.duration(1500)
.attr("r", function (d){return d;});
//**********************************************************************************
// Update: Every node that is bound to data, in this case that is everything we've
// entered and everything that has just been bound from the .data(data) bind.
//**********************************************************************************
circle_array.transition()
.duration(1500)
.delay(1500)
.style('fill', 'steelblue')
.attr('r', function (d){
return d;
});
//**********************************************************************************
// Exit: Every node ('circles') that exists in your selection that you don't have
// bound data to
//**********************************************************************************
circle_array.exit()
.transition().duration(1500).delay(1500)
.style("fill", "red")
.transition().duration(1500).delay(3000)
.attr("r", 0).transition().remove();
}
http://jsfiddle.net/TheMcMurder/H3HTe/
I hope that helps!

Example for animating a scatter plot in d3js

I have a set of about 1000 points that I'd like to scatter plot with d3js. I'd like about 10 points to appear every .1 seconds. Is there a simple example of this somewhere? The d3js tutorials are detailed, but I can't seem to find one for this case.
http://alignedleft.com/tutorials/d3/making-a-scatterplot/ and http://bl.ocks.org/bunkat/2595950 ->this example gives you a basic idea of how to draw a scatter plot!!!
http://swizec.com/blog/quick-scatterplot-tutorial-for-d3-js/swizec/5337 -->Tutorial
You need to understand these first!!!
DEMO FIDDLE for your animation in scatterplot--->http://jsfiddle.net/eaGhB/3/
var w = 960,h = 500,nodes = [];
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
var force = d3.layout.force()
.charge(-300)
.size([w, h])
.nodes(nodes)
.on("tick", tick)
.start();
function tick() {
svg.selectAll("circle")
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; });
}
var interval = setInterval(function () {
var d = {
x: w / 2 + 2 * Math.random()-1 ,
y: h / 2 + 2 * Math.random() - 1
};
svg.append("svg:circle")
.data([d])
.attr("r", 10)
.transition()
.ease(Math.sqrt)
.style("stroke", "gray")
.style("fill", "red")
.attr("r", 10);
if (nodes.push(d) > 20) {
clearInterval(interval);
d3.selectAll('circle').on("mouseover", animate).on("mouseout", function () {
d3.select(this).transition()
.duration(100)
.attr("r", 40);
d3.selectAll('circle').style("fill", "black");
});
}
force.stop()
force.start();
}, 200);
function animate() {
d3.select(this).transition()
.duration(300)
.attr("r", 20);
d3.select(this).style("fill", "green");
var sel = d3.select(this);
sel.moveToFront();
};
d3.selection.prototype.moveToFront = function () {
return this.each(function () {
this.parentNode.appendChild(this);
});
};
https://github.com/mbostock/d3/wiki/Gallery-->these are many example that you can refer too.

Categories