Hi I'm trying to use attrTween for my radar chart.
I made it happen for arc chart so I wanted to apply the same logic to my radar chart but it didn't work out.
can anyone let me know which part I missed?
I would like to shrink the radar chart on 'click'.
the code is as below.
d3.selectAll('.btn').on('click', function(d) {
let selectedone = d3.select(this).attr('class').split(' ')[1]
console.log(selectedone);
d3.select(`.radar${selectedone}`)
.transition()
.duration(1000)
.attrTween('d', shrinkRadar(0))
function shrinkRadar(target) {
return function(d) {
console.log(d)
let interpolate = d3.interpolate(rscale(d.value), target)
return function(t) {
console.log(d.radius);
d.radius = interpolate(t)
return radarLine(d)
}
}
}
// .attrTween('d',shrinkRadar(finalvalue))
})
Full code in the following link
https://codepen.io/jotnajoa/pen/dypJzmZ
Your radar chart's line functions take an array of data to output a path (line and area fill). You are attempting, though, to operate the attrTween like it's manipulating a single piece of data and not an array. Take a look at this quick re-write:
function shrinkRadar(target) {
return function(d) {
// create an array of interpolators
// one for each point in the line
// that run the value from starting to 0
var interps = d.map( da => d3.interpolate(da.value, target));
return function(t) {
// for each point call it's interpolator
// and re-assign the value
d.forEach( (da,i) => {
da.value = interps[i](t);
});
// regenerate path with new value
return radarLine(d)
}
}
}
Running code:
let margin = {
top: 100,
bottom: 100,
left: 100,
right: 100
}
let width = Math.min(700, window.innerWidth - 10) - margin.left - margin.right;
let height = Math.min(width, window.innerHeight - margin.top - margin.bottom - 20);
var data = [
[ //iPhone
{
axis: "Battery Life",
value: 0.22
},
{
axis: "Brand",
value: 0.28
},
{
axis: "Contract Cost",
value: 0.29
},
{
axis: "Design And Quality",
value: 0.17
},
{
axis: "Have Internet Connectivity",
value: 0.22
},
{
axis: "Large Screen",
value: 0.02
},
{
axis: "Price Of Device",
value: 0.21
},
{
axis: "To Be A Smartphone",
value: 0.50
}
],
[ //Samsung
{
axis: "Battery Life",
value: 0.27
},
{
axis: "Brand",
value: 0.16
},
{
axis: "Contract Cost",
value: 0.35
},
{
axis: "Design And Quality",
value: 0.13
},
{
axis: "Have Internet Connectivity",
value: 0.20
},
{
axis: "Large Screen",
value: 0.13
},
{
axis: "Price Of Device",
value: 0.35
},
{
axis: "To Be A Smartphone",
value: 0.38
}
],
[ //Nokia Smartphone
{
axis: "Battery Life",
value: 0.26
},
{
axis: "Brand",
value: 0.10
},
{
axis: "Contract Cost",
value: 0.30
},
{
axis: "Design And Quality",
value: 0.14
},
{
axis: "Have Internet Connectivity",
value: 0.22
},
{
axis: "Large Screen",
value: 0.04
},
{
axis: "Price Of Device",
value: 0.41
},
{
axis: "To Be A Smartphone",
value: 0.30
}
]
];
var color = d3.scaleOrdinal()
.range(['#6678D9', '#61F06C', '#FF36AA'])
var radarChartOptions = {
w: width,
h: height,
margin: margin,
maxValue: 0.5,
levels: 5,
roundStrokes: true,
color: color
};
// blue, green pink inorder
RadarChart('.radarChart', data, radarChartOptions)
function RadarChart(id, data, options) {
let cfg = {
w: 600,
h: 600,
margin: {
top: 20,
right: 20,
bottom: 20,
left: 20
},
levels: 3,
maxValue: 0,
labelFactor: 1.25,
wrapWidth: 60,
opacityArea: 0.35,
dotRadius: 4,
opacityCircles: 0.1,
strokeWidth: 2,
roundStroke: false,
color: ['#6678D9', '#61F06C', '#FF36AA']
}
if ('undefined' !== typeof options) {
for (var i in options) {
if ('undefined' !== typeof options[i]) {
cfg[i] = options[i];
}
} //for i
} //if
// 결론적으로 options에 들어가있는 녀석과 함수에있는 녀석을 매치시키는것
var maxValue =
// Math.max(cfg.maxValue,
d3.max(data,
(i) => d3.max(
i.map((o) => o.value)
// 벨류로 이루어진 어레이를 만들고, 그 어레이에서 최대값을 출력해낸다.
)
)
// )
var allAxis = data[0].map((d) => {
return d.axis
})
var total = allAxis.length,
radius = Math.min(cfg.w / 2, cfg.h / 2),
Format = d3.format('%'),
angleSlice = Math.PI * 2 / total
var rscale = d3.scaleLinear()
.range([0, radius])
.domain([0, maxValue])
d3.select(id).select('svg').remove()
var svg = d3.select(id).append('svg')
.attr('width', cfg.w + cfg.margin.left + cfg.margin.right)
.attr('height', cfg.h + cfg.margin.top + cfg.margin.bottom)
.attr('class',
'radar' + id);
var g = svg.append('g').attr('transform', `translate(${cfg.w/2+cfg.margin.left},${cfg.h/2+cfg.margin.top})`)
var filter = g.append('defs').append('filter').attr('id', 'glow')
var feGaussianBlur = filter.append('feGaussianBlur').attr('stdDeviation', '2.5')
.attr('result', 'coloredBlur')
var feMerge = filter.append('feMerge')
var feMergeNode1 = feMerge.append('feMergeNode').attr('in', 'coloredBlur')
var feMergeNode2 = feMerge.append('feMergeNode').attr('in', 'SourceGraphic')
var axisGrid = g.append('g').attr('class', 'axisWrapper')
axisGrid.selectAll('.levels').data(
d3.range(1, (cfg.levels + 1))
)
.join('circle')
.attr('class', 'gridCircle')
.attr('r', (d) => {
return radius / cfg.levels * d
})
.style('fill', '#CDCDCD')
.style('stroke', '#CDCDCD')
.style('fill-opacity', cfg.opacityCircles)
.style('filter', 'url(#glow)')
axisGrid.selectAll('.axisLabel')
.data(d3.range(1, cfg.levels + 1))
.join('text')
.attr('class', 'axisLabel')
.attr('x', 4)
.attr('y', (d) => {
return -d * radius / cfg.levels
})
.attr('dy', '0.4em')
.style('font-size', '10px')
.attr('fill', '#737373')
.text((d, i) => {
return Format(maxValue * d / cfg.levels)
})
var axis = axisGrid.selectAll('.axis')
.data(allAxis)
.join('g')
.attr('class', 'axis')
axis.append('line')
.attr('x1', 0)
.attr('y1', 0)
.attr("x2", function(d, i) {
return rscale(maxValue * 1.1) * Math.cos(angleSlice * i - Math.PI / 2);
})
.attr("y2", function(d, i) {
return rscale(maxValue * 1.1) * Math.sin(angleSlice * i - Math.PI / 2);
})
.attr("class", "line")
.style("stroke", "white")
.style("stroke-width", "2px");
axis.append("text")
.attr("class", "legend")
.style("font-size", "11px")
.attr("text-anchor", "middle")
.attr("dy", "0.35em")
.attr("x", function(d, i) {
return rscale(maxValue * cfg.labelFactor) * Math.cos(angleSlice * i - Math.PI / 2);
})
.attr("y", function(d, i) {
return rscale(maxValue * cfg.labelFactor) * Math.sin(angleSlice * i - Math.PI / 2);
})
.text(function(d) {
return d
})
.call(wrap, cfg.wrapWidth)
// .call(testfunc, 'testString')
var radarLine = d3.radialLine()
.radius(function(d) {
return rscale(d.value)
})
.angle(function(d, i, t) {
return i * angleSlice
})
.curve(d3.curveCardinalClosed)
// if (cfg.roundStrokes) {
// radarLine.interpolate("cardinal-closed");
// }
const blobWrapper = g.selectAll(".radarWrapper")
.data(data)
.join('g')
.attr("class", "radarWrapper");
blobWrapper
.append("path")
.attr("class", "radarArea")
.attr("d", function(d) {
return radarLine(d)
})
.attr('class', function(d, i) {
return `radar${i}`
})
.style("fill", (d, i) => cfg.color(i))
.style("fill-opacity", cfg.opacityArea)
.on('mouseover', function(d, i) {
d3.selectAll('.radarArea').transition().duration(200)
.style('fill-opacity', 0.1)
d3.select(this).transition().duration(200).style('fill-opacity', cfg.opacityArea);
})
.on('mouseout', function() {
//Bring back all blobs
d3.selectAll(".radarArea")
.transition().duration(200)
.style("fill-opacity", cfg.opacityArea);
});
blobWrapper.append('path')
.attr('class', 'radarStroke')
.attr('d', radarLine)
.attr('class', function(d, i) {
return `radarStroke${i}`
})
.style('stroke', (d, i) => cfg.color(i))
.style('stroke-width', cfg.strokeWidth + 'px')
.style('fill', 'none')
.style('filter', 'url(#glow)')
let round = 0;
blobWrapper.selectAll(".radarCircle")
.data(data)
.data(function(d) {
return d
})
.join('circle')
.attr("class", "radarCircle")
.attr("r", cfg.dotRadius)
.attr("cx", function(d, i) {
return rscale(d.value) * Math.cos(angleSlice * i - Math.PI / 2);
})
.attr("cy", function(d, i) {
return rscale(d.value) * Math.sin(angleSlice * i - Math.PI / 2);
})
.style("fill", function(d, i, j) {
if (i == 0) {
round = round + 1;;
}
return cfg.color(round - 1);
})
.style("fill-opacity", 0.8);
var blobCircleWrapper = g.selectAll('.radarCircleWrapper')
.data(data)
.join('g')
.attr('class', 'radarCircleWrapper')
blobCircleWrapper.selectAll('circles').data(function(d) {
return d
})
.join('circle').attr('class', 'invisibleCircles')
.attr('r', cfg.dotRadius * 1.5)
.attr("cx", function(d, i) {
return rscale(d.value) * Math.cos(angleSlice * i - Math.PI / 2);
})
.attr("cy", function(d, i) {
return rscale(d.value) * Math.sin(angleSlice * i - Math.PI / 2);
})
.style('fill', 'none')
.style('pointer-events', 'all')
.on('mouseover', function(d, i) {
newX = parseFloat(d3.select(this).attr('cx')) - 10;
newY = parseFloat(d3.select(this).attr('cy')) - 10;
tooltip
.attr('x', newX)
.attr('y', newY)
.text(Format(d.value))
.transition().duration(200)
.style('opacity', 1);
})
.on("mouseout", function() {
tooltip.transition().duration(200)
.style("opacity", 0);
});
var tooltip = g.append("text")
.attr("class", "tooltip")
.style("opacity", 0);
d3.selectAll('.btn').on('click', function(d) {
let selectedone = d3.select(this).attr('class').split(' ')[1]
console.log(selectedone);
d3.select(`.radar${selectedone}`)
.transition()
.duration(1000)
.attrTween('d', shrinkRadar(0))
function shrinkRadar(target) {
return function(d) {
var interps = d.map(da => d3.interpolate(da.value, target));
return function(t) {
d.forEach((da, i) => {
da.value = interps[i](t);
});
return radarLine(d)
}
}
}
// .attrTween('d',shrinkRadar(finalvalue))
})
}
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.4, // ems
y = text.attr("y"),
x = text.attr("x"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", x).attr("y", y).attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
}
.radarChart {
width: 1000px;
height: 500px;
}
.buttonwrap {
margin-left: 200px;
text-align: left;
/* border: dashed grey 0.5px; */
}
.btn {
text-align: center;
display: inline-block;
width: 100px;
height: 20px;
border: solid 1px navy;
border-radius: 5px;
margin: auto;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.3.1/d3.min.js"></script>
<svg class="radarChart"></svg>
<div class="buttonwrap">
<div class="btn 0">Show 1</div>
<div class="btn 1">Show 2</div>
<div class="btn 2">show 3</div>
</div>
Related
This is the data I have to draw on a D3 line graph, where the lines are Addon, Loss, New, Repeat. Same should be shown in the legend:
dataf: [
{
drugs: "BENEPALI",
Addon: 328,
Loss: 560,
New: 250,
Repeat: 321,
},
{
drugs: "CIMZIA",
Addon: 328,
Loss: 460,
New: 250,
Repeat: 321,
},
{
drugs: "COSENTYX",
Addon: 265,
Loss: 360,
New: 250,
Repeat: 421,
},
{
drugs: "ENBREL",
Addon: 281,
Loss: 260,
New: 150,
Repeat: 321,
},
{
drugs: "TALTZ",
Addon: 290,
Loss: 560,
New: 150,
Repeat: 321,
},
],
I have some success with drawing the legend and plotting axis but not with line. I think there is a problem with the data.
Here is the code:
<template>
<div>
<b-button #click="esCall(dataf, stacklist, 1460, 350)">es call</b-button>
<div id="kmCurveLegend" style="margin-left: -150px; margin-top: 30px"></div>
<div id="kmCurve" style="margin-top: 10px"></div>
</div>
</template>
<script>
import * as d3 from "d3";
import d3Tip from "d3-tip";
export default {
data() {
return {
dataf: [
{
drugs: "BENEPALI",
Addon: 328,
Loss: 560,
New: 250,
Repeat: 321,
},
{
drugs: "CIMZIA",
Addon: 328,
Loss: 460,
New: 250,
Repeat: 321,
},
{
drugs: "COSENTYX",
Addon: 265,
Loss: 360,
New: 250,
Repeat: 421,
},
{
drugs: "ENBREL",
Addon: 281,
Loss: 260,
New: 150,
Repeat: 321,
},
{
drugs: "TALTZ",
Addon: 290,
Loss: 560,
New: 150,
Repeat: 321,
},
],
stacklist: ["Addon", "Loss", "New", "Repeat"],
xlabel: [],
sumstat: null,
mktSegName: "",
current_stacks: null,
height: 0,
width: 0,
svg: null,
res: null,
x1: null,
minWidth: null,
minHeight: null,
stacks: null,
totalStacks: null,
data: null,
};
},
mounted() {
// this.kmLayout();
},
methods: {
esCall(dataf, stacklist, w, h) {
this.stacks = stacklist;
this.data = dataf;
this.minWidth = w;
this.minHeight = h;
this.kmLayout(this.minWidth, this.minHeight, this.data, this.stacks);
},
kmLayout(w, h, data, stacklist) {
d3.selectAll("#KMGraph").remove();
// set the dimensions and margins of the graph
var margin = { top: 10, right: 30, bottom: 30, left: 60 };
this.width = w - margin.left - margin.right;
this.height = h - margin.top - margin.bottom;
// append the svg object to the body of the page
this.svg = d3
.select("#kmCurve")
.append("svg")
.attr("id", "KMGraph")
.attr("width", this.width + margin.left + margin.right)
.attr("height", this.height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//Grouping according to multiple MS
var updatedData = [];
for (var i in data) {
updatedData.push(data[i]);
}
this.sumstat = d3.group(
updatedData,
(d) => d.Addon && d.Loss && d.New && d.Repeat
);
console.log("this.sumstat", this.sumstat);
var x = d3
.scaleBand()
.domain(
data.map(function (d) {
return d.drugs;
})
)
.range([0, this.width]); // This controls the Horizontal position of the Axis
// Draw the axis
this.svg
.append("g")
.attr("transform", "translate(0," + this.height + ")") // This controls the vertical position of the Axis
.call(d3.axisBottom(x));
// Add Y axis
var y = d3
.scaleLinear()
.domain([
0,
d3.max(data, function (d) {
return +d.Loss + d.Addon + d.New + d.Repeat;
}),
])
.range([this.height, 0]);
this.svg.append("g").call(d3.axisLeft(y));
//tooltip
this.tip = d3Tip()
.attr("class", "d3-tip")
.direction("e")
.html(function (d, i) {
return (
"<div style='margin-top:20%;background-color:whitesmoke;border-radius:4px;color:black;padding:5px'><strong>" +
i[0]
// +
// "</strong><br><strong>( " +
// i[1][parseInt(r / 10)] +
// "," +
// i[1][parseInt(r / 10)].sum +
// ")</strong></div>"
);
});
this.svg.call(this.tip);
var dataArray = Array.from(this.sumstat, ([key, value]) => ({
key,
value,
}));
var color = d3
.scaleOrdinal()
.domain(
stacklist.map(function (d) {
return d;
})
)
.range([
"#e20000",
"#377eb8",
"#4daf4a",
"#250056 ",
"#ff9300",
"#df216d",
"#a65628",
"#0768fd",
"#00c221",
"#9e54b0",
"#ffff33",
"#10558a",
"#595959",
]);
this.svg
.selectAll(".line")
.data(this.sumstat)
.enter()
.append("path")
.attr("class", "lineKM")
.attr("id", (d) => "kmCurve" + d[1])
.attr("fill", "none")
.attr("stroke", function (d) {
console.log(d);
console.log("color", d[0], color(d[0]));
return color(d[0]);
})
.attr("stroke-width", 3.5)
.attr("d", function (d) {
return d3
.line()
.x((d) => {
console.log("5555555555555555555555", x(d.drugs));
return x(d.drugs);
})
.y((d) => {
console.log("66666666666666666666666", y(d.Addon));
return y(d.Addon);
})(
d[1].sort(function (a, b) {
console.log("bbbbbbbbbbbbbbbbbbb", b.Addon - a.Addon);
return b.Addon - a.Addon;
})
);
})
.on("mouseover", function (d) {
d3.selectAll("#kmCurve" + d.replace(/[^a-zA-Z0-9]/g, "_")).style(
"opacity",
0.1
);
d3.select(this).style("opacity", 1);
})
.call(this.tip)
.on("mouseover", this.tip.show)
.on("mouseout", this.tip.hide);
this.svg
.selectAll(".line")
.data(this.sumstat)
.enter()
.append("path")
.attr("class", "lineKM")
.attr("id", (d) => "kmCurve" + d[1])
.attr("fill", "none")
.attr("stroke", function (d) {
console.log(d);
console.log("color", d[0], color(d[0]));
return color(d[0]);
})
.attr("stroke-width", 3.5)
.attr("d", function (d) {
return d3
.line()
.x((d) => {
console.log("5555555555555555555555", x(d.drugs));
return x(d.drugs);
})
.y((d) => {
console.log("66666666666666666666666", y(d.Loss));
return y(d.Loss);
})(
d[1].sort(function (a, b) {
console.log("bbbbbbbbbbbbbbbbbbb", b.Loss - a.Loss);
return b.Loss - a.Loss;
})
);
})
.on("mouseover", function (d) {
d3.selectAll("#kmCurve" + d.replace(/[^a-zA-Z0-9]/g, "_")).style(
"opacity",
0.1
);
d3.select(this).style("opacity", 1);
})
.call(this.tip)
.on("mouseover", this.tip.show)
.on("mouseout", this.tip.hide);
this.svg
.selectAll(".line")
.data(this.sumstat)
.enter()
.append("path")
.attr("class", "lineKM")
.attr("id", (d) => "kmCurve" + d[1])
.attr("fill", "none")
.attr("stroke", function (d) {
console.log(d);
console.log("color", d[0], color(d[0]));
return color(d[0]);
})
.attr("stroke-width", 3.5)
.attr("d", function (d) {
return d3
.line()
.x((d) => {
console.log("5555555555555555555555", x(d.drugs));
return x(d.drugs);
})
.y((d) => {
console.log("66666666666666666666666", y(d.New));
return y(d.New);
})(
d[1].sort(function (a, b) {
console.log("bbbbbbbbbbbbbbbbbbb", b.New - a.New);
return b.New - a.New;
})
);
})
.on("mouseover", function (d) {
d3.selectAll("#kmCurve" + d.replace(/[^a-zA-Z0-9]/g, "_")).style(
"opacity",
0.1
);
d3.select(this).style("opacity", 1);
})
.call(this.tip)
.on("mouseover", this.tip.show)
.on("mouseout", this.tip.hide);
this.svg
.selectAll(".line")
.data(this.sumstat)
.enter()
.append("path")
.attr("class", "lineKM")
.attr("id", (d) => "kmCurve" + d[1])
.attr("fill", "none")
.attr("stroke", function (d) {
console.log(d);
console.log("color", d[0], color(d[0]));
return color(d[0]);
})
.attr("stroke-width", 3.5)
.attr("d", function (d) {
return d3
.line()
.x((d) => {
console.log("5555555555555555555555", x(d.drugs));
return x(d.drugs);
})
.y((d) => {
console.log("66666666666666666666666", y(d.Repeat));
return y(d.Repeat);
})(
d[1].sort(function (a, b) {
console.log("bbbbbbbbbbbbbbbbbbb", b.Repeat - a.Repeat);
return b.Repeat - a.Repeat;
})
);
})
.on("mouseover", function (d) {
d3.selectAll("#kmCurve" + d.replace(/[^a-zA-Z0-9]/g, "_")).style(
"opacity",
0.1
);
d3.select(this).style("opacity", 1);
})
.call(this.tip)
.on("mouseover", this.tip.show)
.on("mouseout", this.tip.hide);
this.createLegend(stacklist);
},
createLegend(stacklist) {
d3.selectAll("#KmLegends").remove();
var stacks = stacklist;
this.current_stacks = stacks;
console.log("stacks", stacks);
this.stackedZ = d3
.scaleOrdinal()
.range([
"#e20000",
"#377eb8",
"#4daf4a",
"#250056 ",
"#ff9300",
"#df216d",
"#a65628",
"#0768fd",
"#00c221",
"#9e54b0",
"#ffff33",
"#10558a",
"#595959",
])
.domain(stacks);
this.legendsvg = d3
.select("#kmCurveLegend")
.append("svg")
.attr("class", "legendKmCurve")
.attr("id", "KmLegends")
.attr("width", 1000)
.attr("height", 50);
this.legendg = this.legendsvg.append("g");
stacks.forEach((d, i) => {
var position = i;
this.legendg
.append("circle")
.attr("class", "legendCircle")
.attr("id", "legendCircle" + d.replace(/[^a-zA-Z0-9]/g, "_"))
.attr("r", 10)
.attr("cy", 20)
.attr("cx", position * 165 + 225)
.attr("fill", this.stackedZ(d))
.on("click", () => {
this.addOrDelete(d);
})
.on("mouseover", () => {
console.log("circle d", d);
d3.selectAll(".lineKM").style("opacity", 0.2);
d3.select("#kmCurve" + d.replace(/[^a-zA-Z0-9]/g, "_")).style(
"opacity",
2
);
d3.selectAll(".legendCircle").style("opacity", 0.1);
d3.select("#legendCircle" + d.replace(/[^a-zA-Z0-9]/g, "_")).style(
"opacity",
2
);
})
.on("mouseout", () => {
d3.selectAll(".lineKM").style("opacity", 1);
d3.selectAll(".legendCircle").style("opacity", 1);
})
.append("svg:title")
.text(d);
this.legendg
.append("text")
.attr("class", "legendKM" + d.replace(/[^a-zA-Z0-9]/g, "_"))
.on("click", (d, i, key) => {
console.log("key of text", key);
if (
document.getElementById(
"kmCurve" + d[0].replace(/[^a-zA-Z0-9]/g, "_").innerHTML
).style.opacity == 1
) {
d3.selectAll(
"#kmCurve" + d[0].replace(/[^a-zA-Z0-9]/g, "_").innerHTML
).style("opacity", 0);
d3.selectAll(
"." + "legendKM" + d[0].replace(/[^a-zA-Z0-9]/g, "_").innerHTML
)
.style("text-decoration", "line-through")
.style("opacity", 0.2);
d3.selectAll(
"#legendCircle" + d[0].replace(/[^a-zA-Z0-9]/g, "_").innerHTML
)
.style("text-decoration", "line-through")
.style("opacity", 0.2);
this.$forceUpdate();
} else {
d3.selectAll(
"#kmCurve" + d[0].replace(/[^a-zA-Z0-9]/g, "_").innerHTML
).style("opacity", 1);
d3.selectAll(
"." + "legendKM" + d[0].replace(/[^a-zA-Z0-9]/g, "_").innerHTML
)
.style("text-decoration", "none")
.style("opacity", 1)
.style("white-space", "nowrap")
.style("text-overflow", "ellipsis");
d3.selectAll(
"#legendCircle" + d[0].replace(/[^a-zA-Z0-9]/g, "_").innerHTML
)
.style("text-decoration", "line-through")
.style("opacity", 1);
this.$forceUpdate();
}
this.$forceUpdate();
this.addOrDelete(d[0].innerHTML);
})
.text(d)
.attr("y", 20)
.attr("x", position * 165 + 240)
.style("font-weight", "bold")
.style("font-size", "18px")
.attr("transform", "translate(0,6)")
.append("svg:title");
this.$forceUpdate();
});
},
addOrDelete(name) {
this.current_stacks.includes(name)
? this.filterData(name)
: this.addData(name);
},
filterData(name) {
console.log("this.current_stacks.length", this.current_stacks.length);
if (this.current_stacks.length == 1) {
return;
}
this.current_stacks = this.current_stacks.filter((obj) => {
return obj != name;
});
this.kmUpdatedLayout(
this.minWidth,
this.minHeight,
this.data,
this.current_stacks
);
this.legendUpdate(name, 0);
},
addData(name) {
if (this.current_stacks.includes(name)) {
return;
}
this.current_stacks.push(name);
this.kmUpdatedLayout(
this.minWidth,
this.minHeight,
this.data,
this.current_stacks
);
this.legendUpdate(name, 1);
},
legendUpdate(name, status) {
status
? d3
.selectAll("." + "legendKM" + name.replace(/[^a-zA-Z0-9]/g, "_"))
.style("text-decoration", "none")
.style("opacity", 1)
: d3
.selectAll("." + "legendKM" + name.replace(/[^a-zA-Z0-9]/g, "_"))
.style("text-decoration", "line-through")
.style("opacity", 0.4);
},
},
};
</script>
<style scoped>
</style>
I have a task to make a d3 graph that should look like the picture below
I started to mock up the graph in codepen: http://codepen.io/Balzzac/pen/YNZqrP?editors=0010 , but I ran into 2 problems that I don't know how to solve:
1) how to make tooltips with names of people (from the dataset);
2) how to make a second vertical axis with a second set of values setOfValues?
My js code:
var setOfValues = ["Value4", "Value5", "Value6"];
var margins = {
top: 30,
left: 100,
right: 20,
bottom: 0
};
var legendPanel = {
width: 0
};
var width = 500 - margins.left - margins.right - legendPanel.width;
var height = 80 - margins.top - margins.bottom
var dataset = [{
data: [{
value: 'Value1',
count: 3,
people: "Anna, Maria, Peter",
}, {
value: 'Value2',
count: 3,
people: "Michael, Martin, Joe",
}, {
value: 'Value3',
count: 2,
people: "Martin, Joe",
}]
}, {
data: [{
value: 'Value1',
count: 2,
people: "Luis, Kim",
}, {
value: 'Value2',
count: 1,
people: "Richard",
}, {
value: 'Value3',
count: 4,
people: "Michael, Martin, Joe, Maria",
}]
}
, {
data: [{
value: 'Value1',
count: 1,
people: "Linda",
}, {
value: 'Value2',
count: 2,
people: "Ben",
}, {
value: 'Value3',
count: 0,
people: "",
}]
}
];
dataset = dataset.map(function (d) {
return d.data.map(function (o, i) {
return {
y: o.count,
x: o.value
};
});
});
var stack = d3.layout.stack();
stack(dataset);
var dataset = dataset.map(function (group) {
return group.map(function (d) {
return {
x: d.y,
y: d.x,
x0: d.y0
};
});
});
var numberOfPeople = 6;
var svg = d3.select('body')
.append('svg')
.attr('width', width + margins.left + margins.right + legendPanel.width)
.attr('height', height + margins.top + margins.bottom)
.append('g')
.attr('transform', 'translate(' + margins.left + ',' + margins.top + ')');
var xMax = numberOfPeople;
var xScale = d3.scale.linear()
.domain([0, xMax])
.range([0, width]);
var values = dataset[0].map(function (d) {
return d.y;
});
var yScale = d3.scale.ordinal()
.domain(values)
.rangeRoundBands([0, height], .2);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('top')
.tickFormat(function(d) { return parseInt(d, 10) })
.ticks(xMax);
var yAxis = d3.svg.axis()
.scale(yScale)
.outerTickSize(0)
.orient('left');
var colors = d3.scale.ordinal().range(["#3E7EAB","#D89218","#EEEEEE"]);
var groups = svg.selectAll('g')
.data(dataset)
.enter()
.append('g')
.style('fill', function (d, i) {
return colors(i);
});
var rects = groups.selectAll('rect')
.data(function (d) {return d; })
.enter()
.append('rect')
.attr('x', function (d) {return xScale(d.x0);})
.attr('y', function (d, i) {return yScale(d.y);})
.attr('height', function (d) {return yScale.rangeBand();})
.attr('width', function (d) {return xScale(d.x);})
.on('mouseover', function (d) {
var xPos = parseFloat(d3.select(this).attr('x')) / 2 + width / 2;
var yPos = parseFloat(d3.select(this).attr('y')) + yScale.rangeBand() / 2;
d3.select('#tooltip')
.style('left', xPos + 'px')
.style('top', yPos + 'px')
.select('#value')
//Question 1: "How to show in tooltip names of people??"
.text("How to show here names of people??");
d3.select('#tooltip').classed('hidden', false);
})
.on('mouseout', function () {d3.select('#tooltip').classed('hidden', true); });
svg.append('g')
.attr('class', 'axis')
.call(yAxis);
svg.append('g')
.attr('class', 'axis')
.call(xAxis);
Result of the code:
I really appreciate your help.
When you map dataset, add the people property to it (and do the same in the second map):
dataset = dataset.map(function(d) {
return d.data.map(function(o, i) {
return {
people: o.people,
y: o.count,
x: o.value
};
});
});
After that, you'll have the people property in the bound data. Thus, just change the text to:
.text(d.people);
Here is your updated code:
var setOfValues = ["Value4", "Value5", "Value6"];
var margins = {
top: 30,
left: 100,
right: 20,
bottom: 0
};
var legendPanel = {
width: 0
};
var width = 500 - margins.left - margins.right - legendPanel.width;
var height = 80 - margins.top - margins.bottom
var dataset = [{
data: [{
value: 'Value1',
count: 3,
people: "Anna, Maria, Peter",
}, {
value: 'Value2',
count: 3,
people: "Michael, Martin, Joe",
}, {
value: 'Value3',
count: 2,
people: "Martin, Joe",
}]
}, {
data: [{
value: 'Value1',
count: 2,
people: "Luis, Kim",
}, {
value: 'Value2',
count: 1,
people: "Richard",
}, {
value: 'Value3',
count: 4,
people: "Michael, Martin, Joe, Maria",
}]
}
, {
data: [{
value: 'Value1',
count: 1,
people: "Linda",
}, {
value: 'Value2',
count: 2,
people: "Ben",
}, {
value: 'Value3',
count: 0,
people: "",
}]
}
];
dataset = dataset.map(function (d) {
return d.data.map(function (o, i) {
return {
people: o.people,
y: o.count,
x: o.value
};
});
});
var stack = d3.layout.stack();
stack(dataset);
var dataset = dataset.map(function (group) {
return group.map(function (d) {
return {
people: d.people,
x: d.y,
y: d.x,
x0: d.y0
};
});
});
var numberOfPeople = 6;
var svg = d3.select('body')
.append('svg')
.attr('width', width + margins.left + margins.right + legendPanel.width)
.attr('height', height + margins.top + margins.bottom)
.append('g')
.attr('transform', 'translate(' + margins.left + ',' + margins.top + ')');
var xMax = numberOfPeople;
var xScale = d3.scale.linear()
.domain([0, xMax])
.range([0, width]);
var values = dataset[0].map(function (d) {
return d.y;
});
var yScale = d3.scale.ordinal()
.domain(values)
.rangeRoundBands([0, height], .2);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('top')
.tickFormat(function(d) { return parseInt(d, 10) })
.ticks(xMax);
var yAxis = d3.svg.axis()
.scale(yScale)
.outerTickSize(0)
.orient('left');
var colors = d3.scale.ordinal().range(["#3E7EAB","#D89218","#EEEEEE"]);
var groups = svg.selectAll('g')
.data(dataset)
.enter()
.append('g')
.style('fill', function (d, i) {
return colors(i);
});
var rects = groups.selectAll('rect')
.data(function (d) {return d; })
.enter()
.append('rect')
.attr('x', function (d) {return xScale(d.x0);})
.attr('y', function (d, i) {return yScale(d.y);})
.attr('height', function (d) {return yScale.rangeBand();})
.attr('width', function (d) {return xScale(d.x);})
.on('mouseover', function (d) {
var xPos = parseFloat(d3.select(this).attr('x')) / 2 + width / 2;
var yPos = parseFloat(d3.select(this).attr('y')) + yScale.rangeBand() / 2;
d3.select('#tooltip')
.style('left', xPos + 'px')
.style('top', yPos + 'px')
.select('#value')
//Question 1: "How to show in tooltip names of people??"
.text(d.people);
d3.select('#tooltip').classed('hidden', false);
})
.on('mouseout', function () {d3.select('#tooltip').classed('hidden', true); });
svg.append('g')
.attr('class', 'axis')
.call(yAxis);
svg.append('g')
.attr('class', 'axis')
.call(xAxis);
.axis path, .axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
#tooltip {
position: absolute;
text-align: left;
height: auto;
padding: 10px;
background: #162F44;
pointer-events: none;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 11px;
color: white;
line-height: 15px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="tooltip" class="hidden">
<p><span id="value"></span>
</p>
</div>
PS: Regarding your second question ("how to make a second vertical axis?"), your desired outcome is not exactly clear. Besides that, as it's not a good practice asking more than one question in a single post, I suggest you post another question, better explaining your problem.
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>Force Layout Example 1</title>
<style>
.node {
fill: #ccc;
stroke: #fff;
stroke-width: 2px;
}
.link {
stroke: #777;
stroke-width: 2px;
}
.line {
stroke: #777;
stroke-width: 2px;
}
</style>
</head>
<body>
<script src='http://d3js.org/d3.v3.min.js'></script>
<script>
var width = 640,
height = 480;
var nodes = [
{ "x": 200, "y": 200 },
{ "x": 500, "y": 300 },
{ "x": 500, "y": 100 },
//{ "x": 650, "y": 100 },
];
//var nodes = [
// { "x": 200, "y": 200 },
// { "x": 500, "y": 300 },
//{ "x": 500, "y": 100 },
//];
//var links = [
// { source: 0, target: 1 },
// { source: 1, target: 2 },
//];
var links = [
{ source: 0, target: 1 },
{ source: 0, target: 2 },
//{ source: 1, target: 3 },
];
var svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height);
var force = d3.layout.force()
.size([width, height])
.nodes(nodes)
.links(links);
force.linkDistance(75);
var link = svg.selectAll('.link')
.data(links)
.enter().append('line')
.attr('class', 'link');
var node = svg.selectAll('.node')
.data(nodes)
.enter().append('rect')
.attr('class', 'node');
var subnode = svg.selectAll('.subnode')
.data(nodes)
.enter().append('circle')
.attr('class', 'subnode');
var subnode2 = svg.selectAll('.subnode2')
.data(nodes)
.enter().append('circle')
.attr('class', 'subnode2');
force.on('end', function() {
node.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", 50)
.attr("height", 20);
subnode.attr('r', width/250)
.attr('cx', function(d) { return (d.x); })
.attr('cy', function(d) { return d.y + 10; });
subnode2.attr('r', width/250)
.attr('cx', function(d) { return d.x+50; })
.attr('cy', function(d) { return d.y + 10; });
link.attr('x1', function(d) { return d.source.x; })
.attr('y1', function(d) { return d.source.y+ 10; })
.attr('x2', function(d) { return d.target.x+50; })
.attr('y2', function(d) { return d.target.y+ 10; });
});
force.start();
var line;
function mousedown() {
var m = d3.mouse(this);
//alert(m[0]+"---"+m[1]);
line = svg.append("line")
.attr('x1', m[0])
.attr('y1', m[1])
.attr('x2', m[0])
.attr('y2', m[1]);
svg.on("mousemove", mousemove);
}
function mousemove() {
var m = d3.mouse(this);
line.attr('x2', m[0])
.attr('y2', m[1]);
}
function mouseup() {
svg.on("mousemove", null);
}
</script>
</body>
</html>
The above solution gives below result:
The problem is I dont understand why the graph is drawn reverse and moreover in above code I have commented out some nodes and links if you uncomment them then there is more chaos the whole nodes are drawn in complete random order i.e. increasing more nodes and links create more chaos.
See the JSBIN : http://jsbin.com/yuyolof/edit?html
Take a look at this jsbin http://jsbin.com/himutohimu/1/edit?html,css,output
(I've added a bit too much info in this just to have a better look at what's going on)
You have two subnodes that have the same node data. You are positioning them on "end" like this:
subnode.attr('r', width/250) // black nodes
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y + 10; });
subnode2.attr('r', width/250) // red nodes
.attr('cx', function(d) { return d.x + 50; })
.attr('cy', function(d) { return d.y + 10; });
I've colored the nodes differently in order to better see how this works.
In order for your lines to connect to one kind of subnodes you need to either follow the x and y of the black nodes or the x and y of the red nodes:
// x1 and y1 are the starting point of the line, so in order to follow the
// red nodes, we need to move accordingly with +50 for x and +10 for y.
// the same goes for x2, y2 which are the coordinates for the end of the line
link.attr('x1', function(d) { return d.source.x + 50; })
.attr('y1', function(d) { return d.source.y + 10; })
.attr('x2', function(d) { return d.target.x + 50; })
.attr('y2', function(d) { return d.target.y + 10; });
//Or if you need your lines to follow the black nodes/ dots then x1, y1
// and x2,y2 need to move accordingly to your subnode's x and y,
// so x as it is and y plus 10
// it is one or the other
link.attr('x1', function(d) { return d.source.x; })
.attr('y1', function(d) { return d.source.y + 10; })
.attr('x2', function(d) { return d.target.x; })
.attr('y2', function(d) { return d.target.y + 10; });
So, it is a matter of what nodes (dots) you want to connect and then move the line according to their respective x and y.
Hope this helps!
Good luck!
Edit: How this works with more nodes: http://jsbin.com/nodaruwere/1/edit?html,output
I am using D3 to create two level grouped category bar chart with brush functionality on x axis.But X axis line not displayed in FireFox even though i didn't add any css related to hide x axis.
X axis line appeared in Chrome and IE.
NOTE:here also if we click on Full Page x axis line display.else it doesn't display.
// Code goes here
var barsData = [
{
"value":100,
"key":"03-08-2016,0",
"secondKey":"Windows 7/Chrome49"
},
{
"value":40,
"key":"04-08-2016,1",
"secondKey":"Windows 7/Chrome49"
},
{
"value":20,
"key":"05-08-2016,2",
"secondKey":"Windows 7/Chrome49"
},
{
"value":100,
"key":"03-08-2016,3",
"secondKey":"Windows 7/Chrome50"
},
{
"value":27,
"key":"04-08-2016,4",
"secondKey":"Windows 7/Chrome50"
},
{
"value":57,
"key":"05-08-2016,5",
"secondKey":"Windows 7/Chrome50"
},
{
"value":40,
"key":"04-08-2016,6",
"secondKey":"Windows 7/Firefox44"
},
{
"value":60,
"key":"05-08-2016,7",
"secondKey":"Windows 7/Firefox44"
},
{
"value":50,
"key":"04-08-2016,8",
"secondKey":"Windows 7/Chrome47"
},
{
"value":40,
"key":"05-08-2016,9",
"secondKey":"Windows 7/Chrome47"
},
{
"value":80,
"key":"04-08-2016,10",
"secondKey":"Windows 7/Firefox45"
},
{
"value":60,
"key":"05-08-2016,11",
"secondKey":"Windows 7/Firefox45"
},
{
"value":0,
"key":"04-08-2016,12",
"secondKey":"Windows 7/IE10"
},
{
"value":40,
"key":"05-08-2016,13",
"secondKey":"Windows 7/IE10"
},
{
"value":50,
"key":"04-08-2016,14",
"secondKey":"Windows 7/Firefox42"
}
];
var osLevelData = [
{
"key":"Windows 7/Chrome49",
"values":[
{
"value":100,
"key":"03-08-2016,0",
"secondKey":"Windows 7/Chrome49"
},
{
"value":40,
"key":"04-08-2016,1",
"secondKey":"Windows 7/Chrome49"
},
{
"value":20,
"key":"05-08-2016,2",
"secondKey":"Windows 7/Chrome49"
}
],
"centerBarPosVal":"03-08-2016,0",
"lastBarPosVal":"05-08-2016,2"
},
{
"key":"Windows 7/Chrome50",
"values":[
{
"value":100,
"key":"03-08-2016,3",
"secondKey":"Windows 7/Chrome50"
},
{
"value":27,
"key":"04-08-2016,4",
"secondKey":"Windows 7/Chrome50"
},
{
"value":57,
"key":"05-08-2016,5",
"secondKey":"Windows 7/Chrome50"
}
],
"centerBarPosVal":"03-08-2016,3",
"lastBarPosVal":"05-08-2016,5"
},
{
"key":"Windows 7/Firefox44",
"values":[
{
"value":40,
"key":"04-08-2016,6",
"secondKey":"Windows 7/Firefox44"
},
{
"value":60,
"key":"05-08-2016,7",
"secondKey":"Windows 7/Firefox44"
}
],
"centerBarPosVal":"04-08-2016,6",
"lastBarPosVal":"05-08-2016,7"
},
{
"key":"Windows 7/Chrome47",
"values":[
{
"value":50,
"key":"04-08-2016,8",
"secondKey":"Windows 7/Chrome47"
},
{
"value":40,
"key":"05-08-2016,9",
"secondKey":"Windows 7/Chrome47"
}
],
"centerBarPosVal":"04-08-2016,8",
"lastBarPosVal":"05-08-2016,9"
},
{
"key":"Windows 7/Firefox45",
"values":[
{
"value":80,
"key":"04-08-2016,10",
"secondKey":"Windows 7/Firefox45"
},
{
"value":60,
"key":"05-08-2016,11",
"secondKey":"Windows 7/Firefox45"
}
],
"centerBarPosVal":"04-08-2016,10",
"lastBarPosVal":"05-08-2016,11"
},
{
"key":"Windows 7/IE10",
"values":[
{
"value":0,
"key":"04-08-2016,12",
"secondKey":"Windows 7/IE10"
},
{
"value":40,
"key":"05-08-2016,13",
"secondKey":"Windows 7/IE10"
}
],
"centerBarPosVal":"04-08-2016,12",
"lastBarPosVal":"05-08-2016,13"
},
{
"key":"Windows 7/Firefox42",
"values":[
{
"value":50,
"key":"04-08-2016,14",
"secondKey":"Windows 7/Firefox42"
}
],
"centerBarPosVal":"04-08-2016,14",
"lastBarPosVal":"04-08-2016,14"
}
];
var barColor = "#4A7B9D";
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 860 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var min_margin = {
top: height,
right: margin.right + 10,
bottom: margin.bottom,
left: margin.left + 10
},
min_height = 10,
min_width = 860 - min_margin.left - min_margin.right;
//first scale
var x = d3.scale.ordinal().rangeRoundBands([0, width], .2);
//second scale
var groupx = d3.scale.ordinal().rangeRoundBands([0, width], .2);
//scroll scale
var min_x = d3.scale.ordinal().rangeRoundBands([0, width], .2);
//y scale
var y = d3.scale.linear().range([height, 0]);
//Add domain for X scale
x.domain(barsData.map(function(d) {
return d.key;
}));
//Add domain for X scale
groupx.domain(osLevelData.map(function(d) {
return d.key;
}));
//scroll domain
min_x.domain(barsData.map(function(d) {
return d.key;
}));
//domain Y
y.domain([0, d3.max(barsData, function(d) {
return d.value;
})]);
//x axis
var xAxis = d3.svg.axis()
.scale(x)
.tickFormat(function(d){
return d.split(",")[0];
})
.orient("bottom");
//group axis
var groupAxis = d3.svg.axis()
.scale(groupx)
.tickFormat(function(d){
return d;
})
.orient("bottom");
//scroll axis
var min_xAxis = d3.svg.axis()
.scale(min_x)
.tickFormat(function(d){
return d;
})
.orient("bottom");
var main_xZoom = d3.scale.linear()
.range([0, width])
.domain([0, width]);
// y axis
var yAxis = d3.svg.axis()
.scale(y)
.tickFormat(function(d){
return d;
})
.orient("left")
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", 800 + margin.top + margin.bottom);
//main g
var main = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
main.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height + min_height + margin.bottom);
// Add the x axis DOM elements
var xDOM = main.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (height + min_height) + ")")
.attr("clip-path", "url(#clip)")
.call(xAxis)
//.selectAll(".tick text")
//.call(wrap, x.rangeBand());
xDOM.selectAll(".tick").append("line")
.attr("class","groupline")
.attr("y2",30)
.attr("transform", function(d,index) {
/*var position;
osLevelData.forEach(function(ddd){
if(ddd.key === d){
position = ddd.lastBarPosVal;
}
})*/
return "translate(" + (x.rangeBand()/2) + ",0)";
//console.log("d ..",d);
});
// Add the group axis DOM elements
var groupDOM = main.append("g")
.attr("class", "x1 axis")
.attr("transform", "translate(0," + (height + min_height + 30) + ")")
.attr("clip-path", "url(#clip)")
.call(groupAxis)
.selectAll(".tick")
.attr("transform", function(d) {
var centerPos;
osLevelData.forEach(function(ddd){
if(ddd.key === d){
centerPos = ddd.centerBarPosVal;
}
})
return "translate(" + (x(centerPos)+(x.rangeBand()/2)) + ",0)";
});
//.selectAll(".tick text")
//.call(wrap, groupx.rangeBand());
groupDOM.append("line")
.attr("class","groupline")
.attr("y2",30)
.attr("transform", function(d) {
var lastPos;
var centerPos;
osLevelData.forEach(function(ddd){
if(ddd.key === d){
lastPos = ddd.lastBarPosVal;
centerPos = ddd.centerBarPosVal;
}
})
return "translate(" + ((x(lastPos)+x.rangeBand()) - (x(centerPos)+(x.rangeBand()/2))) + ",0)";
});
//scroll DOM element
var mini_x_append = main.append("g")
.attr("transform", "translate(0," + (margin.top + height + 60) + ")")
.attr("width", min_width);
main.append("g")
.attr("class", "y axis")
.call(yAxis);
//create group with all bars
main.append("g")
.attr("clip-path", "url(#clip)")
.selectAll(".rect")
.data(barsData)
.enter().append("rect")
.attr("class", "rect")
.attr("x", function(d) {
return x(d.key);
})
.attr("width", x.rangeBand())
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
})
.attr("fill",barColor);
//To display bar value on bars
main.selectAll("text.bar")
.data(barsData)
.enter().append("text")
.attr("class", "bar")
.attr("text-anchor", "middle")
.attr("x", function(d) { return x(d.key) + x.rangeBand()/2; })
.attr("y", function(d) { return y(d.value) - 5; })
.text(function(d) { return d.value+"%"; });
var xBrush = d3.svg.brush().x(min_x).on("brush", xBrushed);
//d3.svg.brush().x(groupx).on("brush", xBrushed);
var x_arc = d3.svg.arc()
.outerRadius(min_height / 2)
.startAngle(0)
.endAngle(function(d, i) {
return i ? -Math.PI : Math.PI;
});
var brush_x_grab = mini_x_append.append("g")
.attr("class", "x brush")
.call(xBrush);
brush_x_grab.selectAll(".resize").append("path")
.attr("transform", "translate(0," + min_height / 2 + ")")
.attr("d", x_arc)
.attr("fill","#FF0000");
brush_x_grab.selectAll("rect").attr("height", min_height).style("visibility","visible").attr("fill","#D3D3D3");
// Called to re-draw the bars on the main chart when the brush on the x axis
// has been altered.
function xBrushed() {
var originalRange = main_xZoom.range();
main_xZoom.domain(xBrush.empty() ? originalRange : xBrush.extent());
x.rangeRoundBands([main_xZoom(originalRange[0]), main_xZoom(originalRange[1])], .2);
groupx.rangeRoundBands([main_xZoom(originalRange[0]), main_xZoom(originalRange[1])], .2);
main.selectAll(".rect")
.data(barsData)
.attr("width", function(d) {
return x.rangeBand();
})
.attr("x", function(d) {
return x(d.key);
});
main.selectAll(".bar")
.data(barsData)
.attr("x", function(d) { return x(d.key) + x.rangeBand()/2; })
.attr("y", function(d) { return y(d.value) - 5; })
.text(function(d) { return d.value+"%"; });
main.select("g.x.axis").call(xAxis).selectAll(".tick .groupline")
.attr("transform", function(d,index) {
return "translate(" + (x.rangeBand()/2) + ",0)";
});
main.select("g.x1.axis").call(groupAxis).selectAll(".tick")
.attr("transform", function(d) {
var centerPos;
osLevelData.forEach(function(ddd){
if(ddd.key === d){
centerPos = ddd.centerBarPosVal;
}
})
return "translate(" + (x(centerPos)+(x.rangeBand()/2)) + ",0)";
})
.selectAll(".tick .groupline")
.attr("transform", function(d) {
var centerPos;
var lastPos;
osLevelData.forEach(function(ddd){
if(ddd.key === d){
lastPos = ddd.lastBarPosVal;
centerPos = ddd.centerBarPosVal;
}
})
return "translate(" + ((x(lastPos)+x.rangeBand()) - (x(centerPos)+(x.rangeBand()/2))) + ",0)";
})
//main.selectAll(
};
// This comes from the example at http://bl.ocks.org/mbostock/7555321
// for wrapping long axis tick labels
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
};
// Set the initial brush selections.
// svg.select(".x.brush").call(xBrush.extent(main_xZoom.domain()));
svg.select(".x.brush").call(xBrush.extent([0, 220]));
//svg.select(".y.brush").call(yBrush.extent(mini_y0.domain()));
// Forces a refresh of the brushes and main chart based
// on the selected extents.
xBrushed();
//yBrushed();
function type(d) {
d.frequency = +d.frequency;
return d;
}
/* Styles go here */
g.axis path,
g.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
g.brush rect.extent {
fill-opacity: 0.5;
fill:#FF0000;
}
.resize path {
fill-opacity: 0.2;
}
.bar {
fill: steelblue;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
</body>
I have an array of objects which I bind to a g-element. For each g-element I generate a rect-element. It works so far but now I want to have a dynamic number of "parts" in each g-element which should generate a new rect and text element inside the existing rect.
Here you will find my example or on fiddle.net/tnnomejg/
var config = {
width: 600
};
var data = [
{
height: 150,
y: 0,
parts: [
{
title: "Hello",
x: 50,
y: 60,
},
{
title: "World",
x: 350,
y: 60,
}
],
},
{
height: 150,
y: 155,
parts: [
{
title: "Demo",
x: 280,
y: 215,
}
],
},
];
var svg = d3.select("body").append("svg").attr("width", config.width).attr("height", 500);
var g = svg.selectAll("g")
.data(data)
.enter()
.append("g");
g.append("rect")
.attr("x", 1)
.attr("y", function(d) { return d.y; })
.attr("width", config.width)
.attr("height", function(d) { return d.height; });
Thx.
You could use a subselection binding your parts' data to the corresponding elements using an accessor function like this:
var parts = g.selectAll("g.part")
.data(function(d) { return d.parts; })
.enter()
.append("g")
.attr("class", "part");
Having this subselection at your hands you may insert/append content accessing the corresponding data bound to each group/part.
var config = {
width: 600
};
var data = [
{
height: 150,
y: 0,
parts: [
{
title: "Hello",
x: 50,
y: 60
},
{
title: "World",
x: 350,
y: 60
}
]
},
{
height: 150,
y: 155,
parts: [
{
title: "Demo",
x: 280,
y: 215
}
]
}
];
var svg = d3.select("body").append("svg").attr("width", config.width).attr("height", 500);
var g = svg.selectAll("g")
.data(data)
.enter()
.append("g");
g.append("rect")
.attr("x", 1)
.attr("y", function(d) { return d.y; })
.attr("width", config.width)
.attr("height", function(d) { return d.height; });
var parts = g.selectAll("g.part")
.data(function(d) { return d.parts; })
.enter()
.append("g")
.attr("class", "part");
parts.append("rect")
.attr({
"class": "part",
"x": function(d) { return d.x; },
"y": function(d) { return d.y; },
"width": 200,
"height":80
});
parts.append("text")
.attr({
"class": "part",
"x": function(d) { return d.x; },
"y": function(d) { return d.y; }
})
.text(function(d) { return d.title; });
g.part rect {
fill:white;
}
g.part text {
fill: red;
stroke: none;
font-size:20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Use d3 selector each method.
g.each(function(d, i) {
var rect = d3.select(this);
d.parts.forEach(function(p) {
rect.append("rect")
.style("fill", "aliceblue")
.attr("x", p.x)
.attr("y", p.y)
.attr("width", config.width / 3)
.attr("height", d.height / 2);
rect.append("text")
.style("stroke", "brown")
.attr("x", p.x)
.attr("y", p.y)
.text(p.title);
});
});
var config = {
width: 600
};
var data = [{
height: 150,
y: 0,
parts: [{
title: "Hello",
x: 50,
y: 60,
}, {
title: "World",
x: 350,
y: 60,
}],
}, {
height: 150,
y: 155,
parts: [{
title: "Demo",
x: 280,
y: 215,
}],
}, ];
var svg = d3.select("body").append("svg").attr("width", config.width).attr("height", 500);
var g = svg.selectAll("g")
.data(data)
.enter()
.append("g");
g.append("rect")
.attr("x", 1)
.attr("y", function(d) {
return d.y;
})
.attr("width", config.width)
.attr("height", function(d) {
return d.height;
});
g.each(function(d, i) {
var rect = d3.select(this);
d.parts.forEach(function(p) {
rect.append("rect")
.style("fill", "aliceblue")
.attr("x", p.x)
.attr("y", p.y)
.attr("width", config.width / 3)
.attr("height", d.height / 2);
rect.append("text")
.style("stroke", "brown")
.attr("x", p.x)
.attr("y", p.y)
.text(p.title);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>