d3 + drawing svgs + incorrectly overwriting - javascript

this i my jsfiddle that shows 2 piecharts which gives me something like this in the html view:
<svg width="220" height="220">
<svg width="220" height="220">
this is my fiddle where I am trying to insert an svg before the 2 piecharts, but I am not writing it correctly, 1 of the pie charts gets over written.
Can anyone advise how I can have all 3 svgs showing?
All my code as it is in the 2nd fiddle
var width = $(document).width()-50 || 960,
height = $(document).height()-50 ||500;
//attaches/appends a svg tag to a body tag with a width and height
/* COMMENT IN */
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
data=[[10, 5],[32, 24]]
z = d3.scale.category20c();
//attaches/appends a svg tag to a body tag with a width and height
var svg2 = d3.select("body").selectAll("svg")
.data(data)
.enter().append("svg:svg")
.attr("width", 220)
.attr("height", 220)
.append("svg:g")
.attr("transform", "translate(" + 100 + "," + 100 + ")")
svg2.selectAll("path")
.data(d3.layout.pie())
.enter().append("svg:path")
.attr("d", d3.svg.arc()
.innerRadius(10)
.outerRadius(100))
.style("fill", function(d, i) { return z(i); });
NOTE: Some further background is that the first svg is a map that I have working. And What I am trying to do is create a layer on top of this which will be piecharts, in this case just 2, but you have to start somewhere :)

Your interpretation of the results is slightly wrong. The first pie chart doesn't get overwritten, it won't get rendered in the first place. In the commented in version you are appending the svg for your map, which works fine and can easily be identified by the width and height values assigned to it.
When trying to append the SVGs for your pie charts, you are selecting all SVGs in the body by doing d3.select("body").selectAll("svg"). This selection will include the previously appended SVG. You are then binding your data to the selection which will compute the data join. Since you are binding an array of data without any key function the join will be based on the index. Thus, the existing SVG will correspond to the first element in the data array and will be put to the update selection. As your code only works on the enter selection the first pie chart will never be drawn because it doesn't belong to this selection. The second one is output as expected.
One way to work around this is to use a CSS marker class for the pie charts. That way you could limit the second selection to the SVGs having the marker class:
var svg2 = d3.select("body").selectAll("svg.pie") // select with class "pie"
.data(data)
.enter().append("svg:svg")
.attr("width", 220)
.attr("height", 220)
.classed("pie", true) // set marker class for all pie charts
.append("svg:g")
.attr("transform", "translate(" + 100 + "," + 100 + ")")
Because no SVG is present having class pie, all your data will end up in the enter selection rendering an SVG containing a pie chart for every data element as expected. I have updated your JSFiddle to show the effect.

Related

D3:reflect data changes on visualization keeping the same array size. Trying to represent a sort algorithm visually

