I am begginer to d3.js and getting errors, I customized the example for my needs but gettings these in the console. Please help. Thank you in Advance.
Error: Invalid value for <path> attribute d="M-1.8369701987210297e-14,-100A100,100 0 1,1 NaN,NaNLNaN,NaNA60,60 0 1,0 -1.1021821192326178e-14,-60Z"a # d3.v3.min.js:1
d3.v3.min.js:1 Error: Invalid value for <path> attribute d="MNaN,NaNA100,100 0 1,1 NaN,NaNLNaN,NaNA60,60 0 1,0 NaN,NaNZ"
var getData = function () {
var size = 3;
var data =[];
var text = "";
data=[
{
label:"Saved",
value:200,
},{
label:"Ordered",
value:1255,
},{
label:"Shipped",
value:760,
},{
label:"Backordered",
value:150,
},
{
label:"Cancelled",
value:250,
},
];
d3.select("#data").html(text);
return data;
};
console.log(getData());
var chart = donut(250,200,16,40)
.$el(d3.select("#chart"))
.data(getData())
.render();
d3.select("#refresh").on("click", function () {
chart.data(getData()).render();
});
function donut(width,height,label_font_size,value_font_size) {
// Default settings
var $el = d3.select("body")
var data = {};
// var showTitle = true;
var width = width,
height = height,
radius = Math.min(width, height) / 2;
//var arc = d3.svg.arc().outerRadius(radius);
var arcOver = d3.svg.arc()
.innerRadius(radius + 20);
var currentVal;
var color = d3.scale.category20();
var pie = d3.layout.pie()
.sort(null)
.value(function (d) {
console.log(d);
return d.value.value;
});
var svg, g, arc;
var object = {};
// Method for render/refresh graph
object.render = function () {
if (!svg) {
arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius - (radius / 2.5));
svg = $el.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
g = svg.selectAll(".arc")
.data(pie(d3.entries(data)))
.enter().append("g")
.attr("class", "arc");
g.append("path")
// Attach current value to g so that we can use it for animation
.each(function (d) {
//this._current = d;
})
.attr("d", arc)
.style("fill", function (d,i) {
return color(i);
});
/*g.append("text")
.attr("transform", function (d,i) {
return "translate(" + arc.centroid(d,i) + ")";
})
.attr("dy", ".35em")
.style("text-anchor", "middle");
g.select("text").text(function (d) {
//return d.data.key;
});
*/
svg.append("text")
.datum(data)
.attr("x", 0)
.attr("y", 0)
.attr("class", "text-tooltip")
.style("text-anchor", "middle")
.attr("font-weight", "bold")
.style("font-size", radius / 2.5 + "px");
g.on("mouseover", function (obj) {
var tooltipText=svg.select("text.text-tooltip");
tooltipText.attr("fill", function (d,i) {
return color(i);
});
tooltipText.append("tspan")
.attr("dy", -15)
.attr("x",0)
.attr("class", "text-tooltip-label")
.style("font-size", label_font_size + "px")
.text(function(d) {return d[obj.data.key].label;});
tooltipText.append("tspan")
.attr("dy", ".9em") // offest by 1.2 em
.attr("x",0)
.attr("class", "text-tooltip-value")
.style("font-size", value_font_size + "px")
.text(function(d) {return d[obj.data.key].value;});
d3.select(this)
.attr("stroke","white")
.transition()
.duration(1000)
.attr("d", arcOver)
.attr("stroke-width",6);
});
g.on("mouseout", function (obj) {
svg.select("text.text-tooltip").text("");
d3.select(this).transition()
.attr("d", arc)
.attr("stroke","none");
});
} else {
g.data(pie(d3.entries(data))).exit().remove();
g.select("path")
.transition().duration(200)
.attrTween("d", function (a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function (t) {
return arc(i(t));
};
});
g.select("text")
.attr("transform", function (d,i) {
return "translate(" + arc.centroid(d,i) + ")";
});
svg.select("text.text-tooltip").datum(data);
}
return object;
};
// Getter and setter methods
object.data = function (value) {
if (!arguments.length) return data;
data = value;
return object;
};
object.$el = function (value) {
if (!arguments.length) return $el;
$el = value;
return object;
};
object.width = function (value) {
if (!arguments.length) return width;
width = value;
radius = Math.min(width, height) / 2;
return object;
};
object.height = function (value) {
if (!arguments.length) return height;
height = value;
radius = Math.min(width, height) / 2;
return object;
};
return object;
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="chart"></div>
Related
Edit: I have edited the question, earlier values were not reflecting on the x axis now that got resolved .Now as the slider moves the data doesn't change in the graph and i can see changing x values.
I'm trying to implement a jquery slider in d3 following this link. The example is generating data of some random numbers while my data is in the below format:
{storeid: 5722646637445120, peoplesum: 87, date: "2018-06-03"}
{storeid: 5722646637445120, peoplesum: 90, date: "2018-06-04"}
{storeid: 5722646637445120, peoplesum: 114, date: "2018-06-05"}
I'm able to trigger events when i move my slider but the values are not reflecting on the graph now. So that mean my view is not updating the SVG correctly which is my assumption. I could be wrong here as well.
My x axis should have the date range and y should be peoplesum and it could be a single or a multiline graph.
Updated Code:
private initSvg() {
d3.select("svg").remove();
this.svg = d3.select("#d3Id")
.append("svg")
.attr("width", this.width + this.margin.left + this.margin.right)
.attr("height", this.height + this.margin.top + this.margin.bottom)
.append("g")
.attr("transform",
"translate(" + this.margin.left + "," + this.margin.top + ")")
.attr("stroke-width", 2);
}
private initAxis() {
// Parse the date / time
var parseDate = timeParse("%b %Y");
this.formatTime = timeParse("%e %B");
// Set the ranges
this.x = d3Scale.scaleTime().range([0, this.width]);
this.y = d3Scale.scaleLinear().range([this.height, 0]);
}
private drawAxis() {
var X = this.x;
var Y = this.y;
this.xAxis = d3.axisBottom(this.x)
.ticks(5);
// var xAxis = d3.axisBottom(this.x).tickSize(-this.height).ticks(3);
// // Add the x-axis.
// this.svg.append("svg:g")
// .attr("class", "x axis")
// .attr("transform", "translate(0," + this.height + ")")
// .call(xAxis);
this.yAxis = d3.axisLeft(this.y)
.ticks(5);
// Define the line
this.priceline = d3Shape.line()
.x(function (d) { return X(new Date(d['date'])); })
.y(function (d) { return Y(d['peoplesum']); });
}
private drawLine() {
if ( this.data[0]['d3_parameter_maker'] === true)
{
this.x.domain([0, d3.max(this.data, function (d) { return d['date']; })]);
}
else if ( this.data[0]['d3_parameter_maker'] === undefined)
{
//console.log("aksdad")
var mindate = new Date(this.dashboard_date['startTime']),
maxdate = new Date(this.dashboard_date['endTime']);
this.x.domain([mindate,maxdate]);
}
console.log(new Date(this.dashboard_date['startTime']));
// Scale the range of the data
var svgVar = this.svg;
var pricelineVar = this.priceline;
var margin = this.margin;
var height = this.height;
let thisObj = this;
this.mouseOver = [];
let X = this.x;
let Y = this.y;
if ( this.mouseFlag < 0) {
for ( let i = 0; i < this.peopleInSumArr.length; i++) {
this.mouseOver[i] = true;
}
} else {
for (let i = 0; i < this.peopleInSumArr.length; i++) {
if (i !== this.mouseFlag) {
this.mouseOver[i] = false;
}
}
this.mouseOver[this.mouseFlag] = true;
}
this.y.domain([0, d3.max(this.data, function (d) { return d['peoplesum']; })]);
// Nest the entries by symbol
var dataNest = d3.nest()
.key(function (d) { return d['storeid']; })
.entries(this.data);
// set the colour scale
var color = d3.scaleOrdinal(d3.schemeCategory10);
var legendSpace = this.width / dataNest.length; // spacing for the legend
var div1 = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
dataNest.forEach(function (data, i) {
thisObj.svg.append("path")
.attr("class", "line")
.style("fill", "none")
.attr("d", thisObj.priceline(data.values))
.attr('opacity', thisObj.mouseOver !== undefined && thisObj.mouseOver[i] === true ? 1 : 0.2)
.style('cursor', 'pointer')
.style("stroke", function () { // Add the colours dynamically
return data['color'] = color(data.key);
})
.attr("stroke-width", 3)
.on('click', function () { // on mouse in show line, circles and text
thisObj.mouseFlag = i;
thisObj.initSvg();
thisObj.initAxis();
thisObj.drawAxis();
thisObj.drawLine();
});
// Add the scatterplot
thisObj.svg.selectAll("dot")
.data(data.values)
.enter().append("circle")
.attr("r", 5)
.attr("cx", function (d) { return thisObj.x(new Date(d.date)); })
.attr("cy", function (d) { return thisObj.y(d.peoplesum); })
.style('cursor', 'pointer')
.on("mouseover", function (d) {
div1.transition()
.duration(200)
.style("opacity", .9);
// tslint:disable-next-line:no-unused-expression
div1.html("<b>Date: </b>" + d.date + "<br/>" + "<b>Sum: </b>" + d.peoplesum.toFixed(2))
.style('position', 'absolute')
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px")
.style('text-align', 'center')
.style('width', '100px')
.style('height', '30px')
.style('padding', '2px')
.style('font', '12px sans-serif')
.style('background-color', 'lightsteelblue')
.style('border', '0px')
.style('border-radius', '8px')
.style('cursor', 'pointer')
.style('pointer-events', 'none');
})
.on("mouseout", function (d) {
div1.transition()
.duration(500)
.style("opacity", 0);
});
// Add the X Axis
// Add the Y Axis
thisObj.svg.append("text")
.attr("x", (legendSpace / 2) + i * legendSpace) // space legend
.attr("y", height + (margin.bottom / 2) + 5)
.attr("class", "legend") // style the legend
.style('cursor', 'pointer')
.style("fill", function () { // Add the colours dynamically
return data['color'] = color(data.key);
})
.text(data.key)
.attr("stroke-width", 3)
.on('click', function () { // on mouse in show line, circles and text
thisObj.mouseFlag = i;
thisObj.initSvg();
thisObj.initAxis();
thisObj.drawAxis();
thisObj.drawLine();
});
});
// Add the X Axis
let xAxisSelection= this.svg.append("g")
.attr("class", "x-axis")
.attr("transform", "translate(0," + this.height + ")")
xAxisSelection.call(d3.axisBottom(this.x));
// Add the Y Axis
let yAxisLeft =this.svg.append("g")
.attr("class", "y axis")
yAxisLeft.call(d3.axisLeft(this.y));
var clip = this.svg.append("defs").append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("id", "clip-rect")
.attr("x", "0")
.attr("y", "0")
.attr("width", this.width)
.attr("height", this.height);
// Add the line by appending an svg:path element with the data line we created above
// do this AFTER the axes above so that the line is above the tick-lines
var path = this.svg.append("svg")
.attr("class","path")
.attr("clip-path", "url(#clip)")
.attr( thisObj.priceline(this.data));
function zoom(begin, end) {
thisObj.x.domain([begin, end - 1]);
var t = thisObj.svg.transition().duration(0);
var size = moment(moment(end).toDate()).diff(moment(begin).toDate(), 'days');
console.log("size",size);
var step = size / 10;
var ticks = [];
const startDate = new Date(moment(begin).toDate());
for (var i = 0; i <= 10; i++) {
var xAxisDate = new Date(moment(begin).toDate())
// Add a day
xAxisDate.setDate(startDate.getDate() + i)
ticks.push(xAxisDate);
}
xAxisSelection.call(d3.axisBottom(thisObj.x.domain(d3.extent(ticks))));
}
//console.log("this.data)",this.data)
$(function() {
$( "#slider-range" ).slider({
range: true,
min: new Date(mindate).getTime() / 1000,
max: new Date(maxdate).getTime() / 1000,
step: 86400,
values: [ new Date(mindate).getTime() / 1000, new Date(maxdate).getTime() / 1000 ],
slide: function( event, ui ) {
//console.log("ui.values[0]",ui.values)
var begin = d3.min([(new Date(ui.values[ 0 ] *1000).toDateString() ), thisObj.data.length]);
var end = new Date(ui.values[ 1 ] *1000).toDateString(); // 0]);
//console.log("begin:", moment(begin).toDate(), "end:", moment(end).format('yyyy-mm-dd'), thisObj.data);
console.log(begin);
console.log(end);
zoom(begin, end);
}
});
});
}
}
Please find below the screenshot of the graph
Adding the clip path functionality because data was going beyond y axis.
private initSvg() {
d3.select("svg").remove();
this.svg = d3.select("#d3Id")
.append("svg")
.attr("width", this.width + this.margin.left + this.margin.right)
.attr("height", this.height + this.margin.top + this.margin.bottom)
.append("g")
.attr("transform",
"translate(" + this.margin.left + "," + this.margin.top + ")")
.attr("stroke-width", 2);
}
private initAxis() {
// Parse the date / time
// var parseDate = timeParse("%b %Y");
// this.formatTime = timeParse("%e %B");
// Set the ranges
this.x = d3Scale.scaleTime().range([0, this.width]);
this.y = d3Scale.scaleLinear().range([this.height, 0]);
}
private drawAxis() {
var X = this.x;
var Y = this.y;
this.xAxis = d3.axisBottom(this.x)
.ticks(5);
// var xAxis = d3.axisBottom(this.x).tickSize(-this.height).ticks(3);
// // Add the x-axis.
// this.svg.append("svg:g")
// .attr("class", "x axis")
// .attr("transform", "translate(0," + this.height + ")")
// .call(xAxis);
this.yAxis = d3.axisLeft(this.y)
.ticks(5);
// Define the line
this.priceline = d3Shape.line()
.x(function(d) {
return X(new Date(d['date']));
})
.y(function(d) {
return Y(d['peoplesum']);
});
}
private drawLine() {
if (this.data[0]['d3_parameter_maker'] === true) {
this.x.domain([1, d3.max(this.data, function(d) {
return parseInt(d['date']);
})]);
} else if (this.data[0]['d3_parameter_maker'] === undefined) {
var mindate = new Date(this.dashboard_date['startTime']),
maxdate = new Date(this.dashboard_date['endTime']);
this.x.domain([mindate, maxdate]);
}
console.log(new Date(this.dashboard_date['startTime']));
// Scale the range of the data
var svgVar = this.svg;
var pricelineVar = this.priceline;
var margin = this.margin;
var height = this.height;
let thisObj = this;
this.mouseOver = [];
let X = this.x;
let Y = this.y;
if (this.mouseFlag < 0) {
for (let i = 0; i < this.peopleInSumArr.length; i++) {
this.mouseOver[i] = true;
}
} else {
for (let i = 0; i < this.peopleInSumArr.length; i++) {
if (i !== this.mouseFlag) {
this.mouseOver[i] = false;
}
}
this.mouseOver[this.mouseFlag] = true;
}
this.y.domain([0, d3.max(this.data, function(d) {
return d['peoplesum'];
})]);
var clip = thisObj.svg.append("defs").append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("id", "clip-rect")
.attr("x", "0")
.attr("y", "0")
.attr("width", this.width)
.attr("height", this.height);
// Nest the entries by symbol
var dataNest = d3.nest()
.key(function(d) {
return d['storeid'];
})
.entries(this.data);
// set the colour scale
var color = d3.scaleOrdinal(d3.schemeCategory10);
var legendSpace = this.width / dataNest.length; // spacing for the legend
var div1 = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
dataNest.forEach(function(data, i) {
thisObj.svg.append("path")
.attr("class", "line")
.style("fill", "none")
.datum(data)
.attr("d", function(d) {
return thisObj.priceline(d.values);
})
.attr('opacity', thisObj.mouseOver !== undefined && thisObj.mouseOver[i] === true ? 1 : 0.2)
.style('cursor', 'pointer')
.style("stroke", function() { // Add the colours dynamically
return data['color'] = color(data.key);
})
.attr("stroke-width", 3)
.attr("clip-path", "url(#clip)")
.on('click', function() { // on mouse in show line, circles and text
thisObj.mouseFlag = i;
thisObj.initSvg();
thisObj.initAxis();
thisObj.drawAxis();
thisObj.drawLine();
});
// Add the scatterplot
thisObj.svg.selectAll("dot")
.data(data.values)
.enter().append("circle")
.attr("r", 5)
.attr("cx", function(d) {
return thisObj.x(new Date(d.date));
})
.attr("cy", function(d) {
return thisObj.y(d.peoplesum);
})
.style('cursor', 'pointer')
.on("mouseover", function(d) {
div1.transition()
.duration(200)
.style("opacity", .9);
// tslint:disable-next-line:no-unused-expression
div1.html("<b>Date: </b>" + d.date + "<br/>" + "<b>Sum: </b>" + d.peoplesum.toFixed(2))
.style('position', 'absolute')
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px")
.style('text-align', 'center')
.style('width', '100px')
.style('height', '30px')
.style('padding', '2px')
.style('font', '12px sans-serif')
.style('background-color', 'lightsteelblue')
.style('border', '0px')
.style('border-radius', '8px')
.style('cursor', 'pointer')
.style('pointer-events', 'none');
})
.on("mouseout", function(d) {
div1.transition()
.duration(500)
.style("opacity", 0);
});
// Add the X Axis
// Add the Y Axis
thisObj.svg.append("text")
.attr("x", (legendSpace / 2) + i * legendSpace) // space legend
.attr("y", height + (margin.bottom / 2) + 5)
.attr("class", "legend") // style the legend
.style('cursor', 'pointer')
.style("fill", function() { // Add the colours dynamically
return data['color'] = color(data.key);
})
.text(data.key)
.attr("stroke-width", 3)
.on('click', function() { // on mouse in show line, circles and text
thisObj.mouseFlag = i;
thisObj.initSvg();
thisObj.initAxis();
thisObj.drawAxis();
thisObj.drawLine();
});
});
// Add the X Axis
let xAxisSelection = this.svg.append("g")
.attr("class", "x-axis")
.attr("transform", "translate(0," + this.height + ")")
xAxisSelection.call(d3.axisBottom(this.x));
// Add the Y Axis
this.svg.append("g")
.attr("class", "axis")
.call(d3.axisLeft(this.y));
if (this.data[0]['d3_parameter_maker'] === undefined)
{
this.daily_Data_slider(mindate,xAxisSelection,maxdate)
}
else
{
this.hourly_Data_slider(xAxisSelection)
}
}
private daily_Data_slider(mindate,xAxisSelection,maxdate){
var svgVar = this.svg;
var pricelineVar = this.priceline;
var margin = this.margin;
var height = this.height;
let thisObj = this;
function zoom(begin, end) {
thisObj.x.domain([new Date(begin), new Date(end)]);
var t = thisObj.svg.transition().duration(0);
var size = moment(moment(end).toDate()).diff(moment(begin).toDate(), 'days');
console.log("size", size);
var step = size / 10;
var ticks = [];
const startDate = new Date(moment(begin).toDate());
for (var i = 0; i <= 10; i++) {
var xAxisDate = new Date(moment(begin).toDate())
// Add a day
xAxisDate.setDate(startDate.getDate() + i)
ticks.push(xAxisDate);
}
xAxisSelection.call(d3.axisBottom(thisObj.x.domain(d3.extent(ticks))));
// Redraw the line:
d3.selectAll("circle")
.attr("cx", function(d) {
return thisObj.x(new Date(d.date));
})
.attr("cy", function(d) {
return thisObj.y(d.peoplesum);
})
d3.selectAll(".line").attr("d", function(d) {
return thisObj.priceline(d.values);
})
}
//console.log("this.data)",this.data)
$(function() {
$("#slider-range").slider({
range: true,
min: new Date(mindate).getTime() / 1000,
max: new Date(maxdate).getTime() / 1000,
step: 86400,
values: [new Date(mindate).getTime() / 1000, new Date(maxdate).getTime() / 1000],
slide: function(event, ui) {
//console.log("ui.values[0]",ui.values)
var begin = d3.min([(new Date(ui.values[0] * 1000).toDateString()), thisObj.data.length]);
var end = new Date(ui.values[1] * 1000).toDateString(); // 0]);
//console.log("begin:", moment(begin).toDate(), "end:", moment(end).format('yyyy-mm-dd'), thisObj.data);
console.log("begin", begin);
console.log("end", end);
if (new Date(moment(moment(moment(begin).toDate()).add(10, 'days')).format("YYYY-MM-DD")) <= new Date(end)) {
zoom(begin, end);
}
}
});
});
}
I am new to D3 and trying to dynamically update the chart if the source json is modified. But I am not able to achieve this.
Please check this plunkr
Js:
var width = 500,
height = 500,
radius = Math.min(width, height) / 2;
var x = d3.scale.linear()
.range([0, 2 * Math.PI]);
var y = d3.scale.sqrt()
.range([0, radius]);
var color = d3.scale.category10();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + (height / 2 + 10) + ") rotate(-90 0 0)");
var partition = d3.layout.partition()
.value(function(d) {
return d.size;
});
var arc = d3.svg.arc()
.startAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x)));
})
.endAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
})
.innerRadius(function(d) {
return Math.max(0, y(d.y));
})
.outerRadius(function(d) {
return Math.max(0, y(d.y + d.dy));
});
//d3.json("/d/4063550/flare.json", function(error, root) {
var root = initItems;
var g = svg.selectAll("g")
.data(partition.nodes(root))
.enter().append("g");
var path = g.append("path")
.attr("d", arc)
.style("fill", function(d) {
return color((d.children ? d : d.parent).name);
})
.on("click", click)
.each(function(d) {
this.x0 = d.x;
this.dx0 = d.dx;
});
//.append("text")
var text = g.append("text")
.attr("x", function(d) {
return y(d.y);
})
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.attr("transform", function(d) {
return "rotate(" + computeTextRotation(d) + ")";
})
.text(function(d) {
return d.name;
})
.style("fill", "white");
function computeTextRotation(d) {
var angle = x(d.x + d.dx / 2) - Math.PI / 2;
return angle / Math.PI * 180;
}
function click(d) {
console.log(d)
// fade out all text elements
if (d.size !== undefined) {
d.size += 100;
};
text.transition().attr("opacity", 0);
path.transition()
.duration(750)
.attrTween("d", arcTween(d))
.each("end", function(e, i) {
// check if the animated element's data e lies within the visible angle span given in d
if (e.x >= d.x && e.x < (d.x + d.dx)) {
// get a selection of the associated text element
var arcText = d3.select(this.parentNode).select("text");
// fade in the text element and recalculate positions
arcText.transition().duration(750)
.attr("opacity", 1)
.attr("transform", function() {
return "rotate(" + computeTextRotation(e) + ")"
})
.attr("x", function(d) {
return y(d.y);
});
}
});
} //});
// Word wrap!
var insertLinebreaks = function(t, d, width) {
alert(0)
var el = d3.select(t);
var p = d3.select(t.parentNode);
p.append("g")
.attr("x", function(d) {
return y(d.y);
})
// .attr("dx", "6") // margin
//.attr("dy", ".35em") // vertical-align
.attr("transform", function(d) {
return "rotate(" + computeTextRotation(d) + ")";
})
//p
.append("foreignObject")
.attr('x', -width / 2)
.attr("width", width)
.attr("height", 200)
.append("xhtml:p")
.attr('style', 'word-wrap: break-word; text-align:center;')
.html(d.name);
alert(1)
el.remove();
alert(2)
};
//g.selectAll("text")
// .each(function(d,i){ insertLinebreaks(this, d, 50 ); });
d3.select(self.frameElement).style("height", height + "px");
// Interpolate the scales!
function arcTween(d) {
var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, 1]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function(d, i) {
return i ? function(t) {
return arc(d);
} : function(t) {
x.domain(xd(t));
y.domain(yd(t)).range(yr(t));
return arc(d);
};
};
}
function arcTweenUpdate(a) {
console.log(path);
var _self = this;
var i = d3.interpolate({ x: this.x0, dx: this.dx0 }, a);
return function(t) {
var b = i(t);
console.log(window);
_self.x0 = b.x;
_self.dx0 = b.dx;
return arc(b);
};
}
setTimeout(function() {
path.data(partition.nodes(newItems))
.transition()
.duration(750)
.attrTween("d", arcTweenUpdate)
}, 2000);
In addition to what #Cyril has suggested about removing the following line:
d3.select(self.frameElement).style("height", height + "px");
I made further modifications in your fiddle: working fiddle
The idea used here is to add a function updateChart which takes the items and then generate the chart:
var updateChart = function (items) {
// code to update the chart with new items
}
updateChart(initItems);
setTimeout(function () { updateChart(newItems); }, 2000);
This doesn't use the arcTweenUpdate function you have created but I will try to explain the underlying concept:
First, you will need to JOIN the new data with your existing data:
// DATA JOIN - Join new data with old elements, if any.
var gs = svg.selectAll("g").data(partition.nodes(root));
then, ENTER to create new elements if required:
// ENTER
var g = gs.enter().append("g").on("click", click);
But, we also need to UPDATE the existing/new path and text nodes with new data:
// UPDATE
var path = g.append("path");
gs.select('path')
.style("fill", function(d) {
return color((d.children ? d : d.parent).name);
})
//.on("click", click)
.each(function(d) {
this.x0 = d.x;
this.dx0 = d.dx;
})
.transition().duration(500)
.attr("d", arc);
var text = g.append("text");
gs.select('text')
.attr("x", function(d) {
return y(d.y);
})
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.attr("transform", function(d) {
return "rotate(" + computeTextRotation(d) + ")";
})
.text(function(d) {
return d.name;
})
.style("fill", "white");
and, after everything is created/updated remove the g nodes which are not being used i.e. EXIT:
// EXIT - Remove old elements as needed.
gs.exit().transition().duration(500).style("fill-opacity", 1e-6).remove();
This whole pattern of JOIN + ENTER + UPDATE + EXIT is demonstrated in following articles by Mike Bostock:
General Update Pattern - I
General Update Pattern - II
General Update Pattern - III
In side the fiddle the setTimeout is not running because:
d3.select(self.frameElement).style("height", height + "px");
You will get Uncaught SecurityError: Failed to read the 'frame' property from 'Window': Blocked a frame with origin "https://fiddle.jshell.net" from accessing a frame with origin and the setTimeout never gets called.
So you can remove this line d3.select(self.frameElement).style("height", height + "px"); just for the fiddle.
Apart from that:
Your timeout function should look like this:
setTimeout(function() {
//remove the old graph
svg.selectAll("*").remove();
root = newItems;
g = svg.selectAll("g")
.data(partition.nodes(newItems))
.enter().append("g");
/make path
path = g.append("path")
.attr("d", arc)
.style("fill", function(d) {
return color((d.children ? d : d.parent).name);
})
.on("click", click)
.each(function(d) {
this.x0 = d.x;
this.dx0 = d.dx;
});
//make text
text = g.append("text")
.attr("x", function(d) {
return y(d.y);
})
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.attr("transform", function(d) {
return "rotate(" + computeTextRotation(d) + ")";
})
.text(function(d) {
return d.name;
})
.style("fill", "white");
}
working fiddle here
for the enter() and transitions to work you need to give d3 a way to identify each item in your data. the .data() function has a second parameter that lets you return something to use as an id. enter() will use the id to decide whether the object is new.
try changing
path.data(partition.nodes(newItems))
.data(partition.nodes(root));
to
path.data(partition.nodes(newItems), function(d){return d.name});
.data(partition.nodes(root), function(d){return d.name});
I've made a pie-chart and trying to display it on a page using require.js but can't do it correctly. Firebug shows that there is svg on this page with certain size and the page is empty. I tried to implement other moodules - they work well.
File main.js:
require.config({
paths: {
'd3': "http://d3js.org/d3.v3.min"
},
shim: {
'd3': {exports:'d3'}
}
});
require([
'd3',
'pie-chart'
], function (d3, piechart) {
d3.select("body").append("h1").text("Successfully loaded D3 version " + d3.version);
d3.select("body").append("svg");
});
File pie-chart.js:
define('pie-chart', function () {
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var legendRectSize = 18;
var legendSpacing = 4;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var percentageFormat = d3.format("%");
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.values;
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
d3.json("staff.json", function(error, json_data) {
var data = d3.nest()
.key(function(d) {
return d.Position;
})
.rollup(function(d) {
return d.length;
}).entries(json_data);
data.forEach(function(d) {
d.percentage = d.values / json_data.length;
});
console.log(data)
console.log("data variable", data);
console.log("pie(data)", pie(data));
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc")
.on('mouseover', function() {
var current = this;
var others = svg.selectAll(".arc").filter(function(el) {
return this != current
});
others.selectAll("path").style('opacity', 0.8);
})
.on('mouseout', function() {
var current = this;
d3.select(this)
.style('opacity', 1);
var others = svg.selectAll(".arc").filter(function(el) {
return this != current
});
others.selectAll("path").style('opacity', 1);
});
g.append("path")
.attr("d", arc)
.style("fill", function(d, i) {
return color(d.data.key);
});
g.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) {
console.log("d is", d);
return percentageFormat(d.data.percentage);
});
var legend = d3.select("body").append("svg")
.attr("class", "legend")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {
return color(d.key);
});
legend.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) { return d.key; });
});
});
File d3.js I wrapped this way:
define('d3', function () {
// require.js code
});
d3 is not dependent on JQuery. I removed the jquery from your plunk, also you do not need the shim attribute. Your main.js should be something like this:
require.config({
paths: {
'd3': "https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.9/d3.min"
}
});
require([
'd3',
'pie-chart'
], function ( d3, $, piechart) {
});
Here is the working plunk link: http://plnkr.co/edit/Le4tpejMvPxLA08isacW?p=preview
I'm trying to display a sunburst with text. I used the code from a sunburst example and tried to add text to it (worked), but when it updates, my text disappears. I can only get it to display all text or text when loaded then no text. So when it updates, the text is either gone or is not in sync with the data.
Can anybody tell me what is wrong because I don't know anymore.
Original code: http://bl.ocks.org/mbostock/4348373
My adaptions:
var width = 1060,
height = 900,
radius = Math.min(width, height) / 2;
var x = d3.scale.linear()
.range([0, 2 * Math.PI]);
var y = d3.scale.sqrt()
.range([0, radius]);
var color = d3.scale.category20c();
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + (height / 2) + ")");
var partition = d3.layout.partition()
.value(function(d) { return d.size; });
var arc = d3.svg.arc()
.startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))); })
.endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); })
.innerRadius(function(d) { return Math.max(0, y(d.y)); })
.outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); });
d3.json("http://localhost:50043/data.json", function (error, root) {
if (error) throw error;
var data = partition.nodes(root);
var path = svg.selectAll("path")
.data(partition.nodes(root))
.enter()
//.append("g");
.append("path")
//.attr("display", function (d) { return d.depth ? null : "none"; })
.attr("d", arc)
.style("fill", function (d) { return color((d.children ? d : d.parent).name); })
.on("click", click);
var text = svg.selectAll("text")
.data(data)
.enter()
.append("text")
.classed("label", true)
.attr("x", function (d) { return d.x; })
.attr("text-anchor", "middle")
// translate to the desired point and set the rotation
.attr("transform", function (d) {
if (d.depth > 0) {
return "translate(" + arc.centroid(d) + ")" +
"rotate(" + getAngle(d) + ")";
} else {
return null;
}
})
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.attr("pointer-events", "none")
.text(function (d) { return d.name; });
function click(data) {
text.remove();
text = svg.selectAll("text")
.data(data)
.enter()
.append("text")
.classed("label", true)
.attr("x", function(d) { return d.x; })
.attr("text-anchor", "middle")
// translate to the desired point and set the rotation
.attr("transform", function(d) {
if (d.depth > 0) {
return "translate(" + arc.centroid(d) + ")" +
"rotate(" + getAngle(d) + ")";
} else {
return null;
}
})
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.attr("pointer-events", "none")
.text(function (d) { return d.name; });
path.transition()
.duration(750)
.attrTween("d", arcTween(data));
}
function getAngle(d) {
// Offset the angle by 90 deg since the '0' degree axis for arc is Y axis, while
// for text it is the X axis.
var thetaDeg = (180 / Math.PI * (arc.startAngle()(d) + arc.endAngle()(d)) / 2 - 90);
// If we are rotating the text by more than 90 deg, then "flip" it.
// This is why "text-anchor", "middle" is important, otherwise, this "flip" would
// a little harder.
return (thetaDeg > 90) ? thetaDeg - 180 : thetaDeg;
}
function arcTween(d) {
var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, 1]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function (d, i) {
return i
? function (t) { return arc(d); }
: function (t) { x.domain(xd(t)); y.domain(yd(t)).range(yr(t)); return arc(d); };
};
}
});
d3.select(self.frameElement).style("height", height + "px");
Update: You can find my code on https://github.com/KenBonny/D3-Chart-Test
You can view the handywork on github page: https://kenbonny.github.io/D3-Chart-Test/
It's because you are not passing any data to click
wirte function click() { instead of function click(data) {
and I think it will work
I created a simple age pyramid bar chart with D3.js using this example as a guide: http://www.jasondavies.com/d3-pyramid/. This works fine but I want to dynamically update this chart based on a users selection of data. When I append the new data to the existing bars the < g > element and rects grow beyond the width of the svg container. My first thought was to set a max width of the < g > elements to the width of the container hoping that the rects would scale accordingly like they did in the initial rendering but that does not seem to be possible.
My biggest issue ( besides needing to clean up the code :) ) is that I don't understand why the bar widths look great in the initial rendering but then grow exceedingly in the update. I think this is probably some fundamental misunderstanding i have with D3/SVG but I could use some guidance.
Any help is appreciated!
Initial Chart Generation (this works)
var ageChart,
ageBar,
ageBars,
ageTotal,
dataRange,
yScale,
topMargin,
ageChartWidth,
ageLabelSpace,
ageInnerMargin,
commas = d3.format(",.0f");
function generateAgeChart(data) {
ageData = processAgeData(data);
ageLabelSpace = 25;
ageInnerMargin = width / 2 + ageLabelSpace;
var outerMargin = 30,
gap = 8,
leftLabel = "Female",
rightLabel = "Male",
height = 180;
barWidth = height / ageData.length;
width = 200;
ageChartWidth = width - ageInnerMargin - outerMargin;
topMargin = 25;
yScale = d3.scale.linear().domain([0, ageData.length]).range([0, height - topMargin]);
dataRange = d3.max(ageData.map(function (d) { return Math.max(d.female, d.male) }));
ageTotal = d3.scale.linear().domain([0, dataRange]).range([0, ageChartWidth - ageLabelSpace]);
/* main panel */
ageChart = d3.select("#chart-3").append("svg")
.attr("class", "d3-chart")
.attr("width", width)
.attr("height", height);
/* female label */
ageChart.append("text")
.attr("class", "bar-label")
.text(leftLabel)
.attr("x", width - ageInnerMargin)
.attr("y", topMargin - 3)
.attr("text-anchor", "end");
/* male label */
ageChart.append("text")
.attr("class", "bar-label")
.text(rightLabel)
.attr("x", ageInnerMargin)
.attr("y", topMargin - 3);
/* bars and data labels */
ageBar = ageChart.selectAll("g.bar")
.data(ageData)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function (d, i) {
return "translate(0," + (yScale(i) + topMargin) + ")";
});
var highlight = function (c) {
return function (d, i) {
ageBar.filter(function (d, j) {
return i === j;
}).attr("class", c);
};
};
ageBar
.on("mouseover", highlight("highlight bar"))
.on("mouseout", highlight("bar"));
ageBar.append("rect")
.attr("class", "femalebar")
.attr("height", barWidth - gap);
ageBar.append("text")
.attr("class", "femalebar")
.attr("dx", -3)
.attr("dy", "1.7em")
.attr("text-anchor", "end");
ageBar.append("rect")
.attr("class", "malebar")
.attr("height", barWidth - gap)
.attr("x", ageInnerMargin);
ageBar.append("text")
.attr("class", "malebar")
.attr("dx", 3)
.attr("dy", "1.7em");
/* sharedLabels */
ageBar.append("text")
.attr("class", "shared")
.attr("x", width / 2)
.attr("dy", "1.7em")
.attr("text-anchor", "middle")
.text(function (d) { return d.sharedLabel; });
// Draw the chart
ageBars = d3.selectAll("g.bar")
.data(ageData);
ageBars.selectAll("rect.malebar")
.transition()
.attr("width", function (d) { return ageTotal(d.male); });
ageBars.selectAll("rect.femalebar")
.transition()
.attr("x", function (d) { return ageInnerMargin - ageTotal(d.female) - 2 * ageLabelSpace; })
.attr("width", function (d) { return ageTotal(d.female); });
ageBars.selectAll("text.malebar")
.text(function (d) { return commas(d.male); })
.transition().attr("x", function (d) { return ageInnerMargin + ageTotal(d.male); });
ageBars.selectAll("text.femalebar")
.text(function (d) { return commas(d.female); })
.transition()
.attr("x", function (d) { return ageInnerMargin - ageTotal(d.female) - 2 * ageLabelSpace; });
// Title
ageChart.append("text")
.attr("x", (width / 2))
.attr("y", 10)
.attr("text-anchor", "middle")
.attr("font-size", "10pt")
.style("fill", "#333960")
.style("font-weight", "bold")
//.style("text-decoration", "underline")
.style("font-weight", "bold")
.text("Age");
}
Re-Draw Chart Function (the problem area)
function redrawAgeChart(data) {
// Get and process data
ageData = processAgeData(data);
width = 200;
height = 180;
outerMargin = 30;
topMargin = 25;
gap = 8;
height = 180;
barWidth = height / ageData.length;
ageLabelSpace = 25;
ageInnerMargin = width / 2 + ageLabelSpace;
ageChartWidth = width - ageInnerMargin - outerMargin;
yScale = d3.scale.linear().domain([0, ageData.length]).range([0, height - topMargin]);
dataRange = d3.max(ageData.map(function (d) { return Math.max(d.female, d.male) }));
ageTotal = d3.scale.linear().domain([0, dataRange]).range([0, ageChartWidth - ageLabelSpace]);
ageBars = d3.selectAll("g.bar")
.data(ageData);
ageBars.selectAll("rect.malebar")
.transition()
.attr("width", function (d) { return ageTotal(d.male); });
ageBars.selectAll("rect.femalebar")
.transition()
.attr("x", function (d) { return ageInnerMargin - ageTotal(d.female) - 2 * ageLabelSpace; })
.attr("width", function (d) { return ageTotal(d.female); });
ageBars.selectAll("text.malebar")
.text(function (d) { return commas(d.male); })
.transition().attr("x", function (d) { return ageInnerMargin + ageTotal(d.male); });
ageBars.selectAll("text.femalebar")
.text(function (d) { return commas(d.female); })
.transition().attr("x", function (d) { return ageInnerMargin - ageTotal(d.female) - 2 * ageLabelSpace; });
}
Get data function (you can hit this service... it is public)
function processAgeData(data) {
ageData = [];
var totalF_6_17 = 0;
var totalF_18_34 = 0;
var totalF_35_54 = 0;
var totalF_55_plus = 0;
var totalF_under5 = 0;
var totalM_6_17 = 0;
var totalM_18_34 = 0;
var totalM_35_54 = 0;
var totalM_55_plus = 0;
var totalM_under5 = 0;
// Loop through return to build a new array of values
$.each(data.features, function (key, val) {
var f_6_17 = val.properties.f_6_17;
var f_18_34 = val.properties.f_18_34;
var f_35_54 = val.properties.f_35_54;
var f_55_plus = val.properties.f_55_plus;
var f_under5 = val.properties.f_under5;
var m_6_17 = val.properties.m_6_17;
var m_18_34 = val.properties.m_18_34;
var m_35_54 = val.properties.m_35_54;
var m_55_plus = val.properties.m_55_plus;
var m_under5 = val.properties.m_under5;
totalF_6_17 = totalF_6_17 + f_6_17;
totalF_18_34 = totalF_18_34 + f_18_34;
totalF_35_54 = totalF_35_54 + f_35_54;
totalF_55_plus = totalF_55_plus + f_55_plus;
totalF_under5 = totalF_under5 + f_under5;
totalM_6_17 = totalM_6_17 + m_6_17;
totalM_18_34 = totalM_18_34 + m_18_34;
totalM_35_54 = totalM_35_54 + m_35_54;
totalM_55_plus = totalM_55_plus + m_55_plus;
totalM_under5 = totalM_under5 + m_under5;
});
var under5Obj = new Object();
under5Obj.sharedLabel = "< 5";
under5Obj.female = totalF_under5;
under5Obj.male = totalM_under5;
var age6_17Obj = new Object();
age6_17Obj.sharedLabel = "6 - 17";
age6_17Obj.female = totalF_6_17;
age6_17Obj.male = totalM_6_17;
var age18_34Obj = new Object();
age18_34Obj.sharedLabel = "18 - 34";
age18_34Obj.female = totalF_18_34;
age18_34Obj.male = totalM_18_34;
var age35_54Obj = new Object();
age35_54Obj.sharedLabel = "35 - 54";
age35_54Obj.female = totalF_35_54;
age35_54Obj.male = totalM_35_54;
var over55Obj = new Object();
over55Obj.sharedLabel = "55 +";
over55Obj.female = totalF_55_plus;
over55Obj.male = totalM_55_plus;
ageData.push(under5Obj);
ageData.push(age6_17Obj);
ageData.push(age18_34Obj);
ageData.push(age35_54Obj);
ageData.push(over55Obj);
return ageData;
}
// Age Chart Request
//// use this url to get the entire dataset which should display correctly
url = 'http://gis.drcog.org/geoserver/DRCOGPUB/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=DRCOGPUB:rea_demographics_age_county_view&maxFeatures=10000&outputFormat=json&propertyName=f_under5,f_6_17,f_18_34,f_35_54,f_55_plus,m_under5,m_6_17,m_18_34,m_35_54,m_55_plus,geoid&format_options=callback:redrawAgeChart'
//// use this for the update request
selectionUrl = "http://gis.drcog.org/geoserver/DRCOGPUB/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=DRCOGPUB:rea_demographics_age_county_view&maxFeatures=10000&outputFormat=json&propertyName=f_under5,f_6_17,f_18_34,f_35_54,f_55_plus,m_under5,m_6_17,m_18_34,m_35_54,m_55_plus,geoid&format_options=callback:redrawAgeChart&cql_filter=geoid%20IN%20('08059')"
$.ajax({
type: 'get',
url: url,
dataType: "jsonp",
crossDomain: true,
cache: false,
error: function (jqXHR, textStatus, errorThrown) { console.log(textStatus); }
});
Well, I learned a bit about D3 and SVG and managed to catch my rather silly mistakes. I was appending the data to the rect elements in the wrong way. I solved this by selecting out male/female bars directly from the chart svg separately and appending the data to each of those selections. This now works as planned.
yScale = d3.scale.linear().domain([0, ageData.length]).range([0, height - topMargin]);
dataRange = d3.max(ageData.map(function (d) { return Math.max(d.female, d.male) }));
ageTotal = d3.scale.linear().domain([0, dataRange]).range([0, ageChartWidth - ageLabelSpace]);
ageChart.selectAll("rect.malebar")
.data(ageData)
.transition()
.attr("width", function (d) { return ageTotal(d.male); });
ageChart.selectAll("rect.femalebar")
.data(ageData)
.transition()
.attr("x", function (d) { return ageInnerMargin - ageTotal(d.female) - 2 * ageLabelSpace; })
.attr("width", function (d) { return ageTotal(d.female); });
ageChart.selectAll("text.malebar")
.data(ageData)
.text(function (d) { return commas(d.male); });
});
ageChart.selectAll("text.femalebar")
.data(ageData)
.text(function (d) { return commas(d.female); });