d3.js Tesselations and Image positioning/scaling - javascript

//First part is to create tessellation shapes.
http://jsfiddle.net/NYEaX/1526/
this code creates a tessellation shape of rects - and a circle -- but there must be a cleaner and better way of looping through rects first placing a set of cols and rows.
$(document).ready(function() {
function maskMaker(el) {
var backcolor = $(el).data("color");
var backopacity = $(el).data("opacity");
// Set the main elements for the series chart
var svgroot = d3.select($(el)[0]).append("svg");
var mask = svgroot
.append("defs")
.append("mask")
.attr("id", "myMask");
mask.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", "1200px")
.attr("height", 500)
.style("fill", "white")
.style("opacity", backopacity);
mask.append("circle")
.attr("cx", 350)
.attr("cy", 250)
.attr("r", 70);
function tesselateShape(mask, shape, x, y, size) {
if (shape == "rect") {
mask.append("g")
.attr("transform", "translate(30) rotate(45 50 50)")
.append("rect")
.attr("x", x)
.attr("y", y)
.attr("width", size)
.attr("height", size);
}
}
var size = 200;
var xStart = 500;
var yStart = -500;
var shapes = ["rect", "rect", "rect", "rect", "rect", "rect"];
var batch = 3; //group in 3 and then move into a new col.
var batchCount = 1;
for (i = 0; i < shapes.length; i++) {
tesselateShape(mask, "rect", xStart, yStart, size);
yStart += size;
xStart += size;
if (i % batch) {
var xStart = 500 + (-size * batchCount);
var yStart = -500 + (size * batchCount);
batchCount++;
}
}
/*
//col1
tesselateShape(mask, "rect", 400, -600, 200);
tesselateShape(mask, "rect", 600, -400, 200);
tesselateShape(mask, "rect", 800, -200, 200);
//col2
tesselateShape(mask, "rect", 600, -800, 200);
tesselateShape(mask, "rect", 800, -600, 200);
tesselateShape(mask, "rect", 1000, -400, 200);
*/
var svg = svgroot
.attr("class", "series")
.attr("width", "1200px")
.attr("height", "500px")
.append("g")
.attr("transform", "translate(0,0)")
var rect = svg
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", "1200px")
.attr("height", 500)
.attr("mask", "url(#myMask)")
.style("fill", backcolor);
}
//var el = $(".mask"); //selector
$('[data-role="mask"]').each(function(index) {
console.log("test")
maskMaker(this);
});
});
//Second part is to add in a rounded masked image and label, pointer
Would involve a def pattern part
http://jsfiddle.net/NYEaX/652/
[{
"label": "Google Street View",
"link": "https://maps.googleapis.com/maps/api/streetview?size=600x300&location=46.414382,10.013988&heading=151.78&pitch=-0.76"
}]
a json blob like
var defs = patternsSvg.append('svg:defs');
defs.append('svg:pattern')
.attr('id', index+"-"+value.userName.toLowerCase())
.attr('width', 65)
.attr('height', 65)
.append('svg:image')
.attr('xlink:href', value.userImage)
.attr('x', 0)
.attr('y', 0)
.attr('width', 65)
.attr('height', 65);
labels and pointer
//__labels
var labels = mask.append("g")
.attr("class", "labels")
//__ enter
var labels = labels.selectAll("text")
.data(data);
labels.enter()
.append("text")
.attr("text-anchor", "middle")
//__ update
labels
.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
})
.text(function(d) {
return d.label;
})
.each(function(d) {
var bbox = this.getBBox();
d.sx = d.x - bbox.width / 2 - 2;
d.ox = d.x + bbox.width / 2 + 2;
d.sy = d.oy = d.y + 5;
})
.transition()
.duration(300)
labels
.transition()
.duration(300)
//__ exit
labels.exit().remove();
//__labels
//__labels
//__pointers
var pointers = mask.append("g")
.attr("class", "pointers")
pointers.append("defs").append("marker")
.attr("id", "circ")
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("refX", 3)
.attr("refY", 3)
.append("circle")
.attr("cx", 3)
.attr("cy", 3)
.attr("r", 3);
var pointers = pointers.selectAll("path.pointer")
.data(data);
//__ enter
pointers.enter()
.append("path")
.attr("class", "pointer")
.style("fill", "none")
.style("stroke", "black")
.attr("marker-end", "url(#circ)");
//__ update
pointers
.attr("d", function(d) {
if (d.cx > d.ox) {
return "M" + d.sx + "," + d.sy + "L" + d.ox + "," + d.oy + " " + d.cx + "," + d.cy;
} else {
return "M" + d.ox + "," + d.oy + "L" + d.sx + "," + d.sy + " " + d.cx + "," + d.cy;
}
})
.transition()
.duration(300)
pointers
.transition()
.duration(300)
//__ exit
pointers.exit().remove();
//__pointers

Related

d3 tooltip - appended circle failing to follow values of line graph

