Related
Hi I am trying to implement d3js donut chart in my angular 7 application. Here is the stackblitz link for the same :
https://stackblitz.com/edit/angular-with-d3js-charts-cne1eb?file=src%2Fapp%2Fapp.component.html
Current output which I am getting:
I am trying to adjust right side's legend position like this :
right side legend needs to be vertically aligned properly
Each square color box needs to circle
Legend text should be multi-line.
I am unable to achieve this. Can anyone help me out with this ?
Thanks a ton in advance.
See the fixed code in the snippet below:
var w = 583,
h = 500,
r = 150,
inner = 180 / 2,
color = d3
.scaleOrdinal()
.range([
"rgb(3,17,142)",
"rgb(19,37,180)",
"rgb(37,58,217)",
"rgb(55,78,255)",
"rgb(94,113,255)",
"rgb(133,147,254)",
"rgb(210,215,252)",
]),
margin = { top: 20, right: 20, bottom: 50, left: 100 };
var data = [
{ label: "Received", value: 194 },
{ label: "Allocated", value: 567 },
{ label: "In Progress", value: 1314 },
{ label: "Cancelled", value: 793 },
{ label: "Returned", value: 1929 },
{ label: "Hold", value: 1383 },
{ label: "Rejected", value: 2 },
];
var total = d3.sum(data, function (d) {
return d3.sum(d3.values(d));
});
var svg = d3
.select("#chart-1")
.append("div")
.classed("svg-container", true) //container class to make it responsive
.append("svg") // Place the chart in 'pie-chart-div'
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "0 0 600 500")
.classed("svg-content-responsive", true)
.attr("width", "100%")
.attr("height", "100%");
var vis = svg
.data([data])
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", -w / 2 + " " + -h / 2 + " " + w + " " + h)
.attr("preserveAspectRatio", "xMinYMin");
var textTop = vis
.append("text")
.attr("dy", ".35em")
.style("text-anchor", "middle")
.attr("class", "textTop")
.text("Total Orders")
.attr("fill", "rgb(112,112,112)")
.attr("y", -10),
textBottom = vis
.append("text")
.attr("dy", ".35em")
.attr("fill", "rgb(112,112,112)")
.style("text-anchor", "middle")
.attr("class", "textBottom")
.text(total.toFixed(2) + "m")
.attr("y", 10);
var arc = d3.arc().innerRadius(inner).outerRadius(r);
var arcOver = d3
.arc()
.innerRadius(inner + 5)
.outerRadius(r + 5);
var pie = d3.pie().value(function (d) {
return d.value;
});
var arcs = vis
.selectAll("g.slice")
.data(pie)
.enter()
.append("svg:g")
.attr("class", "slice")
.on("mouseover", function (d) {
d3.select(this)
.select("path")
.transition()
.duration(200)
.attr("d", arcOver);
textTop.text(d3.select(this).datum().data.label).attr("y", -10);
textBottom
.text(d3.select(this).datum().data.value.toFixed(2))
.attr("y", 10);
})
.on("mouseout", function (d) {
d3.select(this)
.select("path")
.transition()
.duration(100)
.attr("d", arc);
textTop.text("Total Orders").attr("y", -10);
textBottom.text(total.toFixed(2) + "m");
});
arcs
.append("svg:path")
.style("fill", function (d, i) {
return color(i);
})
.attr("d", arc);
var legend = svg
.append("g")
.classed('legend', true)
.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function (d, i) {
return "translate(" + (r + 20) + "," + ((i - data.length / 2) * 45 + 10) + ")";
});
legend
.append("circle")
.attr("r", 6)
.attr('cy', 10)
.attr('cx', 8)
.style("fill", function (d, i) {
return color(i);
});
legend
.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.attr("fill", "rgb(112,112,112)")
.style('font-weight', 'bold')
.text(function (d) {
return d.label;
});
legend
.append("text")
.attr("x", 24)
.attr("y", 30)
.attr("fill", "rgb(112,112,112)")
.text(d => `${d.value} orders`);
.legend text {
font-family: 'Ubuntu';
font-size: 12px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="chart-1" />
Here is a forked Stackblitz: https://stackblitz.com/edit/angular-with-d3js-charts-fw9rkv
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>
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>
I have implement a bar chart to show statistic, but when I update the SVG with new data the old bar won't be remove.
Can someone help to clarify the problem.
I use updateStatisticData to update my chart.
var cwidth = 800, cheight = 900;
var margin = {top: 80, right: 10, bottom: 20, left: 150};
var dataset = {
"series": ["Fail","Pass"],
"colors": ["#e74c3c","#2ecc71"]
};
var width = cwidth - margin.left - margin.right,
height = cheight - margin.top - margin.bottom;
function getStatisticData() {
$(document).ready(function() {
$.get("/statistic-test-result", { "_": $.now() }, function(msg, status){
data = msg['result']; //[{"ip":"x.x.x.x", "pass": 3, "fail": 7, 'total':10}, {...}]
var yScale = d3.scale.ordinal().rangeRoundBands([0, height], .3);
var x = d3.scale.linear().range([0, width]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("top");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
var svg = d3.select("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append('text')
.attr("transform", "translate(" + (width / 2) + ",-" + (margin.top / 2) + ")")
.style("text-anchor", "middle")
.text("Max test items")
yScale.domain(data.map(function(d) {
return d.ip;
}));
x.domain([0, 17]);
svg.append("g")
.attr("class", "x axis")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// add legend
var legend = svg.append("g")
.attr("class", "legend")
legend.selectAll('text')
.data(dataset["colors"])
.enter()
.append("rect")
.attr("x", width-margin.right - 25 - margin.left)
.attr("y", function(d, i){ return -(margin.top / 2) - i * 20})
.attr("width", 10)
.attr("height", 10)
.style("fill", function(d) {
return d;
})
legend.selectAll('text')
.data(dataset["series"])
.enter()
.append("text")
.attr("x", width-margin.right - margin.left)
.attr("y", function(d, i){ return -(margin.top / 2) + 9 - i * 20})
.text(function(d){return d});
var bars = svg.selectAll(".bar")
.data(data);
bars.enter().append('g').attr("class", "bar");
bars.append("rect")
.attr({
'fill':'#e74c3c',
'width':function(d) {
return x(d.fail);
},
'height': function(d) {
return yScale.rangeBand();
},
'y': function(d) {
return yScale(d.ip);
}
});
bars.append('text')
.text(function(d) {
if (d.fail > 0)
return d.fail;
})
.attr({
'fill':'#000',
'y': function(d) {
return yScale(d.ip) + yScale.rangeBand()/2;
},
'x': function(d) {
return 5;
}
});
bars.append("rect")
.attr({
'fill':'#2ecc71',
'width':function(d) {
return x(d.pass);
},
'height': function(d) {
return yScale.rangeBand();
},
'y': function(d) {
return yScale(d.ip);
},
'x':function(d) { return x(d.fail);
}
});
bars.append('text')
.text(function(d) {
if (d.pass > 0)
return d.pass;
})
.attr({
'fill':'#000',
'y': function(d) {
return yScale(d.ip) + yScale.rangeBand()/2;
},
'x': function(d) {
return 5 + x(d.fail);
}
});
return true;
});
});
}
function updateStatisticData() {
$.get("/statistic-test-result", { "_": $.now() }, function(msg, status){
data = msg['result']; //[{"ip":"x.x.x.x", "pass": 3, "fail": 7, 'total':10}, {...}]
var yScale = d3.scale.ordinal().rangeRoundBands([0, height], .3);
var x = d3.scale.linear().range([0, width]);
var svg = d3.select("svg")
var xAxis = d3.svg.axis()
.scale(x)
.orient("top");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
yScale.domain(data.map(function(d) {
return d.ip;
}));
x.domain([0, 17]);
svg.selectAll("g.y.axis")
.call(yAxis);
svg.selectAll("g.x.axis")
.call(xAxis);
var bars = svg.selectAll(".bar")
.data(data);
bars.exit().remove(); //remove old bars
bars.enter().append('g').attr("class", "bar");
bars.append("rect")
.attr({
'fill':'#e74c3c',
'width':function(d) {
return x(d.fail);
},
'height': function(d) {
return yScale.rangeBand();
},
'y': function(d) {
return yScale(d.ip);
}
});
bars.append('text')
.text(function(d) {
if (d.fail > 0)
return d.fail;
})
.attr({
'fill':'#000',
'y': function(d) {
return yScale(d.ip) + yScale.rangeBand()/2;
},
'x': function(d) {
return 5;
}
});
bars.append("rect")
.attr({
'fill':'#2ecc71',
'width':function(d) {
return x(d.pass);
},
'height': function(d) {
return yScale.rangeBand();
},
'y': function(d) {
return yScale(d.ip);
},
'x':function(d) { return x(d.fail);
}
});
bars.append('text')
.text(function(d) {
if (d.pass > 0)
return d.pass;
})
.attr({
'fill':'#000',
'y': function(d) {
return yScale(d.ip) + yScale.rangeBand()/2;
},
'x': function(d) {
return 5 + x(d.fail);
}
});
return true;
});
}
I have a d3 focus/context chart where I would like to be able to pan on the focus portion after brushing the context, and I would like the brushed section of the context area to move in sync with the panning of the focus area. However, when I click on the focus portion after I select a region in the context chart, the focus scale changes and the points no longer show up at the correct coordinates. The following code is available on jsfiddle as well:
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title> - jsFiddle demo</title>
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<link rel="stylesheet" type="text/css" href="/css/result-light.css">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<style type="text/css">
circle {
-webkit-transition: fill-opacity 250ms linear;
}
.selecting circle {
fill-opacity: .5;
}
.selecting circle.selected {
stroke: #f00;
}
.brush .extent {
stroke: #B8C6D0;
fill-opacity: .125;
shape-rendering: crispEdges;
}
#context .axis path.domain {
stroke: lightsteelblue;
stroke-width: 5px;
}
#context .tick {
stroke:black;
stroke-width: 1px;
}
#context .x .tick {
stroke:black;
stroke-width: 2px;
}
.axis path, .axis line {
fill: none;
stroke: #ddd;
stroke-width: 1px;
shape-rendering: crispEdges;
}
.axis path {
stroke: #999;
stroke-width: 2px;
}
</style>
<script type="text/javascript">//<![CDATA[
var data = [{
Id: "1",
Year: 1950,
Relevance: 55,
Category: "Cat1",
SpecFreq: 5,
GenFreq: 10
}, {
Id: "2",
Year: 1975,
Relevance: 25,
Category: "Cat1",
SpecFreq: 2,
GenFreq: 31
}, {
Id: "3",
Year: 1990,
Relevance: 75,
Category: "Cat1",
SpecFreq: 8,
GenFreq: 23
}, {
Id: "4",
Year: 1970,
Relevance: 45,
Category: "Cat1",
SpecFreq: 17,
GenFreq: 60
}, {
Id: "5",
Year: 1985,
Relevance: 90,
Category: "Cat1",
SpecFreq: 17,
GenFreq: 25
}];
$(function () {
//dimensions
var margin = {
top: 5.5,
right: 19.5,
bottom: 39.5,
left: 39.5
};
//data domain extents
var extentX = d3.extent(data, function (d) {
return d.Year;
});
var extentY = d3.extent(data, function (d) {
return d.Relevance;
});
var focusAxisOptions = {
x: {
ticks: {
format: d3.format("d"),
size: -1* (500 - margin.top - margin.bottom),
ticks: 10
},
showLabel: true
},
y: {
ticks: {
format: d3.format(""),
size: -1 * (800 - margin.left - margin.right),
ticks: 10
},
showLabel: true
}
};
var contextAxisOptions = {
x: {
ticks: {
format: d3.format("d"),
size: -1 * (100 - margin.top - margin.bottom),
ticks: 10
},
showLabel: true
},
y: {
ticks: {
format: "",
size: 0,
ticks: 0
},
showLabel: false
}
};
var focus = DrawChart(data, margin, 800 - margin.left - margin.right, 500 - margin.top - margin.bottom, extentX, extentY, focusAxisOptions);
var context = DrawChart(data, margin, 800 - margin.left - margin.right, 100 - margin.top - margin.bottom, extentX, extentY, contextAxisOptions);
MakeContextBrushable(context, focus);
MakeFocusZoomable(focus);
});
function DrawChart(data, margin, width, height, extentX, extentY, axisOptions) {
//pad extents to provide some extra "blank" areas around edge of graph
var paddedExtentX = [extentX[0] - 5, extentX[1] +5];
var paddedExtentY = [extentY[0] - 5, extentY[1] +5];
//scales
var x = d3.scale.linear().domain(paddedExtentX).range([0, width]);
var y = d3.scale.linear().domain(paddedExtentY).range([height, 0]);
var radiusMax = .025 * width;
var radius = d3.scale.sqrt().domain([0, 100]).range([0, radiusMax]);
var color = d3.scale.ordinal().domain(["Cat1", "Cat2", "Cat3"]).range(["#b7b8a0", "#898a72", "#878772"]);
//axes
var xAxis = d3.svg.axis().scale(x).orient("bottom").tickFormat(axisOptions.x.ticks.format).tickSize(axisOptions.x.ticks.size).ticks(axisOptions.x.ticks.ticks);
var yAxis = d3.svg.axis().scale(y).orient("left").tickFormat(axisOptions.y.ticks.format).tickSize(axisOptions.y.ticks.size).ticks(axisOptions.y.ticks.ticks);
//create and size svg element
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("float", "left")
.style("clear", "left");
var g = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); ;
// Add the x-axis.
g.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("font-size", "10px")
.attr("dy", "1.5em");
// Add the y-axis.
g.append("g")
.attr("class", "y axis")
.call(yAxis)
.selectAll("text")
.style("font-size", "10px")
.attr("dx", "-1em");
// Add the x-axis label.
if (axisOptions.x.showLabel) {
g.append("text")
.attr("class", "x label")
.attr("text-anchor", "end")
.attr("x", width / 2)
.attr("y", height + 35)
.text(" Year");
}
// Add the y-axis label.
if (axisOptions.y.showLabel) {
g.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("x", -1 * height / 2)
.attr("y", -40)
.attr("dy", ".75em")
.attr("transform", "rotate(-90)")
.text("Relevance");
}
//plot genFreq
var gGenerally = g.append("g").selectAll("circle")
.data(data)
.enter().append("circle")
.style("fill", function (d) {
return color(d.Category);
})
.attr("cx", function (d) {
return x(d.Year);
})
.attr("cy", function (d) {
return y(d.Relevance);
})
.attr("r", function (d) {
return radius(d.GenFreq);
})
.append("title")
.text(function (d) {
return "(" + d.Year + ", " + d.Relevance + ", " + d.GenFreq + ", " + d.SpecFreq + ")";
});
//plot specFreq
var gWithin = g.append("g").selectAll("circle")
.data(data)
.enter().append("circle")
.style("fill", function (d) {
return "#d6d487";
})
.attr("cx", function (d) {
return x(d.Year);
})
.attr("cy", function (d) {
return y(d.Relevance);
})
.attr("r", function (d) {
return radius(d.SpecFreq);
})
.append("title")
.text(function (d) {
return "(" + d.Year + ", " + d.Relevance + ", " + d.GenFreq + ", " + d.SpecFreq + ")";
});
var chart = {
svg: svg,
g: g,
x: x,
y: y,
xAxis: xAxis,
yAxis: yAxis
}
return chart;
}
function MakeContextBrushable(context, focus) {
var brush = d3.svg.brush().x(context.x).y(context.y)
.on("brushstart", brushstart)
.on("brush", brushmove)
.on("brushend", brushend);
context.g.append("g")
.attr("class", "brush")
.call(brush);
function brushstart() {
context.svg.classed("selecting", true);
}
function brushmove() {
var e = d3.event.target.extent();
var circle = context.svg.selectAll("circle");
circle.classed("selected", function (d) {
return e[0][0] <= d["DecisionYear"] && d["DecisionYear"] <= e[1][0]
&& e[0][1] <= d["Relevance"] && d["Relevance"] <= e[1][1];
});
}
function brushend() {
context.svg.classed("selecting", !d3.event.target.empty());
if (!d3.event.target.empty()) {
var e = d3.event.target.extent();
focus.x.domain([e[0][0], e[1][0]]);
focus.y.domain([e[0][1], e[1][1]]);
focus.g.select(".x.axis").call(focus.xAxis)
.selectAll("text")
.style("font-size", "10px")
.attr("dx", "-1em");
focus.g.select(".y.axis").call(focus.yAxis)
.selectAll("text")
.style("font-size", "10px")
.attr("dx", "-1em");
var circle = focus.svg.selectAll("circle").attr("cx", function (d) { return focus.x(d.Year); })
.attr("cy", function (d) { return focus.y(d.Relevance); })
console.log("BrushEnd Domain: [" + focus.x.domain()[0] + ", " + focus.x.domain()[1] + "]");
}
else {
focus.x.domain(context.x.domain());
focus.y.domain(context.y.domain());
focus.g.select(".x.axis").call(focus.xAxis)
.selectAll("text")
.style("font-size", "10px")
.attr("dx", "-1em");
focus.g.select(".y.axis").call(focus.yAxis)
.selectAll("text")
.style("font-size", "10px")
.attr("dx", "-1em");
var circle = focus.g.selectAll("circle").attr("cx", function (d) { return focus.x(d.Year); })
.attr("cy", function (d) { return focus.y(d.Relevance); })
}
}
}
function MakeFocusZoomable(focus) {
focus.svg.call(d3.behavior.zoom().x(focus.x).y(focus.y).on("zoom", Zoom));
function Zoom() {
focus.svg.select(".x.axis").call(focus.xAxis).selectAll("text")
.style("font-size", "10px")
.attr("dy", "1.5em");
focus.svg.select(".y.axis").call(focus.yAxis).selectAll("text")
.style("font-size", "10px")
.attr("dx", "-1em");
focus.svg.selectAll("circle").attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}
}
</script>
</head>
<body>
<div id="chart">
</div>
</body>
</html>
To reset the brushing after panning, you would need to reset and call brush.extent again:
//Find extent of zoomed area, for example the edges of graphed region
var brushExtent = [x.invert(0), x.invert(width)];
context.select(".brush").call(brush.extent(brushExtent));
Here's an example of focus/context brushing panning synchronization on a line graph:
http://jsfiddle.net/MtXvx/8/
Hope that helps!