the line is repainted in d3.js - d3.js

I'm trying to draw a line based on a few points that come with the help of a setinterval, I'm passing the points one at a time but I could also pass several. for now I want the points of the line to be drawn one at a time but without re-drawing those that have already been drawn. How can I solve this problem ?
http://plnkr.co/edit/JjIiqrf97Y8K7YUJRodo?p=preview
var path=g.selectAll(".line1")
.data(data)
.enter()
.append("path")
.attr("class", "line1")
.attr("d", function(d,i) { return (line(data)); })
.style("stroke", function(d) { return "brown" });
in my real problem, I will receive points in real time that is why I am practicing how to draw point to point and generate an animation

Its because of your loop. You are pushing to datos and then rerendering the entire array.
var datos = new Array();
setInterval(function(){
if(datos.length<newPoints.length){
datos.push(newPoints[count]);
count++;
// Here you are rerendering this new piece of data,
// AND all of the previous pushes of data.
display(datos);
}
}, 1000);

Related

How to move points in an orthogonal map?

I am trying to add red points at certain geolocations on the following map created by Mike Bostock. https://bl.ocks.org/mbostock/3795040. My points show up but don't move with the map. How do I edit the code to make the points move with the map. Thank you.
//add circles to svg
svg.selectAll("circle")
.data([wr,pt,sd,jp,fm])
.enter()
.append("circle")
.attr("cx", function (d) { console.log(projection(d)); return projection(d)[0]; })
.attr("cy", function (d) { return projection(d)[1]; })
.attr("r", "8px")
.attr("fill", "red");
The following is what the above array being referenced.
//points
var wr = [32.6130007, -83.624201];
var pt = [48.9334357, 8.961208];
var sd = [32.715738, -117.1610838];
var jp = [35.6894875, 139.6917064];
var fm = [39.1137602, -76.7267773];
You have to include the circles in the mousemove function:
svg.on("mousemove", function() {
var p = d3.mouse(this);
projection.rotate([λ(p[0]), φ(p[1])]);
svg.selectAll("path").attr("d", path);
//change the circles' positions here:
svg.selectAll("circle").attr("cx", function(d) {
console.log(projection(d));
return projection(d)[0];
})
.attr("cy", function(d) {
return projection(d)[1];
})
});
Here is the updated bl.ocks: https://bl.ocks.org/anonymous/2a6f07cdc12838b296674470ad715bbe/54d6de8d73347081f900c88a203019df74f23ade
PS: Some circles appear to move wrongly: they are correct, though. The thing is that they are behind the globe. To hide those circles you'll have to write another function (which is outside the scope of this answer).
An alternative to hide the circles behind the globe is using a path instead a circle. That way, the projection will automatically clip those paths. Have a look: https://bl.ocks.org/anonymous/9e640195e2c021cd79b5ca9b2238a44c/1c43719a7d6a85d0226cf3c468ac23e570add22d

D3 draw single element from geojson

