How to display each state name in d3 india map using GeoJSON - d3.js

In my project, I am trying to display India map using d3 and GeoJSON. It works properly, but I am finding difficulties to display each state name on top of the respective state. How to find the centroid of each state.
Please help me to find out, Thanks in advance...,
In the below image, it is displaying at top left corner.
Index.html
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<style>
.state {
fill: none;
stroke: #a9a9a9;
stroke-width: 1;
}
.state:hover {
fill-opacity: 0.5;
}
#tooltip {
position: absolute;
text-align: center;
padding: 20px;
margin: 10px;
font: 12px sans-serif;
background: lightsteelblue;
border: 1px;
border-radius: 2px;
pointer-events: none;
}
#tooltip h4 {
margin: 0;
font-size: 14px;
}
#tooltip {
background: rgba(0, 0, 0, 0.9);
border: 1px solid grey;
border-radius: 5px;
font-size: 12px;
width: auto;
padding: 4px;
color: white;
opacity: 0;
}
#tooltip table {
table-layout: fixed;
}
#tooltip tr td {
padding: 0;
margin: 0;
}
#tooltip tr td:nth-child(1) {
width: 50px;
}
#tooltip tr td:nth-child(2) {
text-align: center;
}
</style>
<body>
<div id="tooltip"></div>
<!-- div to hold tooltip. -->
<div style="height: 600px;" id="statesvg"></div>
<!-- svg to hold the map. -->
<!-- <script src="indiaState.js"></script> -->
<!-- creates india State. -->
<script src="d3.v3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
function tooltipHtml(n, id, d) { /* function to create html content string in tooltip div. */
return "<h4>" + id + "</h4>" +
"<h4>" + n + "</h4>";
}
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
var sampleData = {}; /* Sample random data. */
["AP", "AR", "AS", "BR", "CT", "DL", "GA", "GJ", "HR", "HP", "JK", "JH", "KA", "KL", "MP", "MH", "MN", "ML", "MZ", "NL", "OR", "PB", "RJ", "SK", "TN", "TR", "UP", "UT", "WB"]
.forEach(function(d) {
var low = Math.round(100 * Math.random());
sampleData[d] = { color: getRandomColor()};
});
/* draw states on id #statesvg */
//iStates.draw("#statesvg", sampleData, tooltipHtml);
d3.select(self.frameElement).style("height", "600px");
d3.json("county.json", function(json) {
console.log(json)
var projection = d3.geo.mercator()
.scale(1)
.translate([0, 0]);
var path = d3.geo.path()
.projection(projection);
function mouseOver(d) {
d3.select("#tooltip").transition().duration(200).style("opacity", .9);
d3.select("#tooltip").html(tooltipHtml(d.n, d.id, sampleData[d.id]))
.style("left", (d3.event.layerX) + "px")
.style("top", (d3.event.layerY) + "px");
}
function mouseOut() {
d3.select("#tooltip").transition().duration(500).style("opacity", 0);
}
function Click(d) {
delete d.d
console.log(d)
}
var svg = d3.select("#statesvg")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.append("g");
svg.selectAll(".state")
.data(json)
.enter()
.append("path")
.attr("class", "state")
.attr("d", function(d) {
return d.d;
})
.style("fill", function(d) {
return sampleData[d.id].color;
})
.on("mousemove", mouseOver).on("mouseout", mouseOut).on("click", Click);
svg.selectAll("text")
.data(json)
.enter()
.append("text")
.attr("fill", "black")
.attr("x", function(d) {
return path.centroid(d.d)[0];
})
.attr("y", function(d) {
return path.centroid(d.d)[1];
})
.attr("text-anchor", "middle")
.attr("dy", ".35em")
.text(function(d) {
return d.id;
});
});
</script>
</body>
</html>
I tried using below code, but its giving both cordinates as NaN, how to solve this...
var projection = d3.geo.mercator()
.scale(1)
.translate([0, 0]);
var path = d3.geo.path()
.projection(projection);