I have a vertical line graph in d3 and I'm trying to add a tooltip in the form of a line and circle that follows the cursor movement in the vertical. The latter works but the circle fails to follow the path - I've tried different variations but the circle never follows the path, it's currently just following the y-axis of the graph (see attached image for example). I've achieved the same effect for a horizontal plot but when I try to adapt the code for a vertical graph I just can't get the circle to work properly.
I've brought an example together with the code below, still very new to javascript so code is a bit of a mess.
Screenshot of graph with circle (red) failing to follow the path:
function test(test_data) {
// setup params
var margin_ = {top: 30, right: 60, bottom: 30, left: 20},
width_ = 300
height_ = 700
// Add svg
var line_graph = d3.select("#my_dataviz_test")
.append("svg")
.attr("width", width_ + 100)
.attr("height", height_)
.append("g")
.attr("transform",
"translate(" + margin_.left + "," + margin_.top + ")");
d3.csv(test_data,
function(d){
return { output_time_ref: d.output_time_ref = +d.output_time_ref,
output_time: d3.timeParse("%d/%m/%Y %H:%M")(d.output_time),
prediction: d.prediction = +d.prediction,
}
},
function(data) {
// Add x axis
var x_test = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return +d.prediction; })])
.range([ 0, width_ ]);
line_graph.append("g")
.attr("transform", "translate(" + 0 + "," + height_ + ")")
.call(d3.axisBottom(x_test).tickSizeOuter(0).tickSizeInner(0).ticks(2))
.select(".domain").remove();
// Add Y axis
var y_test = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return +d.output_time_ref; })])
.range([ height_, 0 ]);
line_graph.append("g")
.call(d3.axisLeft(y_test).tickSizeOuter(0).tickSizeInner(0).ticks(5))
.select(".domain").remove();
// Add the line
path_test = line_graph.append("path")
.datum(data)
.attr("fill", "none")
.attr("fill", "steelblue")
.attr("fill-opacity", 0.2)
.attr("stroke", "steelblue")
.attr("stroke-width", 1)
.attr("d", d3.line()
.curve(d3.curveBasis)
.x(function(d) { return x_test(d.prediction) })
.y(function(d) { return y_test(d.output_time_ref) })
)
var mouseG2 = line_graph
.append("g")
.attr("class", "mouse-over-effects");
mouseG2
.append("path")
.attr("class", "mouse-line2")
.style("stroke", "#393B45")
.style("stroke-width", "0.5px")
.style("opacity", 0.75)
mouseG2.append("text")
.attr("class", "mouse-text2")
var totalLength2 = path_test.node().getTotalLength();
var mousePerLine2 = mouseG2.selectAll('.mouse-per-line2')
.data(data)
.enter()
.append("g")
.attr("class", "mouse-per-line2");
mousePerLine2.append("circle")
.attr("r", 8)
.style("stroke", 'red')
.style("fill", "none")
.style("stroke-width", "2px")
.style("opacity", "0");
mouseG2
.append('svg:rect')
.attr('width', width_)
.attr('height', height_)
.attr('fill', 'none')
// .attr('opacity', 0.2)
.attr('pointer-events', 'all')
.on('mouseout', function() {
d3.select("#my_dataviz_test")
.selectAll(".mouse-per-line2 circle")
.style("opacity", "0"); })
var mouseover = function(d) {
d3.select("#my_dataviz_test")
.select(".mouse-line2")
.style("opacity", "1")
.select(".mouse-text2")
.style("opacity", "1")
.select(".mouse-per-line2 circle")
.style("opacity", "1");
///////////////////////////////////////////////////
d3.select("#my_dataviz_test")
var mouse2 = d3.mouse(this);
d3.select("#my_dataviz_test")
.select(".mouse-text2")
.attr("y", mouse2[1])
.attr("transform", "translate(" + (mouse2[1]+60) + "," + (mouse2[1]+5) + ") rotate(90)")
d3.select("#my_dataviz_test")
.select(".mouse-line2")
.attr("d", function() {
var d = "M" + width_ + "," + mouse2[1];
d += " " + 0 + "," + mouse2[1];
return d;
})
d3.select("#my_dataviz_test")
.selectAll(".mouse-per-line2")
.attr("transform", function(d, i) {
var beginning2 = 0,
end2 = totalLength2
target2 = null;
while (true){
target2 = Math.floor((beginning2 + end2) / 2);
var pos2 = path_test.node().getPointAtLength(target2);
if ((target2 === end2 || target2 === beginning2) && pos2.y !== mouse2[1]) {
break;
}
if (pos2.y > mouse2[1]) { end2 = target2; }
else if (pos2.y < mouse2[1]) { beginning2 = target2; }
else {break};
}
d3.select("#my_dataviz_test").select('circle')
.style("opacity", 1)
return "translate(" + (pos2.x) + "," + mouse2[1] +")";
});
///////////////////////////////////////////////////
}
var mouseleave = function(d) {
d3.select("#my_dataviz_test")
.select(".mouse-line2")
.style("opacity", "0")
d3.select("#my_dataviz_test")
.select(".circle")
.style("opacity", "0")
}
line_graph
.on("mouseover", mouseover)
.on("mouseleave", mouseleave)
})
}
test_data.csv:
output_time_ref,output_time,prediction
0,04/01/2013 00:00,0
1,04/01/2013 00:30,0
2,04/01/2013 01:00,0
3,04/01/2013 01:30,0
4,04/01/2013 02:00,0
5,04/01/2013 02:30,0
6,04/01/2013 00:00,0
7,04/01/2013 03:30,0
8,04/01/2013 04:00,0
9,04/01/2013 04:30,8.17E-05
10,04/01/2013 05:00,0.002014463
11,04/01/2013 05:30,0.01322314
12,04/01/2013 06:00,0.033264463
13,04/01/2013 06:30,0.059607438
14,04/01/2013 07:00,0.098553719
15,04/01/2013 07:30,0.145661157
16,04/01/2013 08:00,0.186983471
17,04/01/2013 08:30,0.225206612
18,04/01/2013 09:00,0.267561983
19,04/01/2013 09:30,0.314049587
20,04/01/2013 10:00,0.334710744
21,04/01/2013 10:30,0.350206612
22,04/01/2013 11:00,0.359504132
23,04/01/2013 11:30,0.375
24,04/01/2013 12:00,0.393595041
25,04/01/2013 12:30,0.396694215
26,04/01/2013 13:00,0.393595041
27,04/01/2013 13:30,0.385330579
28,04/01/2013 14:00,0.367768595
29,04/01/2013 14:30,0.344008264
30,04/01/2013 15:00,0.320247934
31,04/01/2013 15:30,0.297520661
32,04/01/2013 16:00,0.273760331
33,04/01/2013 16:30,0.254132231
34,04/01/2013 17:00,0.216942149
35,04/01/2013 17:30,0.167355372
36,04/01/2013 18:00,0.123966942
37,04/01/2013 18:30,0.080785124
38,04/01/2013 19:00,0.041115702
39,04/01/2013 19:30,0.015805785
40,04/01/2013 20:00,0.002489669
41,04/01/2013 20:30,2.67E-05
42,04/01/2013 21:00,1.24E-05
43,04/01/2013 21:30,0
44,04/01/2013 22:00,0
45,04/01/2013 22:30,0
46,04/01/2013 23:00,0
47,04/01/2013 23:30,0
You can precisely compute x by y using the input data:
const y = d3.event.layerY - margin_.top;
const curY = y_test.invert(y);
const minY = Math.floor(curY);
const maxY = Math.ceil(curY);
if (data[minY] && data[maxY]) {
const yDelta = curY - minY;
const minP = data[minY].prediction;
const maxP = data[maxY].prediction;
const curP = minP + (maxP - minP) * yDelta;
const xPos = x_test(curP)
...
}
See it's working in the snippet:
const csvData = `output_time_ref,output_time,prediction
0,04/01/2013 00:00,0
1,04/01/2013 00:30,0
2,04/01/2013 01:00,0
3,04/01/2013 01:30,0
4,04/01/2013 02:00,0
5,04/01/2013 02:30,0
6,04/01/2013 00:00,0
7,04/01/2013 03:30,0
8,04/01/2013 04:00,0
9,04/01/2013 04:30,8.17E-05
10,04/01/2013 05:00,0.002014463
11,04/01/2013 05:30,0.01322314
12,04/01/2013 06:00,0.033264463
13,04/01/2013 06:30,0.059607438
14,04/01/2013 07:00,0.098553719
15,04/01/2013 07:30,0.145661157
16,04/01/2013 08:00,0.186983471
17,04/01/2013 08:30,0.225206612
18,04/01/2013 09:00,0.267561983
19,04/01/2013 09:30,0.314049587
20,04/01/2013 10:00,0.334710744
21,04/01/2013 10:30,0.350206612
22,04/01/2013 11:00,0.359504132
23,04/01/2013 11:30,0.375
24,04/01/2013 12:00,0.393595041
25,04/01/2013 12:30,0.396694215
26,04/01/2013 13:00,0.393595041
27,04/01/2013 13:30,0.385330579
28,04/01/2013 14:00,0.367768595
29,04/01/2013 14:30,0.344008264
30,04/01/2013 15:00,0.320247934
31,04/01/2013 15:30,0.297520661
32,04/01/2013 16:00,0.273760331
33,04/01/2013 16:30,0.254132231
34,04/01/2013 17:00,0.216942149
35,04/01/2013 17:30,0.167355372
36,04/01/2013 18:00,0.123966942
37,04/01/2013 18:30,0.080785124
38,04/01/2013 19:00,0.041115702
39,04/01/2013 19:30,0.015805785
40,04/01/2013 20:00,0.002489669
41,04/01/2013 20:30,2.67E-05
42,04/01/2013 21:00,1.24E-05
43,04/01/2013 21:30,0
44,04/01/2013 22:00,0
45,04/01/2013 22:30,0
46,04/01/2013 23:00,0
47,04/01/2013 23:30,0`;
var margin_ = {top: 30, right: 60, bottom: 30, left: 20},
width_ = 300
height_ = 700
// Add svg
var line_graph = d3.select("#my_dataviz_test")
.append("svg")
.attr("width", width_ + 100)
.attr("height", height_)
.append("g")
.attr("transform",
"translate(" + margin_.left + "," + margin_.top + ")");
const point = line_graph.append('circle')
.attr('r', 5)
.style('fill', 'red');
const data = d3.csvParse(csvData).map(d => ({
output_time_ref: +d.output_time_ref,
output_time: d3.timeParse("%d/%m/%Y %H:%M")(d.output_time),
prediction: +d.prediction,
}));
console.log(data);
// Add x axis
var x_test = d3.scaleLinear()
.domain([0, d3.max(data, d => d.prediction)])
.range([ 0, width_ ]);
line_graph.append("g")
.attr("transform", "translate(" + 0 + "," + height_ + ")")
.call(d3.axisBottom(x_test).tickSizeOuter(0).tickSizeInner(0).ticks(2))
.select(".domain").remove();
// Add Y axis
var y_test = d3.scaleLinear()
.domain([0, d3.max(data, d => +d.output_time_ref)])
.range([ height_, 0 ]);
line_graph.append("g")
.call(d3.axisLeft(y_test).tickSizeOuter(0).tickSizeInner(0).ticks(5))
.select(".domain").remove();
// Add the line
path_test = line_graph.append("path")
.datum(data)
.attr("fill", "none")
.attr("fill", "steelblue")
.attr("fill-opacity", 0.2)
.attr("stroke", "steelblue")
.attr("stroke-width", 1)
.attr("d", d3.line()
.curve(d3.curveBasis)
.x(function(d) { return x_test(d.prediction) })
.y(function(d) { return y_test(d.output_time_ref) })
)
line_graph.on('mousemove', () => {
const y = d3.event.layerY - margin_.top;
const curY = y_test.invert(y);
const minY = Math.floor(curY);
const maxY = Math.ceil(curY);
if (data[minY] && data[maxY]) {
const yDelta = curY - minY;
const minP = data[minY].prediction;
const maxP = data[maxY].prediction;
const curP = minP + (maxP - minP) * yDelta;
const xPos = x_test(curP)
// console.log(xPos);
point
.attr('cx', xPos)
.attr('cy', y)
}
// line_graph
// y_test
})
/*
var mouseG2 = line_graph
.append("g")
.attr("class", "mouse-over-effects");
mouseG2
.append("path")
.attr("class", "mouse-line2")
.style("stroke", "#393B45")
.style("stroke-width", "0.5px")
.style("opacity", 0.75)
mouseG2.append("text")
.attr("class", "mouse-text2")
var totalLength2 = path_test.node().getTotalLength();
var mousePerLine2 = mouseG2.selectAll('.mouse-per-line2')
.data(data)
.enter()
.append("g")
.attr("class", "mouse-per-line2");
mousePerLine2.append("circle")
.attr("r", 8)
.style("stroke", 'red')
.style("fill", "none")
.style("stroke-width", "2px")
.style("opacity", "0");
mouseG2
.append('svg:rect')
.attr('width', width_)
.attr('height', height_)
.attr('fill', 'none')
// .attr('opacity', 0.2)
.attr('pointer-events', 'all')
.on('mouseout', function() {
d3.select("#my_dataviz_test")
.selectAll(".mouse-per-line2 circle")
.style("opacity", "0"); })
var mouseover = function(d) {
d3.select("#my_dataviz_test")
.select(".mouse-line2")
.style("opacity", "1")
.select(".mouse-text2")
.style("opacity", "1")
.select(".mouse-per-line2 circle")
.style("opacity", "1");
d3.select("#my_dataviz_test")
var mouse2 = d3.mouse(this);
d3.select("#my_dataviz_test")
.select(".mouse-text2")
.attr("y", mouse2[1])
.attr("transform", "translate(" + (mouse2[1]+60) + "," + (mouse2[1]+5) + ") rotate(90)")
d3.select("#my_dataviz_test")
.select(".mouse-line2")
.attr("d", function() {
var d = "M" + width_ + "," + mouse2[1];
d += " " + 0 + "," + mouse2[1];
return d;
})
d3.select("#my_dataviz_test")
.selectAll(".mouse-per-line2")
.attr("transform", function(d, i) {
var beginning2 = 0,
end2 = totalLength2
target2 = null;
while (true){
target2 = Math.floor((beginning2 + end2) / 2);
var pos2 = path_test.node().getPointAtLength(target2);
if ((target2 === end2 || target2 === beginning2) && pos2.y !== mouse2[1]) {
break;
}
if (pos2.y > mouse2[1]) { end2 = target2; }
else if (pos2.y < mouse2[1]) { beginning2 = target2; }
else {break};
}
d3.select("#my_dataviz_test").select('circle')
.style("opacity", 1)
return "translate(" + (pos2.x) + "," + mouse2[1] +")";
});
}
var mouseleave = function(d) {
d3.select("#my_dataviz_test")
.select(".mouse-line2")
.style("opacity", "0")
d3.select("#my_dataviz_test")
.select(".circle")
.style("opacity", "0")
}
line_graph
.on("mouseover", mouseover)
.on("mouseleave", mouseleave);
*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div id="my_dataviz_test" />

How to rotate the D3 chart so that the selected arc is at the bottom?

I am trying to develop an application where users can click on the D3 pie chart and see information which relates to the selection. How can I rotate the arc's so that the clicked arc comes to the bottom (the centre of the arc clicked on should be at the bottom)? I have been playing around with rotating the pie by selecting the arc group but I would appreciate any idea on how to achieve this.
Here is some code I have so far.
var self = this;
var instanceId = Math.floor(Math.random() * 100000);
var margin = {
top: 5,
right: 15,
bottom: 50,
left: 20
};
self.width = this.htmlElement.parentElement.parentElement.offsetWidth - margin.right - margin.left;
self.height = window.innerHeight / 3 ;
self.curAngle = 0;
self.svgParent.html("<h4 style='color:green;'>Sponsors </h4><br>");
// Update data for the chart
self.updateChartData = function () {
if(!self.svg) {
this.svg = this.svgParent
.classed("svg-container", true)
.append("svg")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "0 0 " + this.width + " " + this.height)
.append("g")
.classed("svg-content-responsive", true);
//.attr("transform", "translate(" + this.width / 2 + "," + this.height / 2 + ")");
}
var svgg = this.svg
.append("g")
.classed("svg-content-responsive", true)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
self.svg.selectAll(".arctext").remove();
self.svg.selectAll(".pie-widget-total-label").remove();
var pie = d3.pie().sort(sort)(self.selectedData.map(function (d: any) {
return d.count;
}));
var path = d3.arc()
.outerRadius(radius)
.innerRadius(radius / 2);
var outsideLabel = d3.arc()
.outerRadius(radius * 0.9)
.innerRadius(radius * 0.9);
var middleLabel = d3.arc()
.outerRadius(radius * 0.75)
.innerRadius(radius * 0.75);
var insideLabel = d3.arc()
.outerRadius(radius * 0.6)
.innerRadius(radius * 0.6);
var g = self.svg
.attr("width", self.width + margin.left + margin.right)
.attr("height", self.height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + self.width / 2 + "," + self.height / 2 + ")");
var defs = g.append("defs");
var lightGradients = defs.selectAll(".arc")
.data(pie)
.enter()
.append("radialGradient")
.attr("id", function (d: any) {
return "lightGradient-" + instanceId + "-" + d.index;
})
.attr("gradientUnits", "userSpaceOnUse")
.attr("cx", "0")
.attr("cy", "0")
.attr("r", "120%");
var darkGradients = defs.selectAll(".arc")
.data(pie)
.enter()
.append("radialGradient")
.attr("id", function (d: any) {
return "darkGradient-" + instanceId + "-" + d.index;
})
.attr("gradientUnits", "userSpaceOnUse")
.attr("cx", "0")
.attr("cy", "0")
.attr("r", "120%");
lightGradients.append("stop")
.attr("offset", "0%")
.attr("style", function (d: any) {
return "stop-color: " + d3.color(color(d.index)) + ";";
});
lightGradients.append("stop")
.attr("offset", "100%")
.attr("style", function (d: any) {
return "stop-color: black;";
});
darkGradients.append("stop")
.attr("offset", "0%")
.attr("style", function (d: any) {
return "stop-color: " + d3.color(color(d.index)).darker(0.5) + ";";
});
darkGradients.append("stop")
.attr("offset", "100%")
.attr("style", function (d: any) {
return "stop-color: black;";
});
self.tooltip = d3.select("body")
.append("div")
.attr("class", "pie-widget-tooltip")
.style("opacity", 0);
self.arc = g.selectAll(".arc")
.data(pie)
.enter()
.append("g")
.attr("class", "arc");
var arcpath = self.arc.append("path")
.attr("id", function (d: any) {
return d.index;
})
.attr("d", path)
.attr("stroke", "white")
.attr("stroke-width", "2px")
.attr("fill", function (d: any) {
return "url(#lightGradient-" + instanceId + "-" + d.index + ")";
})
.on("click", function (d: any) {
console.log("About to send::::" + getStudyLabel(d.index));
self.selectedIndustryTypeService.sendMessage(getStudyLabel(d.index));
self.showDialog();
self.rotateChart();
})
.transition()
.duration(function(d:any, i:any) {
return i * 800;
})
.attrTween('d', function(d:any) {
var i = d3.interpolate(d.startAngle + 0.1, d.endAngle);
return function (t: any) {
d.endAngle = i(t);
return path(d);
}
});
function arcTween(a: any) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t: any) {
return path(i(t));
};
}
var gtext = self.svg
.append("g")
.attr("transform", "translate(" + self.width / 2 + "," + self.height / 2 + ")");
var arctext = gtext.selectAll(".arctext")
.data(pie)
.enter()
.append("g")
.attr("class", "arctext");
var primaryLabelText = arctext.append("text")
.on("click", function (d: any) {
console.log("About to send::::" + getStudyLabel(d.index));
self.selectedIndustryTypeService.sendMessage(getStudyLabel(d.index));
self.showDialog();
})
.transition()
.duration(750)
.attr("transform", function (d: any) {
return "translate(" + middleLabel.centroid(d) + ")";
})
.attr("dy", "-0.75em")
.attr("font-family", "sans-serif")
.attr("font-size", "15px")
.attr("class", "sponsor-pie-widget-label")
.text(function (d: any) {
if (d.endAngle - d.startAngle < 0.3) {
return "";
} else {
return getStudyLabel(d.index);
}
});
var secondaryLabelText = arctext.append("text")
.on("click", function (d: any) {
console.log("About to send::::" + getStudyLabel(d.index));
self.selectedIndustryTypeService.sendMessage(getStudyLabel(d.index));
self.showDialog();
})
.transition()
.duration(750)
.attr("transform", function (d: any) {
return "translate(" + middleLabel.centroid(d) + ")";
})
.attr("dy", "0.75em")
.attr("font-family", "sans-serif")
.attr("font-size", "10px")
.attr("class", "sponsor-pie-widget-label")
.text(function (d: any) {
if (d.endAngle - d.startAngle < 0.3) {
return "";
} else {
return getPatientsLabel(d.index);
}
});
var n = 0;
for (var i = 0; i < self.selectedData.length; i++) {
n = n + this.selectedData[i]["count"];
}
var total = self.svg
.append("g")
.attr("transform", "translate(" + (self.width / 2 - 20 ) + "," + self.height / 2 + ")")
total.append("text")
.attr("class", "pie-widget-total-label")
.text("Total\r\n" + n);
}.bind(self);
self.showDialog = function() {
this.router.navigateByUrl('/myRouteName');
}.bind(self);
self.rotateChart = function() {
self.arc.attr("transform", "rotate(-45)");
}.bind(self);
You could rotate the arcs by changing their start/end angles as appropriate, but this would be more complex than needed.
A simpler solution would be to just rotate a g that holds the entire pie chart, at the same time, rotate any labels the other way so they remain level.
I've just used the canonical pie chart from this block as an example:
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
radius = Math.min(width, height) / 2,
g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var color = d3.scaleOrdinal(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var data = [ {age:"<5", population: 2704659},{age:"5-13", population: 4499890},{age:"14-17", population: 2159981},{age:"18-24", population: 3853788},{age:"25-44", population: 14106543},{age:"45-64", population: 8819342},{age:"≥65", population: 612463} ]
var pie = d3.pie()
.sort(null)
.value(function(d) { return d.population; });
var path = d3.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var label = d3.arc()
.outerRadius(radius - 40)
.innerRadius(radius - 40);
var arc = g.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
arc.append("path")
.attr("d", path)
.attr("fill", function(d) { return color(d.data.age); })
.on("click",function(d) {
// The amount we need to rotate:
var rotate = 180-(d.startAngle + d.endAngle)/2 / Math.PI * 180;
// Transition the pie chart
g.transition()
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ") rotate(" + rotate + ")")
.duration(1000);
// Τransition the labels:
text.transition()
.attr("transform", function(dd) {
return "translate(" + label.centroid(dd) + ") rotate(" + (-rotate) + ")"; })
.duration(1000);
});
var text = arc.append("text")
.attr("transform", function(d) { return "translate(" + label.centroid(d) + ")"; })
.attr("dy", "0.35em")
.text(function(d) { return d.data.age; });
.arc text {
font: 10px sans-serif;
text-anchor: middle;
}
.arc path {
stroke: #fff;
}
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>

Appending element clone to svg group in d3js

I am able to append a rectangle to group on drag drop but it is not working properly. If I drag and drop multiple rectangles on to group, the rectangles are dislocated at different location. I am sure there must be some thing wrong.
Demo: http://jsfiddle.net/wqvLLbLa/
code:
var svgContainer = d3.select("body").append("svg")
.attr("width", 800)
.attr("height", 803);
var rect = svgContainer.append("rect")
.attr("x", 10)
.attr("y", 50)
.attr("width", 51)
.attr("height", 41)
.attr("rx", 10)
.attr("stroke-width", 2)
.attr("stroke", "#7E7E7E")
.style('cursor', 'move')
.style("fill", "white");
function moveRect() {
d3.select(this)
.attr('x', d3.event.x)
.attr('y', d3.event.y);
}
var dragGroup = d3.behavior.drag()
.origin(function () {
var g = this;
return {x: d3.transform(g.getAttribute("transform")).translate[0],
y: d3.transform(g.getAttribute("transform")).translate[1]};
})
.on("drag", function (d, i) {
g = this;
translate = d3.transform(g.getAttribute("transform")).translate;
console.log(translate);
x = d3.event.dx + translate[0],
y = d3.event.dy + translate[1];
d3.select(g).attr("transform", "translate(" + x + "," + y + ")");
d3.event.sourceEvent.stopPropagation();
});
var group = svgContainer.append("g")
.attr("id", "mygroup")
.call(dragGroup)
.style('cursor', 'move')
.attr("transform", "translate(20, 20)");
group.append("rect")
.attr("x", 250)
.attr("y", 250)
.attr("width", 151)
.attr("height", 141)
.attr("rx", 10)
.attr("stroke-width", 2)
.attr("stroke", "#7E7E7E")
.style("fill", "white");
var circleDrag = d3.behavior.drag()
.origin(function ()
{
var t = d3.select(this);
return {x: t.attr("cx"), y: t.attr("cy")};
})
var rectDrag = d3.behavior.drag()
.origin(function ()
{
var t = d3.select(this);
return {x: t.attr("x"), y: t.attr("y")};
})
.on('dragend', function (d) {
var mouseCoordinates = d3.mouse(this);
var groupTransform = d3.transform(group.attr("transform"));
var groupX = groupTransform.translate[0];
var groupY = groupTransform.translate[1];
var rect = group.select("rect");
var rectX = +rect.attr("x");
var rectY = +rect.attr("y");
var rectWidth = +rect.attr("width");
var rectHeight = +rect.attr("height");
if (mouseCoordinates[0] > groupX + rectX
&& mouseCoordinates[0] < groupX + rectX + rectWidth
&& mouseCoordinates[1] > groupY + rectY
&& mouseCoordinates[1] < groupY + rectY + rectHeight) {
//Append new element
var newRect = d3.select("g").append("rect")
.classed("drg", true)
.attr("x", 100)
.attr("y", 100)
.attr("rx", 10)
.attr("width", 51)
.attr("height", 41)
.attr("x", mouseCoordinates[0])
.attr("y", mouseCoordinates[1])
.style("fill", "white")
.style("stroke-width", 2)
.style("stroke", "#CDB483");
}
else
{
var newRect = d3.select("svg").append("rect")
.classed("drg", true)
.attr("x", 100)
.attr("y", 100)
.attr("rx", 10)
.attr("width", 51)
.attr("height", 41)
.attr("x", mouseCoordinates[0])
.attr("y", mouseCoordinates[1])
.style("fill", "white")
.style("stroke-width", 2)
.style("stroke", "#CDB483")
.call(
d3.behavior.drag()
.on('drag', moveRect).origin(function () {
var t = d3.select(this);
return {x: t.attr("x"), y: t.attr("y")};
}));
}
});
rect.call(rectDrag);
Responding to the update question - as in the comments.
The reason for the duplicates is because you are appending a new element to the targetG after your bounds check, instead of targetCircle.
The easiest way to fix this would be to simply remove targetCircle after appending the new circle like so
targetCircle.remove();
Fiddle - http://jsfiddle.net/afmLhofL/

add circle in a spiral chart with d3js with line connecting to center

hi I created a spiral chart in d3.js, and I want to add circle to different position of the spiral lines.according to there values.
circle closes to the center will have highest priority.
any idea how to do that.
here is the code which i wrote
var width = 400,
height = 430
num_axes = 8,
tick_axis = 1,
start = 0
end = 4;
var theta = function(r) {
return -2*Math.PI*r;
};
var arc = d3.svg.arc()
.startAngle(0)
.endAngle(2*Math.PI);
var radius = d3.scale.linear()
.domain([start, end])
.range([0, d3.min([width,height])/2-20]);
var angle = d3.scale.linear()
.domain([0,num_axes])
.range([0,360])
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width/2 + "," + (height/2+8) +")");
var pieces = d3.range(start, end+0.001, (end-start)/1000);
var spiral = d3.svg.line.radial()
.interpolate("cardinal")
.angle(theta)
.radius(radius);
//svg.append("text")
// .text("And there was much rejoicing!")
// .attr("class", "title")
// .attr("x", 0)
// .attr("y", -height/2+16)
// .attr("text-anchor", "middle")
//svg.selectAll("circle.tick")
// .data(d3.range(end,start,(start-end)/4))
// .enter().append("circle")
// .attr("class", "tick")
// .attr("cx", 0)
// .attr("cy", 0)
// .attr("r", function(d) { return radius(d); })
svg.selectAll(".axis")
.data(d3.range(num_axes))
.enter().append("g")
.attr("class", "axis")
.attr("transform", function(d) { return "rotate(" + -angle(d) + ")"; })
.call(radial_tick)
.append("text")
.attr("y", radius(end)+13)
.text(function(d) { return angle(d) + "°"; })
.attr("text-anchor", "middle")
.attr("transform", function(d) { return "rotate(" + -90 + ")" })
svg.selectAll(".spiral")
.data([pieces])
.enter().append("path")
.attr("class", "spiral")
.attr("d", spiral)
.attr("transform", function(d) { return "rotate(" + 90 + ")" });
function radial_tick(selection) {
selection.each(function(axis_num) {
d3.svg.axis()
.scale(radius)
.ticks(5)
.tickValues( axis_num == tick_axis ? null : [])
.orient("bottom")(d3.select(this))
d3.select(this)
.selectAll("text")
.attr("text-anchor", "bottom")
.attr("transform", "rotate(" + angle(axis_num) + ")")
});
}
please see the second solution for my implementation. Help me with connecting the circle with the center
Here is a model for the technique you seem to be looking for...
var width = 400,
height = 430,
num_axes = 8,
tick_axis = 1,
start = 0,
end = 4,
testValue = 2;
var theta = function (r) {
return -2 * Math.PI * r;
};
var arc = d3.svg.arc()
.startAngle(0)
.endAngle(2 * Math.PI);
var radius = d3.scale.linear()
.domain([start, end])
.range([0, (d3.min([width, height]) / 2 - 20)]);
var angle = d3.scale.linear()
.domain([0, num_axes])
.range([0, 360]);
var chart = d3.select("#chart")
.style("width", width + "px");
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + (height / 2 + 8) + ")");
var pieces = d3.range(start, end + 0.001, (end - start) / 500);
var spiral = d3.svg.line.radial()
.interpolate("linear")
.angle(theta)
.radius(radius);
svg.append("text")
.text("Title")
.attr("class", "title")
.attr("x", 0)
.attr("y", -height/2+16)
.attr("text-anchor", "middle")
svg.selectAll("circle.tick")
.data(d3.range(end,start,(start-end)/4))
.enter().append("circle")
.attr("class", "tick")
.style({fill: "black", opacity: 0.1})
.attr("cx", 0)
.attr("cy", 0)
.attr("r", function(d) { return radius(d); })
svg.selectAll(".axis")
.data(d3.range(num_axes))
.enter().append("g")
.attr("class", "axis")
.attr("transform", function (d) { return "rotate(" + -angle(d) + ")"; })
.call(radial_tick)
.append("text")
.attr("y", radius(end) + 13)
.text(function (d) { return angle(d) + "°"; })
.attr("text-anchor", "middle")
.attr("transform", function (d) { return "rotate(" + -90 + ")" })
svg.selectAll(".axis path")
.style({fill: "none", stroke: "black"})
.attr("stroke-dasharray", "5 5")
svg.selectAll(".spiral")
.data([pieces])
.enter().append("path")
.attr("class", "spiral")
.attr("fill", "none")
.attr("stroke", "black")
.attr("d", spiral)
.attr("transform", function (d) { return "rotate(" + 90 + ")" });
function radial_tick(selection) {
selection.each(function (axis_num) {
d3.svg.axis()
.scale(radius)
.ticks(5)
.tickValues(axis_num == tick_axis ? null : [])
.tickSize(1)
.orient("bottom")(d3.select(this))
d3.select(this)
.selectAll("text")
.attr("text-anchor", "bottom")
.attr("transform", "rotate(" + angle(axis_num) + ")")
});
}
function radialScale(x) {
var t = theta(x), r = radius(x);
d3.select(this)
.attr("cx", r * Math.cos(t))
.attr("cy", r * Math.sin(t))
}
slider = SliderControl("#circleSlider", "data", update, [start, end], ",.3f");
function update(x) {
if (typeof x != "undefined") testValue = x;
var circles = svg.selectAll(".dataPoints")
.data([testValue]);
circles.enter().append("circle");
circles.attr("class", "dataPoints")
.style({ fill: "black", opacity: 0.6 })
.attr("r", 10)
.each(radialScale)
circles.exit().remove();
return testValue
}
function SliderControl(selector, title, value, domain, format) {
var accessor = d3.functor(value), rangeMax = 1000,
_scale = d3.scale.linear().domain(domain).range([0, rangeMax]),
_$outputDiv = $("<div />", { class: "slider-value" }),
_update = function (value) {
_$outputDiv.css("left", 'calc( '
+ (_$slider.position().left + _$slider.outerWidth()) + 'px + 1em )')
_$outputDiv.text(d3.format(format)(value));
$(".input").width(_$outputDiv.position().left + _$outputDiv.outerWidth() - _innerLeft)
},
_$slider = $(selector).slider({
value: _scale(accessor()),
max: rangeMax,
slide: function (e, ui) {
_update(_scale.invert(ui.value));
accessor(_scale.invert(ui.value));
}
}),
_$wrapper = _$slider.wrap("<div class='input'></div>")
.before($("<div />").text(title + ":"))
.after(_$outputDiv).parent(),
_innerLeft = _$wrapper.children().first().position().left;
_update(_scale.invert($(selector).slider("value")))
return d3.select(selector)
};
.domain {
stroke-width: 1px;
}
<link href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="chart">
<div id="circleSlider"></div>
</div>

Displaying total value for each item in a chord chart

I was working on the chord diagram and I am unable to show the total value for each product. I want it to display the total count for each category when I hover over the radial arc for a particular category. The example is taken from http://bl.ocks.org/mbostock/4062006. Any prompt help would be greatly appreciated. Thank you :)
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.chord path {
fill-opacity: .67;
stroke: #000;
stroke-width: .5px;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// From http://mkweb.bcgsc.ca/circos/guide/tables/
var matrix = [
[11975, 5871, 8916, 2868],
[ 1951, 10048, 2060, 6171],
[ 8010, 16145, 8090, 8045],
[ 1013, 990, 940, 6907]
];
var chord = d3.layout.chord()
.padding(.05)
.sortSubgroups(d3.descending)
.matrix(matrix);
var width = 960,
height = 500,
innerRadius = Math.min(width, height) * .41,
outerRadius = innerRadius * 1.1;
var fill = d3.scale.ordinal()
.domain(d3.range(4))
.range(["#000000", "#FFDD89", "#957244", "#F26223"]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
svg.append("g").selectAll("path")
.data(chord.groups)
.enter().append("path")
.style("fill", function(d) { return fill(d.index); })
.style("stroke", function(d) { return fill(d.index); })
.attr("d", d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius))
.on("mouseover", fade(.1))
.on("mouseout", fade(1));
var ticks = svg.append("g").selectAll("g")
.data(chord.groups)
.enter().append("g").selectAll("g")
.data(groupTicks)
.enter().append("g")
.attr("transform", function(d) {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
+ "translate(" + outerRadius + ",0)";
});
ticks.append("line")
.attr("x1", 1)
.attr("y1", 0)
.attr("x2", 5)
.attr("y2", 0)
.style("stroke", "#000");
ticks.append("text")
.attr("x", 8)
.attr("dy", ".35em")
.attr("transform", function(d) { return d.angle > Math.PI ? "rotate(180)translate(-16)" : null; })
.style("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.text(function(d) { return d.label; });
svg.append("g")
.attr("class", "chord")
.selectAll("path")
.data(chord.chords)
.enter().append("path")
.attr("d", d3.svg.chord().radius(innerRadius))
.style("fill", function(d) { return fill(d.target.index); })
.style("opacity", 1)
.append("svg:title").text(function(d, i) { return "Value : " + d.source.value });;
// Returns an array of tick angles and labels, given a group.
function groupTicks(d) {
var k = (d.endAngle - d.startAngle) / d.value;
return d3.range(0, d.value, 1000).map(function(v, i) {
return {
angle: v * k + d.startAngle,
label: i % 5 ? null : v / 1000 + "k"
};
});
}
// Returns an event handler for fading a given chord group.
function fade(opacity) {
return function(g, i) {
svg.selectAll(".chord path")
.filter(function(d) { return d.source.index != i && d.target.index != i; })
.transition()
.style("opacity", opacity);
};
}
</script>
`

Categories