d3 - Position of text on a map - javascript

im am trying to put labels on the administrative regions on this map of Ukraine. Chrome developer tools tells me that they exist with the right label and coordinates. Nonetheless they dont appear on the map.
The map
The code ive used:
d3.json('ukraine.geojson', function(data){
var group = canvas.selectAll('g')
.data(data.features)
.enter()
.append('g')
var projection = d3.geo.mercator().scale([2400]).translate([-900,2650]);
var path = d3.geo.path().projection(projection);
var areas = group.append('path')
.attr('d',path)
.attr('class','area')
.attr('fill','steelblue');
group.append('text')
.attr('x', function(d){ return path.centroid(d)[0];})
.attr('y', function(d){ return path.centroid(d)[1];})
.attr('text',function(d){return d.properties.NAME_1;})
.style("font-size","14px");
Does anybody know, why they dont appear on the map ?

If you change your output from:
<text x="414.512507938402" y="272.3826318168442" text="Cherkasy" style="font-size: 14px;"></text>
to:
<text x="414.512507938402" y="272.3826318168442" style="font-size: 14px;">Cherkasy</text>
You will see the text appear.
If you look at your code you can drop the .attr('text',function(d){return d.properties.NAME_1;}) portion and use something like .text(function(d) {return d.properties.NAME_1;});.

Related

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/

How to update/overwrite map and legend content using d3

I've put together a choropleth map using d3, helped by examples written by Mike Bostock. I'm new to d3 (and HTML, JavaScript, CSS to be honest).
I've got as far as creating the map and the legend, and being able to switch between different data sets. The map and source code can be viewed here on bl.ocks.org
Glasgow Index of Deprivation 2012
The problem I'm having now is working out how to replace the map and legend content when switching between the different datasets. As you can see at the moment, when a different dataset is selected, the content is simply added on top of the existing content.
I've tried following the advice given by Stephen Spann in this answer, and the code he provided in a working fiddle. But to no avail.
As I understand, I should add the g append to the svg variable in the beginning like so...
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g");
Then select it when updating like so...
var appending = svg.selectAll("g")
.attr("class", "S12000046_geo")
.data(topojson.feature(glasgowdep, glasgowdep.objects.S12000046_geo).features);
// add new elements
appending.enter().append("path");
// update existing elements
appending.style("fill",
function (d) {
return color(choro[d.id]);
})
.style("stroke", "#cfcfcf")
.attr("d", path)
// rollover functionality to display tool tips
.on("mouseover", function (d) {
tip.show(d)
d3.select(this)
.transition().duration(200)
.style("fill", "red");
})
.on("mouseout", function () {
tip.hide()
d3.select(this)
.transition().duration(200)
.style("fill",
function (d) {
return color(choro[d.id]);
});
})
// build the map legend
var legend = d3.select('#legend')
.append('ul')
.attr('class', 'list-inline');
var keys = legend.selectAll('li.key')
.data(color.range());
var legend_items = ["Low", "", "", "", "", "", "", "", "High"];
keys.enter().append('li')
.attr('class', 'key')
.style('border-top-color', String)
.text(function (d, i) {
return legend_items[i];
});
// remove old elements
appending.exit().remove();
A solution could be the following: at your code in http://bl.ocks.org/niallmackenzie/8a763afd14e195154e63 try adding the following line just before you build the map legend (line 220 in index.html):
d3.select('#legend').selectAll('ul').remove();
Every time you update your data, you empty first the #legend.
Thanks to the advice from Lars and the solution proposed by nipro, the following works. By adding the following code just above the section that builds the legend, the legend is emptied first before it gets updated:
d3.select('#legend')
.selectAll('ul')
.remove();
// build the map legend
var legend = d3.select('#legend')
...
And by doing the same for the main map, we can first empty the map before updating it:
d3.select("g")
.selectAll("path")
.remove();
// build the choropleth map
var appending = svg.selectAll("g")
...
The full working updated code can been seen on bl.ocks.org here.

Displaying labels on horizontal chart with d3.js

I'm creating a simple chart with d3.js using the following code. Now suppose the var data is properly formatted (ex. "[20,15,15,40,10]"), the horizontal chart displays properly but I'd like to add some labels on the left and I'm a bit overwhelmed by d3.js . I was trying to insert an extra div containing the label before every other div representing and showing the data, but I can't understand how or if that's the proper way.
The result should look something like:
Label 1 |=============================40%==|
Label 2 |=================30%==|
Label 3 |======15%==|
and so on. The bars display just fine, but how do I add the labels on the left ? This is the piece of script that displays the bars (given a div with id 'chart' and the proper css).
chart = d3.select('#chart')
.append('div').attr('class', 'chart')
.selectAll('div')
.data(data).enter()
.append('div')
.transition().ease('elastic')
.style('width', function(d) { return d + '%'; })
.text(function(d) { return d + '%'; });
You'll find a working example here . So, how do I add labels on the left of every bar so that the user knows what every bar represents ?
I think your idea of appending a <div> containing a label before each bar is reasonable. To do so, you first need to append a <div> for each bar, then append a <div> containing the label, and finally append a <div> containing the bars. I've updated your example with a JSFiddle here, with the Javascript code below (some changes were also required to the CSS):
// Add the div containing the whole chart
var chart = d3.select('#chart').append('div').attr('class', 'chart');
// Add one div per bar which will group together both labels and bars
var g = chart.selectAll('div')
.data([52,15,9,3,3,2,2,2,1,1]).enter()
.append('div')
// Add the labels
g.append("div")
.style("display", "inline")
.text(function(d, i){ return "Label" + i; });
// Add the bars
var bars = g.append("div")
.attr("class", "rect")
.text(function(d) { return d + '%'; });
// Execute the transition to show the bars
bars.transition()
.ease('elastic')
.style('width', function(d) { return d + '%'; })
Screenshot of JSFiddle output

Filling nodes with images in D3

So I'm trying to fill in the node circles with an image from a url. The url points to cloudfront. I looked at this question
Adding an image within a circle object in d3 javascript?
However, all I get are blank images. I double checked and made sure that the images exist, yet they won't fill the image. Here is the d3 node.
var node = g.selectAll(".node");
node = node.data(nodes, function(d) {return d.id;});
node.enter().append("circle")
.attr("class", "node").attr("r", 10)
.attr("id", function(d,i) {
return "node"+d.id;
})
.attr("fill", function(d) {
if (d.mediapath !== undefined) {
var url = cloudfront + d.mediapath;
url = "https://github.com/favicon.ico"
return "url("+url+")";
return "url(#bg"+d.id+")";
}
return color(d.group); })
.on("mouseover", function(d) {
d3.select("p").text(d.handle);
})
.on("click", clicked);
Not all images have an image associated with them. If they do, they have a mediapath attribute, and we use it. Otherwise, we color it. The coloring of the nodes with no images is fine. However, all I get is a white fill instead of an image for the ones that need an image.
Any help is appreciated. Thanks.

Adding a span icon to a D3 bar chart

I'm working on a fairly basic bar chart where I'm trying to have a span icon that appears, anchored at the start of each bar. Which icon appears is dependent on the class of the bar. For example, if the bar is blue, I want a certain icon vs. if the bar is red.
I've appended and added the span which shows up in the console, but is not actually appearing any where in the chart.
I have the icons stored as spans in my css, one for each version of the value name that gets plugged in.
I've tried a variety of selections, ordering, etc. But can't get it to stick.
var bars = svg.selectAll('.bar')
.data(data)
.enter().append('g')
.attr('class', 'bar');
bars.append('rect')
var icons = svg.selectAll('rect')
.data(data)
.enter().append("span")
.attr("class", function(d, i) {
return "icon-" + d.value + "-right";
})
.attr('dx', -6)
.attr('dy', (bar_height / 2) +5)
.attr('text-anchor', 'start');
You should use foreignObject element to insert HTML into SVG.
Like so:
var icons = svg.selectAll('foreignObject').data(data);
icons.enter().append("foreignObject")
.attr("class", function(d) { return "icon-" + d.value + "-right"; })
.append("xhtml:body")
.append("xhtml:span");
Also you can use text element to add icons to the SVG:
var icons = svg.selectAll('text').data(data);
icons.enter().append("text")
.html("&#xf00d") // utf-8 character for the icon

Categories

Resources