Why is appended svg not positioned at 0,0? - d3.js

I have a scale created with D3 and then I pull in an svg sprite using d3.xml. Why is the sprite not in the top left hand corner of the svg before translation? is there a way to ensure the sprite is appended too the exact top left of the SVG?
d3.csv('planets.csv').then(function(data){
var margin = { top: window.innerHeight * 0.3, left: 170, bottom: window.innerHeight * 0.3, right: 50 };
var height = (window.innerHeight - margin.top - margin.bottom)*0.5;
var chartWidth = window.innerWidth - margin.left - margin.right;
var maxDist = d3.max(data, function(d) { return d.distance; });
var xScale = d3.scaleLinear().domain([0, maxDist]).range([0, chartWidth]);
var xAxisTop = d3.axisTop(xScale);
var svg = d3.select('#vis')
.append('svg')
.attr('width', chartWidth + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('class', 'chart')
.attr('transform', 'translate(' + margin.left + ', '+ margin.top + ')');
var xAxisDrawTop = svg.insert('g',':first-child')
.attr('class','x_axis_top')
.call(xAxisTop);
var xAxisDrawTopTorchContainer = d3.select('.x_axis_top').append('g').attr('class','rocket_container')
d3.xml('torch.svg').then(torch=>{
torchSprite = torch.documentElement;
xAxisDrawTopTorchContainer.node().append(torchSprite);
})

The solution was to avoid creating a document fragment by using:
d3.select('relevant-g element').append('image').attr('type','image/svg+xml').attr('xlink:href','torch.svg');
this ensures the appended sprite is mapped to the coordinates 0,0 of the existing SVG in the DOM.

Related

data bars on the xaxis should be aligned properly

I am building my first bar chart using d3.js v5 what I want is that the bars should be aligned properly on the xaxis
I have almost done building the chart but can't figure out the problem
var headingOne;
var headingTwo;
var dataset;
var description;
var frequency;
var barPadding = 20;
document.addEventListener("DOMContentLoaded", function() {
var req = new XMLHttpRequest(); // Next time will use d3.json();
req.open(
"GET",
"https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/GDP-data.json",
true
);
req.send();
req.onload = function() {
let json = JSON.parse(req.responseText);
headingOne = json.source_name;
headingTwo = `From ${json.from_date.substring(0,4)} to ${json.to_date.substring(0,4)}`;
dataset = json.data;
descripton = json.description;
d3
.select("body")
.append("h1")
.text(headingOne)
.attr("class", "headings")
.attr("id", "title");
d3
.select("body")
.append("h2")
.text(headingTwo)
.attr("class", "headings");
var margin = { top: 20, right: 20, bottom: 50, left: 40 },
height = 600 - margin.top - margin.bottom,
width = 1100 - margin.left - margin.right;
var minDate = new Date(dataset[0][0]);
var maxDate = new Date(dataset[dataset.length - 1][0]);
var xScale = d3.scaleTime().domain([minDate, maxDate]).range([barPadding, width - barPadding]);
var yScale = d3.scaleLinear().domain([0, d3.max(dataset, d => d[1])]).range([height, barPadding]);
var xAxis = d3.axisBottom().scale(xScale);
var yAxis = d3.axisLeft().scale(yScale);
var svg = d3
.select("body")
.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 + ")");
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", (d, i) => i * (width / dataset.length))
.attr("data-date", (d) => d[0])
.attr("y", (d) => yScale(d[1]))
.attr("data-gdp", (d) => d[1])
.attr("width", width / dataset.length)
.attr("height", d => height - yScale(d[1]))
svg.append("g").attr("transform", "translate("+barPadding+"," + (height) + ")").attr("id", "x-axis").call(xAxis);
svg.append("g").attr("transform", "translate("+margin.left+", 0)").attr("id", "y-axis").call(yAxis);
};
});
I expect the bars properly aligned on the xaxis.
Now the bars started before the xaxis (start of the xaxis towards left) starting point which is wrong but finished in the right position (end of the xaxis towards right)
the data is exceeding the limit.

Center Pie Chart in SVG Element

I've already read:
https://bl.ocks.org/mbostock/3887235
http://zeroviscosity.com/d3-js-step-by-step/step-1-a-basic-pie-chart
Center align a pie chart on svg
Consider the following:
var dataAsCsv = `Col1,Col2
Type1,123456
Type2,789012
Type3,34567`;
var data = d3.csvParse(dataAsCsv);
var margin = {top: 50, right: 20, bottom: 50, left: 80},
width = 1400 - margin.left - margin.right,
height = 700 - margin.top - margin.bottom;
var svgPie = d3.select('body').append('svg')
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var gPie = svgPie.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var radius = Math.min(width, height) / 2;
var color = d3.scaleOrdinal(d3.schemeCategory20b);
var label = d3.arc()
.outerRadius(radius - 40)
.innerRadius(radius - 40);
var path = d3.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var pie = d3.pie()
.value(function(d) { return d.Col2; })
.sort(null);
var arc = gPie.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
arc.append("path")
.attr("d", path)
.attr("fill", function(d) { return color(d.data.Col1); });
arc.append("text")
.attr("transform", function(d) { return "translate(" + label.centroid(d) + ")"; })
.attr("dy", "0.35em")
.text(function(d) { return d.data.Col1; });
<script src="https://d3js.org/d3.v4.js"></script>
I am trying to center the pie chart vertically and horizontally with respect to the entire svg element that it is in. I tried modifying my code to the examples above to no avail.
You just have to translate the parent g element at half width horizontally and at half height vertically:
Instead of:
var gPie = svgPie.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
write:
var gPie = svgPie.append("g")
.attr("transform", "translate(" + width/2 + "," + height/2 + ")");
Check the demo:
var dataAsCsv = `Col1,Col2
Type1,123456
Type2,789012
Type3,34567`;
var data = d3.csvParse(dataAsCsv);
var margin = {top: 50, right: 20, bottom: 50, left: 80},
width = 1400 - margin.left - margin.right,
height = 700 - margin.top - margin.bottom;
var svgPie = d3.select('body').append('svg')
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var gPie = svgPie.append("g")
.attr("transform", "translate(" + width/2 + "," + height/2 + ")");
var radius = Math.min(width, height) / 2;
var color = d3.scaleOrdinal(d3.schemeCategory20b);
var label = d3.arc()
.outerRadius(radius - 40)
.innerRadius(radius - 40);
var path = d3.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var pie = d3.pie()
.value(function(d) { return d.Col2; })
.sort(null);
var arc = gPie.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
arc.append("path")
.attr("d", path)
.attr("fill", function(d) { return color(d.data.Col1); });
arc.append("text")
.attr("transform", function(d) { return "translate(" + label.centroid(d) + ")"; })
.attr("dy", "0.35em")
.text(function(d) { return d.data.Col1; });
<script src="https://d3js.org/d3.v4.js"></script>

Making SVG container 100% width and height of parent container in D3 v4 (instead of by pixels)

I have a parent container (div.barChartContainer) whose height and width are calculated from the viewport, ex: width: calc(100vh - 200px). The SVG container is appended to the div.barChartContainer element.
I am struggling with how to get the SVG element to be 100% width and height of its parent element, hoping I could get some help. For now I have static pixel amounts (where it currently renders properly, but is non responsive).
Thanks!
var margin = {top: 20, right: 20, bottom: 30, left: 80},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// set the ranges
var y = d3.scaleBand()
.range([height, 0])
.padding(0.4);
var x = d3.scaleLinear()
.range([0, width]);
var svg = d3.select(".barChartContainer").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 + ")");
The solution was to comment out/remove the width and height attribute values and instead add the following two lines:
//- .attr("width", width + margin.left + margin.right)
//- .attr("height", height + margin.top + margin.bottom)
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "0 0 960 500")
Check the svg is drawn with parent height and width.
Ref: https://bl.ocks.org/curran/3a68b0c81991e2e94b19
var parentDiv = document.getElementById("parentDiv");
var svg = d3.select(parentDiv).append("svg");
function redraw(show) {
// Extract the width and height that was computed by CSS.
var width = parentDiv.clientWidth;
var height = parentDiv.clientHeight;
// Use the extracted size to set the size of an SVG element.
svg
.attr("width", width)
.attr("height", height);
if (show === false) {
svg.style('visibility', 'hidden');
} else {
svg.style('visibility', 'visible');
}
svg.attr("style", "outline: thin solid red;")
.append("circle")
.attr("cx", 30)
.attr("cy", 30)
.attr("r", 20);
}
// Draw for the first time to initialize.
redraw(false);
// Redraw after sometime
setTimeout(redraw, 1000);
#parentDiv {
height: calc(100vh - 100px); /** output container is small for display */
width: calc(100vw - 100px);
display: block;
border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.10/d3.min.js"></script>
<div id="parentDiv"></div>