Since your json is returning the actual path d element and not topojson, I'd just use getBBox on the path directly. I also simplified your selections to group the path and the text:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script data-require="d3#3.5.17" data-semver="3.5.17" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
<style>
.state {
fill: none;
stroke: #a9a9a9;
stroke-width: 1;
}
.state:hover {
fill-opacity: 0.5;
}
#tooltip {
position: absolute;
text-align: center;
padding: 20px;
margin: 10px;
font: 12px sans-serif;
background: lightsteelblue;
border: 1px;
border-radius: 2px;
pointer-events: none;
}
#tooltip h4 {
margin: 0;
font-size: 14px;
}
#tooltip {
background: rgba(0, 0, 0, 0.9);
border: 1px solid grey;
border-radius: 5px;
font-size: 12px;
width: auto;
padding: 4px;
color: white;
opacity: 0;
}
#tooltip table {
table-layout: fixed;
}
#tooltip tr td {
padding: 0;
margin: 0;
}
#tooltip tr td:nth-child(1) {
width: 50px;
}
#tooltip tr td:nth-child(2) {
text-align: center;
}
</style>
</head>
<body>
<div id="tooltip"></div>
<!-- div to hold tooltip. -->
<div style="height: 600px;" id="statesvg"></div>
<!-- svg to hold the map. -->
<!-- <script src="indiaState.js"></script> -->
<!-- creates india State. -->
<!--<script src="d3.v3.min.js"></script>-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
function tooltipHtml(n, id, d) { /* function to create html content string in tooltip div. */
return "<h4>" + id + "</h4>" +
"<h4>" + n + "</h4>";
}
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
var sampleData = {}; /* Sample random data. */
["AP", "AR", "AS", "BR", "CT", "DL", "GA", "GJ", "HR", "HP", "JK", "JH", "KA", "KL", "MP", "MH", "MN", "ML", "MZ", "NL", "OR", "PB", "RJ", "SK", "TN", "TR", "UP", "UT", "WB"]
.forEach(function(d) {
var low = Math.round(100 * Math.random());
sampleData[d] = { color: getRandomColor()};
});
/* draw states on id #statesvg */
//iStates.draw("#statesvg", sampleData, tooltipHtml);
d3.select(self.frameElement).style("height", "600px");
d3.json("https://api.myjson.com/bins/l36bq", function(json) {
//console.log(json)
var projection = d3.geo.mercator()
.scale(1)
.translate([0, 0]);
var path = d3.geo.path()
.projection(projection);
function mouseOver(d) {
d3.select("#tooltip").transition().duration(200).style("opacity", .9);
d3.select("#tooltip").html(tooltipHtml(d.n, d.id, sampleData[d.id]))
.style("left", (d3.event.layerX) + "px")
.style("top", (d3.event.layerY) + "px");
}
function mouseOut() {
d3.select("#tooltip").transition().duration(500).style("opacity", 0);
}
function Click(d) {
delete d.d
console.log(d)
}
var svg = d3.select("#statesvg")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.append("g");
var eS = svg.selectAll(".state")
.data(json)
.enter()
.append("g");
eS.append("path")
.attr("class", "state")
.attr("d", function(d) {
return d.d;
})
.style("fill", function(d) {
return sampleData[d.id].color;
})
.on("mousemove", mouseOver).on("mouseout", mouseOut).on("click", Click)
eS.append("text")
.attr("fill", "black")
.attr("transform", function(d) {
var bbox = this.previousSibling.getBBox();
return "translate(" + (bbox.x + bbox.width/2) + "," + (bbox.y + bbox.height/2) + ")";
})
.attr("text-anchor", "middle")
.attr("dy", ".35em")
.text(function(d) {
return d.id;
});
});
</script>
</body>
</html>

Related

Pie Chart not rendering in dashboard area using D3 v5

