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>
Related
Edit: Not sure why the bars are not showing up! Just after how to get the update animation to work on this visualization, using width and x to update the bars after an arc is selected!
I've got a horizontal stacked bar chart that I am trying to update smoothly as seen in this verticle example (https://bl.ocks.org/HarryStevens/7e3ec1a6722a153a5d102b6c42f4501d). I am struggling to get it to work. I have tried using CSS and transitioning the 'width' attribute but it doesn't work as intended. I was wondering if someone might be able to help.
How it works is that if you select one of the arcs, the data would be recalculated. Can you aid me?
var w = 800;
var h = 800;
const svg = d3.select('#radial-chart')
.append('svg')
.attr('width', w)
.attr('height', h);
const arc = d3.arc()
const handleMouseOver = (d, i, n) => {
svg.selectAll('path')
.transition().duration(300)
.style('opacity', 0.35);
d3.select(n[i])
.transition().duration(300)
.style('opacity', 0.35)
}
const handleMouseOut = (d, i, n) => {
svg.selectAll('path')
.transition().duration(300)
.style('opacity', 0.35)
.style('stroke-width', 0)
}
var arcData = [{
domain: '1',
innerRadius: 0,
outerRadius: (h / 1.75),
startAngle: Math.PI * -20 / 180,
endAngle: Math.PI * 20 / 180
},
{
domain: '2',
innerRadius: 0,
outerRadius: (h / 2),
startAngle: Math.PI * 20 / 180,
endAngle: Math.PI * 60 / 180
}
];
var colorScale = d3.scaleOrdinal()
.domain(["1", "2"])
.range(["#E8A82B", "#000000"]);
const slices = arcData.map(d => arc(d));
svg.selectAll('path')
.data(slices)
.enter()
.append('path')
.attr('transform', 'translate(325,550)')
.attr("d", d => d)
.attr("id", (d, i) => "arc" + i)
.style("fill", (d, i) => colorScale(i))
.style("z-index", 100)
.style("opacity", 0.5)
// .attr('class', 'selected')
.on('mouseover', handleMouseOver)
.on('mouseout', handleMouseOut)
.on('click', function(d, i) {
d3.select(this).classed("selected", d3.select(this).classed("selected") ? false : true)
d3.select('image#arc' + i).classed("selected", d3.select(this).classed("selected") ? true : false)
})
//Bar
const bar1 = d3.select('#bar1').append('svg').attr('width', 800).attr('height', 175);
var colorScale = d3.scaleOrdinal()
.range(["#02029D", "#223AAF", '#446FBD', '#669CCB']);
const x = d3.scaleLinear()
.range([0, 800]);
const y = d3.scaleLinear()
.rangeRound([200])
var n_sumSeventyk = 500,
n_sum71kto149k = 600,
n_sum150kto249k = 200,
n_sum250k = 100
if ($('path#arc0').hasClass('selected') === true) {
var n_sumSeventyk = n_sumSeventyk - 100,
n_sum71kto149k = n_sum71kto149k - 300,
n_sum150kto249k = n_sum150kto249k - 25,
n_sum250k = n_sum250k - 65
} else {}
if ($('path#arc1').hasClass('selected') === true) {
var n_sumSeventyk = n_sumSeventyk - 75,
n_sum71kto149k = n_sum71kto149k - 200,
n_sum150kto249k = n_sum150kto249k - 90,
n_sum250k = n_sum250k - 80
} else {};
var bar1Data = [{
position: 1,
label: '70k',
value: n_sumSeventyk
},
{
position: 2,
label: '71K - 149K',
value: n_sum71kto149k
},
{
position: 3,
label: '71K - 149K',
value: n_sum150kto249k
},
{
position: 4,
label: '71K - 149K',
value: n_sum250k
}
];
//sort bars based on value
bar1Data = bar1Data.sort(function(a, b) {
return d3.descending(a.value, b.value);
})
d3.interval(function() {
var n_sumSeventyk = 600;
if ($('path#arc0').hasClass('selected') === true) {
var n_sumSeventyk = n_sumSeventyk - 100
var bar1Data = [{
position: 1,
label: '70k',
value: n_sumSeventyk
},
{
position: 2,
label: '71K - 149K',
value: n_sum71kto149k
},
{
position: 3,
label: '71K - 149K',
value: n_sum150kto249k
},
{
position: 4,
label: '71K - 149K',
value: n_sum250k
},
];
} else {
var n_sumSeventyk = n_sumSeventyk
var bar1Data = [{
position: 1,
label: '70k',
value: n_sumSeventyk,
},
{
position: 2,
label: '71K - 149K',
value: n_sum71kto149k
},
{
position: 3,
label: '71K - 149K',
value: n_sum150kto249k
},
{
position: 4,
label: '71K - 149K',
value: n_sum250k
},
];
}
update(bar1Data)
}, 400)
update(bar1Data)
;
bar1.append("text")
.attr("x", (400))
.attr("y", 80)
.attr("text-anchor", "middle")
.style('font-family', 'marsBook')
.style("font-size", "24px")
.text("Household Income")
function update(bar1Data) {
x.domain([0, d3.sum(bar1Data, d => d.value)])
y.domain([0, d3.max(bar1Data).value])
//Join data to rects
const rects = bar1.selectAll('rect')
.data(bar1Data)
rects.exit().remove();
rects.attr('width', (d) => {
return x(d.value)
})
.attr('height', 200)
.attr('class', (d, i) => {
return 'bar' + i
})
.attr('x', function(d, i) {
return x(sum(bar1Data.map((e) => e.value), 0, i));
})
.attr('y', 100)
rects.enter()
.append('rect')
.attr('width', (d) => {
return x(d.value)
})
.attr('height', 200)
.attr('x', (d, i) => {
// Note that I use `.map` here to take only the `.value` attribute
// from the data and create an array of numbers. Then I pass it to `x`
// to be transformed (could also be the other way around if you'd like,
// so sum(bar1Data.map((e) => x(e.value)), 0, i)
return x(sum(bar1Data.map((e) => e.value), 0, i));
})
.attr('y', 100)
.style("fill", (d, i) => colorScale(i))
function sum(array, start, end) {
var total = 0;
for (var i = start; i < end; i++) total += array[i];
return total;
}
console.log(bar1Data)
}
}
/* Header */
html {
font-family: Arial, Helvetica, sans-serif;
}
body {
margin: 0;
padding: 0;
}
* {
box-sizing: border-box;
}
header {
width: 100%;
top: 0;
margin: 0;
padding-top: 0;
padding: 0.781vw 0;
padding-bottom: 0;
background-color: transparent;
}
p {
font-family: MarsBook;
margin: 0;
}
ul {
display: flex;
flex-direction: row;
align-items: center;
list-style: none;
width: 90%;
padding: 0;
margin: 0;
z-index: 0;
background-color: transparent;
}
header h1 {
color: #0000a0;
margin-left: 2vw;
margin-right: 0.5vw;
font-family: marsBold;
flex-wrap: nowrap;
white-space: nowrap;
font-size: 2vw;
}
header p {
font-size: 2.5vw;
color: #00dcfa;
}
header .subtitle {
margin-left: 0.5vw;
font-family: marsBold;
font-size: 2vw;
flex-wrap: nowrap;
white-space: nowrap;
}
.container {
display: flex;
flex-direction: row;
}
#bars {
display: flex;
flex-direction: column;
margin-left: 150px;
margin-top: -10px;
z-index: 100000000000000000000
}
#bars h1 {
align-content: center;
text-align: center;
}
.selected {
opacity: 1!important
}
image#arc0,
image#arc1,
image#arc2,
image#arc3,
image#arc4,
image#arc5,
image#arc6,
image#arc7,
image#arc8 {
opacity: 0.35
}
image#arc0:hover,
image#arc1:hover,
image#arc2:hover,
image#arc3:hover,
image#arc4:hover,
image#arc5:hover,
image#arc6:hover,
image#arc7:hover,
image#arc8:hover {
opacity: 1
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id='radial-chart'></div>
<div id='bar1'></div>
I did it using CSS on the rects on the dom.
rect {
transition: width 0.3s, x 0.3s ease;
-webkit-transition: x 0.3s, width 0.3s ease;
-moz-transition: width 0.3s, x 0.3s ease;
}
<!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
For example I have svg container with 800px width and 600px height.
I wand to add a tick every 40px, so it will be 20 ticks on x-coordinate (with numbers from 0 to 20) and 15 ticks on y-coordinate (with numbers from 0 to 15).
here is an image
How can I do it? I use d3.js v5
There is my code (with zooming and dragging):
<!DOCTYPE html>
<meta charset="utf-8">
<style>
* {
margin: 0;
padding: 0;
}
html, body {
width: 100%;
height: 100%;
border: 0;
overflow: hidden;
display: block;
}
.box, .reset, .x, .y, .board {
display: block;
}
.box {
position: relative;
width: 100%;
height: 100%;
}
.reset {
position: absolute;
left: 0;
top: 0;
width: 20px;
height: 20px;
border-right: 1px solid #E5E5E5;
border-bottom: 1px solid #E5E5E5;
background-color: #FCFCFC;
z-index: 100;
}
.x {
position: absolute;
left: 20px;
top: 0;
width: calc(100% - 20px);
height: 20px;
background-color: #FCFCFC;
}
.y {
position: absolute;
left: 0;
top: 20px;
width: 20px;
height: calc(100% - 20px);
background-color: #FCFCFC;
}
.board {
position: absolute;
left: 0;
top: 0;
width: calc(100% - 20px);
height: calc(100% - 20px);
}
#board {
padding-left: 20px;
padding-top: 20px;
}
body {
font: 10px sans-serif;
shape-rendering: crispEdges;
background-color: #E5E5E5;
}
path.domain {
stroke: none;
}
g.tick line {
stroke: #D3D3D3;
stroke-width: 2;
}
g.tick text {
fill: #C4C4C4;
}
</style>
<body>
<div class="box">
<div class="reset"></div>
<div class="x"></div>
<div class="y"></div>
<div class="board"></div>
</div>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
function isInteger(num) {
return (num ^ 0) === num;
}
var width = window.innerWidth
|| document.documentElement.clientWidth
|| document.body.clientWidth;
var height = window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight;
var svg = d3.select(".board").append("svg")
.attr("id", "board")
.attr("width", width - 20)
.attr("height", height - 20)
// .append("g");
var zoom = d3.zoom()
// .scaleExtent([1, 32])
.on("zoom", zoomed);
svg.call(zoom);
x_scale = d3.scaleLinear().domain([0, 20]).range([0, width]);
y_scale = d3.scaleLinear().domain([0, 20]).range([0, height]);
var x_axis = d3.axisTop(x_scale)
.ticks(?) //I dont know what do I have to write
.tickFormat(function(d, i) {
if (isInteger(d)) {
return d;
}
})
.tickSize(5)
var x_axis_group = svg.append("g")
.attr("transform", "translate(20, 0)")
.call(x_axis);
var y_axis = d3.axisLeft(y_scale)
.ticks(?) /I dont know what do I have to write
.tickFormat(function(d, i) {
if (isInteger(d)) {
return d;
}
})
.tickSize(5);
var y_axis_group = svg.append("g")
.attr("transform", "translate(0, 20)")
.call(y_axis);
function zoomed() {
var new_x_scale = d3.event.transform.rescaleX(x_scale);
var new_y_scale = d3.event.transform.rescaleY(y_scale);
x_axis_group.call(x_axis.scale(new_x_scale));
y_axis_group.call(y_axis.scale(new_y_scale));
}
</script>
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>
I want to make a similar tooltip like this example but in my chart the tooltips do not show at the end of each bar. I was trying to fix it by adjusting offset([-10, 350]). I can see the tooltips moved but not all of them appear at the end. Anyone can tell me how to fix it? Thanks a lot!
<!DOCTYPE html>
<html>
<style>
.axis {
font-family: Helvetica;
font-size: 1em;
font-weight: bold;
color: #444444;
}
.axis path,
.axis line {
fill: none;
stroke: white;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.bar:hover {
opacity: 0.7;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: #aaaaaa;
color: #aa123f;
border-radius: 6px;
font-family: Helvetica;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: #aaaaaa;
content: "\25BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
</style>
<head>
<!-- D3.js -->
<script src='http://d3js.org/d3.v3.min.js'></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
</head>
<body>
<div id="barChart"></div>
<script>
var data = [
{y:"Group1", x: 3.5},
{y:"Group2", x: 4.5},
{y:"Group3", x: 3.8}
];
var margin = {top: 40, right: 20, bottom: 30, left: 80},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.
linear().
range([0, width]);
var y = d3.scale.
ordinal().
rangeRoundBands([0, height], 0.3);
var xAxis = d3.
svg.
axis().
scale(x).
orient("bottom");
var yAxis = d3.svg.axis().
scale(y).
orient("left");
x.domain([1, 5]);
y.domain(data.map(function(d) { return d.y; }));
var tipBars = d3.tip().
attr('class', 'd3-tip').
offset([-10, 350]).
html(function(d) {
return '<strong style="color:grey">Result:</strong> <span style="color:grey">' + d.x +
'</span>';
});
var svg = d3.select("#barChart").
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 + ")");
var plot = svg.
append("g").
attr('transform', 'translate(' + 0 + ',' + 0 + ')');
var plotBars = plot.selectAll("g").
data(data).
enter().
append("g").
attr('class', 'bars');
svg.call(tipBars);
var bars = plotBars.
append("rect").
attr("x", function(d) { return 0; }).
attr("width",function(d) { return x(d.x);}).
attr("y", function(d) { return y(d.y);}).
attr("height", y.rangeBand()).
attr("class", "bar").
attr("fill","grey").
on('mouseover', tipBars.show).
on('mouseout', tipBars.hide);
svg.append("g").
attr("class", "x axis").
style('font-family', ' Helvetica').
call(xAxis);
svg.append("g").
attr("class", "y axis").
style('font-family', ' Helvetica').
call(yAxis);
</script>
</body>
</html>
Use d3.tip.direction() to set the positioning. Docs
var tipBars = d3.tip()
.attr('class', 'd3-tip')
.direction('e')
.html(function(d) {
return '<strong style="color:grey">Result:</strong> <span style="color:grey">' + d.x +
'</span>';
});