I have a radial d3 chart that I would like to plot / overlay on to a Leaflet / Mapbox map at lat,long co-ordinates.
I have looked around and the solutions all seem to be plotting circles onto the map using cx, cy positions, which is not what I need.
Can I add the SVG surrounding the chart to the map?
Below is the code and a link to the radial chart (which may or may not be of any use for this question as I just want to know how to plot an SVG on to the map)
CODE FOR D3 CHART
var width = 300;
var height = 300;
var twoPi = 2 * Math.PI; // Full circle
var formatPercent = d3.format(".0%");
var data = [{
"range": "0-20",
"count": 20
}, {
"range": "21-40",
"count": 10
}, {
"range": "41-60",
"count": 17
}, {
"range": "61-80",
"count": 49
}, {
"range": "81-100",
"count": 90
}];
var max = d3.max(data, function(d) {
return +d.count;
});
var percent = d3.max(data, function(d) {
return +d.count / 10;
});
var radiusBackground = .25;
var radiusForeground = .25;
var gap = 28;
var maxCount = max + percent;
var svg = d3.select(map.getPanes().overlayPane).append("svg")
.attr("width", 1000)
.attr("height", 1000)
.attr("transform", "translate(" + map.latLngToLayerPoint(latLng).x + "," + map.latLngToLayerPoint(latLng).y + ")");
var g = svg.append("g")
.attr("transform", "translate(" + width / 10 + "," + height / 10 + ")")
.attr("class", "leaflet-zoom-hide");
var background = g.selectAll(".twoPi")
.data(data).enter()
.append("path")
.attr("class", "twoPi")
.each(full);
var foreground = g.selectAll(".outerPath")
.data(data).enter()
.append("path")
.attr("class", "outerPath")
.each(drawArc);
map.on("viewreset", update);
update();
function update() {
foreground.attr("transform",
function(d) {
return "translate("+
map.latLngToLayerPoint(latLng).x/10000 +","+
map.latLngToLayerPoint(latLng).y/10000 +")";
}
)
background.attr("transform",
function(d) {
return "translate("+
map.latLngToLayerPoint(latLng).x/10000 +","+
map.latLngToLayerPoint(latLng).y/10000 +")";
}
)
}
function full() {
var arc = d3.svg.arc()
.startAngle(0)
.endAngle(twoPi)
.innerRadius(0 + gap * radiusBackground)
.outerRadius(26 + gap * radiusBackground);
d3.select(this)
.attr("transform", "translate(" + (width / 2.5) + "," + (height / 2.5) + ")")
.attr("d", arc)
.style("opacity", "0.5");
radiusBackground++;
}
function drawArc(d, i) {
var arc = d3.svg.arc()
.startAngle(0)
.endAngle(twoPi * (d.count / maxCount))
.innerRadius(0 + gap * radiusForeground)
.outerRadius(26 + gap * radiusForeground);
d3.select(this)
.attr("transform", "translate(" + (width / 2.5) + "," + (height / 2.5) + ")")
.attr("d", arc)
.attr("id", "path" + i)
.on("mouseover", function() {
d3.select(this)
.style("fill", "red")
.style("cursor", "pointer");
})
.on("mouseout", function() {
d3.select(this)
.style("fill", "#8FAAE5");
})
radiusForeground++;
var text = g.append("text")
.attr("x", 12 + radiusForeground)
.attr("dy", 20)
.style("pointer-events", "none");
text.append("textPath")
.attr("fill", "white")
.attr("xlink:href", "#path" + i)
.text(d.count);
}
CODEPEN LINK TO D3 CHART
CODE FOR MAP (CURRENTLY WRONG I ASSUME)
L.mapbox.accessToken = 'ACCESS TOKEN HERE';
var map = L.mapbox.map('map', 'NAME HERE', {zoomControl: false}).setView([53.4054, -2.9858], 16);
var myLayer = L.mapbox.featureLayer().addTo(map);
var geojson = [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [53.4054, -2.9858]
},
"properties": {
"icon": {
"className": "sensor-icon",
//"html": "▼",
"iconSize": null
}
}
},
];
myLayer.on('layeradd', function(e) {
var marker = e.layer,
feature = marker.feature;
marker.setIcon(L.divIcon(feature.properties.icon));
});
myLayer.setGeoJSON(geojson);
new L.Control.Zoom({ position: 'bottomleft' }).addTo(map);
var latLng = new L.LatLng(geojson[0].geometry.coordinates[0], geojson[0].geometry.coordinates[1]);
Thank you in advance
You'll want to use http://leafletjs.com/reference.html#map-panes. Specifically, you'll do map.getPanes().overlayPane, which will return an element you can append your SVG into.
Related
I am working on a d3 applicaton - with a pie chart -- I would like to get animation onload and on a call to action. Like when the chart becomes visible during a scroll.
Where the pie segments grow around the central pivot. So tween or snap to the other segment like a relay race
http://jsfiddle.net/pg886/192/
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
<div class="piechart" data-role="piechart" data-width=400 data-height=400 data-radius=30 data-innerradius=20
data-data=x>
</div>
<style>
.piechart{
/*border: 1px solid black;*/
/*text-align: center;
font-size: 12px;*/
}
</style>
<script>
$( document ).ready(function() {
console.log("test")
var $this = $('.piechart');
var data = [{
"label": "Apples",
"value": 100
},
{
"label": "Pears",
"value": 120
},
{
"label": "Bananas",
"value": 20
}];
var w = $this.data("width");
var h = $this.data("height");
var ir = $this.data("innerradius");
var r = $this.data("radius");
function colores_google(n) {
var colores_g = ["#f7b363", "#448875", "#c12f39", "#2b2d39", "#f8dd2f"];
//var colores_g = ["#47abd5", "#005a70", "#f5a0a3", "#ff7276", "#a9a19c", "#d0743c", "#ff8c00"];
return colores_g[n % colores_g.length];
}
var radius = Math.min(w, h) / 4;
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var labelArc = d3.svg.arc()
.outerRadius(radius - r)
.innerRadius(radius - ir);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.value; });
var chart = d3.select('.piechart').append("svg")
.attr("class", "chart")
.attr("width", w)
.attr("height", h)
.attr("transform", "translate(0,0)");
var piechart = chart
.append("g")
.attr("class", "piechart")
.attr("width", (radius*2))
.attr("transform", "translate(0,"+h/4+")");
var path_group = piechart.append("g")
.attr("class", "path_group")
.attr("transform", "translate(90," + ((h / 4) - 20) + ")");
var padding = 45;
var legendPaddingTop = 30;
var legend = chart.append("g")
.attr("class", "legend")
.attr("width", w/2)
.attr("height", h)
.attr("transform", "translate(" + (w - 50) + "," + (h / 4) + ")");
var label_group = legend.append("svg:g")
.attr("class", "label_group")
.attr("transform", "translate(" + (-(w / 3) + 20) + "," + 0 + ")");
var legend_group = legend.append("svg:g")
.attr("class", "legend_group")
.attr("transform", "translate(" + (-(w / 3) - 100) + "," + 0 + ")");
var g = path_group.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d, i) {
return colores_google(i);
});
var legendHeight = legendPaddingTop;
var ySpace = 18;
//draw labels
var labels = label_group.selectAll("text.labels")
.data(data);
labels.enter().append("svg:text")
.attr("class", "labels")
.attr("dy", function(d, i) {
legendHeight+=ySpace;
return (ySpace * i) + 4;
})
.attr("text-anchor", function(d) {
return "start";
})
.text(function(d) {
return d.label;
});
labels.exit().remove();
//draw labels
//draw legend
var legend = legend_group.selectAll("circle").data(data);
legend.enter().append("svg:circle")
.attr("cx", 100)
.attr("cy", function(d, i) {
return ySpace * i;
})
.attr("r", 7)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {
return colores_google(i);
});
legend.exit().remove();
//draw legend
//reset legend height
//console.log("optimum height for legend", legendHeight);
$this.find('.legend').attr("height", legendHeight);
function type(d) {
d.value = +d.value;
return d;
}
});
</script>
So you can achieve this pretty easily, and there are a couple of blocks that will help you.
Arc Tween Firstly this block gives you an example of how to tween an arc. Basically you can't get that automatically so you have to write your own attrTween function. Fortunately this is pretty simple and Mike Bostock gives a really good example in there.
Here's a code sample - but the link gives a really good verbose description of what's going on.
.attrTween("d", function(d) {
var interpolate = d3.interpolate(d.endAngle, newAngle);
return function(t) {
d.endAngle = interpolate(t);
return arc(d);
};
}
Next you want something like this Donut with transitions. This is actually the end-result for where you're trying to get to. This effect is really easy to achieve, all you need to do is set your angles correctly and the timings.
Angles: So here you want both the endAngle and the startAngle to be the same at the start (which should be the endAngle value of the previous segment or 0 for the first segment).
Timing: You want to allow 1 animation to complete before you start the next, simply by delaying them. You can see how that's done with this snippet:
.transition()
.delay(function(d,i) { return i * 500; })
.duration(500)
.attrTween(...)
const dataset = [
{ age: "<5", population: 2704659 },
{ age: "5-13", population: 4499890 },
{ age: "14-17", population: 2159981 },
{ age: "18-24", population: 3853788 },
{ age: "25-44", population: 14106543 },
{ age: "45-64", population: 8819342 },
{ age: "≥65", population: 612463 },
];
const TIME = 2000 / dataset.length;
const color = d3.scaleOrdinal(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
const pie = d3.pie()
.sort(null)
.value(function(d) { return d.population; });
const path = d3.arc()
.innerRadius(0)
.outerRadius(350);
d3.select("#container")
.selectAll(".arc")
.data(pie(dataset))
.enter()
.append("g")
.attr("class", "arc")
.append("path")
.attr("fill", function(d) { return color(d.data.age); })
.transition()
.duration(TIME)
.ease(d3.easeLinear)
.delay(function(d, i) { return i * TIME; })
.attrTween("d", function(d) {
// Note the 0.1 to prevent errors generating the path
const angleInterpolation = d3.interpolate(d.startAngle + 0.1, d.endAngle);
return function(t) {
d.endAngle = angleInterpolation(t);
return path(d);
}
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="800" height="800">
<g id="container" transform="translate(400, 400)"></g>
</svg>
I have js code that plots a simple polar chart in the HTML page. I am trying to have it update the data on the plot every 1 second.
I was able to update the data and plot the new points, however, there are two things I have not figured out yet.
Right now every second I am cleaning the plot area and plotting everything new, instead I would like to update the data by creating a smooth sliding transition from the previous position to the new one.
The second issue is that the labeling of the points is not being deleted with the old point position, which creates multiple labeling on top of each other.
What should I add in order to create this smooth transition for the plotted points and their labels?
Here is my code snippet:
<html>
<head>
<meta charset='ISO-8859-1'>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body style='background-color:lightgray'>
<div id="chart" style='width: 400px; height: 400px; padding-left: 5px; padding-bottom: 5px;'></div>
<script>
var color = d3.scale.category20();
var deg2rad = Math.PI / 180;
var width = 400,
height = 350,
radius = Math.min(width, height) / 2 - 30;
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var r = d3.scale.linear()
.domain([90, 0])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {
return r(d[1]);
})
.angle(function(d) {
return -d[0] + Math.PI / 2;
});
var gr = null;
createSkyplot();
updateSkyPlot();
function createSkyplot() {
//////////////////////
gr = svg.append("g")
.attr("class", "r axis")
.selectAll("g")
.data(r.ticks(5))
.enter().append("g");
gr.append("circle").attr("r", r).style('fill', 'white');
gr.append("text")
.attr("y", function(d) {
return -r(d) - 4;
})
.attr("transform", "rotate(20)")
.style("text-anchor", "middle")
.style('fill', 'blue')
.text(function(d) {
return d;
});
/////////////////////
/////////////////////
var ga = svg.append("g")
.attr("class", "a axis")
.selectAll("g")
.data(d3.range(0, 360, 45))
.enter().append("g")
.attr("transform", function(d) {
return "rotate(" + (d - 90) + ")";
});
ga.append("line").attr("x2", radius).style('stroke', 'black').style('stroke-dasharray', '1,8');
ga.append("text")
.attr("x", radius + 6)
.attr("dy", ".35em")
.style("text-anchor", function(d) {
return d < 360 && d > 90 ? "end" : null;
})
.attr("transform", function(d) {
return d < 360 && d > 90 ? "rotate(180 " + (radius + 3) + ",0)" : null;
})
.text(function(d) {
return d + "°";
});
/////////////////////
}
function updateSkyPlot() {
var pos = [{
"position": [1, Math.random() * 20, Math.random() * 20],
"label": 1
}, {
"position": [3, Math.random() * 20, Math.random() * 20],
"label": 5
}];
var r = d3.scale.linear()
.domain([90, 0])
.range([0, radius]);
var line = d3.svg.line.radial()
.radius(function(d) {
return r(d[1]);
})
.angle(function(d) {
return -d[0] + Math.PI / 2;
});
svg.selectAll('circle').remove();
gr.append("circle").attr("r", r).style('fill', 'white');
var points = svg.selectAll("point")
.data(pos)
.enter()
.append("a") // The container
.attr("transform", function(d) {
var coors = line([d.position]).slice(1).slice(0, -1);
return "translate(" + coors + ")"
});
points.append("circle")
.attr("class", "point")
.attr("r", 10)
.attr("fill", function(d, i) {
return color(i);
});
points.append("text")
.attr("class", "label")
.text(function(d) {
return d.label
})
.style("font-size", "10")
.attr("transform", "translate(-4,5)");
setTimeout(updateSkyPlot, 1000);
}
</script>
</body>
</html>
Code can also be seen here.
I have tried adding
svg.selectAll("label").remove(); or svg.selectAll('point.label').remove();
but it does not work and when i add svg.selectAll("text").remove(); it removes all the text in the plot which is obviously something i don't want.
Any help on fixing those issues would be appreciated. Thank you.
Here in my d3 radial chart, I am trying to get the label text just above the segment arcs instead of keeping outside the outer circle.
Fiddle
var width = 360,
height = 300,
barHeight = height / 2 - 40;
var formatNumber = d3.format("s");
var color = d3.scale.ordinal()
.range(["#F15D5D","#FAD64B"]);
var svg = d3.select('#chart').append("svg")
.attr("width", width)
.attr("height", height)
.attr('class','radial')
.append("g")
.attr("transform", "translate(" + width/2 + "," + height/2 + ")");
var data = [{
"name": "ABC",
"value":4
},
{
"name": "XYZ",
"value":5
},{
"name": "DEF",
"value":2
},
{
"name": "GHI",
"value":3
},{
"name": "JKL",
"value":1
}];
data.sort(function(a,b) { return b.value - a.value; });
var extent = [0, d3.max(data, d=>d.value)];
var barScale = d3.scale.linear()
.domain(extent)
.range([0, barHeight]);
var keys = data.map(function(d,i) { return d.name; });
var numBars = keys.length;
// X scale
var x = d3.scale.linear()
.domain(extent)
.range([0, -barHeight]);
// X axis
var xAxis = d3.svg.axis()
.scale(x).orient("left")
.ticks(3)
.tickFormat(formatNumber);
// Inner circles
var circles = svg.selectAll("circle")
.data(x.ticks(5))
.enter().append("circle")
.attr("r", function(d) {return barScale(d);})
.style("fill", "none")
//.style("stroke", "black")
//.style("stroke-dasharray", "2,2")
.style("stroke-width",".5px");
// Create arcs
var arc = d3.svg.arc()
.startAngle(function(d,i) {
var a = (i * 2 * Math.PI) / numBars;
var b = ((i + 1) * 2 * Math.PI) / numBars;
var d = (b-a) / 4;
var x = a+d;
var y = b-d;
return x;//(i * 2 * Math.PI) / numBars;
})
.endAngle(function(d,i) {
var a = (i * 2 * Math.PI) / numBars;
var b = ((i + 1) * 2 * Math.PI) / numBars;
var d = (b-a) / 4;
var x = a+d;
var y = b-d;
return y;//((i + 1) * 2 * Math.PI) / numBars;
})
.innerRadius(0);
// Render colored arcs
var segments = svg.selectAll("path")
.data(data)
.enter().append("path")
.each(function(d) { d.outerRadius = 0; })
.style("fill", function (d) { return color(d.name); })
.attr("d", arc);
// Animate segments
segments.transition().ease("elastic").duration(1000).delay(function(d,i) {return (25-i)*50;})
.attrTween("d", function(d,index) {
var i = d3.interpolate(d.outerRadius, barScale(+d.value));
return function(t) { d.outerRadius = i(t); return arc(d,index); };
});
// Outer circle
svg.append("circle")
.attr("r", barHeight)
.classed("outer", true)
.style("fill", "none")
//.style("stroke", "black")
.style("stroke-width",".5px");
// Apply x axis
svg.append("g")
.attr("class", "x axis")
.call(xAxis);
// Labels
var labelRadius = barHeight * 1.025;
var labels = svg.append("g")
.classed("labels", true);
labels.append("def")
.append("path")
.attr("id", "label-path")
.attr("d", "m0 " + -labelRadius + " a" + labelRadius + " " + labelRadius + " 0 1,1 -0.01 0");
labels.selectAll("text")
.data(keys)
.enter().append("text")
.style("text-anchor", "middle")
.style("font-weight","bold")
.style("fill", function(d, i) {return "#555";})
.append("textPath")
.attr("xlink:href", "#label-path")
.attr("startOffset", function(d, i) {return i * 100 / numBars + 50 / numBars + '%';})
.text(function(d) {return d.toUpperCase(); });
We can set the position of the <defs>'s paths using the same data you used to create the arcs.
First, let's create an enter selection:
var labels = svg.selectAll("foo")
.data(data)
.enter()
.append("g")
.classed("labels", true);
Then, we append the paths using the barScale for each value (hardcoded padding of 4px here):
labels.append("def")
.append("path")
.attr("id", (d, i) => "label-path" + i)
.attr("d", d => "m0 " + -(barScale(d.value) + 4) +
" a" + (barScale(d.value) + 4) + " " +
(barScale(d.value) + 4) + " 0 1,1 -0.01 0");
Please notice that we have to use unique IDs. Then, we change the IDs in the text-paths:
.attr("xlink:href", (d, i) => "#label-path" + i)
Here is your updated fiddle: https://jsfiddle.net/qt3e0rex/
And the same code in the Stack snippet:
var width = 360,
height = 300,
barHeight = height / 2 - 40;
var formatNumber = d3.format("s");
var color = d3.scale.ordinal()
.range(["#F15D5D", "#FAD64B"]);
var svg = d3.select('body').append("svg")
.attr("width", width)
.attr("height", height)
.attr('class', 'radial')
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var data = [{
"name": "ABC",
"value": 4
}, {
"name": "XYZ",
"value": 5
}, {
"name": "DEF",
"value": 2
}, {
"name": "GHI",
"value": 3
}, {
"name": "JKL",
"value": 1
}];
data.sort(function(a, b) {
return b.value - a.value;
});
var extent = [0, d3.max(data, d => d.value)];
var barScale = d3.scale.linear()
.domain(extent)
.range([0, barHeight]);
var keys = data.map(function(d, i) {
return d.name;
});
var numBars = keys.length;
// X scale
var x = d3.scale.linear()
.domain(extent)
.range([0, -barHeight]);
// X axis
var xAxis = d3.svg.axis()
.scale(x).orient("left")
.ticks(3)
.tickFormat(formatNumber);
// Inner circles
var circles = svg.selectAll("circle")
.data(x.ticks(5))
.enter().append("circle")
.attr("r", function(d) {
return barScale(d);
})
.style("fill", "none")
//.style("stroke", "black")
//.style("stroke-dasharray", "2,2")
.style("stroke-width", ".5px");
// Create arcs
var arc = d3.svg.arc()
.startAngle(function(d, i) {
var a = (i * 2 * Math.PI) / numBars;
var b = ((i + 1) * 2 * Math.PI) / numBars;
var d = (b - a) / 4;
var x = a + d;
var y = b - d;
return x; //(i * 2 * Math.PI) / numBars;
})
.endAngle(function(d, i) {
var a = (i * 2 * Math.PI) / numBars;
var b = ((i + 1) * 2 * Math.PI) / numBars;
var d = (b - a) / 4;
var x = a + d;
var y = b - d;
return y; //((i + 1) * 2 * Math.PI) / numBars;
})
.innerRadius(0);
// Render colored arcs
var segments = svg.selectAll("path")
.data(data)
.enter().append("path")
.each(function(d) {
d.outerRadius = 0;
})
.style("fill", function(d) {
return color(d.name);
})
.attr("d", arc);
// Animate segments
segments.transition().ease("elastic").duration(1000).delay(function(d, i) {
return (25 - i) * 50;
})
.attrTween("d", function(d, index) {
var i = d3.interpolate(d.outerRadius, barScale(+d.value));
return function(t) {
d.outerRadius = i(t);
return arc(d, index);
};
});
// Outer circle
svg.append("circle")
.attr("r", barHeight)
.classed("outer", true)
.style("fill", "none")
//.style("stroke", "black")
.style("stroke-width", ".5px");
// Apply x axis
svg.append("g")
.attr("class", "x axis")
.call(xAxis);
// Labels
var labelRadius = barHeight * 1.025;
var labels = svg.selectAll("foo")
.data(data)
.enter()
.append("g")
.classed("labels", true);
labels.append("def")
.append("path")
.attr("id", (d, i) => "label-path" + i)
.attr("d", d => "m0 " + -(barScale(d.value) + 4) + " a" + (barScale(d.value) + 4) + " " + (barScale(d.value) + 4) + " 0 1,1 -0.01 0");
labels.append("text")
.style("text-anchor", "middle")
.style("font-weight", "bold")
.style("fill", function(d, i) {
return "#555";
})
.append("textPath")
.attr("xlink:href", (d, i) => "#label-path" + i)
.attr("startOffset", function(d, i) {
return i * 100 / numBars + 50 / numBars + '%';
})
.text(function(d) {
return d.name.toUpperCase();
});
body {
background-color:#fff;
}
.axis path, .axis line {
fill: none;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.tick text, line {
display:none;
}
circle {
stroke:#ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I have some problem in d3 v4 when I draw a chord. The question is 'Uncaught TypeError: g_outer.selectAll(...).data(...).enter is not a function' at the 53th in my code. But when I see the source code refering to https://bost.ocks.org/mike/uberdata/ and https://github.com/d3/d3/blob/master/CHANGES.md#chords-d3-chord. I have modified some errors, but it still can't work.
Here is my code:
<html>
<head>
<meta charset="UTF-8">
<title>Chord char</title>
</head>
<body>
<script src="http://d3js.org/d3.v4.min.js"></script>
<script>
var city_name = [ "English" , "America" , "German" , "Japan" , "Austrilia" ];
var population = [
[ 1000, 3045, 4567, 1234, 3714 ],
[ 3214, 2000, 2060, 124 , 3234 ],
[ 8761, 6545, 3000, 8045, 647 ],
[ 3211, 1067, 3214, 4000, 1006 ],
[ 2146, 1034, 6745, 4764, 5000 ],
];
var chord_layout = d3.chord(population)
.padAngle(0.03)
.sortSubgroups(d3.descending);
var groups = chord_layout.sortGroups();
var chords = chord_layout.sortChords();
var width = 600;
var height = 600;
var innerRadius = width/2 * 0.7;
var outerRadius = innerRadius * 1.1;
var color20 = d3.scaleOrdinal(d3.schemeCategory20);
//add element
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height)
.append("g")
.attr("transform", "translate(" + width/2 + "," + height/2 + ")");
//draw nodes
var outer_arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var g_outer = svg.append("g");
//add color
g_outer.selectAll("path")
.data(groups)
.enter().append("path")
.style("fill", function(d) { return color20(d.index); })
.style("stroke", function(d) { return color20(d.index); })
.attr("d", outer_arc );
//add text
g_outer.selectAll("text")
.data(groups)
.enter()
.append("text")
.each( function(d,i) {
d.angle = (d.startAngle + d.endAngle) / 2; //calculate the average of the start angle and the end angle
d.name = city_name[i]; //assignment for the city
})
.attr("dy", ".35em") //width
.attr("transform", function(d){ //angle
return "rotate(" + (d.angle * 180 / Math.PI ) + ")" +
"translate(0,"+ -1.0*(outerRadius+10) +")" +
( ( d.angle > Math.PI*3/4 && d.angle < Math.PI*5/4 ) ? "rotate(180)" : "");
}) //to spin when the angle between 135 to 225 degrees
.text(function(d){
return d.name;
})
//add chord
var inner_chord = d3.ribbon()
.radius(innerRadius);
svg.append("g")
.attr("class", "chord")
.selectAll("path") //use the path as element
.attr("d", inner_chord) //
.style("fill", function(d) { return color20(d.source.index); })
.style("opacity" ,1)
.on("mouseover", function(d,i){
d3.select(this)
.style("fill","yellow");
})
.on("mouseout", function(d,i){
d3.select(this)
.transition()
.duration(1000)
.style("fill",color20(d.source.index));
});
</script>
</body>
</html>
I want to draw a chord like this:
But in google chrome, the chord can not appear. I am new to d3, can you help me? I will appreciate it if you give me a good idea! Thankyou!
You have a slew of mistakes in your code. The biggest this that you aren't understanding how d3.chord function works. The general flow in d3 is to set-up your layout function before giving it any data. In your case like this:
var chord_layout = d3.chord()
.padAngle(0.03)
.sortSubgroups(d3.descending);
var groups = chord_layout(population);
After fixing that, I blended this example with your code to produce:
<html>
<head>
<meta charset="UTF-8">
<title>Chord char</title>
</head>
<body>
<script src="http://d3js.org/d3.v4.min.js"></script>
<script>
var city_name = ["English", "America", "German", "Japan", "Austrilia"];
var population = [
[1000, 3045, 4567, 1234, 3714],
[3214, 2000, 2060, 124, 3234],
[8761, 6545, 3000, 8045, 647],
[3211, 1067, 3214, 4000, 1006],
[2146, 1034, 6745, 4764, 5000],
];
var chord_layout = d3.chord()
.padAngle(0.03)
.sortSubgroups(d3.descending);
var width = 600;
var height = 600;
var innerRadius = width / 2 * 0.7;
var outerRadius = innerRadius * 1.1;
var color20 = d3.scaleOrdinal(d3.schemeCategory20);
//add element
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
//draw nodes
var outer_arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var groups = chord_layout(population);
var g_outer = svg.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.datum(groups);
var group = g_outer.append("g")
.attr("class", "groups")
.selectAll("g")
.data(function(chords) { return chords.groups; })
.enter().append("g");
//add color
group.append("path")
.style("fill", function(d) {
return color20(d.index);
})
.style("stroke", function(d) {
return color20(d.index);
})
.attr("d", outer_arc);
//add text
group.append("text")
.attr("dy", ".35em") //width
.attr("transform", function(d,i) { //angle
d.angle = (d.startAngle + d.endAngle) / 2; //calculate the average of the start angle and the end angle
d.name = city_name[i]; //assignment for the city
return "rotate(" + (d.angle * 180 / Math.PI) + ")" +
"translate(0," + -1.0 * (outerRadius + 10) + ")" +
((d.angle > Math.PI * 3 / 4 && d.angle < Math.PI * 5 / 4) ? "rotate(180)" : "");
}) //to spin when the angle between 135 to 225 degrees
.text(function(d) {
return d.name;
});
//add chord
var inner_chord = d3.ribbon()
.radius(innerRadius);
g_outer.append("g")
.attr("class", "ribbons")
.selectAll("path")
.data(function(chords) { return chords; })
.enter().append("path")
.attr("d", inner_chord)
.style("fill", function(d) {
return color20(d.source.index);
})
.style("stroke", "black")
.style("opacity", 0.6)
.on("mouseover", function(d, i) {
d3.select(this)
.style("fill", "yellow");
})
.on("mouseout", function(d, i) {
d3.select(this)
.transition()
.duration(1000)
.style("fill", color20(d.source.index));
});
</script>
</body>
</html>
I am trying to display labels on my simple D3.js pie chart with the following code:
HTML:
<div id="chart"></div>
JS:
(function(d3) {
var last_login_today = <?php echo json_encode($last_login_today_count); ?>;
var last_login_before_today = <?php echo json_encode($did_not_login_today_count); ?>;
'use strict';
var dataset = [
{ label: 'Logged in Today', count: last_login_today },
{ label: 'Logged in Before Today', count: last_login_before_today }
];
var width = 360;
var height = 360;
var radius = Math.min(width, height) / 2;
var color = d3.scale.category20b();
var svg = d3.select('#chart')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + (width / 2) +
',' + (height / 2) + ')');
var arc = d3.svg.arc()
.outerRadius(radius);
var pie = d3.layout.pie()
.value(function(d) { return d.count; })
.sort(null);
var path = svg.selectAll('path')
.data(pie(dataset))
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d, i) {
return color(d.data.label);
});
})(window.d3);
The chart itself is populating correctly but the labels "Logged in Today" and "Logged in Before Today" are not displaying.
I'll start with this, labels on pies are a pain when doing them dynamically due to unknown string lengths.
I use this to find the angle point, so I can put it left/right
function midAngle(d) {
return d.startAngle + ((d.endAngle - d.startAngle) / 2);
}
And you'll need this
var labels = svg.append("g")
.attr("class", "labels")
.selectAll("text")
.data(pie(dataset))
.enter()
.append("text")
.attr("transform", function (d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
var d2 = interpolate(0);
var pos = arc.centroid(d2);
pos[0] = radius * (midAngle(d2) < Math.PI ? 1.2 : -1.2));
return "translate(" + pos + ")";
})
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function (d) {
return d.data.Label;
});