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>
Related
I am creating row chart using dc.js in angular, but the chart is not aligned properly. See the screenshot below:
var chartData=dc.rowChart(".div");
chartData
.width(900)
.x(d3.scaleLinear().domain([0, 20]))
.elasticX(true)
.colorAccessor(d => d.key)
.ordinalColors(this.mainColor)
.dimension(countryDimension)
.group(remove_empty_bins(countryGroup))
.margins({ top: 10, right: 120, bottom: 70, left: 30 })
.transitionDuration(500)
.renderLabel(true)
.label(function (d) {
return dc.utils.printSingleValue(d.x0 + d.x);
})
.renderlet(function (chart) {
chart.selectAll("g.x text")
.attr('font-size', '14px')
.attr('dy', '0px')
.attr('dx', '5px')
.attr('font-weight', '100px')
//.attr('transform', "rotate(35)")
.attr("text-anchor", "start")
.attr("text", "Data")
})
.renderlet(function (chart) {
chart.selectAll("g.x text")
.attr("transform", "translate(" + (650 / 2) + " ," +
(900 + 20 + 20) + ")")
.style("text-anchor", "middle")
.text("Data1");
})
.renderlet(function (chart) {
chart.selectAll("g.chart-body text").attr('dy', '15px')
.attr('dx', '0px')
.attr('z-index', '3')
.attr('font-size', '16px')
.attr('font-weight', '100px')
.attr("text-anchor", "start")
.text("Data")
})
.label(p => {
return p.key
})
.keyAccessor(function (d) {
return d.value['name'];
});
var ValueAccessor = chartData.valueAccessor(function (d) {
var count = 0;
var count1 = 0;
if (d.value['count'] > maxValue) {
maxValue = d.value['count'];
}
if (maxValue > 1) {
chartData.xAxis().ticks(4).tickFormat(d3.format("d"));
}
else {
chartData.xAxis().ticks(1).tickFormat(d3.format("d"));
}
return d.value['count'];
});
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 want to apply a scale to circles generated with d3 force from an array.
This code produces the right x axis, but there are loads of circles produced with the class 'goalamount' and they are all offscreen by tens of thousands of pixels. There should only be six circles in the goalamount class and they should all scale to the xaxis - what am I doing wrong?
const data = [{
x: 2020,
cx: 0,
colour: "#69306D",
scY: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x: 2020,
cx: 0,
colour: "#247BA0",
scY: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x: 2020,
cx: 0,
colour: "#3F762C",
y1: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x: 2020,
cx: 0,
colour: "#F25F5C",
y1: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x: 2022,
cx: 0,
colour: "#0C3957",
y1: 0,
y2: 170,
rad: 10,
amt: 5000
},
{
x: 2055,
cx: 0,
colour: "#BF802F",
y1: 0,
y2: 50,
rad: 10,
amt: 15000
}
];
const maxYear = Math.max.apply(Math, data.map(function(o) {
return o.x;
}));
const svg = d3.select("svg");
const pxX = svg.attr("width");
const pxY = svg.attr("height");
let tickLabelOffset = 170;
let minDotX = Math.min.apply(Math, data.map(function(o) {
return o.y1;
}))
if (minDotX < -20) {
tickLabelOffset += minDotX + 20;
}
const makeScale = (arr, accessor, range) => {
return d3.scaleLinear()
.domain(d3.extent(arr, accessor))
.range(range)
.nice()
}
const thisYear = new Date().getFullYear()
let tickTens = [];
for (let i = thisYear; i < maxYear; i++) {
if (i % 10 === 0) {
tickTens.push(i)
}
}
const scX = makeScale(data, d => d.x, [0, pxX - 200]);
const scX1 = makeScale(data, d => d.x, [0, pxX - 2020]);
const scY = d3.scaleLinear().domain([0, 100]).range([0, 100]);
const g = d3.axisBottom(scX).tickValues(
tickTens.map((tickVal) => {
return tickVal
})
)
const rad = d3.scaleLinear()
.domain(d3.extent(data, d => d.rad))
.range([3, 10]);
const amt = d3.scaleLinear()
.domain(d3.extent(data, d => d.amt))
.range([20, 50]);
for (let dotindex = 0; dotindex < data.length; dotindex++) {
if (data[dotindex - 1]) {
if (data[dotindex - 1].x === data[dotindex].x) {
data[dotindex].scY = data[dotindex - 1].scY - 20
}
}
}
const ticked = () => {
var u = d3.select('svg')
.append("g")
.attr("class", "goalAmounts")
.selectAll('goalAmounts')
.data(data)
u.enter()
.append("circle")
// .attr( "transform", "translate(" + 2000 + "," + 50 + ")")
.attr("r", d => amt(d.amt))
.merge(u)
.attr("fill", d => d.colour)
.attr("cx", d => scX(d.x))
.attr("cy", d => scY(d.y2))
u.exit().remove()
}
svg.append("g")
.attr("transform", "translate(" + 50 + "," + (pxY - 200) + ")")
.call(g)
.selectAll(".tick text")
.attr("fill", "#7A7A7A")
svg.selectAll("circle")
.data(data)
.enter()
.append("g")
.attr("class", "circles")
.append("circle")
.attr("transform", "translate(" + 100 + "," + 650 + ")")
.attr("fill", "white")
.attr("stroke", d => d.colour)
.attr("stroke-width", "2px")
.attr("cx", d => scX(d.x))
.attr("cy", d => scY(d.y2))
.attr("r", d => rad(d.rad));
svg.selectAll(".domain")
.attr("stroke", "#BDBDBD")
.attr("stroke-width", "2px")
.attr("transform", "translate(" + 50 + "," + 150 + ")")
svg.selectAll(".tick line")
.attr("stroke", "#BDBDBD")
.attr("stroke-width", "4px")
.attr("transform", "translate(" + 50 + "," + 150 + ")")
svg.selectAll(".tick text")
.attr("font-size", 20)
.attr("transform", "translate(" + 50 + "," + tickLabelOffset + ")")
.attr("font-weight", "bold")
.attr("dy", "0.5em")
d3.forceSimulation(data)
.force('charge', d3.forceManyBody())
.force('center', d3.forceCenter(pxX / 2, pxY / 2))
.force('collision', d3.forceCollide().radius(function(d) {
return d.amt
}))
.on('tick', ticked);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class="App">
<svg id="demo1" width="1200" height="700">
</svg>
</div>
Use d3.forceX and d3.forceY instead, so you draw the nodes towards their intended position. Also, d3-force populates x and y properties of the nodes, so you need to use d.x1 or something instead. scX(d.x) caused the huge values of the nodes.
const data = [{
x1: 2020,
cx: 0,
colour: "#69306D",
scY: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x1: 2020,
cx: 0,
colour: "#247BA0",
scY: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x1: 2020,
cx: 0,
colour: "#3F762C",
y1: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x1: 2020,
cx: 0,
colour: "#F25F5C",
y1: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x1: 2022,
cx: 0,
colour: "#0C3957",
y1: 0,
y2: 170,
rad: 10,
amt: 5000
},
{
x1: 2055,
cx: 0,
colour: "#BF802F",
y1: 0,
y2: 50,
rad: 10,
amt: 15000
}
];
const maxYear = Math.max.apply(Math, data.map(function(o) {
return o.x1;
}));
const svg = d3.select("svg");
const pxX = svg.attr("width");
const pxY = svg.attr("height");
let tickLabelOffset = 170;
let minDotX = Math.min.apply(Math, data.map(function(o) {
return o.y1;
}))
if (minDotX < -20) {
tickLabelOffset += minDotX + 20;
}
const makeScale = (arr, accessor, range) => {
return d3.scaleLinear()
.domain(d3.extent(arr, accessor))
.range(range)
.nice()
}
const thisYear = new Date().getFullYear()
let tickTens = [];
for (let i = thisYear; i < maxYear; i++) {
if (i % 10 === 0) {
tickTens.push(i)
}
}
const scX = makeScale(data, d => d.x1, [0, pxX - 200]);
const scX1 = makeScale(data, d => d.x1, [0, pxX - 2020]);
const scY = d3.scaleLinear().domain([0, 100]).range([0, 100]);
const g = d3.axisBottom(scX).tickValues(
tickTens.map((tickVal) => {
return tickVal
})
)
const rad = d3.scaleLinear()
.domain(d3.extent(data, d => d.rad))
.range([3, 10]);
const amt = d3.scaleLinear()
.domain(d3.extent(data, d => d.amt))
.range([20, 50]);
for (let dotindex = 0; dotindex < data.length; dotindex++) {
if (data[dotindex - 1]) {
if (data[dotindex - 1].x1 === data[dotindex].x1) {
data[dotindex].scY = data[dotindex - 1].scY - 20
}
}
}
const ticked = () => {
circles
.attr("cx", d => d.x)
.attr("cy", d => d.y);
}
svg.append("g")
.attr("transform", "translate(" + 50 + "," + (pxY - 200) + ")")
.call(g)
.selectAll(".tick text")
.attr("fill", "#7A7A7A")
const circles = svg.append("g")
.attr("class", "circles")
.attr( "transform", "translate(" + 100 + "," + 100 + ")")
.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("fill", "white")
.attr("stroke", d => d.colour)
.attr("stroke-width", "2px")
.attr("cx", d => scX(d.x1))
.attr("cy", d => scY(d.y2))
.attr("r", d => rad(d.rad));
svg.selectAll(".domain")
.attr("stroke", "#BDBDBD")
.attr("stroke-width", "2px")
.attr("transform", "translate(" + 50 + "," + 150 + ")")
svg.selectAll(".tick line")
.attr("stroke", "#BDBDBD")
.attr("stroke-width", "4px")
.attr("transform", "translate(" + 50 + "," + 150 + ")")
svg.selectAll(".tick text")
.attr("font-size", 20)
.attr("transform", "translate(" + 50 + "," + tickLabelOffset + ")")
.attr("font-weight", "bold")
.attr("dy", "0.5em")
d3.forceSimulation(data)
.force("x", d3.forceX(d => scX(d.x1)))
.force("y", d3.forceY(d => scY(d.y2)))
.force('collision', d3.forceCollide().radius(d => rad(d.rad)))
.on("tick", ticked);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class="App">
<svg id="demo1" width="1200" height="700">
</svg>
</div>
Assuming I have data like this:
const data = [
{
month: 1
apples: ...,
bananas: ...,
cherries: ...,
dates: ...,
},
{
month: 2
apples: ...,
bananas: ...,
cherries: ...,
dates: ...,
},
{
month: 3
apples: ...,
bananas: ...,
cherries: ...,
dates: ...,
}
]
Going for 12 months, using keys of ['apples','bananas','cherries','dates']. The d3.stack() will produce an array for 4 bars with 12 sets of values. This makes sense. However, what if I wanted to create 12 bars with the keys broken up so it's sets of 4 values.
Is it possible to flip things on their heads in this fashion?
You just need to convert the data into the required format and flip the axis and data configurations in the chart as shown below.
Existing :
var margin = {
top: 20,
right: 160,
bottom: 35,
left: 30
};
var width = 500 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
/* Data in strings like it would be if imported from a csv */
var data = [{
year: "2006",
redDelicious: "10",
mcintosh: "15",
oranges: "9",
pears: "6"
},
{
year: "2007",
redDelicious: "12",
mcintosh: "18",
oranges: "9",
pears: "4"
},
{
year: "2008",
redDelicious: "05",
mcintosh: "20",
oranges: "8",
pears: "2"
},
{
year: "2009",
redDelicious: "01",
mcintosh: "15",
oranges: "5",
pears: "4"
}
];
var parse = d3.time.format("%Y").parse;
// Transpose the data into layers
var dataset = d3.layout.stack()(["redDelicious", "mcintosh", "oranges", "pears"].map(function(fruit) {
return data.map(function(d) {
return {
x: parse(d.year),
y: +d[fruit]
};
});
}));
// Set x, y and colors
var x = d3.scale.ordinal()
.domain(dataset[0].map(function(d) {
return d.x;
}))
.rangeRoundBands([10, width - 10], 0.02);
var y = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.y;
});
})])
.range([height, 0]);
var colors = ["b33040", "#d25c4d", "#f2b447", "#d9d574"];
// Define and draw axes
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
.tickSize(-width, 0, 0)
.tickFormat(function(d) {
return d
});
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(d3.time.format("%Y"));
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Create groups for each series, rects for each segment
var groups = svg.selectAll("g.cost")
.data(dataset)
.enter().append("g")
.attr("class", "cost")
.style("fill", function(d, i) {
return colors[i];
});
var rect = groups.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.x);
})
.attr("y", function(d) {
return y(d.y0 + d.y);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y0 + d.y);
})
.attr("width", x.rangeBand())
.on("mouseover", function() {
tooltip.style("display", null);
})
.on("mouseout", function() {
tooltip.style("display", "none");
})
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 15;
var yPosition = d3.mouse(this)[1] - 25;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d.y);
});
// Draw legend
var legend = svg.selectAll(".legend")
.data(colors)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(30," + i * 19 + ")";
});
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {
return colors.slice().reverse()[i];
});
legend.append("text")
.attr("x", width + 5)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
switch (i) {
case 0:
return "Anjou pears";
case 1:
return "Naval oranges";
case 2:
return "McIntosh apples";
case 3:
return "Red Delicious apples";
}
});
// Prep the tooltip bits, initial display is hidden
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
svg {
font: 10px sans-serif;
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
}
path.domain {
stroke: none;
}
.y .tick line {
stroke: #ddd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
New :
var margin = {
top: 20,
right: 160,
bottom: 35,
left: 30
};
var width = 500 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
/* Data in strings like it would be if imported from a csv */
var data = [{
fruit: "redDelicious",
2006: "10",
2007: "12",
2008: "05",
2009: "01",
2010: "02"
},
{
fruit: "mcintosh",
2006: "15",
2007: "18",
2008: "20",
2009: "15",
2010: "10"
},
{
fruit: "oranges",
2006: "9",
2007: "9",
2008: "8",
2009: "5",
2010: "4"
},
{
fruit: "pears",
2006: "6",
2007: "4",
2008: "2",
2009: "4",
2010: "2"
}
];
var legends = Object.keys(data[0]);
legends.splice(legends.indexOf('fruit'), 1);
// Transpose the data into layers
var dataset = d3.layout.stack()(legends.map(function(year) {
return data.map(function(d) {
return {
x: d.fruit,
y: +d[year]
};
});
}));
// Set x, y and colors
var x = d3.scale.ordinal()
.domain(dataset[0].map(function(d) {
return d.x;
}))
.rangeRoundBands([10, width - 10], 0.02);
var y = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.y;
});
})])
.range([height, 0]);
var colors = ["b33040", "#d25c4d", "#f2b447", "#d9d574","#6aa8e0"];
// Define and draw axes
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
.tickSize(-width, 0, 0)
.tickFormat(function(d) {
return d
});
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
//.tickFormat(d3.time.format("%Y"));
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Create groups for each series, rects for each segment
var groups = svg.selectAll("g.cost")
.data(dataset)
.enter().append("g")
.attr("class", "cost")
.style("fill", function(d, i) {
return colors[i];
});
var rect = groups.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.x);
})
.attr("y", function(d) {
return y(d.y0 + d.y);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y0 + d.y);
})
.attr("width", x.rangeBand())
.on("mouseover", function() {
tooltip.style("display", null);
})
.on("mouseout", function() {
tooltip.style("display", "none");
})
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 15;
var yPosition = d3.mouse(this)[1] - 25;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d.y);
});
// Draw legend
var legend = svg.selectAll(".legend")
.data(colors)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(30," + i * 19 + ")";
});
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {
return colors.slice().reverse()[i];
});
legend.append("text")
.attr("x", width + 5)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
return legends.reverse()[i];
});
// Prep the tooltip bits, initial display is hidden
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
svg {
font: 10px sans-serif;
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
}
path.domain {
stroke: none;
}
.y .tick line {
stroke: #ddd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
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;
});
}