am pretty new to both JS and D3js. I am trying to create a single circle on the map that transitions (Size increases then decreases) through my data. However, as from this example, 4 elements are added to the svg. and the size of each of them changes at the same time. However, what i want to create a single circle than transitions using the count field. Link to my current block:
https://bl.ocks.org/shannondussoye/e8feaa2cf22f7e6a7d12582b923d999f
Thanks
Your code here:
var circle = svg.selectAll("circle")
.data(data.features)
.enter()
.append("circle");
Will append one circle for each item in the array data.features (assuming no circles are already present). As there are four items in the array, you'll have four circles. If you just want to append one circle, change the input data array to an array of one feature:
.data([data.features[0]]).enter().append()...
Then, you can update that data, after the initial append, when you want to transition to a new feature:
.data([data.features[i]])
.attr("attr to be updated",...)
The example below applies this method in a non-geographic setting:
1. append a feature,
2. update the feature with properties from the next item in an array,
3. repeat:
var svg = d3.select("body")
.append("svg")
.attr("width",400)
.attr("height",400);
var data = [{x:100,y:100,r:10},{x:200,y:100,r:30},{x:200,y:200,r:10},{y:200,x:100,r:25}];
var circle = svg.selectAll("circle")
.data([data[0]])
.enter()
.append("circle")
.attr("cx",function(d) { return d.x; })
.attr("cy",function(d) { return d.y; })
.attr("r",function(d) { return d.r; });
var i = 0;
transition();
function transition() {
circle.data([data[++i%4]]) // get the next item in the data array, assign that datum to the feature
.transition()
.attr("cx",function(d) { return d.x; }) // update the properties of the feature
.attr("cy",function(d) { return d.y; })
.attr("r", function(d) { return d.r; })
.each("end",transition) // loop
.duration(2000)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Here is your map with that method (with minimal code changes - though regretfully I changed the json file name)
If you do console.log(data.features.length), you'll have the result 4. That means that data.features has 4 arrays and, of course, your enter selection will have 4 circles.
As those arrays seem to have the same geographic position (all of them point to the Town Hall and have the same coordinates, which is "151.2062183,-33.8732664"), use just one of them. For instance, the first array:
var circle = svg.append("circle")
.datum(data.features[0]);
That will append just one circle, for the first element.
Here is your updated bl.ocks: https://bl.ocks.org/anonymous/7e930937a6d8c0a24c6ca3a033a7cf84/f176a662d4ccd4d33b56101a17e93e6a7e0ef724

Highlighting individual rings in ring chart

I am trying to draw a circular heat or ring-chart. There are several options it seems with d3js. The most popular appears to use the pie layout to make several donut rings Another option is to use a circular heat chart like this one -
Both of these however use filling segments as their way of depicting area size. I wanted however to use lines to depict events over time. With each line occurring within a particular ring.
To get this effect, I've adapted this radial weather chart - http://bl.ocks.org/susielu/b6bdb82045c2aa8225f5
This is my attempt so far:
http://blockbuilder.org/jalapic/12a3a23651f40283d489
It does not have labeling, but each ring (12 total) represents an individual subject. Each segment represents a sample of time (says months here but could be anything). The lines are drawn within each ring that they belong to. I have kept the same variable names as the weather example to enable comparisons between my stripped down code and the author's original code.
This is what it looks like:
My question is how might it be possible to mouseover each ring to make only that ring's contents (i.e. lines) remain visible, i.e. to hide the other rings - this would make viewing the chart easier.
Here is the code for how the rings are made up:
var mycircles = [110,100, 90, 80, 70, 60,50,40,30,20,10,0]
origin.selectAll('circle.axis-green')
.data(mycircles) //original circles
.enter()
.append('circle')
.attr('r', function(d) { return rScale(d)})
.style("fill", "#fff8ee")
.style("opacity", .05)
.attr('class', 'axis record')
.on("mouseover", function(d) {d3.select(this).style("fill", "red");})
.on("mouseout", function(d) {d3.select(this).style("fill", "#fff8ee");
});
As can be seen the rings are actually overlapping circles. Is there a way to achieve what I'm trying to do using the approach I'm taking, or would I have to go back to working something out with segments like in the heatchart or pie layouts?
Looking at your data and code, one method would be to assign a class to each line representing it's ring position. You can then use mouseover and mouseout events to toggle the opacity of those lines.
First, create a couple helper functions:
// which ring is currently highlighted
var curRing = null;
// show all rings
function unShowRing(){
d3.selectAll(".record")
.style("opacity", 1);
curRing = null;
}
// only show current ring
function showRing(ringId){
// all lines that are not in my ring, hide them
d3.selectAll(".record:not(.ring" + ringId + ")")
.style("opacity", 0);
curRing = ringId;
}
Set up the lines a little different:
...
.enter().append('line')
// assign a unique class to each ring's lines
.attr('class', function(d) {
return cl + " ring" + d.recLow/10;
})
// on mouseover only show my ring
.on("mouseover", function(d){
var ringId = d.recLow/10;
showRing(ringId);
})
// on mouseout show all rings
.on("mouseout", function(d){
unShowRing();
})
// this will prevent lines transitioning in from being shown
.style('opacity', function(d){
if (!curRing){
return 1;
} else {
var ringId = d.recLow/10;
return ringId === curRing ? 1 : 0;
}
})
Finally, you'll need to handle the ring "circle" mouseovers as well in case the user mouses over lines or rings:
origin.selectAll('circle.axis-green')
.data(mycircles) //original circles
...
.on("mouseover", function(d) {
d3.select(this).style("fill", "red");
var ringId = d/10;
showRing(ringId);
})
.on("mouseout", function(d) {
d3.select(this).style("fill", "#fff8ee");
unShowRing();
});
Here's the whole thing working.

D3.js nested data - how can i display my circles

I am a beginner on D3.js and hit the wall on the following:
I wish to display the score given by an attendee of an event over time. Then as the attendee can give also comments, I would like to place a circle on the curve of the score in the same colour as the scoring.
I succeeded to do this for a single user.
The code is on JS Fiddle http://jsfiddle.net/roestigraben/8s1t8hb3/
Then, trying to extend this to multiple attendees, I run into problems.
The JSFiddle http://jsfiddle.net/roestigraben/Lk2kf1gh/
This code displays nicely the score data for the 3 attendees simulated. However the circles to display the possible comments (there is only one in the data set) from the attendees do not work
I try to filter the attendees array
svg.selectAll("circle")
.data(data.filter(function(d, i){ if(d.comment){return d}; })) // condition here
.enter().append("circle")
.attr("class", "dotLarge")
.attr({r: 5})
.attr("cx", function(d) { return x(d.time); })
.attr("cy", function(d) { return y(d.status); })
I think I need to go deeper into the nesting, but ....my ignorance.
Thanks a lot
Peter
The code where you're displaying your circles doesn't even come close to matching the format of your data. I don't know that you're having a problem with the nest, but you probably want a slightly different data structure when it comes to graphing your comments.
I have updated your fiddle here: http://jsfiddle.net/Lk2kf1gh/7/
The important bit is:
var comments = attendees.map(function(d) {
return {
name: d.name,
comments: d.values.filter(function(e) {
return e.comment != undefined;
})
};
});
//generation of the circles to indicate a comment
// needs to be done with a filter
svg.selectAll("g.comments")
.data(comments)
.enter()
.append("g")
.attr("class", function(d) { return "comments " + d.name })
.selectAll("circle.comment")
.data(function(d) {
return d.comments;
})
.enter()
.append("circle")
.attr("class", "dotLarge comment")
.attr("r", 5)
.attr("cx", function(e) { return x(e.time); })
.attr("cy", function(e) { return y(e.status); });
The first part of this code creates a new data structure that is more focused on the comment information. The second part creates the circles.
Note: I've grouped each person into their own g element and then created a circle for each of their comments. This makes use of d3 nested selections and data binding. Hopefully you can follow what is happening.
I've also added a few more comments to your data for testing. I didn't bother to fix any of the cosmetic issues, I'll leave that up to you.

How do I dynamically update displayed elements in D3

I have a function that loads an initial array of points onto a map using D3:
var fires = []; //huge array of objects
function update(selection) {
feature = g.selectAll("path")
.data(selection);
feature.attr("class", "update");
feature.enter().append("path")
.attr("class", "enter")
.style("fill", function(d) {return colorScale(d.area)});
feature.exit().remove();
}
I call it initially with a large array:
update(fires);
I also have a histogram that allows me to narrow down the selection based on years. Using a D3 brush, on 'brushend' I call a function called brushed:
function brushed() {
if (!d3.event.sourceEvent) return; // only transition after input
var extent0 = brush.extent();
startYear = Math.floor(extent0[0]);
endYear = Math.floor(extent0[1]);
var selectedFires = fires.filter(function(fire) {
if(fire.year >= startYear && fire.year <= endYear) return fire;
});
console.log(selectedFires.length); //does reflect the proper amount of elements
update(selectedFires);
}
When I narrow the selection, the points on the map disappear as expected. When I widen it, the points/elements do not return. The array is getting the correct elements in it, they're just not being appended to the SVG.
I'm missing something fundamental as the examples I've seen: http://bl.ocks.org/mbostock/3808218 appear to append elements just fine.
Without seeing the rest of the code (which really helps), and focusing on your selection piece alone, try this:
function update(selection) {
// binding the data
feature = g.selectAll(".path")
.data(selection);
// exit selection
feature
.exit()
.remove();
// enter selection
feature
.enter()
.append("path")
.attr("class","path");
// update selection
feature
.style("fill", function(d) {return colorScale(d.area)});
// update selection
feature
.style("fill", function(d) {return colorScale(d.area)})
.attr("d",path); // this was the missing piece!
}
NOTE: you also want to comment out where you hardcoded the extent of the brush:
//brush
var brush = d3.svg.brush()
.x(areaYearScale)
//.extent([1984, 2013]) // comment this out
.on("brushend", brushed);

Resources