I have been trying to render a simple two value pie chart using D3.JS v5 in the lower right corner to no avail. Can someone please assist me with this - the code can be found here:
Codepen
<body>
<div id = "orgChart"></div>
<div id = "mapChart"></div>
<div id = "pieChart"></div>
<script>
/******************************************************************************
Pie Chart
******************************************************************************/
function makePie() {
var widthPie = (window.innerWidth * 0.3) ,
heightPie = (window.innerHeight * 0.3);
var data = [
{name: "Males", count: 43, percent: 61 }
, {name: "Females", count: 27, percent: 39}
];
var pie = d3.pie().value(d=>d.count).padAngle(0.025)(data);
var arcMkr = d3.arc().innerRadius(20).outerRadius(35)
.cornerRadius(4);
var scC = d3.scaleOrdinal(d3.schemePastel1)
.domain(pie.map(d=>d.index));
var g = d3.select("#pieChart")
.append("g").attr("transform", "translate(widthPie/2, heightPie/2)");
g.selectAll("path.x").data(pie).enter().append("path")
.attr("d", arcMkr)
.attr("fill", d=> scC(d.index)).attr("stroke", "grey");
g.selectAll("text.x" ).data( pie ).enter().append( "text")
.text(d => d.data.name +": " + d.data.percent + "%")
.attr("x", d=>arcMkr.innerRadius(20).centroid(d)[0])
.attr("y", d=>arcMkr.innerRadius(20).centroid(d)[1])
.attr("font-family", "sans-serif").attr( "font-size", 8)
.attr("text-anchor", "middle")
;
}
makePie();
</script>
As #Tom Shanley has indicated in the comments, the reason of why your pie chart is not rendered as expected is because you need to create a SVG first.
Notice that I've also changed some CSS properties of #pieChart for improving the snippet visibility, although it is not necessary for your purposes.
<!DOCTYPE html>
<html lang = 'en'>
<head>
<meta charset = 'utf-8'>
<meta name = 'viewport' content = 'width = device-width, initial-scale = 1.0'>
<meta http-equiv = 'X-UA-Compatible' content = 'ie=edge'>
<meta name = 'author' content = "Tom Bellmer">
<meta name = 'date.created' content="03/05/2020">
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v5.min.js"></script>
<title>Org Chart</title>
<style>
body{
background-color: #faf2e4;
font-family: sans-serif;
font-size: 1.2em;
}
text{font-size: .6em}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1px;
}
.node text {
font: 9px sans-serif;
font-weight: normal;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1px;
}
#orgChart{
position:absolute;
top: 10px;
left: 10px;
width: 65%;
height: 85%;
}
#mapChart{
position:absolute;
top: 10px;
left: 66%;
width: 34%;
height: 50%;
}
#pieChart{
position:absolute;
top: 51%;
left: 66%;
width: 34%;
height: 55%;
background-color: crimson;
}
circle {
/* fill: #FF8533; */
fill: steelblue;
fill-opacity: .8;
stroke: #fff;
}
circle:hover { fill: red;}
div.tooltip {
position: absolute;
text-align: center;
width: 130px;
height: 14px;
padding: 2px;
font: 11px sans-serif;
background: dodgerblue;
color: white;
border: 0px;
pointer-events: none;
}
svg g{
fill: white;
stroke: black;
}
svg text{fill: black;}
</style>
</head>
<body>
<div id = "orgChart"></div>
<div id = "mapChart"></div>
<div id = "pieChart"></div>
<script>
/******************************************************************************
Pie Chart
******************************************************************************/
function makePie() {
var widthPie = (window.innerWidth * 0.5) ,
heightPie = (window.innerHeight * 0.5);
var data = [
{name: "Males", count: 43, percent: 61 },
{name: "Females", count: 27, percent: 39}
];
var pie = d3.pie().value(d=>d.count).padAngle(0.025)(data);
var arcMkr = d3.arc().innerRadius(20).outerRadius(35)
.cornerRadius(4);
var scC = d3.scaleOrdinal(d3.schemePastel1)
.domain(pie.map(d=>d.index));
// Modified ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var svg = d3.select("#pieChart")
.append("svg")
.attr("width", widthPie)
.attr("height", heightPie);
var g = svg.append("g").attr("transform", `translate(${widthPie/2}, ${heightPie/2})`);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
g.selectAll("path.x").data(pie).enter().append("path")
.attr("d", arcMkr)
.attr("fill", d=> scC(d.index)).attr("stroke", "grey");
g.selectAll("text.x" ).data( pie ).enter().append( "text")
.text(d => d.data.name +": " + d.data.percent + "%")
.attr("x", d=>arcMkr.innerRadius(20).centroid(d)[0])
.attr("y", d=>arcMkr.innerRadius(20).centroid(d)[1])
.attr("font-family", "sans-serif").attr( "font-size", 8)
.attr("text-anchor", "middle");
}
makePie();
</script>
</body>