Animating D3 donut chart on load

I have a donut chart with five different arcs inside of it. The data will not be updated but I want to have a transition of the whole graph being drawn as a circle when the page loads, starting at the selected angle (in my case 1.1*PI). Here is the jsfiddle of the graph: http://jsfiddle.net/Nw62g/
var data = [
{name: "one", value: 10375},
{name: "two", value: 7615},
{name: "three", value: 832},
{name: "four", value: 516},
{name: "five", value: 491}
];
var margin = {top: 20, right: 20, bottom: 20, left: 20};
width = 400 - margin.left - margin.right;
height = width - margin.top - margin.bottom;
var chart = d3.select("body")
.append('svg')
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + ((width/2)+margin.left) + "," +
((height/2)+margin.top) + ")");
var radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#3399FF", "#5DAEF8", "#86C3FA", "#ADD6FB", "#D6EBFD"]);
var arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius - 20);
var pie = d3.layout.pie()
.sort(null)
.startAngle(1.1*Math.PI)
.endAngle(3.1*Math.PI)
.value(function(d) { return d.value; });
var g = chart.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.style("fill", function(d) { return color(d.data.name); })
.attr("d", arc);
How would I go about achieving this result?
You can do this with an attribute tween:
.attrTween('d', function(d) {
var i = d3.interpolate(d.startAngle+0.1, d.endAngle);
return function(t) {
d.endAngle = i(t);
return arc(d);
}
});
This animates the segment from start to end angle. To do this across the entire circle, you can use delayed transitions:
.transition().delay(function(d, i) { return i * 500; }).duration(500)
Complete example here. You can adjust start segment, duration, etc to your liking.

