d3.js horizontal stacked bar chart with 2 vertical axes and tooltips - d3.js

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.

Related

d3 shrinking radar chart shape using attrTween

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>

D3.js Stacked Bar OnClick Action Not Triggered

I am in the middle of creating two related stacked bar charts. Clicking on a bar of the top chart should trigger the drawing of the bottom chart. Oddly, the mouseover and mousemove actions work but the click action doesn't. what do I need to change for the click action to work?
Note: before saying that I am not respecting the "Do Not Repeat Yourself Principle" in my code, remember that Kent Beck says: "First, get it to work, then get it right, finally get it to work fast". I am still in the get it to work phase.
Feel free to skip the data creation section.
Here is my code:
var app = {};
app.allBarsDatasets = [
{
"xAxisTickValue": "10-1",
"barValue": 17
},
{
"xAxisTickValue": "10-2",
"barValue": 17
},
{
"xAxisTickValue": "10-3",
"barValue": 17
}
];
app.allBarsDatasets2 = [
[
{
"xAxisTickValue": "10-1",
"barValue": 10
},
{
"xAxisTickValue": "10-2",
"barValue": 6
},
{
"xAxisTickValue": "10-3",
"barValue": 7
}
],
[
{
"xAxisTickValue": "10-1",
"barValue": 6
},
{
"xAxisTickValue": "10-2",
"barValue": 8
},
{
"xAxisTickValue": "10-3",
"barValue": 10
}
]
];
app.allLinesDatasets =
{
"points": [
{
"x": 1,
"y": 10
},
{
"x": 2,
"y": 8
},
{
"x": 3,
"y": 14
}
],
"color": "blue"
};
app.busStopsWaitTimes = {
"1": {
"days": {
"we": {
"10-1": [
17,
14,
14,
4,
8,
13,
11,
3,
2,
14,
14,
8,
9,
1,
9,
9,
9,
17,
1,
20
],
"10-2": [
13,
12,
3,
5,
18,
14,
17,
5,
9,
12,
19,
3,
8,
9,
20,
3,
14,
5,
7,
13
],
"10-3": [
18,
8,
8,
7,
10,
20,
16,
17,
6,
13,
5,
11,
11,
14,
18,
17,
11,
17,
4,
3
]
}
},
"name": "Adderley"
}
};
app.populateBusStopsWaitSelectionForm = function () {
let stopOptions = `<option value="">Select a stop</option>`;
$.each(app.busStopsWaitTimes, function (idx, stop) {
stopOptions += `<option value={"stopId":${idx}}>${stop.name}</option>`;
});
$("#busStopAnalysis_Stops").html(stopOptions);
}
app.populateBusStopsWaitSelectionForm();
$("#busStopAnalysis_Stops").change(function() {
let values = $("#busStopAnalysis_Stops").val();
if (values !== "") {
values = JSON.parse(values);
let daysOptions = `<option value="">Select a day</option>`;
if ("we" in app.busStopsWaitTimes[values.stopId].days) {
daysOptions += `<option value={"dayKey":"we"}>Wednesday</option>`
}
$("#busStopAnalysis_Days").html(daysOptions);
} else {
$("#busStopAnalysis_Days").html("<option>Please select a route</option>");
}
});
$("#drawBusStopAnalysisChart").on("click", function (evt) {
evt.preventDefault();
const stopInfo = JSON.parse($("#busStopAnalysis_Stops").val());
const dayInfo = JSON.parse($("#busStopAnalysis_Days").val());
if (stopInfo !== "" || dayInfo !== "") {
const allBarsDatasets = [];
const allBarsDatasets2 = [[],[]]
const allLinesdatasets = [];
const linePoints = [];
let i = 1;
$.each(app.busStopsWaitTimes[stopInfo.stopId]["days"][dayInfo.dayKey], function (idx, timeslot) {
timeslot.sort(function (a,b) {
return a - b;
});
let Percentile25th = timeslot[parseInt(timeslot.length / 4)];
let Percentile50th = timeslot[parseInt(timeslot.length / 2)];
let Percentile75th = timeslot[parseInt((timeslot.length / 4) * 3)];
allBarsDatasets.push({
xAxisTickValue: idx,
barValue: Percentile75th
});
allBarsDatasets2[0].push({
xAxisTickValue: idx,
barValue: Percentile25th
});
allBarsDatasets2[1].push({
xAxisTickValue: idx,
barValue: Percentile75th - Percentile25th
});
linePoints.push({x : i, y : Percentile50th});
i++;
});
allLinesdatasets.push({points:linePoints,color:"blue"});
app.drawBusStopAnalysisOneDayChart(allBarsDatasets, allBarsDatasets2, allLinesdatasets);
}
});
app.drawBusStopAnalysisOneDayChart = function (allBarsDatasets, allBarsDatasets2, allLinesdatasets) {
app.allLinesdatasets = allLinesdatasets;
$("#busStopAnalysis_OneDayChart").html("");
var barColor = '#384a60';
// calculate total frequency by state for all segment.
// var fD = app.allBarsDatasets.map(function(d){return [d.xAxisTickValue,d.barValue];});
var fD = allBarsDatasets.map(function(d){return [d.xAxisTickValue,d.barValue];});
var margin = {top: 20, right: 100, bottom: 30, left: 100},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var padding = 100;
//create svg for histogram.
var svg = d3.select("#busStopAnalysis_OneDayChart").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 + ")");
// create function for x-axis mapping.
var x = d3.scale.ordinal().rangeRoundBands([0, width], 0.1, 0)
.domain(fD.map(function(d) { return d[0]; }));
// Add x-axis to the histogram svg.
svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.svg.axis()
.scale(x)
.orient("bottom")
.innerTickSize(-height)
.outerTickSize(0)
.tickPadding(10));
// create function for y-axis mapping.
var yMin = 0;
var yMax = d3.max(fD.map(function(d) { return d[1]; }));
var y = d3.scale.linear().range([height, 0])
.domain([0, d3.max(fD, function(d) { return d[1]; })]);
var yScaleGridLines = d3.scale.linear()
.domain([0, yMax])
.range([height, 0]);
var yAxisGridLines = d3.svg.axis()
.scale(yScaleGridLines)
.orient("left")
.innerTickSize(-width)
.outerTickSize(0)
.tickPadding(10);
svg.append("g")
.attr("class", "y axis")
.call(yAxisGridLines);
// You would think d3 draws bar by bar but it draws level by level
// therefore you need to create stacks which are sub-arrays whose contents
// are arrays of elements at the same level
// to achieve that
// call stack,
// call map and iterate over each array
// call map iterate over all elements within an array while creating points based on values to visualize
var layers = d3.layout.stack() (
allBarsDatasets2.map(
function(barDataset) {
return barDataset.map(
function(d) {
return {x: d.xAxisTickValue, y:d.barValue};
}
)
}
)
);
var z = d3.scale.category10();
var layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) {
var x;
if (i === 0) {
x = "transparent";
} else {
// x = z(i);
x = "#686868";
}
return x;
});
var mouseG = d3.select("#busStopAnalysis_Charts").append("g")
.attr("class", "mouse-over-effects");
// this is the vertical line
svg.append("path")
.attr("class", "mouse-line")
.style("stroke", "black")
.style("stroke-width", "1px")
.style("opacity", "1");
var tooltip = d3.select("#busStopAnalysis_Charts")
.append('div')
.attr('id', 'tooltip');
$("#tooltip").css('display', 'none');
layer.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.y + d.y0); })
.attr("height", function (d) { return y(d.y0) - y(d.y + d.y0); })
.attr("width", x.rangeBand() - 1)
.on("mousemove", function (d) {
var mouse = d3.mouse(this);
// move the vertical line
d3.select(".mouse-line")
.attr("d", function() {
var d = "M" + mouse[0] + "," + height;
d += " " + mouse[0] + "," + 0;
return d;
});
})
.on("mouseover", function (d) {
var mouse = d3.mouse(this);
console.log("first chart");
console.log(mouse);
d3.select("#tooltip")
.style("left", mouse[0] + "px")
.style("top", mouse[1] + "px")
.style("width", "auto")
.style("height", "auto")
.html("Day: " + $("#busStopAnalysis_Days option:selected").text() + "<br>Time Range: " + d.x + "<br>Avg Wait: " + d.y);
$("#tooltip").css("display", "");
})
.on("mouseout", function() {
$("#tooltip").css("display", "none");
})
.on("click", function (d) {
app.drawAllDaysStopAnalysisChart(d);
});
// Beginning of line things drawing
// Add min and max x and y borrowed from weird lines
var xMin = app.allLinesdatasets.reduce(function(pv,cv){
var currentXMin = cv.points.reduce(function(pv,cv){
return Math.min(pv,cv.x);
},100)
return Math.min(pv,currentXMin);
},100);
var xMax = app.allLinesdatasets.reduce(function(pv,cv){
var currentXMax = cv.points.reduce(function(pv,cv){
return Math.max(pv,cv.x);
},0)
return Math.max(pv,currentXMax);
},0);
var yMin = app.allLinesdatasets.reduce(function(pv,cv){
var currentYMin = cv.points.reduce(function(pv,cv){
return Math.min(pv,cv.y);
},100)
return Math.min(pv,currentYMin);
},100);
var yMax = app.allLinesdatasets.reduce(function(pv,cv){
var currentYMax = cv.points.reduce(function(pv,cv){
return Math.max(pv,cv.y);
},0)
return Math.max(pv,currentYMax);
},0);
var yScaleGridLines = d3.scale.linear()
.domain([0, yMax])
.range([height, 0]);
var yAxisGridLines = d3.svg.axis()
.scale(yScaleGridLines)
.orient("left")
.innerTickSize(-width)
.outerTickSize(0)
.tickPadding(10);
var xScaleGridLines = {};
xScaleGridLines = d3.scale.linear()
.domain([xMin, xMax])
.range([0, width]);
var xAxisGridLines = d3.svg.axis()
.scale(xScaleGridLines)
.orient("bottom")
.innerTickSize(-height)
.outerTickSize(0)
.tickPadding(10);
var lineGridLines = d3.svg.line()
.interpolate('step-after')
.x(function(d) { return xScaleGridLines(d.x); })
.y(function(d) { return yScaleGridLines(d.y); });
$.each(app.allLinesdatasets, function (idx, dataset) {
svg.append("path")
.data([dataset.points])
.attr("class", "line")
.attr("d", lineGridLines)
.style("stroke", function(){
// return dataset.color;
return "#FF9900";
});
});
}
/********************************************************************
* Append and Draw Second Chart
********************************************************************/
app.drawAllDaysStopAnalysisChart = function drawAllDateStopAnalysis (timeRange) {
const stopInfo = JSON.parse($("#busStopAnalysis_Stops").val());
const allBarsDatasets = [];
const allBarsDatasets2 = [[],[]]
const allLinesdatasets = [];
const linePoints = [];
if (stopInfo !== "" || dayInfo !== "") {
let i = 1;
$.each(app.busStopsWaitTimes[stopInfo.stopId]["days"], function (idx, day) {
const stopRangeWaitTimeInfo = day[timeRange.x];
stopRangeWaitTimeInfo.sort(function (a,b) {
return a - b;
});
let Percentile25th = stopRangeWaitTimeInfo[parseInt(stopRangeWaitTimeInfo.length / 4)];
let Percentile50th = stopRangeWaitTimeInfo[parseInt(stopRangeWaitTimeInfo.length / 2)];
let Percentile75th = stopRangeWaitTimeInfo[parseInt((stopRangeWaitTimeInfo.length / 4) * 3)];
allBarsDatasets.push({
xAxisTickValue: idx,
barValue: Percentile75th
});
allBarsDatasets2[0].push({
xAxisTickValue: idx,
barValue: Percentile25th
});
allBarsDatasets2[1].push({
xAxisTickValue: idx,
barValue: Percentile75th - Percentile25th
});
linePoints.push({x : i, y : Percentile50th});
i++;
});
allLinesdatasets.push({points:linePoints,color:"orange"});
}
$("#busStopAnalysis_AllDaysChart").html("");
var barColor = '#384a60';
var fD = allBarsDatasets.map(function(d){return [d.xAxisTickValue,d.barValue];});
var margin = {top: 20, right: 100, bottom: 30, left: 100},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var padding = 100;
//create svg for histogram.
var svg = d3.select("#busStopAnalysis_AllDaysChart").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 + ")");
// create function for x-axis mapping.
var x = d3.scale.ordinal().rangeRoundBands([0, width], 0.1, 0)
.domain(fD.map(function(d) { return d[0]; }));
// Add x-axis to the histogram svg.
svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.svg.axis()
.scale(x)
.orient("bottom")
.innerTickSize(-height)
.outerTickSize(0)
.tickPadding(10));
// create function for y-axis mapping.
var yMin = 0;
var yMax = d3.max(fD.map(function(d) { return d[1]; }));
var y = d3.scale.linear().range([height, 0])
.domain([0, d3.max(fD, function(d) { return d[1]; })]);
var yScaleGridLines = d3.scale.linear()
.domain([yMin, yMax])
.range([height, 0]);
var yAxisGridLines = d3.svg.axis()
.scale(yScaleGridLines)
.orient("left")
.innerTickSize(-width)
.outerTickSize(0)
.tickPadding(10);
svg.append("g")
.attr("class", "y axis")
.call(yAxisGridLines);
var layers = d3.layout.stack() (
allBarsDatasets2.map(
function(barDataset) {
return barDataset.map(
function(d) {
return {x: d.xAxisTickValue, y:d.barValue};
}
)
}
)
);
var z = d3.scale.category10();
var layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) {
var x;
if (i === 0) {
x = "transparent";
} else {
x = "#FF9900";
}
return x;
});
// append a g for all the mouse over nonsense
var mouseG = svg.append("g")
.attr("class", "mouse-over-effects");
// this is the vertical line
svg.append("path")
.attr("class", "mouse-line")
.style("stroke", "grey")
.style("stroke-width", "1px")
.style("opacity", "1");
var tooltip = d3.select("#tooltip");
$("#tooltip").css('display', 'none');
layer.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.y + d.y0); })
.attr("height", function (d) { return y(d.y0) - y(d.y + d.y0); })
.attr("width", x.rangeBand() - 1)
.on("mousemove", function (d) {
var mouse = d3.mouse(this);
// move the vertical line
d3.select(".mouse-line")
.attr("d", function() {
var d = "M" + mouse[0] + "," + height;
d += " " + mouse[0] + "," + 0;
return d;
});
})
.on("mouseover", function (d) {
var mouse = d3.mouse(this);
console.log("second chart");
console.log(mouse);
tooltip.style("left", mouse[0] + "px")
.style("top", mouse[1] + "px")
.style("width", "auto")
.style("height", "auto")
.html("Day: " + $("#busStopAnalysis_Days option:selected").text() + "<br>Time Range: " + d.x + "<br>Avg Wait: " + d.y);
$("#tooltip").css("display", "");
})
.on("mouseout", function() {
$("#tooltip").css("display", "none");
});
// Beginning of line things drawing
// Add min and max x and y borrowed from weird lines
var xMin = app.allLinesdatasets.reduce(function(pv,cv){
var currentXMin = cv.points.reduce(function(pv,cv){
return Math.min(pv,cv.x);
},100)
return Math.min(pv,currentXMin);
},100);
var xMax = allLinesdatasets.reduce(function(pv,cv){
var currentXMax = cv.points.reduce(function(pv,cv){
return Math.max(pv,cv.x);
},0)
return Math.max(pv,currentXMax);
},0);
var yMin = allLinesdatasets.reduce(function(pv,cv){
var currentYMin = cv.points.reduce(function(pv,cv){
return Math.min(pv,cv.y);
},100)
return Math.min(pv,currentYMin);
},100);
var yMax = allLinesdatasets.reduce(function(pv,cv){
var currentYMax = cv.points.reduce(function(pv,cv){
return Math.max(pv,cv.y);
},0)
return Math.max(pv,currentYMax);
},0);
var yScaleGridLines = d3.scale.linear()
.domain([0, yMax])
.range([height, 0]);
var yAxisGridLines = d3.svg.axis()
.scale(yScaleGridLines)
.orient("left")
.innerTickSize(-width)
.outerTickSize(0)
.tickPadding(10);
var xScaleGridLines = {};
xScaleGridLines = d3.scale.linear()
.domain([xMin, xMax])
.range([0, width]);
var xAxisGridLines = d3.svg.axis()
.scale(xScaleGridLines)
.orient("bottom")
.innerTickSize(-height)
.outerTickSize(0)
.tickPadding(10);
var lineGridLines = d3.svg.line()
.interpolate('step-after')
.x(function(d) { return xScaleGridLines(d.x); })
.y(function(d) { return yScaleGridLines(d.y); });
$.each(app.allLinesdatasets, function (idx, dataset) {
svg.append("path")
.data([dataset.points])
.attr("class", "line")
.attr("d", lineGridLines)
.style("stroke", function(){
// return dataset.color;
return "#FF3300";
});
});
}
#busStopAnalysis_Charts .axis path,
#busStopAnalysis_Charts .axis line{
fill: none;
stroke: black;
}
#busStopAnalysis_Charts .line{
fill: none;
stroke: blue;
stroke-width: 2px;
}
#busStopAnalysis_Charts .tick text{
font-size: 12px;
}
#busStopAnalysis_Charts .tick line{
opacity: 0.2;
}
#busStopAnalysis_Charts #tooltip {
position: absolute;
text-align: center;
color: white;
padding: 10px 10px 10px 10px;
display: inline-block;
font: 12px sans-serif;
background-color: #384a60;
border: 3px solid #2f3e50;
-webkit-border-radius: 30px;
-moz-border-radius: 30px;
border-radius: 30px;
-webkit-box-shadow: 2px 2px 4px #888;
-moz-box-shadow: 2px 2px 4px #888;
box-shadow: 2px 2px 4px #888;
}
#busStopAnalysis_Charts #tooltip.hidden {
display: none;
}
#busStopAnalysis_Charts #tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 16px;
line-height: 20px;
}
<div id="busStopAnalysisChartArea_Form">
<div id="busStopAnalysisChartArea_Form_TableRow">
<div id="busStopAnalysisChartArea_Form_Stop">
<label for="family" class="control-label"></label>
<select class="form-control dataset-column" style="width:auto;" id="busStopAnalysis_Stops"></select>
</div>
<div id="busStopAnalysisChartArea_Form_Days">
<label for="family" class="control-label"></label>
<div>
<select class="form-control dataset-column" style="width:auto;float:left;" id="busStopAnalysis_Days"></select>
draw the chart
</div>
</div>
</div>
</div>
<div id="busStopAnalysis_Charts">
<div id="busStopAnalysis_OneDayChart"></div>
<div id="busStopAnalysis_AllDaysChart"></div>
<div>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
Your line is right under the mouse, preventing you to click the rectangle.
Solution: move it a little bit to the left:
var d = "M" + (mouse[0] - 2) + "," + height;
//moving it here ---------^
Here is your updated code:
var app = {};
app.allBarsDatasets = [
{
"xAxisTickValue": "10-1",
"barValue": 17
},
{
"xAxisTickValue": "10-2",
"barValue": 17
},
{
"xAxisTickValue": "10-3",
"barValue": 17
}
];
app.allBarsDatasets2 = [
[
{
"xAxisTickValue": "10-1",
"barValue": 10
},
{
"xAxisTickValue": "10-2",
"barValue": 6
},
{
"xAxisTickValue": "10-3",
"barValue": 7
}
],
[
{
"xAxisTickValue": "10-1",
"barValue": 6
},
{
"xAxisTickValue": "10-2",
"barValue": 8
},
{
"xAxisTickValue": "10-3",
"barValue": 10
}
]
];
app.allLinesDatasets =
{
"points": [
{
"x": 1,
"y": 10
},
{
"x": 2,
"y": 8
},
{
"x": 3,
"y": 14
}
],
"color": "blue"
};
app.busStopsWaitTimes = {
"1": {
"days": {
"we": {
"10-1": [
17,
14,
14,
4,
8,
13,
11,
3,
2,
14,
14,
8,
9,
1,
9,
9,
9,
17,
1,
20
],
"10-2": [
13,
12,
3,
5,
18,
14,
17,
5,
9,
12,
19,
3,
8,
9,
20,
3,
14,
5,
7,
13
],
"10-3": [
18,
8,
8,
7,
10,
20,
16,
17,
6,
13,
5,
11,
11,
14,
18,
17,
11,
17,
4,
3
]
}
},
"name": "Adderley"
}
};
app.populateBusStopsWaitSelectionForm = function () {
let stopOptions = `<option value="">Select a stop</option>`;
$.each(app.busStopsWaitTimes, function (idx, stop) {
stopOptions += `<option value={"stopId":${idx}}>${stop.name}</option>`;
});
$("#busStopAnalysis_Stops").html(stopOptions);
}
app.populateBusStopsWaitSelectionForm();
$("#busStopAnalysis_Stops").change(function() {
let values = $("#busStopAnalysis_Stops").val();
if (values !== "") {
values = JSON.parse(values);
let daysOptions = `<option value="">Select a day</option>`;
if ("we" in app.busStopsWaitTimes[values.stopId].days) {
daysOptions += `<option value={"dayKey":"we"}>Wednesday</option>`
}
$("#busStopAnalysis_Days").html(daysOptions);
} else {
$("#busStopAnalysis_Days").html("<option>Please select a route</option>");
}
});
$("#drawBusStopAnalysisChart").on("click", function (evt) {
evt.preventDefault();
const stopInfo = JSON.parse($("#busStopAnalysis_Stops").val());
const dayInfo = JSON.parse($("#busStopAnalysis_Days").val());
if (stopInfo !== "" || dayInfo !== "") {
const allBarsDatasets = [];
const allBarsDatasets2 = [[],[]]
const allLinesdatasets = [];
const linePoints = [];
let i = 1;
$.each(app.busStopsWaitTimes[stopInfo.stopId]["days"][dayInfo.dayKey], function (idx, timeslot) {
timeslot.sort(function (a,b) {
return a - b;
});
let Percentile25th = timeslot[parseInt(timeslot.length / 4)];
let Percentile50th = timeslot[parseInt(timeslot.length / 2)];
let Percentile75th = timeslot[parseInt((timeslot.length / 4) * 3)];
allBarsDatasets.push({
xAxisTickValue: idx,
barValue: Percentile75th
});
allBarsDatasets2[0].push({
xAxisTickValue: idx,
barValue: Percentile25th
});
allBarsDatasets2[1].push({
xAxisTickValue: idx,
barValue: Percentile75th - Percentile25th
});
linePoints.push({x : i, y : Percentile50th});
i++;
});
allLinesdatasets.push({points:linePoints,color:"blue"});
app.drawBusStopAnalysisOneDayChart(allBarsDatasets, allBarsDatasets2, allLinesdatasets);
}
});
app.drawBusStopAnalysisOneDayChart = function (allBarsDatasets, allBarsDatasets2, allLinesdatasets) {
app.allLinesdatasets = allLinesdatasets;
$("#busStopAnalysis_OneDayChart").html("");
var barColor = '#384a60';
// calculate total frequency by state for all segment.
// var fD = app.allBarsDatasets.map(function(d){return [d.xAxisTickValue,d.barValue];});
var fD = allBarsDatasets.map(function(d){return [d.xAxisTickValue,d.barValue];});
var margin = {top: 20, right: 100, bottom: 30, left: 100},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var padding = 100;
//create svg for histogram.
var svg = d3.select("#busStopAnalysis_OneDayChart").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 + ")");
// create function for x-axis mapping.
var x = d3.scale.ordinal().rangeRoundBands([0, width], 0.1, 0)
.domain(fD.map(function(d) { return d[0]; }));
// Add x-axis to the histogram svg.
svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.svg.axis()
.scale(x)
.orient("bottom")
.innerTickSize(-height)
.outerTickSize(0)
.tickPadding(10));
// create function for y-axis mapping.
var yMin = 0;
var yMax = d3.max(fD.map(function(d) { return d[1]; }));
var y = d3.scale.linear().range([height, 0])
.domain([0, d3.max(fD, function(d) { return d[1]; })]);
var yScaleGridLines = d3.scale.linear()
.domain([0, yMax])
.range([height, 0]);
var yAxisGridLines = d3.svg.axis()
.scale(yScaleGridLines)
.orient("left")
.innerTickSize(-width)
.outerTickSize(0)
.tickPadding(10);
svg.append("g")
.attr("class", "y axis")
.call(yAxisGridLines);
// You would think d3 draws bar by bar but it draws level by level
// therefore you need to create stacks which are sub-arrays whose contents
// are arrays of elements at the same level
// to achieve that
// call stack,
// call map and iterate over each array
// call map iterate over all elements within an array while creating points based on values to visualize
var layers = d3.layout.stack() (
allBarsDatasets2.map(
function(barDataset) {
return barDataset.map(
function(d) {
return {x: d.xAxisTickValue, y:d.barValue};
}
)
}
)
);
var z = d3.scale.category10();
var layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) {
var x;
if (i === 0) {
x = "transparent";
} else {
// x = z(i);
x = "#686868";
}
return x;
});
var mouseG = d3.select("#busStopAnalysis_Charts").append("g")
.attr("class", "mouse-over-effects");
// this is the vertical line
svg.append("path")
.attr("class", "mouse-line")
.style("stroke", "black")
.style("stroke-width", "1px")
.style("opacity", "1");
var tooltip = d3.select("#busStopAnalysis_Charts")
.append('div')
.attr('id', 'tooltip');
$("#tooltip").css('display', 'none');
layer.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.y + d.y0); })
.attr("height", function (d) { return y(d.y0) - y(d.y + d.y0); })
.attr("width", x.rangeBand() - 1)
.on("mousemove", function (d) {
var mouse = d3.mouse(this);
// move the vertical line
d3.select(".mouse-line")
.attr("d", function() {
var d = "M" + (mouse[0] - 2) + "," + height;
d += " " + (mouse[0] - 2) + "," + 0;
return d;
});
})
.on("mouseover", function (d) {
var mouse = d3.mouse(this);
d3.select("#tooltip")
.style("left", mouse[0] + "px")
.style("top", mouse[1] + "px")
.style("width", "auto")
.style("height", "auto")
.html("Day: " + $("#busStopAnalysis_Days option:selected").text() + "<br>Time Range: " + d.x + "<br>Avg Wait: " + d.y);
$("#tooltip").css("display", "");
})
.on("mouseout", function() {
$("#tooltip").css("display", "none");
})
.on("click", function (d) {
app.drawAllDaysStopAnalysisChart(d);
});
// Beginning of line things drawing
// Add min and max x and y borrowed from weird lines
var xMin = app.allLinesdatasets.reduce(function(pv,cv){
var currentXMin = cv.points.reduce(function(pv,cv){
return Math.min(pv,cv.x);
},100)
return Math.min(pv,currentXMin);
},100);
var xMax = app.allLinesdatasets.reduce(function(pv,cv){
var currentXMax = cv.points.reduce(function(pv,cv){
return Math.max(pv,cv.x);
},0)
return Math.max(pv,currentXMax);
},0);
var yMin = app.allLinesdatasets.reduce(function(pv,cv){
var currentYMin = cv.points.reduce(function(pv,cv){
return Math.min(pv,cv.y);
},100)
return Math.min(pv,currentYMin);
},100);
var yMax = app.allLinesdatasets.reduce(function(pv,cv){
var currentYMax = cv.points.reduce(function(pv,cv){
return Math.max(pv,cv.y);
},0)
return Math.max(pv,currentYMax);
},0);
var yScaleGridLines = d3.scale.linear()
.domain([0, yMax])
.range([height, 0]);
var yAxisGridLines = d3.svg.axis()
.scale(yScaleGridLines)
.orient("left")
.innerTickSize(-width)
.outerTickSize(0)
.tickPadding(10);
var xScaleGridLines = {};
xScaleGridLines = d3.scale.linear()
.domain([xMin, xMax])
.range([0, width]);
var xAxisGridLines = d3.svg.axis()
.scale(xScaleGridLines)
.orient("bottom")
.innerTickSize(-height)
.outerTickSize(0)
.tickPadding(10);
var lineGridLines = d3.svg.line()
.interpolate('step-after')
.x(function(d) { return xScaleGridLines(d.x); })
.y(function(d) { return yScaleGridLines(d.y); });
$.each(app.allLinesdatasets, function (idx, dataset) {
svg.append("path")
.data([dataset.points])
.attr("class", "line")
.attr("d", lineGridLines)
.style("stroke", function(){
// return dataset.color;
return "#FF9900";
});
});
}
/********************************************************************
* Append and Draw Second Chart
********************************************************************/
app.drawAllDaysStopAnalysisChart = function drawAllDateStopAnalysis (timeRange) {
const stopInfo = JSON.parse($("#busStopAnalysis_Stops").val());
const allBarsDatasets = [];
const allBarsDatasets2 = [[],[]]
const allLinesdatasets = [];
const linePoints = [];
if (stopInfo !== "" || dayInfo !== "") {
let i = 1;
$.each(app.busStopsWaitTimes[stopInfo.stopId]["days"], function (idx, day) {
const stopRangeWaitTimeInfo = day[timeRange.x];
stopRangeWaitTimeInfo.sort(function (a,b) {
return a - b;
});
let Percentile25th = stopRangeWaitTimeInfo[parseInt(stopRangeWaitTimeInfo.length / 4)];
let Percentile50th = stopRangeWaitTimeInfo[parseInt(stopRangeWaitTimeInfo.length / 2)];
let Percentile75th = stopRangeWaitTimeInfo[parseInt((stopRangeWaitTimeInfo.length / 4) * 3)];
allBarsDatasets.push({
xAxisTickValue: idx,
barValue: Percentile75th
});
allBarsDatasets2[0].push({
xAxisTickValue: idx,
barValue: Percentile25th
});
allBarsDatasets2[1].push({
xAxisTickValue: idx,
barValue: Percentile75th - Percentile25th
});
linePoints.push({x : i, y : Percentile50th});
i++;
});
allLinesdatasets.push({points:linePoints,color:"orange"});
}
$("#busStopAnalysis_AllDaysChart").html("");
var barColor = '#384a60';
var fD = allBarsDatasets.map(function(d){return [d.xAxisTickValue,d.barValue];});
var margin = {top: 20, right: 100, bottom: 30, left: 100},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var padding = 100;
//create svg for histogram.
var svg = d3.select("#busStopAnalysis_AllDaysChart").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 + ")");
// create function for x-axis mapping.
var x = d3.scale.ordinal().rangeRoundBands([0, width], 0.1, 0)
.domain(fD.map(function(d) { return d[0]; }));
// Add x-axis to the histogram svg.
svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.svg.axis()
.scale(x)
.orient("bottom")
.innerTickSize(-height)
.outerTickSize(0)
.tickPadding(10));
// create function for y-axis mapping.
var yMin = 0;
var yMax = d3.max(fD.map(function(d) { return d[1]; }));
var y = d3.scale.linear().range([height, 0])
.domain([0, d3.max(fD, function(d) { return d[1]; })]);
var yScaleGridLines = d3.scale.linear()
.domain([yMin, yMax])
.range([height, 0]);
var yAxisGridLines = d3.svg.axis()
.scale(yScaleGridLines)
.orient("left")
.innerTickSize(-width)
.outerTickSize(0)
.tickPadding(10);
svg.append("g")
.attr("class", "y axis")
.call(yAxisGridLines);
var layers = d3.layout.stack() (
allBarsDatasets2.map(
function(barDataset) {
return barDataset.map(
function(d) {
return {x: d.xAxisTickValue, y:d.barValue};
}
)
}
)
);
var z = d3.scale.category10();
var layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) {
var x;
if (i === 0) {
x = "transparent";
} else {
x = "#FF9900";
}
return x;
});
// append a g for all the mouse over nonsense
var mouseG = svg.append("g")
.attr("class", "mouse-over-effects");
// this is the vertical line
svg.append("path")
.attr("class", "mouse-line")
.style("stroke", "grey")
.style("stroke-width", "1px")
.style("opacity", "1");
var tooltip = d3.select("#tooltip");
$("#tooltip").css('display', 'none');
layer.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.y + d.y0); })
.attr("height", function (d) { return y(d.y0) - y(d.y + d.y0); })
.attr("width", x.rangeBand() - 1)
.on("mousemove", function (d) {
var mouse = d3.mouse(this);
// move the vertical line
d3.select(".mouse-line")
.attr("d", function() {
var d = "M" + mouse[0] + "," + height;
d += " " + mouse[0] + "," + 0;
return d;
});
})
.on("mouseover", function (d) {
var mouse = d3.mouse(this);
console.log("second chart");
console.log(mouse);
tooltip.style("left", mouse[0] + "px")
.style("top", mouse[1] + "px")
.style("width", "auto")
.style("height", "auto")
.html("Day: " + $("#busStopAnalysis_Days option:selected").text() + "<br>Time Range: " + d.x + "<br>Avg Wait: " + d.y);
$("#tooltip").css("display", "");
})
.on("mouseout", function() {
$("#tooltip").css("display", "none");
});
// Beginning of line things drawing
// Add min and max x and y borrowed from weird lines
var xMin = app.allLinesdatasets.reduce(function(pv,cv){
var currentXMin = cv.points.reduce(function(pv,cv){
return Math.min(pv,cv.x);
},100)
return Math.min(pv,currentXMin);
},100);
var xMax = allLinesdatasets.reduce(function(pv,cv){
var currentXMax = cv.points.reduce(function(pv,cv){
return Math.max(pv,cv.x);
},0)
return Math.max(pv,currentXMax);
},0);
var yMin = allLinesdatasets.reduce(function(pv,cv){
var currentYMin = cv.points.reduce(function(pv,cv){
return Math.min(pv,cv.y);
},100)
return Math.min(pv,currentYMin);
},100);
var yMax = allLinesdatasets.reduce(function(pv,cv){
var currentYMax = cv.points.reduce(function(pv,cv){
return Math.max(pv,cv.y);
},0)
return Math.max(pv,currentYMax);
},0);
var yScaleGridLines = d3.scale.linear()
.domain([0, yMax])
.range([height, 0]);
var yAxisGridLines = d3.svg.axis()
.scale(yScaleGridLines)
.orient("left")
.innerTickSize(-width)
.outerTickSize(0)
.tickPadding(10);
var xScaleGridLines = {};
xScaleGridLines = d3.scale.linear()
.domain([xMin, xMax])
.range([0, width]);
var xAxisGridLines = d3.svg.axis()
.scale(xScaleGridLines)
.orient("bottom")
.innerTickSize(-height)
.outerTickSize(0)
.tickPadding(10);
var lineGridLines = d3.svg.line()
.interpolate('step-after')
.x(function(d) { return xScaleGridLines(d.x); })
.y(function(d) { return yScaleGridLines(d.y); });
$.each(app.allLinesdatasets, function (idx, dataset) {
svg.append("path")
.data([dataset.points])
.attr("class", "line")
.attr("d", lineGridLines)
.style("stroke", function(){
// return dataset.color;
return "#FF3300";
});
});
}
#busStopAnalysis_Charts .axis path,
#busStopAnalysis_Charts .axis line{
fill: none;
stroke: black;
}
#busStopAnalysis_Charts .line{
fill: none;
stroke: blue;
stroke-width: 2px;
}
#busStopAnalysis_Charts .tick text{
font-size: 12px;
}
#busStopAnalysis_Charts .tick line{
opacity: 0.2;
}
#busStopAnalysis_Charts #tooltip {
position: absolute;
text-align: center;
color: white;
padding: 10px 10px 10px 10px;
display: inline-block;
font: 12px sans-serif;
background-color: #384a60;
border: 3px solid #2f3e50;
-webkit-border-radius: 30px;
-moz-border-radius: 30px;
border-radius: 30px;
-webkit-box-shadow: 2px 2px 4px #888;
-moz-box-shadow: 2px 2px 4px #888;
box-shadow: 2px 2px 4px #888;
}
#busStopAnalysis_Charts #tooltip.hidden {
display: none;
}
#busStopAnalysis_Charts #tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 16px;
line-height: 20px;
}
<div id="busStopAnalysisChartArea_Form">
<div id="busStopAnalysisChartArea_Form_TableRow">
<div id="busStopAnalysisChartArea_Form_Stop">
<label for="family" class="control-label"></label>
<select class="form-control dataset-column" style="width:auto;" id="busStopAnalysis_Stops"></select>
</div>
<div id="busStopAnalysisChartArea_Form_Days">
<label for="family" class="control-label"></label>
<div>
<select class="form-control dataset-column" style="width:auto;float:left;" id="busStopAnalysis_Days"></select>
draw the chart
</div>
</div>
</div>
</div>
<div id="busStopAnalysis_Charts">
<div id="busStopAnalysis_OneDayChart"></div>
<div id="busStopAnalysis_AllDaysChart"></div>
<div>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>