d3.geo.albersUsa() not function

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Store Location</title>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script src="http://d3js.org/queue.v1.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
.boundary {
fill: none;
stroke: #dfdfdf;
stroke-linejoin: round;
}
#map {
text-align: center;
}
circle {
fill: blue;
opacity:.9;
}
text{
font-family: 'PT Sans', sans-serif;
font-weight: 300;
font-size: 12px;
z-index: 900;
}
#tooltip {
position: absolute;
width: 200px;
height: auto;
padding: 10px;
background-color: #ff9436;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
pointer-events: none;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 16px;
line-height: 20px;
}
</style>
</head>
<body>
<div id="tooltip" >
<p><strong>Store name and address:</strong></p>
<p><span id="value"></span></p>
<svg id="spendChart"></svg>
</div>
<div id="map"></div>
<script>
var height = 600;
var width = 900, centered;
var projection = d3.geo.albersUsa()
.scale(1200)
.translate([width/2, height/2]);
console.log("working..!!!")
var path = d3.geo.path().projection(projection);
var svg = d3.select("#map")
.append("svg")
.attr("width", width)
.attr("height", height);
var map = svg.append("g")
.attr("class", "boundary");
var usa, usData, spendData, locationData;
var monthParser = d3.time.format("%b");
d3.select("#tooltip").classed("hidden", true);
queue()
.defer(d3.json, 'us.json')
.defer(d3.json, 'newstorelocations.json')
.defer(d3.json, 'newstorespend.json')
.await(ready);
function onHover(location) {
var pos = projection([location.lon, location.lat]),
html = location.name +
"<br />located at: <br />" +
location.AddressLine1 +
",<br />" +
location.City +
"<br />" +
location.StateCode;
d3.select("#value").html(html);
d3.select("#tooltip").attr("left", pos[0] + "px").attr("top", pos[1] + "px").classed("hidden", false);
d3.select(this).style("fill", "red");
var locationSpendData = spendData.filter(function(d) { return d.StoreDescription == location.StoreDescription; });
locationSpendData.forEach(function(d) {
d.month = monthParser.parse(d.MonthName);
});
var chartHeight = 150, chartWidth = 200,
x = d3.time.scale().domain(d3.extent(locationSpendData, function(d) { return d.month; }))
.range([0, chartWidth]),
y = d3.scale.linear().domain([0, d3.max(locationSpendData, function(d) { return d.TotalSpend; })])
.range([chartHeight, 0]);
chart = d3.select("svg#spendChart").attr("height", chartHeight)
.attr("width", chartWidth);
chart.selectAll("rect.bar")
.data(locationSpendData)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.month); })
.attr("y", function(d) { return y(d.TotalSpend); })
.attr("width", chartWidth / locationSpendData.length)
.attr("height", function(d) { return height - y(d.TotalSpend); });
}
function offHover() {
d3.select(this).style("fill", "blue");
d3.select("#spendChart").html("");
d3.select("#tooltip").classed("hidden", true);
}
function ready(error, us, locations, spend) {
usData = us;
locationData = locations;
spendData = spend;
usa = map.selectAll('path')
.data(topojson.feature(usData, usData.objects.states).features);
usa.enter()
.append('path')
.attr('d', path)
.attr('fill', 'gray');
svg.selectAll('circle')
.data(locationData)
.enter()
.append('circle')
.attr('cx', function(d) {return projection([d.lon, d.lat])[0]})
.attr('cy', function(d) {return projection([d.lon, d.lat])[1]})
.attr('r', 4)
.on("mouseover", onHover)
.on("mouseout", offHover);
};
</script>
</body>
</html>
i am creating USA map and location of store on it...
but i getting an error as shown in the image that
i added to us.json and to another .json file for location ...
but no output is getting shown in browser.
i am getting error like this.. .
Uncaught TypeError: Cannot read property 'objects' of undefined

D3JS: Unable to append a text element only once

