Related
I am setting about to manually append tick marks to a arc visual I'm working on. What remains is placing the axis tick marks about the outer edges of the chart at every 30 degrees. I have appended the 0 degree mark (which is easy, no math involved really) then I realized I may need to use Math.sin() and Math.cosin() to calculate the right x,y coordinates for the other ticks, since they are at angles. My 30 degree tick worked fine. So with 0 tick and 30 tick both appended correctly at the right size and place, one would assume that the math and code are both fine. However, mysteriously, the 60 degree tick is way off. Notice that it's in the inside of the chart and not where it should be, at the 60 degree mark, between the top and 30 degree tick. It's almost as if it thinks I'm giving it an entirely different adjacent (to use trig terminology). Code below:
var margins = {top:100, bottom:300, left:100, right:100};
var height = 600;
var width = 600;
var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate("+margins.left+","+margins.top+")");
var data = [
{'fmc':'xx','funds':30,'top':35,'bottom':-30},
{'fmc':'xx','funds':44,'top':5,'bottom':-40},
{'fmc':'xx','funds':2,'top':20,'bottom':-10},
{'fmc':'xx','funds':22,'top':10,'bottom':-25},
{'fmc':'xx','funds':35,'top':4,'bottom':-20},
];
var yScale = d3.scaleLinear()
.range([height,0])
.domain([-50,50]);
var realScale = d3.scaleLinear()
.range([0,height/2])
.domain([0,50]);
var dScale = d3.scaleLinear()
.domain([-50,50])
.range([Math.PI,0]);
let arc = d3.arc()
.innerRadius(0)
.outerRadius(function(d) {return realScale(d.funds)})
.startAngle(function(d) {return dScale(d.top)})
.endAngle(function(d) {return dScale(d.bottom)});
graphGroup.append('g')
.call(d3.axisRight(yScale).ticks(5))
.selectAll('text')
.attr('text-anchor','end')
.attr('transform','translate(-15,0)');
graphGroup.append('line')
.attr('x1', 0)
.attr('x2',width/2)
.attr('y1', yScale(0))
.attr('y2', yScale(0))
.style('stroke','#000')
.style('stroke-width',2)
.style('stroke-dasharray','2,2');
var arcAxis = d3.arc()
.innerRadius(0)
.outerRadius(width/2)
.startAngle(0)
.endAngle(Math.PI);
var grid1 = d3.arc()
.innerRadius(0)
.outerRadius(realScale(20))
.startAngle(0)
.endAngle(Math.PI);
var grid2 = d3.arc()
.innerRadius(0)
.outerRadius(realScale(40))
.startAngle(0)
.endAngle(Math.PI);
graphGroup.append("path")
.attr("d", grid1)
.style('fill','none')
.style('stroke','#a6a6a6')
.style('stroke-width','1px')
.style('stroke-dasharray','2,2')
.attr("transform", "translate(0,300)");
graphGroup.append("path")
.attr("d", grid2)
.style('fill','none')
.style('stroke','#a6a6a6')
.style('stroke-width','1px')
.style('stroke-dasharray','2,2')
.attr("transform", "translate(0,300)");
graphGroup.append("path")
.attr("d", arcAxis)
.style('fill','none')
.style('stroke','#000')
.style('stroke-width','1px')
.attr("transform", "translate(0,300)");
graphGroup.append('line')
.attr('x1',width/2)
.attr('x2',(width/2)+6)
.attr('y1',width/2)
.attr('y2',width/2)
.style('stroke','#000')
.style('stroke-width','1px');
graphGroup.append('line')
.attr('y1', Math.sin((Math.PI / 180)*30)*300)
.attr('y2', Math.sin((Math.PI / 180)*30)*294)
.attr('x1', Math.cos((Math.PI / 180)*30)*300)
.attr('x2', Math.cos((Math.PI / 180)*30)*306)
.style('stroke','#000')
.style('stroke-width','1px');
graphGroup.append('line')
.attr('y1', Math.sin((Math.PI / 180)*60)*300)
.attr('y2', Math.sin((Math.PI / 180)*60)*294)
.attr('x1', Math.cos((Math.PI / 180)*60)*300)
.attr('x2', Math.cos((Math.PI / 180)*60)*306)
.style('stroke','#000')
.style('stroke-width','1px');
graphGroup
.selectAll()
.data(data)
.enter()
.append("path")
.attr('transform','translate(0,300)')
.attr("class", "arc")
.attr("d", arc)
.style('fill', "#003366")
.style('opacity',.3);
<script src="https://d3js.org/d3.v5.min.js"></script>
Question
To me this looks like a bug, if we go just a few lines of code up, we can see the result rendered correctly, so why would changing 30 to 60 result in something totally different?
Here is a solution:
const addMark = (g, angle, fromRadius, toRadius) => {
const angleR = angle * Math.PI / 180;
const x1 = fromRadius * Math.sin(angleR);
const y1 = fromRadius * -Math.cos(angleR);
const x2 = toRadius * Math.sin(angleR);
const y2 = toRadius * -Math.cos(angleR);
console.log(x1, x2, y1, y2)
g.append('line')
.attr('y1', y1)
.attr('y2', y2)
.attr('x1', x1)
.attr('x2', x2)
.attr('transform', 'translate(0, 300)')
.style('stroke','red');
}
var margins = {top:100, bottom:300, left:100, right:100};
var height = 600;
var width = 600;
var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate("+margins.left+","+margins.top+")");
var data = [
{'fmc':'xx','funds':30,'top':35,'bottom':-30},
{'fmc':'xx','funds':44,'top':5,'bottom':-40},
{'fmc':'xx','funds':2,'top':20,'bottom':-10},
{'fmc':'xx','funds':22,'top':10,'bottom':-25},
{'fmc':'xx','funds':35,'top':4,'bottom':-20},
];
var yScale = d3.scaleLinear()
.range([height,0])
.domain([-50,50]);
var realScale = d3.scaleLinear()
.range([0,height/2])
.domain([0,50]);
var dScale = d3.scaleLinear()
.domain([-50,50])
.range([Math.PI,0]);
let arc = d3.arc()
.innerRadius(0)
.outerRadius(function(d) {return realScale(d.funds)})
.startAngle(function(d) {return dScale(d.top)})
.endAngle(function(d) {return dScale(d.bottom)});
graphGroup.append('g')
.call(d3.axisRight(yScale).ticks(5))
.selectAll('text')
.attr('text-anchor','end')
.attr('transform','translate(-15,0)');
graphGroup.append('line')
.attr('x1', 0)
.attr('x2',width/2)
.attr('y1', yScale(0))
.attr('y2', yScale(0))
.style('stroke','#000')
.style('stroke-width',2)
.style('stroke-dasharray','2,2');
var arcAxis = d3.arc()
.innerRadius(0)
.outerRadius(width/2)
.startAngle(0)
.endAngle(Math.PI);
var grid1 = d3.arc()
.innerRadius(0)
.outerRadius(realScale(20))
.startAngle(0)
.endAngle(Math.PI);
var grid2 = d3.arc()
.innerRadius(0)
.outerRadius(realScale(40))
.startAngle(0)
.endAngle(Math.PI);
graphGroup.append("path")
.attr("d", grid1)
.style('fill','none')
.style('stroke','#a6a6a6')
.style('stroke-width','1px')
.style('stroke-dasharray','2,2')
.attr("transform", "translate(0,300)");
graphGroup.append("path")
.attr("d", grid2)
.style('fill','none')
.style('stroke','#a6a6a6')
.style('stroke-width','1px')
.style('stroke-dasharray','2,2')
.attr("transform", "translate(0,300)");
graphGroup.append("path")
.attr("d", arcAxis)
.style('fill','none')
.style('stroke','#000')
.style('stroke-width','1px')
.attr("transform", "translate(0,300)");
graphGroup.append('line')
.attr('x1',width/2)
.attr('x2',(width/2)+6)
.attr('y1',width/2)
.attr('y2',width/2)
.style('stroke','#000')
.style('stroke-width','1px');
const addMark = (g, angle, fromRadius, toRadius) => {
const angleR = angle * Math.PI / 180;
const x1 = fromRadius * Math.sin(angleR);
const y1 = fromRadius * -Math.cos(angleR);
const x2 = toRadius * Math.sin(angleR);
const y2 = toRadius * -Math.cos(angleR);
console.log(x1, x2, y1, y2)
g.append('line')
.attr('y1', y1)
.attr('y2', y2)
.attr('x1', x1)
.attr('x2', x2)
.attr('transform', 'translate(0, 300)')
.style('stroke','red')
.style('stroke-width', 2)
}
graphGroup
.selectAll()
.data(data)
.enter()
.append("path")
.attr('transform','translate(0,300)')
.attr("class", "arc")
.attr("d", arc)
.style('fill', "#003366")
.style('opacity',.3);
addMark(graphGroup, 15, 300, 306);
addMark(graphGroup, 30, 300, 306);
addMark(graphGroup, 45, 300, 306);
addMark(graphGroup, 60, 300, 306);
addMark(graphGroup, 75, 300, 306);
addMark(graphGroup, 90, 300, 306);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
I have a .json file with data, and I'd like to make a d3 donut (pie) chart from it. I'm not especially fluent in javascript, and every example I can find either pulls from inline json data or the json file is structured differently than mine (mine is a list of dictionaries; theirs are often single dictionaries). I've been troubleshooting for a few days, and somehow can't land on anything that actually works. Any thoughts/tips?
The example at https://www.d3-graph-gallery.com/graph/donut_label.html uses inline json data to render a donut chart with labels. I've attempted to modify it that code by:
pulling json data from /data/all-facet-digitized.json
pull labels each dictionary's "facet" key ("true" and "false"), and values from each dictionary's "count" key (373977 and 55433).
change the color scale domain to match the facet keys ("true" and "false")
/data/all-facet-digitized.json looks like:
[
{
"count": "55433",
"facet": "true"
},
{
"count": "373977",
"facet": "false"
}
]
Code in the of my html file looks like:
<div id="chart"></div> <!-- div containing the donut chart -->
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
// set the dimensions and margins of the graph
var width = 450
height = 450
margin = 40
// The radius of the pieplot is half the width or half the height (smallest one) minus margin.
var radius = Math.min(width, height) / 2 - margin
// append the svg object to the div called 'chart'
var svg = d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// Parse the Data
d3.json("/data/all-facet-digitized.json", function(data) {
// set the color scale
var color = d3.scaleOrdinal()
.domain(["true","false"])
.range(d3.schemeDark2);
// Compute the position of each group on the pie:
var pie = d3.pie()
.sort(null) // Do not sort group by size
.value(function(d) {return d.count; })
var data_ready = pie(d3.entries(data))
// The arc generator
var arc = d3.arc()
.innerRadius(radius * 0.5) // This is the size of the donut hole
.outerRadius(radius * 0.8)
// Another arc that won't be drawn. Just for labels positioning
var outerArc = d3.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9)
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
svg
.selectAll('allSlices')
.data(data_ready)
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d){ return(color(d.facet)) })
.attr("stroke", "white")
.style("stroke-width", "2px")
.style("opacity", 0.7)
// Add the polylines between chart and labels:
svg
.selectAll('allPolylines')
.data(data_ready)
.enter()
.append('polyline')
.attr("stroke", "black")
.style("fill", "none")
.attr("stroke-width", 1)
.attr('points', function(d) {
var posA = arc.centroid(d) // line insertion in the slice
var posB = outerArc.centroid(d) // line break: we use the other arc generator that has been built only for that
var posC = outerArc.centroid(d); // Label position = almost the same as posB
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2 // we need the angle to see if the X position will be at the extreme right or extreme left
posC[0] = radius * 0.95 * (midangle < Math.PI ? 1 : -1); // multiply by 1 or -1 to put it on the right or on the left
return [posA, posB, posC]
})
// Add the polylines between chart and labels:
svg
.selectAll('allLabels')
.data(data_ready)
.enter()
.append('text')
.text( function(d) { console.log(d.facet) ; return d.facet} )
.attr('transform', function(d) {
var pos = outerArc.centroid(d);
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
pos[0] = radius * 0.99 * (midangle < Math.PI ? 1 : -1);
return 'translate(' + pos + ')';
})
.style('text-anchor', function(d) {
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
return (midangle < Math.PI ? 'start' : 'end')
})
})
</script>
My result renders as an empty space:
<div id="chart">
<svg width="450" height="450">
<g transform="translate(225,225)"></g>
</svg>
</div>
The schemeDark2 doens't exist in d3 v4. I've replaced it with schemeCategory10:
var color = d3.scaleOrdinal()
.domain(["true","false"])
.range(d3.schemeCategory10);
Since you have an array of objects, you don't need d3.entries. That takes an object and converts it to an array where each key is an item of the array. But since you already have an array here, you can put it directly in pie():
// Compute the position of each group on the pie:
var pie = d3.pie()
.sort(null) // Do not sort group by size
.value(function(d) {return d.count; })
var data_ready = pie(data)
Now that you've got the data, you can access it on any of the functions: try putting console.log(data_ready) to see what's available. You'll see that the data is bound for each object as the .data property. pie() takes an array and puts it in a format that's convenient to make pie charts with.
Say we want to access the facet property: we would access that as item.data.facet. So in your functions, to access, you can do:
svg
.selectAll('allSlices')
.data(data_ready)
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d){ return(color(d.data.facet)) })
<head></head>
<div id="chart"></div> <!-- div containing the donut chart -->
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
// set the dimensions and margins of the graph
var width = 450
height = 450
margin = 40
// The radius of the pieplot is half the width or half the height (smallest one) minus margin.
var radius = Math.min(width, height) / 2 - margin
// append the svg object to the div called 'chart'
var svg = d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// Parse the Data
var data = [
{
"count": "55433",
"facet": "true"
},
{
"count": "373977",
"facet": "false"
}
]
// set the color scale
var color = d3.scaleOrdinal()
.domain(["true","false"])
.range(d3.schemeCategory10);
// Compute the position of each group on the pie:
var pie = d3.pie()
.sort(null) // Do not sort group by size
.value(function(d) {return d.count; })
var data_ready = pie(data)
console.log('data_r', data_ready)
// The arc generator
var arc = d3.arc()
.innerRadius(radius * 0.5) // This is the size of the donut hole
.outerRadius(radius * 0.8)
// Another arc that won't be drawn. Just for labels positioning
var outerArc = d3.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9)
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
svg
.selectAll('allSlices')
.data(data_ready)
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d){ return(color(d.data.facet)) })
.attr("stroke", "white")
.style("stroke-width", "2px")
.style("opacity", 0.7)
// Add the polylines between chart and labels:
svg
.selectAll('allPolylines')
.data(data_ready)
.enter()
.append('polyline')
.attr("stroke", "black")
.style("fill", "none")
.attr("stroke-width", 1)
.attr('points', function(d) {
var posA = arc.centroid(d) // line insertion in the slice
var posB = outerArc.centroid(d) // line break: we use the other arc generator that has been built only for that
var posC = outerArc.centroid(d); // Label position = almost the same as posB
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2 // we need the angle to see if the X position will be at the extreme right or extreme left
posC[0] = radius * 0.95 * (midangle < Math.PI ? 1 : -1); // multiply by 1 or -1 to put it on the right or on the left
return [posA, posB, posC]
})
// Add the polylines between chart and labels:
svg
.selectAll('allLabels')
.data(data_ready)
.enter()
.append('text')
.text( function(d) { return d.data.facet} )
.attr('transform', function(d) {
var pos = outerArc.centroid(d);
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
pos[0] = radius * 0.99 * (midangle < Math.PI ? 1 : -1);
return 'translate(' + pos + ')';
})
.style('text-anchor', function(d) {
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
return (midangle < Math.PI ? 'start' : 'end')
})
</script>
Ok, the issues here is that you've completely missed how data_ready is structured after converting the JSON response. You might want to add console.log(data_ready) just after you set data_ready and inspect it in the console for better understanding of the following fixes.
First a color fix:
.attr('fill', function(d){ return(color(d.data.value.facet)) })
Then a data fix:
.value(function(d) {return d.value.count; })
And lastly a label fix:
.text( function(d) { console.log(d.data.key) ; return d.data.value.facet } )
Your script should look like this:
// set the dimensions and margins of the graph
var width = 450
height = 450
margin = 40
// The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
var radius = Math.min(width, height) / 2 - margin
// append the svg object to the div called 'my_dataviz'
var svg = d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
d3.json("/data/all-facet-digitized.json", function(data) {
// set the color scale
var color = d3.scaleOrdinal()
.domain(["true","false"])
.range(d3.schemeDark2);
// Compute the position of each group on the pie:
var pie = d3.pie()
.sort(null) // Do not sort group by size
.value(function(d) {return d.value.count; })
var data_ready = pie(d3.entries(data))
// The arc generator
var arc = d3.arc()
.innerRadius(radius * 0.5) // This is the size of the donut hole
.outerRadius(radius * 0.8)
// Another arc that won't be drawn. Just for labels positioning
var outerArc = d3.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9)
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
svg
.selectAll('allSlices')
.data(data_ready)
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d){ return(color(d.data.value.facet)) })
.attr("stroke", "white")
.style("stroke-width", "2px")
.style("opacity", 0.7)
// Add the polylines between chart and labels:
svg
.selectAll('allPolylines')
.data(data_ready)
.enter()
.append('polyline')
.attr("stroke", "black")
.style("fill", "none")
.attr("stroke-width", 1)
.attr('points', function(d) {
var posA = arc.centroid(d) // line insertion in the slice
var posB = outerArc.centroid(d) // line break: we use the other arc generator that has been built only for that
var posC = outerArc.centroid(d); // Label position = almost the same as posB
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2 // we need the angle to see if the X position will be at the extreme right or extreme left
posC[0] = radius * 0.95 * (midangle < Math.PI ? 1 : -1); // multiply by 1 or -1 to put it on the right or on the left
return [posA, posB, posC]
})
// Add the polylines between chart and labels:
svg
.selectAll('allLabels')
.data(data_ready)
.enter()
.append('text')
.text( function(d) { console.log(d.data.key) ; return d.data.value.facet } )
.attr('transform', function(d) {
var pos = outerArc.centroid(d);
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
pos[0] = radius * 0.99 * (midangle < Math.PI ? 1 : -1);
return 'translate(' + pos + ')';
})
.style('text-anchor', function(d) {
var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
return (midangle < Math.PI ? 'start' : 'end')
})
})
I am creating a sunburst for big data. To make it more readable, I need to assign different color for each node (ideally different shades of the same color for every subtree).
I've already tried with :
d3.scaleSequential()
d3.scale.ordinal()
d3.scale.category20c()
I think it can work but I am not sure where to put it exactly. For the moment it works only with one color for every subtree.
var width = 500;
var height = 500;
var radius = Math.min(width, height) / 2;
var color = d3.scaleSequential().domain([1,10]).interpolator(d3.interpolateViridis);
var g = d3.select('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
var partition = d3.partition() //.layout
.size([2 * Math.PI, radius]);
d3.json("file:///c:\\Users\\c1972519\\Desktop\\Stage\\tests_diagrams\\figure_4.8_ex3\\data2.json", function(error, nodeData){
if (error) throw error;
var root = d3.hierarchy(nodeData)
.sum(function(d){
return d.size;
});
partition(root);
var arc = d3.arc()
.startAngle(function(d) { return d.x0; })
.endAngle(function(d) { return d.x1; })
.innerRadius(function(d) { return d.y0; })
.outerRadius(function(d) { return d.y1; });
var arcs = g.selectAll('g')
.data(root.descendants())
.enter()
.append('g')
.attr("class", "node")
.append('path')
.attr("display", function (d) { return d.depth ? null : "none"; })
.attr("d", arc)
.style('stroke', '#fff')
.style("fill", function(d){return color(d)});
}
So I would like to have different shade on every subtree to make it more readable.
Anyone have an idea?
can you try with scaleLinear.
var x = d3.scaleLinear([10, 130], [0, 960]);
or
var color = d3.scaleLinear([10, 100], ["brown", "steelblue"]);
Example:
https://bl.ocks.org/starcalibre/6cccfa843ed254aa0a0d
Documentation:
https://github.com/d3/d3-scale/blob/master/README.md#scaleLinear
Linear Scales
d3.scaleLinear([[domain, ]range]) <>
Constructs a new continuous scale with the specified domain and range, the default interpolator and clamping disabled. If either domain or range are not specified, each defaults to [0, 1]. Linear scales are a good default choice for continuous quantitative data because they preserve proportional differences. Each range value y can be expressed as a function of the domain value x: y = mx + b.
I have this donut chart currently working in an AngularJS app:
But the design mockup says we would like this, note the border-radius property on the green portion of the arc:
How do I add a border-radius to the SVG that d3js outputs, the code I'm currently using looks like this:
let data = [
{
label: 'Data',
count: scope.data
},
{
label: 'Fill',
count: 100 - scope.data
}
];
let width = 60;
let height = 60;
let radius = Math.min(width, height) / 2;
let color = d3.scale
.ordinal()
.range(['#3CC692', '#F3F3F4']);
let selector = '#donut-asset-' + scope.chartId;
d3
.select(selector)
.selectAll('*')
.remove();
let svg = d3
.selectAll(selector)
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr(
'transform',
'translate(' + width / 2 + ',' + height / 2 + ')'
);
let arc = d3.svg
.arc()
.innerRadius(23)
.outerRadius(radius);
let pie = d3.layout
.pie()
.value(function(d) {
return d.count;
})
.sort(null);
let path = svg
.selectAll('path')
.data(pie(data))
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d, i) {
return color(d.data.label);
});
let legend = svg
.selectAll('.legend')
.data(data)
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
return 'translate(' + 0 + ',' + 0 + ')';
});
legend
.append('text')
.attr('x', 1)
.attr('y', 1)
.attr('text-anchor', 'middle')
.attr('dominant-baseline', 'central')
.text(function(d) {
return d.count + '%';
});
};
I know to use cornerRadius but when I do it sets a radius for both arcs, it just needs to exist on the colored one. Any ideas? Thanks in advance for any help!
You can apply a corner radius to a d3 arc which allows rounding on the corners:
let arc = d3.svg
.arc()
.innerRadius(23)
.outerRadius(radius)
.cornerRadius(10);
But, the downside is that all arcs' borders are rounded:
If you apply the cornerRadius to only the darkened arc - the other arc won't fill in the background behind the rounded corners. Instead, we could append a circular arc (full donut) and place the darkened arc on top with rounding (my example doesn't adapt your code, just shows how that it can be done, also with d3v4 which uses d3.arc() rather than d3.svg.arc() ):
var backgroundArc = d3.arc()
.innerRadius(30)
.outerRadius(50)
.startAngle(0)
.endAngle(Math.PI*2);
var mainArc = d3.arc()
.innerRadius(30)
.outerRadius(50)
.cornerRadius(10)
.startAngle(0)
.endAngle(function(d) { return d/100*Math.PI* 2 });
var data = [10,20,30,40,50] // percents.
var svg = d3.select("body").append("svg")
.attr("width", 600)
.attr("height", 200);
var charts = svg.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform",function(d,i) {
return "translate("+(i*100+50)+",100)";
});
charts.append("path")
.attr("d", backgroundArc)
.attr("fill","#ccc")
charts.append("path")
.attr("d", mainArc)
.attr("fill","orange")
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
Try playing with stroke attributes like:
stroke
stroke-dasharray
stroke-dashoffset
stroke-linecap
stroke-linejoin
stroke-miterlimit
stroke-opacity
stroke-width
And set width of bar to lower values, or 0.
Reference: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute
But the better way is to make charts on canvas, because you can draw everything you want. Or to use an library.
Following on from http://stackoverflow.com/questions/18766430/how-can-i-do-d3-svg-arc-within-a-given-map-projection, I have a slightly modified version where I want to display an arc at a certain Azimuth, Zenith Angle and a given opening angle. My efforts so far are below.
If I create an arc at (Az,Ze) 0,90 and 0,45 the arcs are where I expect them. However, if I want an arc at every Az = 45 degrees for Ze = 45 degrees, the arc tends to go off the projection instead of going around.
Any idea what is happening here? Jsfiddle: http://jsfiddle.net/3p9c4kzo/1/
var width = 600,
height = 600;
var flippedStereographic = function(lam, phi) {
var cosl = Math.cos(lam),
cosp = Math.cos(phi),
k = 1 / (1 + cosl * cosp);
return [ k * cosp * Math.sin(lam), -k * Math.sin(phi) ];
};
var projection = d3.geo
.projection(flippedStereographic)
.rotate([0, -90])
.scale(180)
.translate([width / 2, height / 2])
.clipAngle(90)
.precision(.1);
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("defs").append("path")
.datum({
type: "Sphere"
})
.attr("id", "sphere")
.attr("d", path);
svg.append("use")
.attr("class", "stroke")
.attr("xlink:href", "#sphere");
svg.append("use")
.attr("class", "fill")
.attr("xlink:href", "#sphere");
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
function geoArc(center, radius, startAngle, endAngle, steps) {
coordinates = []
for (var i = 0; i <= steps; i++) {
var curAngle = (startAngle + (endAngle - startAngle) * i / steps);
coordinates[i] = d3.geo.rotation(center)(d3.geo.rotation([0, 0,
curAngle])([radius, 0]))
}
return {
type: "LineString",
coordinates: coordinates
};
}
svg.append("path")
.datum(geoArc([0,90], 30, 0, 360, 40))
.classed("circle", true)
.attr("d", path);
svg.append("path")
.datum(geoArc([0,45], 30, 0, 360, 40))
.classed("circle", true)
.attr("d", path);
svg.append("path")
.datum(geoArc([45,45], 30, 0, 360, 40))
.classed("circle", true)
.attr("d", path);
svg.append("path")
.datum(geoArc([90,45], 30, 0, 360, 40))
.classed("circle", true)
.attr("d", path);
It seems the code is appropriate for both Orthographic and Stereographic projections. Orthographic distorts and is not conformal, whereas Stereographic is conformal and does not distort (well, not as much).
Orthographic
https://au.mathworks.com/help/map/ortho.html
Stereographic
https://au.mathworks.com/help/map/stereo.html