Related
I am following this example, but I am using d3 v4. My d.date have no values. Can't figure out why. The csv file has correct time format. Can you help me?
<script type="text/javascript">
var margin = {top: 20, right: 80, bottom: 30, left: 50},
w = 800 - margin.left - margin.right,
h = 600 - margin.top - margin.bottom;
var x = d3.scaleTime()
.domain([new Date("January 1, 2012"), new Date("May 31, 2017")])
.range([0, w]);
var y = d3.scaleLinear()
.range([h, 0]);
var xAxis = d3.axisBottom()
.scale(x)
.tickFormat(d3.timeFormat("%m/%Y"));
var yAxis = d3.axisLeft()
.scale(y)
var parseDate = d3.timeParse("%d%m%Y");
console.log(parseDate);
var color = d3.scaleOrdinal(d3.schemeCategory10);
var line = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.stat); });
var svg = d3.select("body").append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("svg:g")
.attr("class", "x_axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis);
svg.append("svg:g")
.attr("class", "y_axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("fill", "#000")
.text("Numbers");
var menu = d3.select("#menu select")
.on("change", change);
d3.csv("Data4.csv", function(csv) {
console.log(csv);
medias = csv
redraw();
});
d3.select(window)
.on("keydown", function() { altKey = d3.event.altKey; })
.on("keyup", function() { altKey = false; });
var altKey;
function change() {
clearTimeout(timeout);
d3.transition()
.duration(altKey ? 7500 : 1500)
.each(redraw);
}
function redraw() {
var nested = d3.nest()
.key(function(d) { return d.indicatorCode;})
.object(medias)
var series = menu.property("value");
var data = nested[series];
console.log(data);
var keyring = d3.keys(data[0]).filter(function(key) {
return (key !== "Sel_name" && key !== "monthCode" && key !== "indicatorCode" && key !== "date");
// console.log(keyring);
});
var transpose = keyring.map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: new Date(parseDate(d.date)), stat: d[name]};
})
};
});
console.log(transpose);
x.domain([
d3.min(transpose, function(c) { return d3.min(c.values, function(d) { return d.date; }); }),
d3.max(transpose, function(c) { return d3.max(c.values, function(d) { return d.date; }); })
]);
y.domain([
d3.min(transpose, function(c) { return d3.min(c.values, function(d) { return d.stat; }); }),
d3.max(transpose, function(c) { return d3.max(c.values, function(d) { return d.stat; }); })
]);
var media = svg.selectAll(".media")
.data(transpose)
.enter().append("g")
.attr("class", "media")
.attr("id", function(d) { return d.name; });
media.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
media.append("text")
.attr("class", "names")
.datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.stat) + ")"; })
.attr("x", 4)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
var mediaUpdate = d3.transition(media);
mediaUpdate.select("path")
.attr("d", function(d) { return line(d.values); });
mediaUpdate.select("text")
.attr("transform", function(d) { return "translate(" + x(d.values[d.values.length - 1].date) + "," + y(d.values[d.values.length - 1].stat) + ")"; });
d3.transition(svg).select("y_axis")
.call(yAxis);
d3.transition(svg).select("x_axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis);
}
var timeout = setTimeout(function() {
menu.property("value", "NSPM").node().focus();
change();
}, 7000);
function smedia() {
var chkbox = document.getElementById("statmedia");
if (chkbox.checked) {
document.getElementById("media5").style.cssText = "opacity:1;",
document.getElementById("media6").style.cssText = "opacity:1;",
document.getElementById("media7").style.cssText = "opacity:1;",
document.getElementById("media8").style.cssText = "opacity:1;"
} else {
document.getElementById("media5").style.cssText = "",
document.getElementById("media6").style.cssText = "",
document.getElementById("media7").style.cssText = "",
document.getElementById("media8").style.cssText = "";
}};
function nstmedia() {
var chkbox = document.getElementById("nonstmedia")
if (chkbox.checked) {
document.getElementById("media1").style.cssText = "opacity:1;",
document.getElementById("media2").style.cssText = "opacity:1;",
document.getElementById("media3").style.cssText = "opacity:1;",
document.getElementById("media4").style.cssText = "opacity:1;",
document.getElementById("media5").style.cssText = "opacity:1;",
document.getElementById("media6").style.cssText = "opacity:1;",
document.getElementById("media7").style.cssText = "opacity:1;",
document.getElementById("media8").style.cssText = "opacity:1;",
document.getElementById("media9").style.cssText = "opacity:1;"
} else {
document.getElementById("media1").style.cssText = "",
document.getElementById("media2").style.cssText = "",
document.getElementById("media3").style.cssText = "",
document.getElementById("media4").style.cssText = "",
document.getElementById("media5").style.cssText = "",
document.getElementById("media6").style.cssText = "",
document.getElementById("media7").style.cssText = "",
document.getElementById("media8").style.cssText = "",
document.getElementById("media9").style.cssText = "";
}};
</script>
My cvs file looks like following:
Sel_name,indicatorCode,date,monthCode,media1,media2,media3,media4,media5,media6,media7,media8,media9,media10,media11,media12,media13
Num_posts_pmon,NPPM,31/01/2012,m1201,217,146,0,1114,0,0,0,0,0,0,0,0,54
Num_posts_pmon,NPPM,29/02/2012,m1202,159,161,0,1402,0,0,0,0,0,0,0,0,31
Num_posts_pmon,NPPM,31/03/2012,m1203,8,7,0,1212,0,0,0,24,0,0,0,28,17
Num_posts_pmon,NPPM,30/04/2012,m1204,10,14,0,1004,0,0,0,111,0,0,26,28,6
Num_posts_pmon,NPPM,31/05/2012,m1205,15,73,0,1070,0,0,0,76,0,0,127,557,6
Num_posts_pmon,NPPM,30/06/2012,m1206,102,396,0,834,0,0,0,97,0,0,23,893,1
Num_posts_pmon,NPPM,31/07/2012,m1207,148,276,0,993,0,0,0,63,0,0,67,602,0
Num_posts_pmon,NPPM,31/08/2012,m1208,167,189,0,909,34,0,0,54,0,0,31,606,3
Num_posts_pmon,NPPM,30/09/2012,m1209,176,196,0,991,254,0,0,80,0,0,81,608,8
Num_posts_pmon,NPPM,31/10/2012,m1210,121,184,0,1064,190,0,0,41,0,0,93,600,100
Num_posts_pmon,NPPM,30/11/2012,m1211,107,137,0,1301,78,0,0,68,0,0,110,416,116
Num_posts_pmon,NPPM,31/12/2012,m1212,73,108,0,1190,40,0,0,41,0,0,163,306,158
Num_posts_pmon,NPPM,31/01/2013,m1301,98,122,0,1266,121,0,0,53,0,0,137,329,201
Num_posts_pmon,NPPM,28/02/2013,m1302,92,108,0,1248,175,0,0,32,0,0,86,221,205
Num_posts_pmon,NPPM,31/03/2013,m1303,95,101,0,1330,88,0,0,59,0,0,112,160,243
Num_posts_pmon,NPPM,30/04/2013,m1304,122,127,0,1764,280,0,0,61,0,0,151,264,189
Num_posts_pmon,NPPM,31/05/2013,m1305,89,90,0,1645,630,0,785,123,715,0,144,275,203
Num_posts_pmon,NPPM,30/06/2013,m1306,83,103,0,1541,830,0,1105,130,1425,0,99,260,268
Num_posts_pmon,NPPM,31/07/2013,m1307,89,111,0,1468,895,0,1515,133,1443,0,40,145,318
Num_posts_pmon,NPPM,31/08/2013,m1308,82,86,0,1508,1105,0,1720,125,1488,0,65,227,250
Num_posts_pmon,NPPM,30/09/2013,m1309,264,117,0,1690,1900,14,1507,195,2515,0,105,226,251
Your specifier is wrong, you're forgetting the forward slashes.
Since your dates are like this:
31/01/2012
Your specifier should be:
var parseDate = d3.timeParse("%d/%m/%Y");
Besides that, you don't need the new Date in the map function:
var date = "31/01/2012";
var parseDate = d3.timeParse("%d/%m/%Y");
console.log(parseDate(date))
console.log(new Date(parseDate(date)))
<script src="https://d3js.org/d3.v4.min.js"></script>
I have a project that almost works the way I want. When a smaller dataset is added, slices are removed. It fails when a larger dataset is added. The space for the arc is added but no label or color is added for it.
This is my enter() code:
newArcs.enter()
.append("path")
.attr("stroke", "white")
.attr("stroke-width", 0.8)
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc);
What am I doing wrong?
I've fixed the code such that it works now:
// Tween Function
var arcTween = function(a) {
var i = d3.interpolate(this.current || {}, a);
this.current = i(0);
return function(t) {
return arc(i(t));
};
};
// Setup all the constants
var duration = 500;
var width = 500
var height = 300
var radius = Math.floor(Math.min(width / 2, height / 2) * 0.9);
var colors = ["#d62728", "#ff9900", "#004963", "#3497D3"];
// Test Data
var d2 = [{
label: 'apples',
value: 20
}, {
label: 'oranges',
value: 50
}, {
label: 'pears',
value: 100
}];
var d1 = [{
label: 'apples',
value: 100
}, {
label: 'oranges',
value: 20
}, {
label: 'pears',
value: 20
}, {
label: 'grapes',
value: 20
}];
// Set the initial data
var data = d1
var updateChart = function(dataset) {
arcs = arcs.data(donut(dataset), function(d) { return d.data.label });
arcs.exit().remove();
arcs.enter()
.append("path")
.attr("stroke", "white")
.attr("stroke-width", 0.8)
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc);
arcs.transition()
.duration(duration)
.attrTween("d", arcTween);
sliceLabel = sliceLabel.data(donut(dataset), function(d) { return d.data.label });
sliceLabel.exit().remove();
sliceLabel.enter()
.append("text")
.attr("class", "arcLabel")
.attr("transform", function(d) {
return "translate(" + (arc.centroid(d)) + ")";
})
.attr("text-anchor", "middle")
.style("fill-opacity", function(d) {
if (d.value === 0) {
return 1e-6;
} else {
return 1;
}
})
.text(function(d) {
return d.data.label;
});
sliceLabel.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + (arc.centroid(d)) + ")";
})
.style("fill-opacity", function(d) {
if (d.value === 0) {
return 1e-6;
} else {
return 1;
}
});
};
var color = d3.scale.category20();
var donut = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var arc = d3.svg.arc()
.innerRadius(radius * .4)
.outerRadius(radius);
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var arc_grp = svg.append("g")
.attr("class", "arcGrp")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
var label_group = svg.append("g")
.attr("class", "lblGroup")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
var arcs = arc_grp.selectAll("path");
var sliceLabel = label_group.selectAll("text");
updateChart(data);
// returns random integer between min and max number
function getRand() {
var min = 1,
max = 2;
var res = Math.floor(Math.random() * (max - min + 1) + min);
//console.log(res);
return res;
}
// Update the data
setInterval(function(model) {
var r = getRand();
return updateChart(r == 1 ? d1 : d2);
}, 2000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I have two y-axis with time as data.
I am trying to add and delete a line when ticks are clicked on the respective axis.
Lines are getting generated but not sure how to remove the lines. i tried using
svg.data([thisData]).remove('line')
but that removes the chart completely.
MORE DETAILS
there is 1-1 relationship between ticks of respective axis.
var data = [{
"inTime": "2013-04-24T00:00:00-05:00",
"outTime": "2013-04-24T00:00:00-05:00"
}, {
"inTime": "2013-04-24T00:00:00-05:00",
"outTime": "2013-04-24T00:00:00-05:00"
}, {
"inTime": "2013-04-24T00:00:00-05:00",
"outTime": "2013-04-24T00:00:00-05:00"
}, {
"inTime": "2013-04-26T00:00:00-05:00",
"outTime": "2013-04-26T00:00:00-05:00"
},
];
var margin = {
top: 40,
right: 40,
bottom: 40,
left: 40
},
width = 600,
height = 700;
//Define Left Y axis
var y = d3.time.scale()
.domain([new Date(data[0].inTime), d3.time.day.offset(new Date(data[data.length - 1].inTime), 1)])
.rangeRound([0, width - margin.left - margin.right]);
//Define Right Y axis
var y1 = d3.time.scale()
.domain([new Date(data[1].inTime), d3.time.day.offset(new Date(data[data.length - 1].outTime), 1)])
.rangeRound([0, width - margin.left - margin.right]);
//Left Yaxis attributes
var yAxis = d3.svg.axis()
.scale(y)
.orient('left')
.tickFormat(d3.time.format('%m/%d %H:%M'))
.tickSize(8)
.tickPadding(8);
//Right Yaxis attributes
var yAxisRight = d3.svg.axis()
.scale(y1)
.orient('right')
.tickFormat(d3.time.format('%m/%d %H:%M'))
.tickSize(8)
.tickPadding(8);
//Create chart
var svg = d3.select('body').append('svg')
.attr('class', 'chart')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');
//Add left Yaxis to group
svg.append('g')
.attr('class', 'y axis')
.attr('transform', 'translate(100, 5)')
.call(yAxis);
//Add right Yaxis to group
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(400, 1)')
.call(yAxisRight);
var parse = d3.time.format('%m/%d %H:%M');
//Function to add a line between two ticks
function addLine(t1, t2) {
var ticks = {};
d3.selectAll('.tick text').each(function(d) {
ticks[d3.select(this).text()] = this;
});
var pos1 = ticks[t1].getBoundingClientRect();
var pos2 = ticks[t2].getBoundingClientRect();
svg.append('line')
.attr('x1', pos1.top - pos1.width)
.attr('y1', pos1.top + 5)
.attr('x2', pos2.left - 5)
.attr('y2', pos2.top + 5)
.style('stroke', 'black')
}
var ticks = svg.selectAll(".tick");
ticks.attr('class', function(d, i) {
return 'ticks' + i;
}).each(function(d, i) {
d3.select(this).append("circle")
.attr('id', function(d) {
return 'tickCircle' + i;
})
.attr('class', function(d) {
return 'tickCircles' + this.id
})
.attr("r", 5)
.on('click', function(d) {
console.log('clicked')
return addLineNew(this);
})
.on('mouseover', function(d){
d3.select(this).style('fill','red'); })
.on('mouseout', function(d){
d3.select(this).style('fill','black'); })
});
ticks.selectAll("line").remove();
var firstTick;
var secondTick;
var secondTickMap={};
var firstTickMap={};
var allLines=[];
//add Line
function addLineNew(element) {
if (firstTick && secondTick) {
firstTick = '';
secondTick = '';
}
if (!firstTick || firstTick === '') {
firstTick = element.id
}
else if ((secondTick != 'undefined' || secondTick === '') && !(secondTick in firstTickMap)) {
secondTick = element.id
}
if (firstTick && secondTick) {
if(firstTick == secondTick){
if(firstTick in firstTickMap){delete firstTickMap.firstTick;}
else if(firstTick in secondTickMap){delete secondTickMap.firstTick;}
if(secondTick in firstTickMap){delete firstTickMap.secondTick;}
else if(secondTick in secondTickMap ){delete secondTickMap.secondTick;}
}
if(!(firstTick in firstTickMap) && !(secondTick in secondTickMap) && !(firstTick in secondTickMap) && !(secondTick in firstTickMap))
{
var firstTickBBox = getBBox(firstTick)
var secondTickBBox = getBBox(secondTick);
var firstTickPos = getCenterPoint(firstTickBBox);
var secondTickPos = getCenterPoint(secondTickBBox);
firstTickMap[firstTick] = firstTick;
secondTickMap[secondTick] = secondTick;
createLine(firstTickPos, secondTickPos)
}
}
}
//get Center Point
function getCenterPoint(element) {
var thisX = element.left + element.width / 2;
var thisY = element.top + element.height / 2;
return [thisX, thisY]
}
function getBBox(element) {
var thisEl = document.getElementById(element).getBoundingClientRect();
return thisEl;
}
//create a line between pointA and pointB
function createLine(pointA, pointB) {
var thisData = {
x1: pointA[0],
y1: pointA[1],
x2: pointB[0],
y2: pointB[1]
};
allLines.push(svg.data([thisData]).append('line')
.attr('x1', function(d) {
console.log(d)
return d.x1;
})
.attr('y1', function(d) {
return d.y1;
})
.attr('x2', function(d) {
return d.x2;
})
.attr('y2', function(d) {
return d.y2;
}).style('stroke', 'black')
.style('stroke-width','1')
.attr('transform', 'translate(' + (-margin.left - 5) + ', ' + (- margin.top - 5) + ')'));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
First off lets clear a couple of things up. When you create the line you push your array of lines to an array like so :
allLines.push(svg.data([thisData]).append('line')...
This is not the correct way to do it. The best way to do it is, when you create a line, push that lines data, e.g x1,y1,x2,y2 etc to an array and use this array to create all the lines at once. This is how D3 works.
So I changed your functions around.
function createLine(pointA, pointB) {
var thisData = {
x1: pointA[0],
y1: pointA[1],
x2: pointB[0],
y2: pointB[1]
};
allLinesData.push(thisData) //push points into array
drawLines(allLinesData); //draw all lines at once from 'allLinesData'
}
Function to draw lines :
function drawLines(data) { //pass the data you want
var line = svg.selectAll('.line').data(data);
line.enter().append('line')
.attr('id', function(d, i) {return 'genLine' + i; })
.attr('x1', function(d) { return d.x1;})
.attr('y1', function(d) { return d.y1; })
.attr('x2', function(d) { return d.x2; })
.attr('y2', function(d) { return d.y2; })
.style('stroke', 'black')
.style('stroke-width', '3')
.on('mouseover', function(d) { d3.select(this).style('stroke', 'red') })
.on('mouseout', function(d) { d3.select(this).style('stroke', 'black') })
.attr('transform', 'translate(' + (-margin.left - 5) + ', ' + (-margin.top - 5) + ')')
line.on('dblclick', function(d) { //delete line
var thisLine = this;
line.each(function(e, i) {
var thisLine2 = this;
if (thisLine.id === thisLine2.id) {
console.log('splice')
allLinesData.splice(i--, 1); //remove from array you use to feed the line drawer
d3.select(this).remove(); //remove it from DOM
}
})
})
line.exit().remove(); //remove unwanted lines
}
Also added on 'mouseover' so you know what line youre on.
Here is a working fiddle : https://jsfiddle.net/reko91/vr09w905/1/
Also if you just want it here :
var data = [{
"inTime": "2013-04-24T00:00:00-05:00",
"outTime": "2013-04-24T00:00:00-05:00"
}, {
"inTime": "2013-04-24T00:00:00-05:00",
"outTime": "2013-04-24T00:00:00-05:00"
}, {
"inTime": "2013-04-24T00:00:00-05:00",
"outTime": "2013-04-24T00:00:00-05:00"
}, {
"inTime": "2013-04-26T00:00:00-05:00",
"outTime": "2013-04-26T00:00:00-05:00"
}, ];
var margin = {
top: 40,
right: 40,
bottom: 40,
left: 40
},
width = 600,
height = 700;
//Define Left Y axis
var y = d3.time.scale()
.domain([new Date(data[0].inTime), d3.time.day.offset(new Date(data[data.length - 1].inTime), 1)])
.rangeRound([0, width - margin.left - margin.right]);
//Define Right Y axis
var y1 = d3.time.scale()
.domain([new Date(data[1].inTime), d3.time.day.offset(new Date(data[data.length - 1].outTime), 1)])
.rangeRound([0, width - margin.left - margin.right]);
//Left Yaxis attributes
var yAxis = d3.svg.axis()
.scale(y)
.orient('left')
.tickFormat(d3.time.format('%m/%d %H:%M'))
.tickSize(8)
.tickPadding(8);
//Right Yaxis attributes
var yAxisRight = d3.svg.axis()
.scale(y1)
.orient('right')
.tickFormat(d3.time.format('%m/%d %H:%M'))
.tickSize(8)
.tickPadding(8);
//Create chart
var svg = d3.select('body').append('svg')
.attr('class', 'chart')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');
//Add left Yaxis to group
svg.append('g')
.attr('class', 'y axis')
.attr('transform', 'translate(100, 5)')
.call(yAxis);
//Add right Yaxis to group
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(400, 1)')
.call(yAxisRight);
var parse = d3.time.format('%m/%d %H:%M');
//Function to add a line between two ticks
function addLine(t1, t2) {
var ticks = {};
d3.selectAll('.tick text').each(function(d) {
ticks[d3.select(this).text()] = this;
});
var pos1 = ticks[t1].getBoundingClientRect();
var pos2 = ticks[t2].getBoundingClientRect();
svg.append('line')
.attr('x1', pos1.top - pos1.width)
.attr('y1', pos1.top + 5)
.attr('x2', pos2.left - 5)
.attr('y2', pos2.top + 5)
.style('stroke', 'black')
}
var ticks = svg.selectAll(".tick");
ticks.attr('class', function(d, i) {
return 'ticks' + i;
}).each(function(d, i) {
d3.select(this).append("circle")
.attr('id', function(d) {
return 'tickCircle' + i;
})
.attr('class', function(d) {
return 'tickCircles' + this.id
})
.attr("r", 5)
.on('click', function(d) {
console.log('clicked')
return addLineNew(this);
})
.on('mouseover', function(d) {
d3.select(this).style('fill', 'red');
})
.on('mouseout', function(d) {
d3.select(this).style('fill', 'black');
})
});
ticks.selectAll("line").remove();
var firstTick;
var secondTick;
var secondTickMap = {};
var firstTickMap = {};
//var allLines = [];
var allLinesData = [];
//add Line
function addLineNew(element) {
if (firstTick && secondTick) {
firstTick = '';
secondTick = '';
}
if (!firstTick || firstTick === '') {
firstTick = element.id
} else if ((secondTick != 'undefined' || secondTick === '') && !(secondTick in firstTickMap)) {
secondTick = element.id
}
if (firstTick && secondTick) {
if (firstTick == secondTick) {
if (firstTick in firstTickMap) {
delete firstTickMap.firstTick;
} else if (firstTick in secondTickMap) {
delete secondTickMap.firstTick;
}
if (secondTick in firstTickMap) {
delete firstTickMap.secondTick;
} else if (secondTick in secondTickMap) {
delete secondTickMap.secondTick;
}
}
if (!(firstTick in firstTickMap) && !(secondTick in secondTickMap) && !(firstTick in secondTickMap) && !(secondTick in firstTickMap)) {
var firstTickBBox = getBBox(firstTick)
var secondTickBBox = getBBox(secondTick);
var firstTickPos = getCenterPoint(firstTickBBox);
var secondTickPos = getCenterPoint(secondTickBBox);
firstTickMap[firstTick] = firstTick;
secondTickMap[secondTick] = secondTick;
createLine(firstTickPos, secondTickPos)
}
}
}
//get Center Point
function getCenterPoint(element) {
var thisX = element.left + element.width / 2;
var thisY = element.top + element.height / 2;
return [thisX, thisY]
}
function getBBox(element) {
var thisEl = document.getElementById(element).getBoundingClientRect();
return thisEl;
}
//create a line between pointA and pointB
function createLine(pointA, pointB) {
var thisData = {
x1: pointA[0],
y1: pointA[1],
x2: pointB[0],
y2: pointB[1]
};
allLinesData.push(thisData) //push points into array
drawLines(allLinesData); //draw all lines at once from 'allLinesData'
}
function drawLines(data) { //pass the data you want
var line = svg.selectAll('.line').data(data);
line.enter().append('line')
.attr('id', function(d, i) {
return 'genLine' + i;
})
.attr('x1', function(d) {
return d.x1;
})
.attr('y1', function(d) {
return d.y1;
})
.attr('x2', function(d) {
return d.x2;
})
.attr('y2', function(d) {
return d.y2;
})
.style('stroke', 'black')
.style('stroke-width', '3')
.on('mouseover', function(d) {
d3.select(this).style('stroke', 'red')
})
.on('mouseout', function(d) {
d3.select(this).style('stroke', 'black')
})
.attr('transform', 'translate(' + (-margin.left - 5) + ', ' + (-margin.top - 5) + ')')
line.on('dblclick', function(d) { //delete line
var thisLine = this;
line.each(function(e, i) {
var thisLine2 = this;
if (thisLine.id === thisLine2.id) {
console.log('splice')
allLinesData.splice(i--, 1); //remove from array you use to feed the line drawer
d3.select(this).remove(); //remove it from DOM
}
})
})
line.exit().remove(); //remove unwanted lines
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.10/d3.min.js"></script>
I want show percentage share of each block in labelled zoomable sunburst chart. I am referring http://bl.ocks.org/metmajer/5480307 this example.
here I want add (%x) share of each block. Please help.
Below is my index.html
<!DOCTYPE html>
<meta charset="utf-8"><style>
path {
stroke: #fff;
fill-rule: evenodd;
}
text {
font-family: Arial, sans-serif;
font-size: 12px;
}
</style> <body>
<script src="http://d3js.org/d3.v3.min.js">
</script> <script>
var width = 960,
height = 700,
radius = Math.min(width, height) / 2;
var x = d3.scale.linear()
.range([0, 2 * Math.PI]);
var y = d3.scale.linear()
.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 + 10) + ")");
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("atmLeads.json", function(error, root) {
var g = svg.selectAll("g")
.data(partition.nodes(root))
.enter().append("g");
var path = g.append("path")
.attr("d", arc).attr("class",function(d){return "ring_"+ d.depth;})
.style("fill", function(d) { return color((d.children ? d : d.parent).name); })
.on("click", click);
var text = g.append("text")
.attr("transform", function(d) { return "rotate(" + computeTextRotation(d) + ")"; })
.attr("x", function(d) { return y(d.y); })
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.text(function(d) { return d.name; });
function click(d) {
// fade out all text elements
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); });
}
});
}
});
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 computeTextRotation(d) {
return (x(d.x + d.dx / 2) - Math.PI / 2) / Math.PI * 180;
}
</script>
Try this code.
var path = g.append("path")
.attr("d", arc).attr("class", function(d) {
return "ring_" + d.depth;
})
.style("fill", function(d) {
return color((d.children ? d : d.parent).name);
})
.on("click", click);
var totalSize = path.node().__data__.value;
var text = g.append("text")
.attr("transform", function(d) {
return "rotate(" + computeTextRotation(d) + ")";
})
.attr("x", function(d) {
return y(d.y);
})
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.text(function(d) {
var percentage = (100 * d.value / totalSize).toPrecision(3);
var percentageString = percentage + "%";
if (percentage < 0.1) {
percentageString = "< 0.1%";
}
return d.name +" "+percentageString;
});
var width = 960,
height = 700,
radius = Math.min(width, height) / 2;
var x = d3.scale.linear()
.range([0, 2 * Math.PI]);
var y = d3.scale.linear()
.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 + 10) + ")");
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));
});
var root = {
"name": "ATM Leads Converted",
"size": 34752,
"children": [
{
"name": "Converted",
"size": 417
}, {
"name": "Failure",
"size": 1366
}, {
"name": "Interested",
"size": 916
}, {
"name": "No Value",
"size": 48932
}, {
"name": "Not Interested",
"size": 14479
}, {
"name": "Not contactable",
"size": 2961
},
{
"name": "Success",
"size": 1142
}, {
"name": "Will Get Back",
"size": 1564
}, {
"name": "Wrong Number",
"size": 358
}
]
};
var g = svg.selectAll("g")
.data(partition.nodes(root))
.enter().append("g");
var path = g.append("path")
.attr("d", arc).attr("class", function(d) {
return "ring_" + d.depth;
})
.style("fill", function(d) {
return color((d.children ? d : d.parent).name);
})
.on("click", click);
var totalSize = path.node().__data__.value;
var text = g.append("text")
.attr("transform", function(d) {
return "rotate(" + computeTextRotation(d) + ")";
})
.attr("x", function(d) {
return y(d.y);
})
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.text(function(d) {
var percentage = (100 * d.value / totalSize).toPrecision(3);
var percentageString = percentage + "%";
if (percentage < 0.1) {
percentageString = "< 0.1%";
}
return d.name +" "+percentageString;
});
function click(d) {
// fade out all text elements
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);
});
}
});
}
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 computeTextRotation(d) {
return (x(d.x + d.dx / 2) - Math.PI / 2) / Math.PI * 180;
}
path {
stroke: #fff;
fill-rule: evenodd;
}
text {
font-family: Arial, sans-serif;
font-size: 12px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I am newbie to d3.js , I am working on a minimap for collapsible tree .
There is always a one click lag in collapsible tree minimap. When user clicks the first node followed by second node , minimap shows the image of first node when user clicks second node.
Could someone please help me with this ?
d3.demo = {};
/** CANVAS **/
d3.demo.canvas = function(width,height) {
"use strict";
var width = 500,
height = 500,
zoomEnabled = true,
dragEnabled = true,
scale = 1,
translation = [0,0],
base = null,
wrapperBorder = 2,
minimap = null,
minimapPadding = 20,
minimapScale = 0.25;
function canvas(selection) {
base = selection;
var xScale = d3.scale.linear()
.domain([-width / 2, width / 2])
.range([0, width]);
var yScale = d3.scale.linear()
.domain([-height / 2, height / 2])
.range([height, 0]);
var zoomHandler = function(newScale) {
if (!zoomEnabled) { return; }
if (d3.event) {
scale = d3.event.scale;
} else {
scale = newScale;
}
if (dragEnabled) {
var tbound = -height * scale,
bbound = height * scale,
lbound = -width * scale,
rbound = width * scale;
// limit translation to thresholds
translation = d3.event ? d3.event.translate : [0, 0];
translation = [
Math.max(Math.min(translation[0], rbound), lbound),
Math.max(Math.min(translation[1], bbound), tbound)
];
}
d3.select(".panCanvas, .panCanvas .bg")
.attr("transform", "translate(" + translation + ")" + " scale(" + scale + ")");
minimap.scale(scale).render();
}; // startoff zoomed in a bit to show pan/zoom rectangle
var zoom = d3.behavior.zoom()
.x(xScale)
.y(yScale)
.scaleExtent([0.5, 5])
.on("zoom.canvas", zoomHandler);
var svg = selection.append("svg")
.attr("class", "svg canvas")
.attr("width", width + (wrapperBorder*2) + minimapPadding*2 + (width*minimapScale))
.attr("height", height + (wrapperBorder*2) + minimapPadding*2)
.attr("shape-rendering", "auto");
var svgDefs = svg.append("defs");
svgDefs.append("clipPath")
.attr("id", "wrapperClipPath")
.attr("class", "wrapper clipPath")
.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height);
svgDefs.append("clipPath")
.attr("id", "minimapClipPath")
.attr("class", "minimap clipPath")
.attr("width", width)
.attr("height", height)
//.attr("transform", "translate(" + (width + minimapPadding) + "," + (minimapPadding/2) + ")")
.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height);
var filter = svgDefs.append("svg:filter")
.attr("id", "minimapDropShadow")
.attr("x", "-20%")
.attr("y", "-20%")
.attr("width", "150%")
.attr("height", "150%");
filter.append("svg:feOffset")
.attr("result", "offOut")
.attr("in", "SourceGraphic")
.attr("dx", "1")
.attr("dy", "1");
filter.append("svg:feColorMatrix")
.attr("result", "matrixOut")
.attr("in", "offOut")
.attr("type", "matrix")
.attr("values", "0.1 0 0 0 0 0 0.1 0 0 0 0 0 0.1 0 0 0 0 0 0.5 0");
filter.append("svg:feGaussianBlur")
.attr("result", "blurOut")
.attr("in", "matrixOut")
.attr("stdDeviation", "10");
filter.append("svg:feBlend")
.attr("in", "SourceGraphic")
.attr("in2", "blurOut")
.attr("mode", "normal");
var minimapRadialFill = svgDefs.append("radialGradient")
.attr({
id:"minimapGradient",
gradientUnits:"userSpaceOnUse",
cx:"500",
cy:"500",
r:"400",
fx:"500",
fy:"500"
});
minimapRadialFill.append("stop")
.attr("offset", "0%")
.attr("stop-color", "#FFFFFF");
minimapRadialFill.append("stop")
.attr("offset", "40%")
.attr("stop-color", "#EEEEEE");
minimapRadialFill.append("stop")
.attr("offset", "100%")
.attr("stop-color", "#E0E0E0");
var outerWrapper = svg.append("g")
.attr("class", "wrapper outer")
.attr("transform", "translate(0, " + minimapPadding + ")");
outerWrapper.append("rect")
.attr("class", "background")
.attr("width", width + wrapperBorder*2)
.attr("height", height + wrapperBorder*2);
var innerWrapper = outerWrapper.append("g")
.attr("class", "wrapper inner")
.attr("clip-path", "url(#wrapperClipPath)")
.attr("transform", "translate(" + (wrapperBorder) + "," + (wrapperBorder) + ")")
.call(zoom);
innerWrapper.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height);
var panCanvas = innerWrapper.append("g")
.attr("class", "panCanvas")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(0,0)");
panCanvas.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height);
minimap = d3.demo.minimap()
.zoom(zoom)
.target(panCanvas)
.minimapScale(minimapScale)
.x(width + minimapPadding)
.y(minimapPadding);
svg.call(minimap);
// startoff zoomed in a bit to show pan/zoom rectangle
zoom.scale(1.75);
zoomHandler(1.75);
/** ADD SHAPE **/
canvas.addItem = function(item) {
panCanvas.node().appendChild(item.node());
minimap.render();
};
canvas.loadTree = function (divID,treeData,height,width) {
var totalNodes = 0;
var maxLabelLength = 0;
// Misc. variables
var i = 0;
var duration = 750;
var root,
rootNode;
// size of the diagram
var viewerWidth = width;
var viewerHeight = height;
var tree = d3.layout.tree()
.size([viewerHeight, viewerWidth]);
// define a d3 diagonal projection for use by the node paths later on.
var diagonal = d3.svg.diagonal()
.projection(function (d) {
return [d.y, d.x];
});
// A recursive helper function for performing some setup by walking through all nodes
function visit(parent, visitFn, childrenFn) {
if (!parent)
return;
visitFn(parent);
var children = childrenFn(parent);
if (children) {
var count = children.length;
for (var i = 0; i < count; i++) {
visit(children[i], visitFn, childrenFn);
}
}
}
// Call visit function to establish maxLabelLength
visit(treeData, function (d) {
totalNodes++;
maxLabelLength = Math.max(d.name.length, maxLabelLength);
}, function (d) {
return d.children && d.children.length > 0 ? d.children : null;
});
// sort the tree according to the node names
function sortTree() {
tree.sort(function (a, b) {
return b.name.toLowerCase() < a.name.toLowerCase() ? 1 : -1;
});
}
// Sort the tree initially incase the JSON isn't in a sorted order.
sortTree();
// Define the zoom function for the zoomable tree
/*function zoom() {
svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}*/
// define the zoomListener which calls the zoom function on the "zoom" event constrained within the scaleExtents
//var zoomListener = d3.behavior.zoom().scaleExtent([0.1, 3]).on("zoom", zoom);
// define the baseSvg, attaching a class for styling and the zoomListener
var baseSvg =panCanvas.append("g");
// Helper functions for collapsing and expanding nodes.
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
function expand(d) {
if (d._children) {
d.children = d._children;
d.children.forEach(expand);
d._children = null;
}
}
// Function to center node when clicked/dropped so node doesn't get lost when collapsing/moving with large amount of children.
// Toggle children function
function toggleChildren(d) {
if (d.children) {
d._children = d.children;
d.children = null;
update(d);
minimap.render();
//centerNode(d);
} else if (d._children) {
d.children = d._children;
d._children = null;
update(d);
minimap.render();
//centerNode(d);
} else {
d.children = null;
var json = {
"useCase" : d.useCase,
"chartType" : d.chartType,
"type" : d.type,
"assetId" : d.assetId,
"name" : d.name,
"childQueriesWithDelim" : d.childQueriesWithDelim,
"imgSrc" : d.imgSrc
};
window.parameterJsonData = JSON.stringify(json);
window.getDataMethod();
window.setChildData = function (childData) {
var childObj = getObjects(childData, 'name', d.name);
if (childObj != null) {
var newnodes = tree.nodes(childObj[0].children).reverse();
d.children = newnodes[0];
update(d);
minimap.render();
//centerNode(d);
}
}
}
}
// Toggle children on click.
function click(d) {
//if (d3.event.defaultPrevented)
//return; // click suppressed
$('#loading' + d.id).show();
toggleChildren(d);
}
function update(source) {
// Compute the new height, function counts total children of root node and sets tree height accordingly.
// This prevents the layout looking squashed when new nodes are made visible or looking sparse when nodes are removed
// This makes the layout more consistent.
$('#loading' + source.id).hide();
var levelWidth = [1];
var childCount = function (level, n) {
if (n.children && n.children.length > 0) {
if (levelWidth.length <= level + 1)
levelWidth.push(0);
levelWidth[level + 1] += n.children.length;
n.children.forEach(function (d) {
childCount(level + 1, d);
});
}
};
childCount(0, root);
var newHeight = d3.max(levelWidth) * 25; // 25 pixels per line
tree = tree.size([newHeight, viewerWidth]);
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Set widths between levels based on maxLabelLength.
nodes.forEach(function (d) {
//d.y = (d.depth * (maxLabelLength * 30)); //maxLabelLength * 10px
// alternatively to keep a fixed scale one can set a fixed depth per level
// Normalize for fixed-depth by commenting out below line
d.y = (d.depth * 150); //500px per level.
});
// Update the nodes…
var node = svgGroup.selectAll("g.node")
.data(nodes, function (d) {
return d.id || (d.id = ++i);
});
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function (d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
})
.on('click', click);
/*nodeEnter.append("circle")
.attr('class', 'nodeCircle')
.attr("r", 0)
.style("fill", function (d) {
return d.hasChild ? "lightsteelblue" : "#fff";
});*/
nodeEnter.append("svg:image")
.attr("class", "nodeCircle")
.attr("xlink:href", function (d) {
return d.imgSrc;
})
.attr("x", "-8px")
.attr("y", "-8px")
.attr("width", function (d) {
if (d.id == rootNode.id) {
return "40px";
} else {
return "16px";
}
})
.attr("height", function (d) {
if (d.id == rootNode.id) {
return "40px";
} else {
return "16px";
}
});
nodeEnter.append("foreignObject").attr("width", 100)
.attr("height", 100).attr("id", function (d) {
return "loading" + d.id;
}).style("display", "none")
.append("xhtml:div").html(
"<img src=\"d3/images/loading.gif\"/>");
nodeEnter.append("a")
.attr("xlink:href", function (d) {
return d.url;
})
.on("mousedown.zoom", function (d) {
if (d.url != null) {
disableDrag();
}
})
.append("text")
.attr("x", function (d) {
return d.hasChild ? -10 : 10;
})
.attr("dy", ".02em")
.attr('class', 'nodeText')
.attr("text-anchor", function (d) {
return d.hasChild ? "end" : "start";
})
.text(function (d) {
var name = d.name.substr(0, d.truncationLimit);
if (d.name != null && d.name.length > d.truncationLimit) {
name = name.concat("...");
}
return name;
})
.style("fill-opacity", 0)
.on("mouseover", function (d) {
var res = d.description ? d.description.split(",") : null;
var desc = "";
for (var i = 0; res != null && i < res.length; i++) {
desc = desc + '<div>' + res[i] + '</div>';
}
if (d.description == null) {
desc = '<div>Name : ' + d.name + '</div>';
}
tooltip.show([d3.event.clientX, d3.event.clientY], desc);
})
.on('mouseout', function () {
tooltip.cleanup()
});
/*nodeEnter.append("foreignObject")
.attr('x', 10)
.attr("width", 100)
.attr("height", 200)
.append("xhtml:p")
.attr('style', 'word-wrap: break-word; text-align:center;')
.append("xhtml:a")
.attr("xlink:href", function (d) {
return d.url;
})
.html(function (d) {
return d.name;
});*/
// Update the text to reflect whether node has children or not.
node.select('text')
.attr("x", function (d) {
return d.hasChild ? -10 : 10;
})
.attr("dy", ".02em")
.attr("text-anchor", function (d) {
return d.hasChild ? "end" : "start";
})
.text(function (d) {
var name = d.name.substr(0, d.truncationLimit);
if (d.name != null && d.name.length > d.truncationLimit) {
name = name.concat("...");
}
return name;
});
// Change the circle fill depending on whether it has children and is collapsed
/*node.select("circle.nodeCircle")
.attr("r", 4.5);*/
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function (d) {
return "translate(" + d.y + "," + d.x + ")";
});
// Fade the text in
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function (d) {
return "translate(" + source.y + "," + source.x + ")";
})
.remove();
/*nodeExit.select("circle")
.attr("r", 0);*/
nodeExit.select("text")
.style("fill-opacity", 0);
// Update the links…
var link = svgGroup.selectAll("path.link")
.data(links, function (d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function (d) {
var o = {
x : source.x0,
y : source.y0
};
return diagonal({
source : o,
target : o
});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function (d) {
var o = {
x : source.x,
y : source.y
};
return diagonal({
source : o,
target : o
});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function (d) {
d.x0 = d.x;
d.y0 = d.y;
});
//canvas.addItem(svgGroup);
minimap.render();
}
// Append a group which holds all nodes and which the zoom Listener can act upon.
var svgGroup = baseSvg.append("g");
// Define the root
root = treeData;
rootNode = treeData;
root.x0 = viewerHeight / 2;
root.y0 = 0;
// Layout the tree initially and center on the root node.
update(root);
function disableDrag() {
baseSvg.on("mousedown.zoom", null);
}
function getObjects(obj, key, val) {
var objects = [];
for (var i in obj) {
if (!obj.hasOwnProperty(i))
continue;
if (typeof obj[i] == 'object') {
objects = objects.concat(getObjects(obj[i], key, val));
} else if (i == key && obj[key] == val) {
objects.push(obj);
}
}
return objects;
}
//d3.select(self.frameElement).style("height", _height + "px");
}
/** RENDER **/
canvas.render = function() {
svgDefs
.select(".clipPath .background")
.attr("width", width)
.attr("height", height);
svg
.attr("width", width + (wrapperBorder*2) + minimapPadding*2 + (width*minimapScale))
.attr("height", height + (wrapperBorder*2));
outerWrapper
.select(".background")
.attr("width", width + wrapperBorder*2)
.attr("height", height + wrapperBorder*2);
innerWrapper
.attr("transform", "translate(" + (wrapperBorder) + "," + (wrapperBorder) + ")")
.select(".background")
.attr("width", width)
.attr("height", height);
panCanvas
.attr("width", width)
.attr("height", height)
.select(".background")
.attr("width", width)
.attr("height", height);
minimap
.x(width + minimapPadding)
.y(minimapPadding)
.render();
};
canvas.zoomEnabled = function(isEnabled) {
if (!arguments.length) { return zoomEnabled }
zoomEnabled = isEnabled;
};
canvas.dragEnabled = function(isEnabled) {
if (!arguments.length) { return dragEnabled }
dragEnabled = isEnabled;
};
canvas.reset = function() {
d3.transition().duration(750).tween("zoom", function() {
var ix = d3.interpolate(xScale.domain(), [-width / 2, width / 2]),
iy = d3.interpolate(yScale.domain(), [-height / 2, height / 2]),
iz = d3.interpolate(scale, 1);
return function(t) {
zoom.scale(iz(t)).x(x.domain(ix(t))).y(y.domain(iy(t)));
zoomed(iz(t));
};
});
};
}
//============================================================
// Accessors
//============================================================
canvas.width = function(value) {
if (!arguments.length) return width;
width = parseInt(value, 10);
return this;
};
canvas.height = function(value) {
if (!arguments.length) return height;
height = parseInt(value, 10);
return this;
};
canvas.scale = function(value) {
if (!arguments.length) { return scale; }
scale = value;
return this;
};
return canvas;
};
/** MINIMAP **/
d3.demo.minimap = function() {
"use strict";
var minimapScale = 0.15,
scale = 1,
zoom = null,
base = null,
target = null,
width = 0,
height = 0,
x = 0,
y = 0,
frameX = 0,
frameY = 0;
function minimap(selection) {
base = selection;
var container = selection.append("g")
.attr("class", "minimap")
.call(zoom);
zoom.on("zoom.minimap", function() {
scale = d3.event.scale;
});
minimap.node = container.node();
var frame = container.append("g")
.attr("class", "frame")
frame.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.attr("filter", "url(#minimapDropShadow)");
var drag = d3.behavior.drag()
.on("dragstart.minimap", function() {
var frameTranslate = d3.demo.util.getXYFromTranslate(frame.attr("transform"));
frameX = frameTranslate[0];
frameY = frameTranslate[1];
})
.on("drag.minimap", function() {
d3.event.sourceEvent.stopImmediatePropagation();
frameX += d3.event.dx;
frameY += d3.event.dy;
frame.attr("transform", "translate(" + frameX + "," + frameY + ")");
var translate = [(-frameX*scale),(-frameY*scale)];
target.attr("transform", "translate(" + translate + ")scale(" + scale + ")");
zoom.translate(translate);
});
frame.call(drag);
/** RENDER **/
minimap.render = function() {
scale = zoom.scale();
container.attr("transform", "translate(" + x + "," + y + ")scale(" + minimapScale + ")");
var node = target.node().cloneNode(true);
node.removeAttribute("id");
base.selectAll(".minimap .panCanvas").remove();
minimap.node.appendChild(node);
var targetTransform = d3.demo.util.getXYFromTranslate(target.attr("transform"));
frame.attr("transform", "translate(" + (-targetTransform[0]/scale) + "," + (-targetTransform[1]/scale) + ")")
.select(".background")
.attr("width", width/scale)
.attr("height", height/scale);
frame.node().parentNode.appendChild(frame.node());
d3.select(node).attr("transform", "translate(1,1)");
};
}
//============================================================
// Accessors
//============================================================
minimap.width = function(value) {
if (!arguments.length) return width;
width = parseInt(value, 10);
return this;
};
minimap.height = function(value) {
if (!arguments.length) return height;
height = parseInt(value, 10);
return this;
};
minimap.x = function(value) {
if (!arguments.length) return x;
x = parseInt(value, 10);
return this;
};
minimap.y = function(value) {
if (!arguments.length) return y;
y = parseInt(value, 10);
return this;
};
minimap.scale = function(value) {
if (!arguments.length) { return scale; }
scale = value;
return this;
};
minimap.minimapScale = function(value) {
if (!arguments.length) { return minimapScale; }
minimapScale = value;
return this;
};
minimap.zoom = function(value) {
if (!arguments.length) return zoom;
zoom = value;
return this;
};
minimap.target = function(value) {
if (!arguments.length) { return target; }
target = value;
width = parseInt(target.attr("width"), 10);
height = parseInt(target.attr("height"), 10);
return this;
};
return minimap;
};
/** UTILS **/
d3.demo.util = {};
d3.demo.util.getXYFromTranslate = function(translateString) {
var split = translateString.split(",");
var x = split[0] ? ~~split[0].split("(")[1] : 0;
var y = split[1] ? ~~split[1].split(")")[0] : 0;
return [x, y];
};
/** RUN SCRIPT **/
treeChart= (function (divID, treeData, height, width) {
var canvasWidth = width;
var shapes = [];
var lastXY = 1;
var zoomEnabled = true;
var dragEnabled = true;
var canvas = d3.demo.canvas(width,height).width(width/2).height(height/2);
d3.select(divID).call(canvas);
canvas.loadTree(divID,treeData,height,width);
});