Last Bar overlap with Right YAxis d3js

I follow https://bl.ocks.org/nanu146/f48ffc5ec10270f55c9e1fb3da8b38f0 and http://bl.ocks.org/Caged/6476579 and make a Bar Graph with line an tooltip
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: orange;
}
.bar:hover {
fill: orangered ;
}
.x.axis path {
display: none;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
/* 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;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.js"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<div>
<svg id="graph"></svg>
</div>
<script>
//https://bl.ocks.org/nanu146/f48ffc5ec10270f55c9e1fb3da8b38f0
function getTextWidth(text, fontSize, fontName) {
c = document.createElement("canvas");
ctx = c.getContext("2d");
ctx.font = fontSize + ' ' + fontName;
return ctx.measureText(text).width;
}
function DataSegregator(array, on) {
var SegData;
OrdinalPositionHolder = {
valueOf: function () {
thisObject = this;
keys = Object.keys(thisObject);
keys.splice(keys.indexOf("valueOf"), 1);
keys.splice(keys.indexOf("keys"), 1);
return keys.length == 0 ? -1 : d3.max(keys, function (d) { return thisObject[d] })
}
, keys: function () {
keys = Object.keys(thisObject);
keys.splice(keys.indexOf("valueOf"), 1);
keys.splice(keys.indexOf("keys"), 1);
return keys;
}
}
array[0].map(function (d) { return d[on] }).forEach(function (b) {
value = OrdinalPositionHolder.valueOf();
OrdinalPositionHolder[b] = OrdinalPositionHolder > -1 ? ++value : 0;
})
SegData = OrdinalPositionHolder.keys().map(function () {
return [];
});
array.forEach(function (d) {
d.forEach(function (b) {
SegData[OrdinalPositionHolder[b[on]]].push(b);
})
});
return SegData;
}
Data = [
{ Date: "1", Categories: [{ Name: "Test Exam Jan", Value: 10 }], LineCategory: [{ Name: "Line1", Value: 69 }] },
{ Date: "2", Categories: [{ Name: "Test Exam Feb", Value: 1 }], LineCategory: [{ Name: "Line1", Value: 89 }] },
{ Date: "3", Categories: [{ Name: "Test Exam March", Value: 1 }], LineCategory: [{ Name: "Line1", Value: 72 }] },
{ Date: "4", Categories: [{ Name: "Test Exam 1", Value: 1 }], LineCategory: [{ Name: "Line1", Value: 75 }] },
{ Date: "5", Categories: [{ Name: "Test Exam 2", Value: 5 }], LineCategory: [{ Name: "Line1", Value: 52 }] },
{ Date: "6", Categories: [{ Name: "Test Exam 3", Value: 3 }], LineCategory: [{ Name: "Line1", Value: 40 }] },
{ Date: "7", Categories: [{ Name: "Test Exam 4", Value: 12 }], LineCategory: [{ Name: "Line1", Value: 37 }] },
{ Date: "8", Categories: [{ Name: "Test Exam 5", Value: 5 }], LineCategory: [{ Name: "Line1", Value: 68 }] },
{ Date: "9", Categories: [{ Name: "Test Exam 6", Value: 3 }], LineCategory: [{ Name: "Line1", Value: 92 }] },
{ Date: "10", Categories: [{ Name: "Test Exam 7", Value: 7 }], LineCategory: [{ Name: "Line1", Value: 95 }] },
{ Date: "11", Categories: [{ Name: "Test Exam 8", Value: 2 }], LineCategory: [{ Name: "Line1", Value: 55 }] },
{ Date: "12", Categories: [{ Name: "Test Exam 9", Value: 9 }], LineCategory: [{ Name: "Line1", Value: 50 }] },
{ Date: "13", Categories: [{ Name: "Test Exam 10",Value: 1 }], LineCategory: [{ Name: "Line1", Value: 25 }] },
{ Date: "14", Categories: [{ Name: "Test Exam 11",Value: 4 }], LineCategory: [{ Name: "Line1", Value: 99 }] },
{ Date: "15", Categories: [{ Name: "Test Exam 12",Value: 7 }], LineCategory: [{ Name: "Line1", Value: 82 }] },
{ Date: "16", Categories: [{ Name: "Test Exam 13",Value: 5 }], LineCategory: [{ Name: "Line1", Value: 32 }] },
]
var margin = { top: 20, right: 30, bottom: 60, left: 40 },
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var textWidthHolder = 0;
/// Adding Date in LineCategory
Data.forEach(function (d) {
d.LineCategory.forEach(function (b) {
b.Date = d.Date;
})
});
var Categories = new Array();
// Extension method declaration
Categories.pro
var Data;
var ageNames;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width],.1);
var XLine = d3.scale.ordinal()
.rangeRoundPoints([0, width], .5);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var YLine = d3.scale.linear().range([height, 0])
.domain([0, d3.max(Data, function (d) { return d3.max(d.LineCategory, function (b) { return b.Value }) })]);
/*var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);*/
var line = d3.svg.line().x(function (d) {
return x0(d.Date) + x0.rangeBand() / 2;
}).y(function (d) { return YLine(d.Value) });
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var YLeftAxis = d3.svg.axis().scale(YLine).orient("right").tickFormat(d3.format(".2s"));
console.log(YLeftAxis)
var svg = d3.select("#graph")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Frequency:</strong> <span style='color:red'>Test</span>";
})
svg.call(tip);
// Bar Data categories
Data.forEach(function (d) {
d.Categories.forEach(function (b) {
if (Categories.findIndex(function (c) { return c.Name===b.Name}) == -1) {
b.Type = "bar";
//console.log(JSON.stringify(b))
Categories.push(b)
}
})
});
// Line Data categories
Data.forEach(function (d) {
d.LineCategory.forEach(function (b) {
if (Categories.findIndex(function (c) { return c.Name === b.Name }) == -1) {
b.Type = "line";
//console.log(JSON.stringify(b))
Categories.push(b)
}
})
});
// Processing Line data
lineData = DataSegregator(Data.map(function (d) { return d.LineCategory }), "Name");
// Line Coloring
LineColor = d3.scale.ordinal();
LineColor.domain(Categories.filter(function (d) { return d.Type == "line" }).map(function (d) { return d.Name }));
LineColor.range(["#d40606", "#06bf00", "#98bdc5", "#671919", "#0b172b"])
x0.domain(Data.map(function (d) { return d.Date; }));
XLine.domain(Data.map(function (d) { return d.Date; }));
x1.domain(Categories.filter(function (d) { return d.Type == "bar" }).map(function (d) { return d.Name})).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(Data, function (d) { return d3.max(d.Categories, function (d) { return d.Value; }); })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + (width) + ",0)")
.call(YLeftAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -10)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Percent");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Population");
var state = svg.selectAll(".state")
.data(Data)
.enter().append("g")
.attr("class", "state")
.attr("transform", function (d) { return "translate(" + x0(d.Date) + ",0)"; });
state.selectAll(".bar")
.data(function (d) { return d.Categories; })
.enter().append("rect")
//.attr("width", x1.rangeBand())
.attr("class", "bar")
.attr("width", x0.rangeBand())
.attr("x", function (d) { return x1(d.Name); })
.attr("y", function (d) { return y(d.Value); })
.attr("height", function (d) { return height - y(d.Value); })
//.style("fill", function (d) { return color(d.Name); })
.on("click",function(d){console.log(d)})
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
.transition().delay(500).attrTween("height", function (d) {
var i = d3.interpolate(0, height - y(d.Value));
return function (t)
{
return i(t);
}
});
// drawaing lines
svg.selectAll(".lines").data(lineData).enter().append("g").attr("class", "line")
.each(function (d) {
Name=d[0].Name
d3.select(this).append("path").attr("d", function (b) { return line(b) }).style({ "stroke-width": "2px", "fill": "none" }).style("stroke", LineColor(Name)).transition().duration(1500);
})
// Legends
/* var LegendHolder = svg.append("g").attr("class", "legendHolder");
var legend = LegendHolder.selectAll(".legend")
.data(Categories.map(function (d) { return {"Name":d.Name,"Type":d.Type}}))
.enter().append("g")
.attr("class", "legend")
.attr("transform", function (d, i) { return "translate(0," +( height+ margin.bottom/2 )+ ")"; })
.each(function (d,i) {
// Legend Symbols
d3.select(this).append("rect")
.attr("width", function () { return 18 })
.attr("x", function (b) {
left = (i+1) * 15 + i * 18 + i * 5 + textWidthHolder;
return left;
})
.attr("y", function (b) { return b.Type == 'bar'?0:7})
.attr("height", function (b) { return b.Type== 'bar'? 18:5 })
.style("fill", function (b) { return b.Type == 'bar' ? color(d.Name) : LineColor(d.Name) });
// Legend Text
d3.select(this).append("text")
.attr("x", function (b) {
left = (i+1) * 15 + (i+1) * 18 + (i + 1) * 5 + textWidthHolder;
return left;
})
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(d.Name);
textWidthHolder += getTextWidth(d.Name, "10px", "calibri");
})
;*/
// Legend Placing
d3.select(".legendHolder").attr("transform", function (d) {
thisWidth = d3.select(this).node().getBBox().width;
return "translate(" + ((width) / 2 - thisWidth / 2) + ",0)";
})
</script>
</body>
If you run above code you will see a barchart like bellow image
the problem is last bar overlap with right axis .which I unable to fix .
any body have any suggestion to fix .please help .
You are not correctly positioning your bars regarding the x coordinate.
Since your groups are already translated...
.attr("transform", function (d) {
return "translate(" + x0(d.Date) + ",0)";
});
... this line:
.attr("x", function (d) { return x1(d.Name); })
Should be simply:
.attr("x", 0)
Here is a fiddle with that change only: https://jsfiddle.net/6gpwmups/

