How to draw multiple item in single data iteration in D3? - d3.js

I am working with d3.js and need some help regarding data usage.
I have a data array that I bind with d3 and render elements in my plot.
However, each plot item has multiple section, like, starting dot, line and ending dot, something like as below:
O-----------O O---------O
O----------O
Now currently, I am binding the data multiple times for each of the items, the steps are,
Bind data array
Render all starting dots
Bind data array
Render all Line dots
Bind data array
Render all ending dots
the problem in this method is order of rendering. if one plot item rendered over another plot item, the ending dot of the first item will be rendering over the line of 2nd plot item. Another issue is if I want to manipulate a plot item, it is problematic to trace all bits and pieces of a single plot item as they are not related in the plot grammatically.
Now my question is is there any way to bind the data once and render all elements (start dot, line and end dot) together? so that the order of rendering is correct? (in this case all elements of 2nd plot item will render over the 1st plot item).
var lineSelection = PlotGroup.selectAll(".Line")
.data(EventList);
lineSelection .enter()
.append("line")
.attr("class", "Line gline")
.attr("x1", function (d) {
return XScale(d.startTime);
})
.attr("y1", (_Position))
.attr("x2", function (d) {
return XScale(d.endTime);
})
.attr("y2", (_Position));
var RectInSelection = PlotGroup.selectAll(".RectIn")
.data(EventList);
RectInSelection.enter()
.append("rect")
.attr("class", "RectIn")
.attr("x", function (d) {
return XScale(d.startTime);
})
.attr("y", function (d) {
return _Position;
})
.attr("width", 16)
.attr("height", 16);
var RectOutSelection = PlotGroup.selectAll(".RectOut")
.data(EventList);
RectOutSelection.enter()
.append("rect")
.attr("class", "RectOut")
.attr("x", function (d) {
return XScale(d.endTime);
})
.attr("y", function (d) {
return _Position;
})
.attr("width", 16)
.attr("height", 16);

I would have made as many groups as data, and to each group i would have made the line and rectangles. This will fix the overlapping issue and multiple data binding issue.
var groups = PlotGroup.selectAll(".mygroup")
.data(EventList)
.enter()
.append("g")
.classed("mygroup", true);
//make line in the group
groups.append("line")
.attr("class", "Line gline")
.attr("x1", function (d) {
return XScale(d.startTime);
})
.attr("y1", (_Position))
.attr("x2", function (d) {
return XScale(d.endTime);
})
.attr("y2", (_Position))
//make in rectangle
groups.append("rect")
.attr("class", "RectIn")
.attr("x", function (d) {
return XScale(d.startTime);
})
.attr("y", function (d) {
return _Position;
})
.attr("width", 16)
.attr("height", 16);
//make out rectangle
groups.append("rect")
.attr("class", "RectOut")
.attr("x", function (d) {
return XScale(d.endTime);
})
.attr("y", function (d) {
return _Position;
})
.attr("width", 16)
.attr("height", 16)
Hope this helps!

Related

D3.js : Duplicate 100% working interactive legend for a bubble map

I have created a interactive legend, that works 100% perfectly fine. I just didnt manage to duplicate it for another column of my dataframe.
It starts with bubbles I'm plotting with the code below. Then I draw the actual legend.
1. creating bubbles
selection
.selectAll('circle')
.data(cities)
//.attr('r', 14)
.attr('r', function(d) {
return Math.max(Math.pow(d.population, 0.57) / 40, 7);})
.attr('cx', function(d) { return projection.latLngToLayerPoint([d.latitude, d.longitude]).x;
})
.attr('cy', function(d) { return projection.latLngToLayerPoint([d.latitude, d.longitude]).y;
})
.attr("class", function(d) { return "bubbles " + d[attribute] }) //Important feature for legend
//.attr("class", function(d) { return "bubbles " + d.category })
.attr('stroke', 'white')
.attr('stroke-width', function(d) {
return 1.2 / projection.scale;})
.style("fill", function(d) {
return color(d[attribute])})
.attr("stroke", "#FFFF")
.attr("stroke-width", 1)
.attr("fill-opacity", .9)
.on("mouseover", showTooltip)
.on("mousemove", moveTooltip)
.on("mouseleave", hideTooltip)
2. Drawing actual legend
svg_chorop.selectAll("mydots")
.data(allgroups)
.enter()
.append("rect")
.attr("x", 0)
.attr("y", function(d, i) {
return 60 + i * (size + 10)
}) // 60 is where the first dot appears. 10 is the distance between dots
.attr("width", size)
.attr("height", size)
.style("fill", function(d) {
return mycolor(d)
})
.style("stroke", "#DCDCDC")
.on("mouseover", highlight)
.on("mouseleave", noHighlight)
My goal : make the exact same legend with another hovering option.
Problem : I can't find a way to ADD another class attribute (in my case d.category, for economic sector) in the first bunch of code.
Fiddle link, so it's easier for you to see the code : jsfiddle
I have simply tried to add another .attr("class",..). It doesn't work. And I can find a similar fix for this problem online.
.attr("class", function(d) { return "bubbles " + d[attribute] }) //Important feature for legend
//.attr("class", function(d) { return "bubbles " + d.category })
Here you can see the two legends, left one functionnal, right, the
one to be implemented
Here you can see the dataframe and the category column

How to define which points for interpolation in radial line