I'm trying to represent a selection sort visually with d3 to show some students and I'm having problems updating the data once the positions swap(I will add the transitions and delays once it's working). The positional attributes don't seem to be working as well, I don't know why, any ideas?. The codepen is here:
HTML:
<div id="canvas">
</div>
CSS:
rect{
border:1px solid black;
}
JS:
function selectionSort(array,svg){
//where the index will position
var positioningIndex=0;
var aux;
var minIndex;
var minVal=Number.MAX_VALUE;
while(positioningIndex<array.length){
//get the index of the mínimum
for(var i=positioningIndex;i<array.length;i++){
if(array[i]<minVal){
minIndex=i;
minVal=array[i];
}
}
//swap the mínimum to the positioningIndex
aux=array[minIndex];
array[minIndex]=array[positioningIndex];
array[positioningIndex]=aux;
//update visualization
svg.selectAll("rect").data(array);
minVal=Number.MAX_VALUE;
++positioningIndex;
}
return array;
}
var dataSet=[10,7,8,44];
var svg=d3.select("#canvas").selectAll("rect")
.append("svg")
.attr("width", 960)
.attr("height", 500);
var rect=svg.data(dataSet)
.enter()
.append("rect");
rect.text(function(el){
return el;
})
.attr("width", 30)
.attr("height", 30)
.attr("x", function(d, i) {
return i*5;
})
.attr("y", 30);
.style("color","green");
array=selectionSort(dataSet,svg);
You've got a lot of mixing up of html and svg elements going on there
First off, your svg element isn't getting appended:
var svg=d3.select("#canvas").selectAll("rect") // <- selectAll("rect") is causing problems
.append("svg")
No existing rect elements at the start means no svg's getting appended (do you mean to add one for each rect?) Edit: and in fact that one error is the cause of everything that happens afterwards - the selectAll("rect") needs moved to the line where elements are added to the svg - not on the line where the svg itself is added -->
var rect=svg.selectAll("rect").data(dataSet) // rect should really be g
.enter()
.append("rect");
Secondly, and because of the above error, the elements called 'rect' that are added (and added directly to the #canvas id div) aren't svg:rect objects - they're just html elements with the name 'rect' - see Is there a way to create your own html tag in HTML5?. The browser just treats them as inline elements, so none of your x's or y's make a difference they just line up one after the other
Finally, if this was svg you wouldn't be able to add text directly to a rect, you'd need to use a group (g) element and add both rect and text elements to that to keep them associated, and style("transform", translate(x,y)) the group element to move them around.
var g=svg.selectAll("g").data(dataSet) // <-- now changed from rect
.enter()
.append("g") // <-- and here
.attr ("transform", function(d,i) {
return "translate("+(i*35)+" 30)";
})
;
// texts n rects added here
g.append("text").text(function(el){
return el;
})
.attr("dy", "1em")
g.append("rect")
.attr("width", 30)
.attr("height", 30)
;
See http://codepen.io/anon/pen/bwJWEa?editors=1111

D3.js - Donut chart click event with multiple rings

I have been trying to implement D3.js donut with multiple rings. But, the problem is with click event as it works fine with click on first ring but, show weird behavior while clicking on the second ring. Also it shows some weird problems with mousehover as well.
{"metaData":null,
"data":{graphDetails":[{"displayName":"MUW","name":"DEF","score":5},{"displayName":"ABC","name":"ABCD","score":15},{"displayName":"DEFA","name":"DEF","score":35}],"graphOneDetails":[{"displayName":"D1","name":"D1","score":11},{"displayName":"D2","name":"D2","score":25},{"displayName":"D3","name":"D3","score":22}]},"success":true}
//Define arc ranges
var arcText = d3.scale.ordinal().rangeRoundBands([0, width], .1, .3);
// Determine size of arcs
var arc = d3.svg.arc().innerRadius(radius - 75).outerRadius(radius - 25);
var arc_2= d3.svg.arc().innerRadius(radius - 25).outerRadius(radius);
//Create the donut pie chart layout
d3.layout.pie().value(function(d){
return d["score"];
}).sort(null);
//Append SVG attributes and append g to the SVG
d3.select("#donut-chart")
.attr("width", width)
.attr("height",height)
.append("g")
.attr("transform","translate("+radius+","+radius+")");
//Define Inner Circle
svg.append("circle")
.attr("cx",0)
.attr("cy",0)
.attr("r",280)
.attr("fill","#fff");
//Calculate SVG paths and fill in the colors
var div = d3.select("body")
.append("div")
.attr("class","tooltip")
.style("opactiy",0);
// Append First Chart
var g = svg.selectAll(".arc").data(pie($scope.categories))
.enter()
.append("g")
.attr("class","arc")
.on("click",function(d, i){
alert(d.data.name)
}).on("mouseover",function(d){
alert(d.data.name);
}).on("mouseout",function(d){
alert(d.data.name);
});
g.append("path")
.attr("d",arc)
.attr("fill","#024");
g.append("text")
.attr("transform", function(d){
return "translate("+arc.centroid(d)+")";
}).attr("dy",".35em")
.style("text-anchor","middle")
.attr("fill","#fff")
.text(function (d,i){
return d.data.displayName
});
g.selectAll(".arc text").call(wrap.arcText.rangeBand());
//Append Second Chart
g.data(pie($scope.divisions)).append("path")
.attr("d",arc_2)
.attr("fill","#eee");
g.on("click, function(d,i){
alert(d.data.name);
}).on("mouseover", function(d){
alert(d.data.name);
});
//Append text to second chart
g.append("text")
.attr("transform", function(d){
return "translate("+arc_2.centroid(d)+")";
}).attr("dy",".35em")
.style("text-anchor","middle")
.attr("fill","#fff")
.text(function (d,i){
return d.data.displayName
});
g.selectAll(".arc text").call(wrap.arcText.rangeBand());
In initial state it works fine, but, when I click one chart it displays the data correctly. And when I click inner chart and updates my json to
{"metaData":null,
"data":{graphDetails":[{"displayName":"MUW","name":"DEF","score":5},{"displayName":"DEFA","name":"DEF","score":35}],"graphOneDetails":[{"displayName":"D1","name":"D1","score":11},{"displayName":"D3","name":"D3","score":22}]},"success":true}
Then it display inner chart as a full donut but, the outer chart comes as an arc instead of full donut. Same problem is happening with the mouse over as while I am hovering over the second chart each and everything comes correctly as a tool-tip. (I didn't include the code of tool-tip). But, I mouse over on ABC and returns me DEFA. So, I think there must be something related to the way I have appended these two arcs.
EDIT 1
Created the JSFidle, with my dataset and it's not showing anything
http://jsfiddle.net/pcr3ogt4/

Drupal 7 + D3 - embed circle-pack visualization in a page

I'm trying to embed a D3 circle-pack in a Drupal 7 basic, page but it's appearing in the wrong section of the page - below the footer. See here: http://www.thedata.co/circlepackzoom10. I've tried different themes but make no difference.
I'm using the php include method with the PHP filter: <?php include './sites/thedata.co/files/html/circle_packZoom-10.html'; .
I'm able to embed a D3 chart in the node body so seems it can be done in some cases.
Using Chrome, JS console defines content area as: <div class="field-item odd" property="content:encoded">. Doesn't work with Firefox either.
Here's the complete code: http://jsfiddle.net/cajmcmahon/9a5xaovv/
var pack = d3.layout.pack()
.size([r, r])
.children(function(d) {
return d.values; // accessor for children
})
.value(function(d) {
return d.values; // accessor for values
});
//Declare SVG attributes
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", w)
.append("g")
.attr("transform", "translate(" + (w - r) / 2 + "," + (w - r) / 2 + ")");
Anyone any idea how to place it within the main content area of the node? Thanks.
Select the element where you want to put the chart instead of selecting "body" in d3.select("body").append("svg").
something like:
d3.select('#divID').append('svg') ... etc
As this method just appends it to the body element of the website.

Positioning multiple SVG elements in div HTML

I have 2 charts which looks like this:
I need to display the tag-cloud next to the bar-chart. I am using D3 to display these charts and my HTML division looks like this (svg1 displays bar-chart and svg2 displays tag-cloud):
I would appreciate any help. Here is the DEMO. In the demonstration I want circle to appear next to the bar chart.
Thanks!
Why not just float the elements like here on your demo
d3.select("body").append("div")
.attr("id","area1")
.style("width", 500)
.style("float","left");
And
function wordCloud(){
var wC = {};
d3.select("body")
.append("svg")
.style("float","left")
.attr("width", 50)
.attr("height", 50).append("circle").attr("cx", 25).attr("cy", 25).attr("r", 25).style("fill", "purple");
return wC;
}

D3 Changing the width attribute of an object depending on it's child

I am trying to change the width of the entire frame of something in D3 after it has already been created, depending on the width of some text inside of it (so that it doesn't get cut off). However, I'm having trouble returning the width of the text element.
The body looks like so:
this.body = d3.select("#" + this.placeholderName)
.append("svg:svg")
.attr("class", "gauge")
.attr("width", this.config.size)
.attr("height", this.config.size + 150);
The text inside the body looks like this:
this.body.append("svg:text")
.attr("x", this.config.cx - 60)
.attr("y", this.config.cy / 2 + fontSize / 2)
.attr("dy", fontSize / 2)
.attr("text-anchor", "start")
.text(this.config.label)
.style("font-size", fontSize + "px")
.style("fill", "#333")
.style("stroke-width", "0px")
Finally, I change the width of the frame with this. What would I need to put as the second argument here?
this.body.attr("width", <WIDTH OF TEXT ABOVE>);
Thanks
First, when you create your text element, give it a class or id so you can easily select it later.
Lets say you give it .attr('class', 'bodyText').
Then you can get its length using SVGTextContentElement.getComputedTextLength()
You can get this value for your text node by using selection.node() to grab the actual text element from a d3 selection, and then calling the method on the node. Like this:
var textLength = d3.select('.bodyText').node().getComputedTextLength();
Then you can use this value when setting your width property.
Have you tried this:
this.body.append("svg:text").attr("id","textValue")
and
var w=d3.select("#textValue").attr("width");
Now
this.body.attr("width", w);
I think it should work.

Categories

Resources