I have this code which generates 9 donuts charts. the thing is that the SVG canvas gets appended so many times to the DOM, as many as donuts charts are drawn, and when I try to place a single text element on the top of the screen (as a big title which explain the graphics), it gets appended 9 times as well (in every SVG canvas which hold each donut). I need to have only one SVG canvas and inside it the 9 donuts, but I'm having a hard time trying to achieve it.
Here's a running snippet of what I have so far.
$("#chartdiv").empty()
var dataObj = {
data1: [19, 81],
data2: [15, 85],
data3: [13, 91],
data4: [8, 92],
data5: [7, 93],
data7: [6, 94],
data8: [5, 95],
data9: [4, 96],
data10: [3, 97],
data11: [2, 98],
data11: [1, 99],
data12: [0, 100],
};
var newarr = ["Manufacturing", "Technology", "Other", "Engineering", "Automotive", "Consulting/professiona services", "Entertaiment and media", "Financial services", "Apparel and textile", "Construction", "Academic and educational", "Legal services"]
var width = 140,
height = 140,
radius = (Math.min(width, height) / 2.8);
function drawDonut(data, divchart, index, chartName) {
var color = ["#00338D", "#dedede"];
var pie = d3.pie()
.sortValues(null)
.value(function(d) {
return d
})(data);
var arc = d3.arc()
.outerRadius(radius - 10)
.innerRadius(radius - (radius / 1.9));
var labelArc = d3.arc()
.outerRadius(radius - 31)
.innerRadius(radius - 31);
var svg = d3.select("#chartdiv")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + 75 + "," + 50 + ")");
var g = svg.selectAll("arc")
.data(pie)
.enter().append("g")
.attr("class", "arc");
function easeInverse(ease) {
return function(e) {
var min = 0,
max = 1;
while (max - min > 1e-3) {
var mid = (max + min) * 0.5;
emid = ease(mid);
if (emid > e) {
max = mid;
} else {
min = mid;
}
}
return max;
}
}
var inverseCubic = easeInverse(d3.easeCubic);
var oneOver2Pi = 1.0 / (2 * Math.PI);
var total_msec = 900;
g.append("path")
.style("fill", function(d, i) {
return color[i];
})
.transition()
.ease(d3.easeLinear)
.delay(function(d) {
return total_msec * inverseCubic(d.startAngle * oneOver2Pi);
})
.duration(function(d) {
return total_msec * (inverseCubic(d.endAngle * oneOver2Pi) - inverseCubic(d.startAngle * oneOver2Pi));
})
.attrTween("d", arcTween);
function arcTween(d) {
var i = d3.interpolate(inverseCubic(d.startAngle * oneOver2Pi), inverseCubic(d.endAngle * oneOver2Pi));
return function(t) {
d.endAngle = 2 * Math.PI * d3.easeCubic(i(t));
return arc(d);
}
}
g.append("text")
.attr("transform", "translate(0,5) scale(1.4)")
.attr("font-weight", "bold")
.attr("class", "st0 donutsTextElemnt")
.attr("font-size", "11px")
.attr("text-anchor", "middle")
.html(data[0] + "%");
svg.append("foreignObject")
.attr("transform", "translate(-30,50) scale(.75)")
.attr("width", 120)
.attr("height", 80)
.attr("class", "caption 3rForeignObj")
.append("xhtml:body")
.html(newarr[index])
.style("background", "white");
var lastDonut = $("#donutdiv").children().eq(6)
lastDonut.css({
'transform': 'translate(0 , 20px)'
});
}
Object.keys(dataObj).forEach(function(d, i) {
drawDonut(dataObj[d], '#pie' + (i + 1), i, 'chart' + (i + 1));
});
body {
background: rgb(92, 29, 186);
height: 100vh;
}
* {
box-sizing: border-box;
}
.list-group-item {
font-size: 24px;
background: inherit;
border: none;
color: #fff!important;
}
/* .action{
font-size: 14px!important;
background: inherit;
border: none;
color:#fff!important;
} */
.list-group-item:hover {
color: black!important;
}
.firstPage {
background: #470a68;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%)
}
.secondPage {
background: #00A3A1;
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
;
-o-transform: translate(-50%, -50%);
;
transform: translate(-50%, -50%);
;
}
.thirdPage {
background: #00A3A1;
}
.fourthPage {
background: #f45f42;
}
.submenuTitle p {
font-size: 30px;
color: #fff;
}
.sectionsTitle {
font-size: 52px!important;
}
.inactive {
display: none;
}
.active {
display: block;
}
#chartdiv {
background: #fff;
}
.chart {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.heightZero {
height: 0;
overflow: hidden
}
#keyframes slideUp {
to {
height: 100%
}
}
.slideUp {
animation: slideUp .6s ease-in forwards;
}
.thirdDonutsDiv {
display: inline
}
a {
font-size: 16px!important;
}
.back {
transform: scale(.7)
}
.container-fluid {
width: 512px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%)
}
.sectionsTitle {
font-family: KPMG Extralight;
color: white;
font-weight: bold;
}
p.mainTitle {
color: #fff;
font-size: 115px;
font-family: KPMG Extralight;
margin-top: 5px;
}
p.standardP {
color: #fff;
font-family: univers;
font-size: 1.666rem;
line-height: 1.2;
margin: 1rem 1;
margin-top: 30px;
}
h3.mainTitle {
color: #fff;
font-family: KPMG;
margin-top: 14px;
margin-bottom: 14px;
font-size: 3.8rem;
font-weight: bolder;
}
p.standardP {
color: #fff;
font-family: univers;
font-size: 1.666rem;
line-height: 1.2;
margin: 1rem 1;
margin-top: 30px;
}
ul {
list-style-type: none;
padding: 0;
margin-right: 10px;
margin-left: 10px;
}
label {
width: 100%;
height: 100%;
padding: 0.85rem 1rem;
background-color: hsla(0, 0%, 100%, 0);
color: #fff;
cursor: pointer;
border-radius: 4px;
margin-bottom: 11px;
transition: all .4s;
font-size: 16px;
font-weight: normal;
}
label:hover {
background-color: white;
color: #000;
transition: background-color .2s
}
.labelUnselected {
background-color: hsla(0, 0%, 100%, .6);
color: #fff;
}
div.mainDiv p {
word-wrap: break-word;
line-height: 100%;
}
input {
display: none;
}
.backSubMenu1 {
text-align: center;
}
/* donut chart 4*/
.st0 {
fill: #00338D;
}
#fourtySeven {
transform: translate(50px, 0px)
}
#twentyEight {
transform: translate(-63.104974985120286px, 86.28081103480314px)
}
.icon2 {
opacity: 0;
animation: fadeIn 1s linear 1.5s forwards;
transform: translate(-50px, -45px)
}
#keyframes fadeIn {
to {
opacity: 1;
}
}
.rect {
opacity: 0;
animation: fadeIn 1.3s linear 1.5s forwards;
}
#keyframes fadeIn {
to {
opacity: 1;
}
}
.ifram {
width: 512px;
}
.iconXY {
opacity: 0;
animation: fadeIn 1.5s linear 1.8s forwards;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v5.min.js"></script>
<div class="row">
<div class="col-sm-12" id="chartdiv"></div>
<div class="text-left backSubMenu1">
<i class="fas fa-chevron-left back" style="font-size: 40px;color:aliceblue" targetX="secondPage">
</i>
</div>
<div id="pie1" class="thirdDonutsDiv"></div>
<div id="pie2" class="thirdDonutsDiv"></div>
<div id="pie3" class="thirdDonutsDiv"></div>
<div id="pie4" class="thirdDonutsDiv"></div>
<div id="pie5" class="thirdDonutsDiv"></div>
<div id="pie6" class="thirdDonutsDiv"></div>
<div id="pie7" class="thirdDonutsDiv"></div>
<div id="pie8" class="thirdDonutsDiv"></div>
<div id="pie9" class="thirdDonutsDiv"></div>
</div>
Here's a more d3ish refactor that achieves your goal of one SVG containing all pie charts:
<!DOCTYPE html>
<html>
<body>
<script src="https://d3js.org/d3.v5.min.js"></script>
<div class="row">
<div class="col-sm-12" id="chartdiv">
</div>
</div>
<script>
var dataObj = {
data1: [19, 81],
data2: [15, 85],
data3: [13, 91],
data4: [8, 92],
data5: [7, 93],
data7: [6, 94],
data8: [5, 95],
data9: [4, 96],
data10: [3, 97],
data11: [2, 98],
data11: [1, 99],
data12: [0, 100],
};
var newarr = ["Manufacturing", "Technology", "Other", "Engineering", "Automotive", "Consulting/professiona services", "Entertaiment and media", "Financial services", "Apparel and textile", "Construction", "Academic and educational", "Legal services"]
var width = 140,
height = 140,
radius = (Math.min(width, height) / 2.8),
numCol = 4;
var svg = d3.select("#chartdiv")
.append("svg")
.attr("width", width * numCol)
.attr("height", height * (newarr.length / numCol));
svg.selectAll(".donut")
.data(d3.values(dataObj))
.enter()
.append("g")
.attr("class", "donut")
.each(drawDonut);
function drawDonut(data, index) {
var color = ["#00338D", "#dedede"];
var pie = d3.pie()
.sortValues(null)
.value(function(d) {
return d
})(data);
var arc = d3.arc()
.outerRadius(radius - 10)
.innerRadius(radius - (radius / 1.9));
var labelArc = d3.arc()
.outerRadius(radius - 31)
.innerRadius(radius - 31);
var xPos = 75 + ((index % numCol) * width),
yPos = 50 + (Math.floor(index / numCol) * height);
var group = svg
.append("g")
.attr("transform", "translate(" + xPos + "," + yPos + ")");
var g = group.selectAll("arc")
.data(pie)
.enter().append("g")
.attr("class", "arc");
function easeInverse(ease) {
return function(e) {
var min = 0,
max = 1;
while (max - min > 1e-3) {
var mid = (max + min) * 0.5;
emid = ease(mid);
if (emid > e) {
max = mid;
} else {
min = mid;
}
}
return max;
}
}
var inverseCubic = easeInverse(d3.easeCubic);
var oneOver2Pi = 1.0 / (2 * Math.PI);
var total_msec = 900;
g.append("path")
.style("fill", function(d, i) {
return color[i];
})
.transition()
.ease(d3.easeLinear)
.delay(function(d) {
return total_msec * inverseCubic(d.startAngle * oneOver2Pi);
})
.duration(function(d) {
return total_msec * (inverseCubic(d.endAngle * oneOver2Pi) - inverseCubic(d.startAngle * oneOver2Pi));
})
.attrTween("d", arcTween);
function arcTween(d) {
var i = d3.interpolate(inverseCubic(d.startAngle * oneOver2Pi), inverseCubic(d.endAngle * oneOver2Pi));
return function(t) {
d.endAngle = 2 * Math.PI * d3.easeCubic(i(t));
return arc(d);
}
}
g.append("text")
.attr("transform", "translate(0,5) scale(1.4)")
.attr("font-weight", "bold")
.attr("class", "st0 donutsTextElemnt")
.attr("font-size", "11px")
.attr("text-anchor", "middle")
.html(data[0] + "%");
svg.append("text")
.attr("transform", "translate(" + (xPos) + "," + (yPos + height / 2.4) + ") scale(.75)")
.attr("text-anchor", "middle")
.text(newarr[index]);
}
</script>
</body>
</html>

Add links and hover effects to multi series donut chart in d3.js

I have a multi series donut chart created with the help of this question D3.js - Donut charts with multiple rings in d3.js, see fiddle below.
I'd like to be able to add hover effects, and also make each part clickable in the sense I'd like to assign a certin href to each slice of the chart. I have looked around quite a bit, but can't get my head around it - d3.js is quite complex for me I guess.
The code I have now: http://jsfiddle.net/mephisto73/o6shxw0d/
(function(){
var $container = $('.chart-container'),
τ = 2 * Math.PI,
width = $container.width(),
height = $container.height(),
outerRadius = Math.min(width,height)/2.5,
innerRadius = (outerRadius/5)*4,
fontSize = (Math.min(width,height)/4);
var dataset = {
weeks: [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
months: [1,1,1,1,1,1,1,1,1],
trimester: [1,1,1]
};
var color = d3.scale.ordinal() .range(['rgba(141,211,199,0.8)','rgb(255,255,179)','rgb(190,186,218)','rgb(251,128,114)','rgb(128,177,211)','rgb(253,180,98)','rgb(179,222,105)','rgb(252,205,229)','rgb(217,217,217)','rgb(188,128,189)','rgb(204,235,197)','rgb(255,237,111)']);
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc();
var svg = d3.select('.chart-container').append("svg")
.attr("width", '100%')
.attr("height", '100%')
.attr('viewBox','0 0 '+Math.min(width,height) +' '+Math.min(width,height) )
.attr('preserveAspectRatio','xMinYMin')
.append("g")
.attr("transform", "translate(" + Math.min(width,height) / 2 + "," + Math.min(width,height) / 2 + ")");
var gs = svg.selectAll("g").data(d3.values(dataset)).enter().append("g").attr("class", "arc");
var path = gs.selectAll("path")
.data(function(d) { return pie(d); })
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", function(d, i, j) { return arc.innerRadius(innerRadius+(40*j)).outerRadius(innerRadius+(5*(j+5)))(d); });
});
$(function(){
var tooltip = d3.select(".tooltip");
var $container = $('.chart-container'),
τ = 2 * Math.PI,
width = $container.width(),
height = $container.height(),
outerRadius = Math.min(width,height)/2.5,
innerRadius = (outerRadius/5)*4,
fontSize = (Math.min(width,height)/4);
var dataset = {
weeks: [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
months: [1,1,1,1,1,1,1,1,1],
trimester: [1,1,1]
};
var color = d3.scale.ordinal() .range(['rgba(141,211,199,0.8)','rgb(255,255,179)','rgb(190,186,218)','rgb(251,128,114)','rgb(128,177,211)','rgb(253,180,98)','rgb(179,222,105)','rgb(252,205,229)','rgb(217,217,217)','rgb(188,128,189)','rgb(204,235,197)','rgb(255,237,111)']);
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc();
var svg = d3.select('.chart-container').append("svg")
.attr("width", '100%')
.attr("height", '100%')
.attr('viewBox','0 0 '+Math.min(width,height) +' '+Math.min(width,height) )
.attr('preserveAspectRatio','xMinYMin')
.append("g")
.attr("transform", "translate(" + Math.min(width,height) / 2 + "," + Math.min(width,height) / 2 + ")");
var gs = svg.selectAll("g").data(d3.values(dataset)).enter().append("g").attr("class", "arc");
var path = gs.selectAll("path")
.data(function(d) { return pie(d); })
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", function(d, i, j) { return arc.innerRadius(innerRadius+(40*j)).outerRadius(innerRadius+(5*(j+5)))(d); })
.on("mousemove", function(d){
tooltip.style("left", d3.event.pageX+10+"px");
tooltip.style("top", d3.event.pageY-25+"px");
tooltip.style("display", "inline-block");
tooltip.select("span").text("Value: "+d.value);
}).on("mouseout",function(){
tooltip.style("display","none");
}).on("click",function(){
//write code to open
});
});
html,body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin:0;
padding:0;
width:100%;
height:100%;
}
text {
font: 10px sans-serif;
}
form {
position: absolute;
right: 10px;
top: 10px;
}
.arc path {
stroke: #fff;
width:5px;
}
.arc path:hover {
background-color:#ccc;
}
.chart-container {
width:70%;
height:70%;
border: 1px dotted silver;
}
svg text{
font-size: 1em;
font-family: sans-serif;
}
.tooltip{
position: absolute;
display: none;
width: auto;
height: auto;
background: none repeat scroll 0 0 white;
border: 0 none;
border-radius: 8px 8px 8px 8px;
box-shadow: -3px 3px 15px #888888;
color: black;
font: 12px sans-serif;
padding: 5px;
text-align: center;
}
path:hover{
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.10/d3.min.js"></script>
<div class="chart-container"></div>
<div class='tooltip'>
<span></span>
</div>
I've added functionality for on mousemove, mouseout and click.
Try to read and do the modification in click function.
Hope you got it,If not ask me for more.

topojson.js:187: Uncaught TypeError: Cannot read property 'type' of undefined

I a trying to make a map with D3 and TopoJSON for the Netherlands, including the provinces.
This is the code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3 Test</title>
<script type="text/javascript" src="http://localhost/webserver/d3/d3.js"></script>
<script type="text/javascript" src="http://localhost/webserver/topojson/topojson.js"></script>
<style type="text/css">
div.bar {
display: inline-block;
width: 20px;
height: 75px;
background-color: teal;
margin-right: 2px;
}
.pumpkin {
fill: rgba(128, 0, 128, 0.75);
stroke: yellow;
stroke-width: 5;
}
.apple {
fill: rgba(0, 255, 0, 0.55);
stroke: green;
stroke-width: 15;
}
.orange {
fill: rgba(255, 255, 0, 0.55);
stroke: orange;
stroke-width: 10;
}
.subunit { fill: #cdc; }
.subunit-label {
fill: #777;
fill-opacity: .25;
font-size: 30px;
font-weight: 300;
text-anchor: middle;}
.provincie {fill: none; }
.Utrecht {fill: #ddd; }
.Zuid-Holland {fill: #dde; }
.Noord-Holland {fill: #dee; }
.Drenthe {fill: #aae; }
.Gelderland {fill: #eee; }
.Friesland {fill: #ddc; }
.Groningen {fill: #dcc; }
.Limburg {fill: #ccc; }
.Noord-Brabant {fill: #ddb; }
.Overijssel {fill: #dbb; }
.Zeeland {fill: #bbb; }
</style>
</head>
<body>
<script type="text/javascript">
var width = 960, height = 860;
var projection = d3.geo.albers()
.center([6, 49.40])
.rotate([0, -1.9])
.parallels([50, 60])
.scale(11000)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("http://localhost/webserver/data/nl.json", function(error, nl) {
svg.selectAll(".subunit")
.data(topojson.object(nl, nl.objects.subunits).geometries)
.enter().append("path")
.attr("class", function(d) { return "subunit " + d.id; })
.attr("d", path);
svg.selectAll(".subunit-label")
.data(topojson.object(nl, nl.objects.subunits).geometries)
.enter().append("text")
.attr("x", -20)
.attr("y", -50)
.attr("class", function(d) { return "subunit-label " + d.id; })
.attr("transform", function(d) { return "translate(" + path.centroid(d) + ")"; })
.attr("dy", ".35em")
.text(function(d) { return d.properties.name; });
svg.selectAll(".provincie")
.data(topojson.object(nl, nl.objects.provincies).geometries)
.enter().append("path")
.attr("class", function(d) { return "provincie " + d.properties.name; })
.attr("d", path);
svg.append("path")
.datum(topojson.object(nl, nl.objects.places))
.attr("d", path)
.attr("class", "place");
svg.selectAll(".place-label")
.data(topojson.object(nl, nl.objects.places).geometries)
.enter().append("text")
.attr("class", "place-label")
.attr("transform", function(d) { return "translate(" + projection(d.coordinates) + ")"; })
.attr("dy", ".35em").text(function(d) { return d.properties.name; });
svg.selectAll(".place-label")
.attr("x", function(d) { return d.coordinates[0] > -1 ? 10 : -10; })
.style("text-anchor", function(d) { return d.coordinates[0] > -1 ? "start" : "end"; });
});
</script>
</body>
</html>
The result is the map of the Netherlands, however it doesn't contain the provinces (with colors and borders).
I get the following error:
Uncaught TypeError: Cannot read property 'type' of undefined topojson.js:187
This is line 186 and 187:
function geometry(o) {
var t = o.type, g = t === "GeometryCollection" ? {type: t, geometries: o.geometries.map(geometry)}
It looks like you are referencing an object that doesn't exist in your topology.
Is it possible your TopoJSON file uses the English spelling of "provinces" rather than the Dutch "provincies"? If it did, then nl.objects.provincies would be null, and you'd either need to refer to nl.objects.provinces in your code, or edit the TopoJSON file to use the Dutch spelling "provincies" instead.
Can you post the contents of nl.json so we can take a look (say, on dropbox)?

Resources