Very much D3 newbie here, feeling my way around adapting an existing radar chart built in D3 v3.5.9
I'm having an issue in interpolating between points when there are zero values between them.
Example:
radar when all data points are present, looks fine
radar when there are zero values
The behaviour I want is for the interpolation to go around the circle, rather than just closing the sections bounded by the non-zero values.
green lines show desired behaviour whenever a zero is encountered
I have used the 'defined' function to find zero values in the source data, but I need to add something else to instruct D3 to draw the connecting lines between the desired points. Something with the index value for d, probably?
Or perhaps 'defined' is not the right function in this case?
var radarLine = d3.svg.line
.radial()
.defined(function (d) {
return d.value !== 0;
})
.interpolate("linear-closed")
.radius(function (d) {
return rScale(d.value);
})
.angle(function (d, i) {
return i * angleSlice;
});
if (cfg.roundStrokes) {
radarLine.interpolate("cardinal-closed");
}
//Create a wrapper for the blobs
var blobWrapper = g
.selectAll(".radarWrapper")
.data(data)
.enter()
.append("g")
.attr("class", "radarWrapper");
//Append the backgrounds
blobWrapper
.append("path")
//.attr("class", "radarArea")
.attr("class", function (d) {
return "radarArea" + " " + d[0].radar_area.replace(/\s+/g, "");
})
.attr("d", function (d, i) {
return radarLine(d);
})
.style("fill", function (d, i) {
return cfg.color(i);
})
.style("fill-opacity", 0);
//Create the outlines
blobWrapper
.append("path")
.attr("class", "radarStroke")
.attr("d", function (d, i) {
return radarLine(d);
})
.style("stroke-width", cfg.strokeWidth + "px")
.style("stroke", function (d, i) {
return cfg.color(i);
})
.style("fill", "none");
Any help would be greatly appreciated!

D3 Problems with merge and adding labels to siblings

I was trying to add labels to circles that are constantly moving in D3. One of the ways I found was to insert the label and the circle to a g tag. However, this causes errors whenever I want to EXIT the elements and merge them (which is not happening right now).
Is there any other way to do it? My graph right now seems to be with lower frames per second because it is not using the merge to update from the current x/y position to the new one.
The code is as follow:
var t = d3.transition()
.duration(0);
// JOIN new data with old elements.
var circles = g.append("g").selectAll('g')
.data(data.values)
.enter()
.append('g')
// EXIT elements
d3.selectAll("circle").transition()
.attr("class", "exit")
.remove();
// EXIT elements
d3.selectAll(".label")
.attr("class", "exit")
.remove();
// ENTER new elements present in new data.
// Circles
circles.append("circle")
.attr("class", "enter")
.attr("fill", function (d) { return color(d.country); })
.attr("cy", function (d) { return y(d.active_cases); })
.attr("cx", function (d) { return x(d.total_deaths) })
.merge(circles)
.transition(t)
.attr("r", 15)
// Labels
circles.append("text")
.attr("class", "label")
.attr("font-size", 10)
.style("text-anchor", "middle")
.style("fill", "white")
.attr("x", function (d) { return x(d.total_deaths) })
.attr("y", function (d) { return 3 + y(d.active_cases) })
.merge(circles)
.text(function (d) { return d.country })

I have to append text in my d3.js code but it's not displaying text

script type="text/javascript">
d3.csv("mydata.csv", function(data){
var svgcontainer = d3.select("body").append("svg")
.attr("width",500)
.attr("height",500)
svgcontainer.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("width" ,function (d) { return d.age * 10;})
.attr("height" ,45)
.attr("y",function(d,i) { return i*50; })
.attr("fill","blue")
svgcontainer.selectAll("text")
.data(data)
.enter()
.append("text")
.attr("fill","white")
//.attr("y",function(d,i) { return i*50 ; })
.text( function (d) { return d.name; })
})
I have to append text in my d3.js code but it's not displaying text.I am new to d3.js so please any one help me. Here is my code-
From the first observation i will get to know the issue related with the text element position in svg. In SVG Coordinate positions are very important to achieve the graphical representation. In above code snippet x and y positions are missing. sample code below:-
svgcontainer.append("text")
.attr("x", function(d) { return x(d.value) - 3; })
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return d.value; });
Example

Coordinates of each center's arc in a pie chart

In order to draw a baton that its starting point takes coordinates of arc's center in a Pie Chart d3 js, How can determinate coordinates of each arcs's center ?
I tried this code but only the first baton was in the right position.
var lines = arcs.selectAll("line")
.data(data)
.enter()
.append("line")
.attr("x1", function(d, i) { return r*Math.cos( parseFloat(d)/r);})
.attr("y1", function(d) { return r*Math.sin( parseFloat(d)/r) ;})
.attr("x2", function(d) { return r*Math.cos( parseFloat(d)/r) + parseFloat(d) ;})
.attr("y2", function(d) { return r*Math.sin( parseFloat(d)/r) + parseFloat(d) ;})
.attr("class", "line")
.style("stroke", function (d) {return color(d.data) ; })
.style("stroke-width", "3px");
Please i need your help.
As pointed out in the comments, the arc.centroid() function provides this functionality:
var pie = d3.layout.pie().value(function(d){ return d.value; });
var arc = d3.svg.arc().innerRadius(0).outerRadius(r);
var centers = pie(data).map(arc.centroid);
Or with a D3 selection:
var centers = [];
arcs.each(function(d) {
centers.push(arc.centroid(d));
});
Complete demo here.

Resources