Adding tooltip in d3.js map - javascript

I'm trying to add a tooltip showing the name of districts when you hover over it in the map in d3.js. The input is a topojson file and I've been able to successfully generate the map with district boundaries and highlight the currently selected district.
For the tooltip I tried doing something similar to this, but nothing happens at all. The code I've used is given below. The tooltip code is towards the end.
var width = 960,
height = 600;
var projection = d3.geo.albers()
.center([87, 28])
.rotate([-85, 0])
.parallels([27, 32]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect")
.attr("width", width)
.attr("height", height);
var g = svg.append("g");
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 1e-6);
d3.json("data/nepal3.json", function(error, npl) {
var districts = topojson.feature(npl, npl.objects.nepal_districts);
projection
.scale(1)
.translate([0, 0]);
var b = path.bounds(districts),
s = .95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height),
t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];
projection
.scale(s)
.translate(t);
g.selectAll(".nepal_districts")
.data(districts.features)
.enter().append("path")
.attr("class", function(d) { return "nepal_districts " + d.id; })
.attr("d", path)
.on("mouseover", function(d,i) {
d3.select(this.parentNode.appendChild(this)).transition().duration(300)
.style({'stroke-width':2,'stroke':'#333333','stroke-linejoin':'round','cursor':'pointer','fill':'#b9270b'});
})
.on("mouseout", function(d,i) {
d3.select(this.parentNode.appendChild(this)).transition().duration(100)
.style({'stroke-width':2,'stroke':'#FFFFFF','stroke-linejoin':'round','fill':'#3d71b6'});
});
g.append("path")
.datum(topojson.mesh(npl, npl.objects.nepal_districts, function(a, b) { return a !== b;}))
.attr("d", path)
.attr("class", "district-boundary");
/* Tooltip */
g.selectAll(".nepal_districts")
.data(districts.features)
.enter().append("text")
.append("svg:rect")
.attr("width", 140)
.attr("height", 140)
.text(function(d) { return d.properties.name; })
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseout", mouseout);
function mouseover() {
div.transition()
.duration(300)
.style("opacity", 1);
}
function mousemove() {
div
.text(d3.event.pageX + ", " + d3.event.pageY)
.style("left", (d3.event.pageX - 34) + "px")
.style("top", (d3.event.pageY - 12) + "px");
}
function mouseout() {
div.transition()
.duration(100)
.style("opacity", 1e-6);
}
});
The CSS is
div.tooltip {
position: absolute;
text-align: center;
width: 60px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: #4c4c4c;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
The code I added for "Tooltip" does nothing at all. What am I doing wrong here?
The topojson file has this format. I wanted to get the "name" property to show up in the Tooltip.
{
"type": "Topology",
"objects": {
"nepal_districts": {
"type": "GeometryCollection",
"geometries": [
{
"type": "Polygon",
"id": 0,
"properties": {
"name": "HUMLA"
},
"arcs": [
[
0,
1,
2,
3
]
]
},

Had a similar problem where I ended up adding absolute positioned tooltip to body element, and modyfying its placement according to mouse position.
Add to directive:
function addTooltip(accessor) {
return function(selection) {
var tooltipDiv;
var bodyNode = d3.select('body').node();
selection.on("mouseover", function(topoData, countryIndex) {
if (!accessor(topoData, countryIndex)) {
return;
}
// Clean up lost tooltips
d3.select('body').selectAll('div.tooltipmap').remove();
formatValue(topoData, countryIndex);
tooltipDiv = d3.select('body').append('div').attr('class', 'tooltipmap');
var absoluteMousePos = d3.mouse(bodyNode);
tooltipDiv.style('left', (absoluteMousePos[0] + 10) + 'px')
.style('top', (absoluteMousePos[1] - 15) + 'px')
.style('opacity', 1)
.style('z-index', 1070);
accessor(topoData, countryIndex) || '';
})
.on('mousemove', function(topoData, countryIndex) {
if (!accessor(topoData, countryIndex)) {
return;
}
var absoluteMousePos = d3.mouse(bodyNode);
tooltipDiv.style('left', (absoluteMousePos[0] + 10) + 'px')
.style('top', (absoluteMousePos[1] - 15) + 'px');
var tooltipText = accessor(topoData, countryIndex) || '';
tooltipDiv.html(tooltipText);
})
.on("mouseout", function(topoData, countryIndex) {
if (!accessor(topoData, countryIndex)) {
return;
}
tooltipDiv.remove();
});
};
.tooltipmap{
background-color: #000000;
margin: 10px;
height: 50px;
width: 150px;
padding-left: 10px;
padding-top: 10px;
border-radius: 5px;
overflow: hidden;
display: block;
color: #FFFFFF;
font-size: 12px;
position: absolute;
opacity: 1;
h6{
margin: 0;
padding: 0;
}
p{
color: #FFFFFF;
}
}
Hope it helps!

Related

How do I properly sort the data for my d3 bubble map so that smaller bubbles show up on top of larger bubbles?

I'm making a bubble map similar to this one: https://observablehq.com/#d3/bubble-map
Everything is working except that my smaller bubbles are not always showing on top of the larger ones. I can't see why, as I've sorted the data before drawing the circles. Can anyone see what I'm doing wrong?
Here is a plunker:
https://plnkr.co/edit/JKWeQKkhN2TQwvNZ?open=lib%2Fscript.js
Code is below. The other files are too large for stack overflow but can be accessed via the Plunker.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.states {
fill: #d3d3d3;
stroke: #ffffff;
stroke-linejoin: round;
}
div.tooltip {
position: absolute;
left: 75px;
text-align: center;
height: 12px;
padding: 8px;
font-size: 13px;
font-family: 'Proxima-Nova', sans-serif;
background: #FFFFFF;
border: 1px solid #989898;
pointer-events: none;
}
.block {
width: 18%;
height: 15px;
display: inline-block;
}
</style>
<body>
<div class="g-chart"></div>
</body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/geo-albers-usa-territories#0.1.0/dist/geo-albers-usa-territories.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js"></script>
<script>
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var margin = { top: 10, left: 10, bottom: 10, right: 10 },
width = window.outerWidth,
width = width - margin.left - margin.right,
mapRatio = .5,
height = width * mapRatio;
const epsilon = 1e-6;
var projection = geoAlbersUsaTerritories.geoAlbersUsaTerritories()
.scale(width)
.translate([width / 2, height / 2]);
var path = d3.geoPath()
.projection(projection);
var map = d3.select(".g-chart").append("svg")
.style('height', height + 'px')
.style('width', width + 'px')
.call(d3.zoom().on("zoom", function () {
map.attr("transform", d3.event.transform)
d3.selectAll()
}))
.append("g");
queue()
.defer(d3.json, "us.json")
.defer(d3.csv, "test.csv")
.await(ready);
d3.selection.prototype.moveToFront = function () {
return this.each(function () {
this.parentNode.appendChild(this);
});
};
d3.selection.prototype.moveToBack = function () {
return this.each(function () {
var firstChild = this.parentNode.firstChild;
if (firstChild) {
this.parentNode.insertBefore(this, firstChild);
}
});
};
function ready(error, us, data) {
if (error) throw error;
data.forEach(function (d) {
d.amount = +d.amount;
})
map.append("g")
.attr("class", "states")
.selectAll("path")
.data(topojson.feature(us, us.objects.states).features)
.enter().append("path")
.attr("d", path);
// sort by descending size so that the smaller circles are drawn on top - not working
map.append('g')
.attr('class', 'facility')
.selectAll("circle")
.data(data.sort((a, b) => +b.amount - +a.amount))
.enter()
.append("circle")
.attr("cx", function (d) {
return projection([d.longitude, d.latitude])[0];
})
.attr("cy", function (d) {
return projection([d.longitude, d.latitude])[1];
})
.attr('r', function (d) {
if (d.amount <= 25) { return 3 }
else if (d.amount > 25 && d.amount <= 50) { return 5 }
else if (d.amount > 50 && d.amount <= 75) { return 7 }
else { return 9 }
})
.style("fill", "#EF4136")
.style("stroke", "#BE2C2D")
.style("stroke-width", 1)
.style("opacity", 0.5)
.on("mouseover", function (d) {
var sel = d3.select(this);
sel.moveToFront();
d3.select(this).transition().duration(0)
.style("opacity", 0.8)
.style("stroke", "#FFFFFF")
div.transition().duration(0)
.style("opacity", .85)
div.text(d.amount)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 30) + "px");
})
.on("mouseout", function () {
var sel = d3.select(this);
d3.select(this)
.transition().duration(0)
.style("opacity", 0.5)
.style("stroke", "#BE2C2D")
div.transition().duration(0)
.style("opacity", 0);
});
//RESPONSIVENESS
d3.select(window).on('resize', resize);
function resize() {
var w = d3.select(".g-chart").node().clientWidth;
width = w - margin.left - margin.right;
height = width * mapRatio;
var newProjection = d3.geoAlbersUsa()
.scale(width)
.translate([width / 2, height / 2]);
path = d3.geoPath()
.projection(newProjection);
map.selectAll("circle")
.attr("cx", function (d) {
return newProjection([d.longitude, d.latitude])[0];
})
.attr("cy", function (d) {
return newProjection([d.longitude, d.latitude])[1];
})
.attr("r", 5)
map
.style('width', width + 'px')
.style('height', height + 'px');
map.selectAll("path").attr('d', path);
}
}
</script>
I would suggest you to split your data to a couple separate datasets grouped by size and create distinct group (g element) for each one. This will also fix issues with circles highlighting.
I slightly updated your plunker to make it work as described (check the lines 91-167) https://plnkr.co/edit/rayo5IZQrBqfqBWR?open=lib%2Fscript.js&preview
Also check the raise and lower methods. They might be a good replacement for your moveToFront and moveToBack methods.
https://riptutorial.com/d3-js/example/18029/svg--the-drawing-order

Add mouse hover containing specific data to d3.js tree map

I put together the following treemap using d3.js. It's the top 20 states in terms of voter turnout. Link
I'm unsure how to add the 'values' into the hover. Ideally, it should show the unique value for each state. I'm able to do it for one (California in the example), but I'm not sure how to make it flexible so that it generates other values for different states.
let margin = {
top: 0,
right: 0,
bottom: 0,
left: 0
},
width = 1100 - margin.left - margin.right,
height = 900 - margin.top - margin.bottom;
// append the svg object to the body of the page
let svg = d3
.select('#treemap')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// Read data
d3.csv('https://raw.githubusercontent.com/oihamza/Interactive-Data-Vis-Fall2020/master/Project%201/stateVoterTurnout.csv', function(data) {
// stratify the data: reformatting for d3.js
var root = d3
.stratify()
.id(function(d) {
return d.name;
}) // Name of the entity (column name is name in csv)
.parentId(function(d) {
return d.parent;
})(
// Name of the parent (column name is parent in csv)
data
);
// data is an object
console.log(data);
let values = data[1].value;
let tooltip = d3
.select('body')
.append('div')
.style('position', 'absolute')
.style('z-index', '10')
.style('visibility', 'hidden')
.style('background-color', 'white')
.style('border', 'solid')
.style('border-width', '2px')
.style('border-radius', '5px')
.style('padding', '5px')
.text(`${values} voters`);
root.sum(function(d) {
return +d.value;
}); // Compute the numeric value for each entity
// Then d3.treemap computes the position of each element of the hierarchy
// The coordinates are added to the root object above
d3.treemap().size([width, height]).padding(3)(root);
console.log(root.leaves());
// use this information to add rectangles:
svg
.selectAll('rect')
.data(root.leaves())
.enter()
.append('rect')
.attr('x', function(d) {
return d.x0;
})
.attr('y', function(d) {
return d.y0;
})
.attr('width', function(d) {
return d.x1 - d.x0;
})
.attr('height', function(d) {
return d.y1 - d.y0;
})
.style('stroke', 'black')
.style('fill', '#945f04')
.on('mouseover', function() {
return tooltip.style('visibility', 'visible');
})
.on('mousemove', function() {
return tooltip
.style('top', d3.event.pageY - 10 + 'px')
.style('left', d3.event.pageX + 10 + 'px');
})
.on('mouseout', function() {
return tooltip.style('visibility', 'hidden');
});
// and to add the text labels
svg
.selectAll('text')
.data(root.leaves())
.enter()
.append('text')
.attr('x', function(d) {
return d.x0 + 10;
}) // +10 to adjust position (more right)
.attr('y', function(d) {
return d.y0 + 20;
}) // +20 to adjust position (lower)
.text(function(d) {
return d.data.name;
})
.attr('font-size', '15px')
.attr('fill', 'white');
});
body,
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: "Montserrat", sans-serif
}
.styling {
transform-origin: center center;
align-content: center;
position: relative;
}
.tooltip {
position: relative;
display: inline-block;
/* border-bottom: 1px dotted black; */
}
.tooltip .tooltiptext {
visibility: hidden;
width: 180px;
background-color: black;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 0;
/* Position the tooltip */
position: absolute;
z-index: 1;
}
.tooltip:hover .tooltiptext {
visibility: visible;
}
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Montserrat">
<span class="styling"><div id="treemap"></div></span>
<!-- Loading this version of d3 -->
<script src="https://d3js.org/d3.v4.js"></script>
When you mouseover or mousemove the rectangle, you can find it's assigned datum with the first argument (often called d) - just like you do when you set attributes with .attr() or styles with .style().
You can set the .text() of the tooltip dynamically, using this d:
let margin = {
top: 0,
right: 0,
bottom: 0,
left: 0
},
width = 1100 - margin.left - margin.right,
height = 900 - margin.top - margin.bottom;
// append the svg object to the body of the page
let svg = d3
.select('#treemap')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// Read data
d3.csv('https://raw.githubusercontent.com/oihamza/Interactive-Data-Vis-Fall2020/master/Project%201/stateVoterTurnout.csv', function(data) {
// stratify the data: reformatting for d3.js
var root = d3
.stratify()
.id(function(d) {
return d.name;
}) // Name of the entity (column name is name in csv)
.parentId(function(d) {
return d.parent;
})(
// Name of the parent (column name is parent in csv)
data
);
let tooltip = d3
.select('body')
.append('div')
.style('position', 'absolute')
.style('z-index', '10')
.style('visibility', 'hidden')
.style('background-color', 'white')
.style('border', 'solid')
.style('border-width', '2px')
.style('border-radius', '5px')
.style('padding', '5px');
root.sum(function(d) {
return +d.value;
}); // Compute the numeric value for each entity
// Then d3.treemap computes the position of each element of the hierarchy
// The coordinates are added to the root object above
d3.treemap().size([width, height]).padding(3)(root);
// use this information to add rectangles:
svg
.selectAll('rect')
.data(root.leaves())
.enter()
.append('rect')
.attr('x', function(d) {
return d.x0;
})
.attr('y', function(d) {
return d.y0;
})
.attr('width', function(d) {
return d.x1 - d.x0;
})
.attr('height', function(d) {
return d.y1 - d.y0;
})
.style('stroke', 'black')
.style('fill', '#945f04')
.on('mouseover', function() {
tooltip.style('visibility', 'visible');
})
.on('mousemove', function(d) {
tooltip
.style('top', d3.event.pageY - 10 + 'px')
.style('left', d3.event.pageX + 10 + 'px')
.text(`${d.data.value} voters`);
})
.on('mouseout', function() {
tooltip.style('visibility', 'hidden');
});
// and to add the text labels
svg
.selectAll('text')
.data(root.leaves())
.enter()
.append('text')
.attr('x', function(d) {
return d.x0 + 10;
}) // +10 to adjust position (more right)
.attr('y', function(d) {
return d.y0 + 20;
}) // +20 to adjust position (lower)
.text(function(d) {
return d.data.name;
})
.attr('font-size', '15px')
.attr('fill', 'white');
});
body,
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: "Montserrat", sans-serif
}
.styling {
transform-origin: center center;
align-content: center;
position: relative;
}
.tooltip {
position: relative;
display: inline-block;
/* border-bottom: 1px dotted black; */
}
.tooltip .tooltiptext {
visibility: hidden;
width: 180px;
background-color: black;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 0;
/* Position the tooltip */
position: absolute;
z-index: 1;
}
.tooltip:hover .tooltiptext {
visibility: visible;
}
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Montserrat">
<span class="styling"><div id="treemap"></div></span>
<!-- Loading this version of d3 -->
<script src="https://d3js.org/d3.v4.js"></script>
It's not necessary to return anything inside these .on() functions.

D3 Family tree zoom implementation doesn't work

Basically i wish to implement a zoom behavior to my family tree like in this example here http://bl.ocks.org/sgruhier/1d692762f8328a2c9957
I've added the call function to where im creating the svg canvas but it does nothing, am i missing something ? Thanks in advance
Here's the code:
<!DOCTYPE html>
<html>
<head>
<title>Fam tree</title>
<script src="http://d3js.org/d3.v2.js"></script>
<style>
body, html {
width: 100%;
height: 100%;
margin: 0;
}
svg {
position: absolute;
top: 0;
left: 0;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}
div.tooltip {
position: absolute;
text-align: center;
width: 200px;
height: 30px;
padding: 8px;
font: 16px sans-serif;
font-weight: bold;
background: #ffff99;
border: solid 1px #aaa;
border-radius: 8px;
pointer-events: none;
background-color: rgba(0,128,128,0.5);
overflow: hidden;
transition: .5s ease
}
</style>
</head>
<body>
<div id="viz"></div>
<script type="text/javascript">
//JSON
var treeData = {"name" : "Steve", "lname" : "Forester", "children" : [
{"name" : "Anna", "lname" : "Woods" },
{"name" : "Boris", "lname" : "Vladimirov" },
{"name" : "Clint", "lname" : "Eastwood", "children": [
{"name" : "Sheldon", "lname" : "Morris" },
{"name" : "Bert", "lname" : "Jefferson" }
]}
]};
// Create a svg canvas
var vis = d3.select("#viz")
.append("svg")
.attr("width", "1500")
.attr("height", "1000")
.call(d3.behavior.zoom().on("zoom", function () {
svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")")
}))
.append("g")
.attr("transform", "translate(100, 100)"); // shift everything to the right
// Add tooltip div
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 1e-6);
// Create a tree "canvas"
var tree = d3.layout.tree()
.size([1200, 500]);
var diagonal = d3.svg.diagonal();
// Preparing the data for the tree layout, convert data into an array of nodes
var nodes = tree.nodes(treeData);
// Create an array with all the links
var links = tree.links(nodes);
var link = vis.selectAll("pathlink")
.data(links)
.enter().append("svg:path")
.attr("class", "link")
.attr("d", diagonal)
var node = vis.selectAll("g.node")
.data(nodes)
.enter().append("svg:g")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
// Hexagon img
node.append("svg:image")
.attr("xlink:href", "prva.png")
.attr("width", 100)
.attr("height", 100)
.attr("x", -50)
.attr("y", -30)
.on("mouseover", mouseover)
.on("mousemove", function(d){mousemove(d);})
.on("mouseout", mouseout)
.attr("fill","red")
.attr("r", 5.5);
function mouseover() {
div.transition()
.duration(300)
.style("opacity", 1);
d3.select(this).attr("xlink:href", "vtora.png");
}
function mousemove(d) {
div
.text(" Name:" + ' ' + d.name + ' ' + d.lname)
.style("left", (d3.event.pageX ) + "px")
.style("top", (d3.event.pageY) + "px");
}
function mouseout() {
div.transition()
.duration(300)
.style("opacity", 1e-6);
d3.select(this).attr("xlink:href", "prva.png");
}
</script>
</body>
</html>