X axis Line not displayed in Firefox if Brush functionality applied on X axis

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>

d3.js focus + context via brushing multiple paths not showing up

I referred to this stackoverflow link for some code but for some reason on my chart, the chart disappears when I try to zoom in.
I tried putting the code and the data in this Vida document but the visualization won't show up altogether, and I still can't figure out the issue there.
Thanks for the help!
svg {
font: 10px sans-serif;
}
path {
fill:none;
stroke:white;
stroke-width:2px;
}
.axis path, .axis line {
fill: none;
stroke: #CCC;
shape-rendering: crispEdges;
}
.brush .extent {
stroke: #fff;
fill-opacity: .125;
shape-rendering: crispEdges;
}
.path_H2O {
stroke:green;
}
.path_OH- {
stroke:red;
}
.path_Ca3SiO5 {
stroke:yellow;
}
var margin = {top: 10, right: 20, bottom: 100, left: 40},
margin2 = {top: 630, right: 20, bottom: 20, left: 40},
width = 1600 - margin.left - margin.right,
height = 700 - margin.top - margin.bottom,
height2 = 700 - margin2.top - margin2.bottom;
var x = d3.scale.linear().range([0, width]),
x2 = d3.scale.linear().range([0, width]),
y = d3.scale.linear().range([height, 0]),
y2 = d3.scale.linear().range([height2, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis().scale(x).orient("bottom"),
xAxis2 = d3.svg.axis().scale(x2).orient("bottom"),
yAxis = d3.svg.axis().scale(y).orient("left");
var brush = d3.svg.brush()
.x(x2)
.on("brush", brush);
var area = function (Concent) {
return d3.svg.area()
.interpolate("step")
.x(function(d) { return x(d.Time); })
.y0(height)
.y1(function(d) { return y(d[Concent]); });
};
var area2 = function (Concent) {
return d3.svg.area()
.interpolate("step")
.x(function(d) { return x2(d.Time); })
.y0(height2)
.y1(function(d) { return y2(d[Concent]); });
};
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var myfunc = function(Time, data){
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "Time"; }));
data.forEach(function(d){
var y0 = 0; });
/* d.concent = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.concent[d.concent.length - 1].y1;});
dataset=data;*/
console.log(Time, data);
x.domain(d3.extent(data.map(function(d) { return d.Time; })));
y.domain([0, d3.max(data.map(function(d) { return d.H2O; }))]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.selectAll('path')
.data(['H2O', 'OH-', 'Ca3SiO5'])
.enter()
.append('path')
.attr('clip-path', 'url(#clip)')
.attr('d', function (col) {
return area(col)(data);
})
.attr('class', function (col) {
return "path_" + col + " data";
});
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "y axis")
.call(yAxis);
context.selectAll('path')
.data(['H2O', 'OH-', 'Ca3SiO5'])
.enter()
.append('path')
.attr('d', function (col) {
return area2(col)(data);
})
.attr('class', function (col) {
return "path_" + col;
});
context.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", height2 + 7);
function brushed() {
x.domain(brush.empty() ? x2.domain() : brush.extent());
focus.selectAll("path.data").attr("d", function (col) { return area(col)(data); });
focus.select(".x.axis").call(xAxis);
}
};
var data= d3.json([
{
"Time":0,
"H2O":0.7223999972,
"Ca3SiO5":0.2775999921,
"Ca(OH)2":0,
"CSH(II)":0,
"H2SiO4--":0,
"H3SiO4-":0,
"Ca++":0,
"CaOH+":0,
"OH-":0,
"Vacancy":"0"
},
{
"Time":0,
"H2O":0.7223999972,
"Ca3SiO5":0.2775999921,
"Ca(OH)2":0,
"CSH(II)":0,
"H2SiO4--":0,
"H3SiO4-":0,
"Ca++":0,
"CaOH+":0,
"OH-":0,
"Vacancy":"0"
},
{
"Time":0.1011666667,
"H2O":0.722445376,
"Ca3SiO5":0.2775546219,
"Ca(OH)2":0,
"CSH(II)":0,
"H2SiO4--":1.66E-006,
"H3SiO4-":0.0007145257,
"Ca++":0.0021455429,
"CaOH+":3.01E-006,
"OH-":0.0035762526,
"Vacancy":"0"
},
{
"Time":0.2025,
"H2O":0.7224635218,
"Ca3SiO5":0.277536476,
"Ca(OH)2":0,
"CSH(II)":0,
"H2SiO4--":5.77E-006,
"H3SiO4-":0.000996856,
"Ca++":0.0029978343,
"CaOH+":1.00E-005,
"OH-":0.0049973131,
"Vacancy":"0"
},
{
"Time":0.3038333333,
"H2O":0.7224757574,
"Ca3SiO5":0.2775242411,
"Ca(OH)2":0,
"CSH(II)":0,
"H2SiO4--":1.19E-005,
"H3SiO4-":0.001183896,
"Ca++":0.0035669817,
"CaOH+":2.03E-005,
"OH-":0.005946632,
"Vacancy":"0"
},
{
"Time":0.4051666667,
"H2O":0.722485082,
"Ca3SiO5":0.277514916,
"Ca(OH)2":0,
"CSH(II)":0,
"H2SiO4--":1.92E-005,
"H3SiO4-":0.0013237783,
"Ca++":0.0039957417,
"CaOH+":3.31E-005,
"OH-":0.0066624743,
"Vacancy":"0"
},
{
"Time":0.5065,
"H2O":0.7224926814,
"Ca3SiO5":0.2775073166,
"Ca(OH)2":0,
"CSH(II)":0,
"H2SiO4--":2.77E-005,
"H3SiO4-":0.0014351806,
"Ca++":0.0043407829,
"CaOH+":4.80E-005,
"OH-":0.0072388754,
"Vacancy":"0"
},
{
"Time":0.611,
"H2O":0.7224993205,
"Ca3SiO5":0.2775006773,
"Ca(OH)2":0,
"CSH(II)":0,
"H2SiO4--":3.68E-005,
"H3SiO4-":0.0015308949,
"Ca++":0.0046383714,
"CaOH+":6.48E-005,
"OH-":0.0077369817,
"Vacancy":"0"
},
{
"Time":0.7155,
"H2O":0.7225050948,
"Ca3SiO5":0.2774949035,
"Ca(OH)2":0,
"CSH(II)":0,
"H2SiO4--":4.60E-005,
"H3SiO4-":0.0016128709,
"Ca++":0.0048938526,
"CaOH+":8.27E-005,
"OH-":0.0081655806,
"Vacancy":"0"
},
{
"Time":0.82,
"H2O":0.7225102312,
"Ca3SiO5":0.2774897666,
"Ca(OH)2":0,
"CSH(II)":0,
"H2SiO4--":5.54E-005,
"H3SiO4-":0.0016845349,
"Ca++":0.0051181417,
"CaOH+":0.0001016937,
"OH-":0.0085426217,
"Vacancy":"0"
},
{
"Time":0.9245,
"H2O":0.722514876,
"Ca3SiO5":0.2774851215,
"Ca(OH)2":0,
"CSH(II)":0,
"H2SiO4--":6.52E-005,
"H3SiO4-":0.0017480743,
"Ca++":0.0053181429,
"CaOH+":0.0001216663,
"OH-":0.0088794869,
"Vacancy":"0"
};
],myfunc);
For the full dataset, please refer to the vida document I posted. Thanks!
I am posting my efforts here since it is easier to type all that is needed in a greater box, if nothing else. I created a FIDDLE to help us out. A few points:
I had to change the variable/myfunc strategy because it was not working. You will need to tell me if this actually worked. The lines are pretty flat but your data seems to show little variation in magnitude and perhaps some tweak in the scaling may help.
You were calling brush but your function is called brushed...that change was enough to activate the brush to do its job.
Well, let's see what you say. I hope this helps.

Resources