d3.js - place text below the circle

I am displaying different radius circles with different color.
I am trying to place the text(radius value) below each circle but not getting displayed though i could see the text in the browser inspector.
following is the code:
var width=960,height=500;
var margin = {top: 29.5, right: 29.5, bottom: 29.5, left: 59.5};
radiusScale = d3.scale.sqrt().domain([1, 100]).range([10, 39]),
colorScale = d3.scale.category10();
// Create the SVG container and set the origin.
var svg = d3.select("#chart").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 i =0;
while (i<=50)
{
console.log("i value is " + i + " value of scale " +i+ " is " + radiusScale(i));
var circle = svg.append("g").append("circle")
.attr("id","circle" + i)
.attr("cx", i*12 )
.attr("cy", 20)
.attr("fill",colorScale(i))
.attr("r", radiusScale(i))
.append("text").attr("dx",i*12).text(function(d){return radiusScale(i)});
i=i+10;
}
should i be adding the text in svg instead of circle to display below the corresponding circles.
SVG will not display text appended to circle elements. You append to the g element:
var g = svg.append("g");
g.append("circle")
.attr("id","circle" + i)
.attr("cx", i*12 )
.attr("cy", 20)
.attr("fill",colorScale(i))
.attr("r", radiusScale(i));
g.append("text").attr("dx",i*12).text(function(d){return radiusScale(i)});
Also note that your function(d) in .text() isn't necessary, you can do just
g.append("text").attr("dx",i*12).text(radiusScale(i));

Resources