D3 text not centered on nodes?

I have a jsfiddle with a scatter plot, on which the corresponding text will not align with the dots.
I believe that this chunk should be displaying the names at the same coordinates as the circles:
.attr("x", function(d){return timeScale(d[1]);})
.attr("y", function(d){return rankScale(d[0]);})
This is the same code I used to place the circles.
Do I misunderstand something?
Since you are (for whatever reason) translating the circles, you should apply the same translate to the texts:
textSelection.attr("transform", "translate("+transRight+","+transDown+")")
Alternatively, don't translate the circles.
Here is your updated fiddle: https://jsfiddle.net/yv3ts1fw/
And here a Stack snippet with the same code:
function call() {
$.ajax({
url: "https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/cyclist-data.json",
data: {
format: 'json'
},
complete: function(xhr, textStatus) {
//console.log(xhr.status);
//console.log(textStatus);
},
error: function() {
$('#container').html('<p>An error has occurred</p>');
},
success: (data) => {
pushArray(data);
},
type: 'GET'
});
}
call();
var timeArray = [];
function pushArray(data) {
data = JSON.parse(data);
for (var i = 0; i < data.length; i++) {
var tempArray = [];
tempArray.push(i);
var hms = data[i].Time;
var a = hms.split(':');
var seconds = ((+a[0]) * 60 + (+a[1]));
tempArray.push(seconds);
timeArray.push(tempArray);
}
var w = $(window).width();
var h = $(window).height();
var margin = {
top: h / 5,
left: w / 10
};
h = h * .8;
w = w * .9;
var svgW = w * .8;
var svgH = h * .8;
w = w * .6;
h = h * .6;
var rankScale = d3.scaleLinear()
.domain([1, 35])
.range([0, h]);
var axisRankScale = d3.scaleLinear()
.domain([35, 1])
.range([h, 0]);
var timeScale = d3.scaleLinear()
.domain([2200, 2400])
.range([0, w]);
var xAxis = d3.axisBottom()
.scale(timeScale);
var yAxis = d3.axisLeft()
.scale(axisRankScale);
var toolTip = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var transDown = 20;
var transRight = 100;
//h = 700;
// h=h*1.2;
//w=w*1.2;
//w= 300;
var theBody = d3.select("#container")
.append("svg")
.attr("height", svgH * 1.4)
.attr("width", w * 1.6)
.attr("transform", "translate(50, 50)")
.append("g")
.attr("transform", "translate(20, 20)");
theBody.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 50)
.attr("x", -(h / 2))
//.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Rank");
theBody.append("text")
.attr("y", h + margin.top)
.attr("x", w / 1.5)
.style("text-anchor", "middle")
.text("Seconds");
theBody.selectAll("foo")
.data(timeArray)
.enter()
.append("text")
.text(function(d) {
var index = d[0];
var _this = data[index];
return _this.Name;
})
.attr("x", function(d) {
return timeScale((d[1]));
})
.attr("y", function(d) {
return rankScale((d[0]));
})
.attr("font-size", "10px")
.attr("transform", "translate(" + (transRight + 16) + "," + transDown + ")");
theBody.selectAll("circle")
.data(timeArray)
.enter()
.append("circle")
.attr("cx", function(d) {
return timeScale(d[1]);
})
.attr("cy", function(d) {
return rankScale((d[0]));
})
.attr("r", 10)
.attr("fill", "green")
.attr("stroke", "black")
.attr("transform", "translate(" + transRight + "," + transDown + ")")
.on("mouseout", function() {
toolTip.style("opacity", 0.0);
})
.on("mouseover", function(d) {
var index = d[0];
var _this = data[index];
var time = _this.Time,
name = _this.Name,
year = _this.Year,
dope = _this.Doping;
if (dope === "") {
dope = "No doping allegations!";
}
toolTip.html(name + "<br>" + year + "<br>" + time + " <br>---<br>" + dope)
.style("opacity", 1)
.style("left", ((d3.event.pageX) + 40) + "px")
.style("top", ((d3.event.pageY) + 0) + "px");
});
}; // end pushArray main
svg {
border: solid;
}
#container {
border: solid green;
width: 100vw;
height: 100vh;
}
div.tooltip {
display: flex;
position: absolute;
justify-content: center;
align-items: center;
text-align: center;
width: 12vw;
height: 25vh;
padding: 2px;
font: 12px sans-serif;
font-weight: bold;
background: rgb(255, 82, 80);
border: 0px;
border-radius: 8px;
pointer-events: none;
opacity: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="container">
<div>tour de france
<div></div>
PS: in your text selection, don't do theBody.selectAll("text"), because you already have texts in that SVG. Instead of that, select something that doesn't exist, like theBody.selectAll("foo").

Legend to be aligned after pie chart but getting aligned before

Hello I want to right align my legend after the pie chart. But with my codes they are getting aligned to left before pie chart. What CSS changes should I make to do this.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Testing Pie Chart</title>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
<style type="text/css">
/* #container {
margin: 5%;
height: 50%;
width: 50%;
}*/
#chart {
position: absolute;
background-color: #eee;
/* height: 50%;
width: 50%; */
}
#chart legend{
position: absolute;
}
.tooltip {
background: #eee;
box-shadow: 0 0 5px #999999;
color: #900C3F;
display: inline-block;
font-size: 12px;
position: absolute;
text-align: center;
width: 10%;
z-index: 10;
opacity: 1;
}
rect {
stroke-width: 2;
}
path {
stroke: #ffffff;
stroke-width: 0.5;
}
div.tooltip {
position: absolute;
z-index: 999;
padding: 10px;
background: #f4f4f4;
border: 0px;
border-radius: 3px;
pointer-events: none;
font-size: 11px;
color: #080808;
line-height: 16px;
border: 1px solid #d4d4d4;
}
</style>
</head>
<body>
<div id="container">
<svg id="chart" viewBox="0 0 960 500" perserveAspectRatio="xMinYMid">
<div id="toolTip" class="tooltip" style="opacity: 0;"></div>
<script type="text/javascript">
var div = d3.select("#toolTip");
var data = [
["192.168.12.1", 20],
["76.09.45.34", 40],
["34.91.23.76", 80],
["192.168.19.32", 16],
["192.168.10.89", 50],
["192.178.34.07", 18],
["192.168.12.98", 30]];
var data = data.map(function(d) {
return {
IP: d[0],
count: d[1]
};
});
var width = 500,
height = 300;
var radius = Math.min(width, height) / 2 - 50;
var legendRectSize = 18,
legendSpacing = 4;
var color = d3.scale.category20b();
var arc = d3.svg.arc()
.outerRadius(radius);
var arcOver = d3.svg.arc()
.outerRadius(radius + 5);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.count; });
var labelArc = d3.svg.arc()
.outerRadius(radius - 40)
.innerRadius(radius - 40);
var svg = d3.select("#chart").append("svg")
.datum(data)
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width/2 + "," + height/2 + ")");
var arcs = svg.selectAll(".arc")
.data(pie)
.enter().append("g")
.attr("class", "arc");
var arcs2 = svg.selectAll(".arc2")
.data(pie)
.enter().append("g")
.attr("class", "arc2");
arcs.append("path")
.attr("fill", function(d, i) { return color(i); })
.on("mouseover", function(d) {
var htmlMsg="";
div.transition()
.style("opacity",0.9);
var total = d3.sum(data.map(function(d) {
return d.count;
}));
var percent = Math.round(1000 * d.data.count / total) / 10;
div.html(
"IP :"+ d.data.IP +""+"<br/>"+
"Count : " + d.data.count +"<br/>" +
"Percent: " + percent + '%'+ htmlMsg)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY) + "px");
svg.selectAll("path").sort(function (a, b) {
if (a != d) return -1;
else return 1;
});
var endAngle = d.endAngle + 0.1;
var startAngle = d.startAngle - 0.1;
var arcOver = d3.svg.arc()
.outerRadius(radius + 10).endAngle(endAngle).startAngle(startAngle);
d3.select(this)
.attr("stroke","white")
.transition()
.ease("bounce")
.duration(1000)
.attr("d", arcOver)
.attr("stroke-width",6);
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
d3.select(this).transition()
.attr("d", arc)
.attr("stroke","none");
})
.transition()
.ease("bounce")
.duration(2000)
.attrTween("d", tweenPie);
function tweenPie(b) {
b.innerRadius = 0;
var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
return function(t) { return arc(i(t)); };
}
var k=0;
arcs2.append("text")
.transition()
.ease("elastic")
.duration(2000)
.delay(function (d, i) {
return i * 250;
})
.attr("x","6")
.attr("dy", ".35em")
.text(function(d) { if(d.data.count >0){ k = k+1; return d.data.count;} }) //We can define any value for (d.data.count >__ according to our need. Like if we have smaller values less than 10 then no need to dislay them as they can be overlapped on pie slice margins)// If not needed to display make this comment
.attr("transform", function(d) { if (k >1){return "translate(" + labelArc.centroid(d) + ") rotate(" + angle(d) + ")";} else{return "rotate(-360)";} })
.attr("font-size", "10px");
function type(d) {
d.count = +d.count;
return d;
}
function angle(d) {
var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;
return a > 90 ? a - 180 : a;
}
var legend = d3.select("#chart")
.append("svg")
.attr("class", "legend")
.attr("width", radius+50)
.attr("height", radius * 2)
.selectAll("g")
.data(color.domain())
.enter()
.append("g")
.attr("transform", "translate(" + width * 1/4 + "," + height * 1/4 + ")")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.data(data)
.text(function(d,i) { return d.IP; });
</script>
</svg>
</div>
<script type="text/javascript">
var chart = $("#chart"),
aspect = chart.width() / chart.height(),
container = chart.parent();
$(window).on("resize", function() {
var targetWidth = container.width();
var targetHeight = container.height();
chart.attr("width", targetWidth);
chart.attr("height", Math.round(targetWidth / aspect));
}).trigger("resize");
</script>
</script>
</body>
</html>
Please give some idea how to solve this problem. I am stuck in this code.
Thanks for help in advance.
You need to add x and y attributes to the legend.
legend.attr("x", width - 65)
.attr("y", 25)
Here is the full code and working example for you

Categories