D3 js bar chart pan - javascript

I try to create bar chart with zoom/pan functionality using d3 js. Zooming works perfectly but when I pan the chart to the left or to the right the bars go out of beside min and max X values. What I'm doing wrong?
please check this image with issue described
Here is the fiddle: https://jsfiddle.net/Cayman/vpn8mz4g/1/
function render_chart(){
var stack = d3.layout.stack();
var dataset = {
"categories": ['2018-06-01T00:00:00.000+03:00',
'2018-06-02T00:00:00.000+03:00',
'2018-06-03T00:00:00.000+03:00',
'2018-06-04T00:00:00.000+03:00',
'2018-06-05T00:00:00.000+03:00',
'2018-06-06T00:00:00.000+03:00',
'2018-06-07T00:00:00.000+03:00',
'2018-06-08T00:00:00.000+03:00',
'2018-06-09T00:00:00.000+03:00',
'2018-06-10T00:00:00.000+03:00',
'2018-06-11T00:00:00.000+03:00',
'2018-06-12T00:00:00.000+03:00'],
"series": ["New York","Mumbai","Bengaluru"],
"colors": ["#3498db","#e74c3c","#2ecc71"],
"layers": [
[
{"y":1,"y0":20,"month":"2018-06-01T00:00:00.000+03:00"},
{"y":2,"y0":18,"month":"2018-06-02T00:00:00.000+03:00"},
{"y":5,"y0":18,"month":"2018-06-03T00:00:00.000+03:00"},
{"y":10,"y0":20,"month":"2018-06-04T00:00:00.000+03:00"},
{"y":14,"y0":23,"month":"2018-06-05T00:00:00.000+03:00"},
{"y":18,"y0":29,"month":"2018-06-06T00:00:00.000+03:00"},
{"y":20,"y0":31,"month":"2018-06-07T00:00:00.000+03:00"},
{"y":20,"y0":29,"month":"2018-06-08T00:00:00.000+03:00"},
{"y":16,"y0":24,"month":"2018-06-09T00:00:00.000+03:00"},
{"y":10,"y0":19,"month":"2018-06-10T00:00:00.000+03:00"},
{"y":5,"y0":23,"month":"2018-06-11T00:00:00.000+03:00"},
{"y":3,"y0":20,"month":"2018-06-12T00:00:00.000+03:00"}
],
[
{"y":12,"y0":24,"month":"2018-06-01T00:00:00.000+03:00"},
{"y":14,"y0":25,"month":"2018-06-02T00:00:00.000+03:00"},
{"y":13,"y0":31,"month":"2018-06-03T00:00:00.000+03:00"},
{"y":16,"y0":32,"month":"2018-06-04T00:00:00.000+03:00"},
{"y":18,"y0":33,"month":"2018-06-05T00:00:00.000+03:00"},
{"y":19,"y0":29,"month":"2018-06-06T00:00:00.000+03:00"},
{"y":20,"y0":27,"month":"2018-06-07T00:00:00.000+03:00"},
{"y":18,"y0":26,"month":"2018-06-08T00:00:00.000+03:00"},
{"y":20,"y0":31,"month":"2018-06-09T00:00:00.000+03:00"},
{"y":17,"y0":29,"month":"2018-06-10T00:00:00.000+03:00"},
{"y":18,"y0":26,"month":"2018-06-11T00:00:00.000+03:00"},
{"y":14,"y0":24,"month":"2018-06-12T00:00:00.000+03:00"}
],[
{"y":8,"y0":24,"month":"2018-06-01T00:00:00.000+03:00"},
{"y":14,"y0":26,"month":"2018-06-02T00:00:00.000+03:00"},
{"y":12,"y0":31,"month":"2018-06-03T00:00:00.000+03:00"},
{"y":15,"y0":33,"month":"2018-06-04T00:00:00.000+03:00"},
{"y":18,"y0":37,"month":"2018-06-05T00:00:00.000+03:00"},
{"y":16,"y0":29,"month":"2018-06-06T00:00:00.000+03:00"},
{"y":17,"y0":27,"month":"2018-06-07T00:00:00.000+03:00"},
{"y":19,"y0":25,"month":"2018-06-08T00:00:00.000+03:00"},
{"y":25,"y0":30,"month":"2018-06-09T00:00:00.000+03:00"},
{"y":23,"y0":31,"month":"2018-06-10T00:00:00.000+03:00"},
{"y":11,"y0":26,"month":"2018-06-11T00:00:00.000+03:00"},
{"y":12,"y0":23,"month":"2018-06-12T00:00:00.000+03:00"}
]
]
}
dataset["categories"].forEach(function(d2) {
d = new Date(d2);
});
dataset["layers"].forEach(function(d) {
d.forEach(function(d2) {
d2.month = new Date(d2.month);
});
});
n = dataset["series"].length, // Number of Layers
m = dataset["layers"].length, // Number of Samples in 1 layer
yGroupMax = d3.max(dataset["layers"], function(layer) { return d3.max(layer, function(d) { return d.y0; }); });
yGroupMin = d3.min(dataset["layers"], function(layer) { return d3.min(layer, function(d) { return d.y; }); });
var pWidth = document.getElementById('chart1').offsetWidth;
var margin = {top: 50, right: 0, bottom: 50, left: 100},
width = pWidth - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.time.scale()
.domain([new Date(dataset["categories"][0]),new Date(dataset["categories"][11]),])
.range([0, width]);
var y = d3.scale.linear()
.domain([yGroupMin, yGroupMax])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(dataset["categories"].length)
.tickPadding(6)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var zoom = d3.behavior.zoom()
.x(x)
.scaleExtent([0, Infinity])
.on("zoom", zoomed);
var svg = d3.select("#chart1").append("svg")
.call(zoom)
.attr("class", "chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var layer = svg.selectAll(".layer")
.data(dataset["layers"])
.enter().append("g")
.attr("class", "layer");
var rect = layer.selectAll("rect")
.data(function(d,i){d.map(function(b){b.colorIndex=i;return b;});return d;})
.enter().append("rect")
.transition()
.duration(500)
.delay(function(d, i) { return i * 10; })
.attr("x", function(d, i, j) {
var qqq = x(d.month);
var qqq2 = x(d.month) + 30 / n * j;
return x(d.month) + 30 / n * j;
})
.attr("width", 30 / n)
.transition()
.attr("y", function(d) { return y(d.y0); })
.attr("height", function(d) { return height - y(d.y0-0)})
.attr("class","bar")
.style("fill",function(d){return dataset["colors"][d.colorIndex];})
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("text")
.attr("x", width/3)
.attr("y", 0)
.attr("dx", ".71em")
.attr("dy", "-.71em")
.text("Min - Max Temperature (Month wise)");
svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
function zoomed() {
console.warn("zoom");
svg.select(".x.axis").call(xAxis);
console.log(d3.event.translate[0] + " " + d3.event.translate[1]);
svg.selectAll(".chart rect").attr("transform", "translate(" + d3.event.translate[0] + ",0)scale(" + d3.event.scale + ", 1)");
}
enter code here
}

You need to add a clip-path into the mix:
var svg = d3.select("#chart1").append("svg")
.call(zoom)
.attr("class", "chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
// create clip-path that's the same size as drawing area
svg.append('defs')
.append('clipPath')
.attr('id', 'clip')
.append('rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', width)
.attr('height', height);
svg = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var layer = svg.selectAll(".layer")
.data(dataset["layers"])
.enter().append("g")
.attr("class", "layer")
.attr('clip-path', 'url(#clip)'); //<-- clip drawing area
Running code:
$(document).ready(function() {
render_chart();
});
function render_chart() {
var stack = d3.layout.stack();
var dataset = {
"categories": ['2018-06-01T00:00:00.000+03:00',
'2018-06-02T00:00:00.000+03:00',
'2018-06-03T00:00:00.000+03:00',
'2018-06-04T00:00:00.000+03:00',
'2018-06-05T00:00:00.000+03:00',
'2018-06-06T00:00:00.000+03:00',
'2018-06-07T00:00:00.000+03:00',
'2018-06-08T00:00:00.000+03:00',
'2018-06-09T00:00:00.000+03:00',
'2018-06-10T00:00:00.000+03:00',
'2018-06-11T00:00:00.000+03:00',
'2018-06-12T00:00:00.000+03:00'
],
"series": ["New York", "Mumbai", "Bengaluru"],
"colors": ["#3498db", "#e74c3c", "#2ecc71"],
"layers": [
[{
"y": 1,
"y0": 20,
"month": "2018-06-01T00:00:00.000+03:00"
},
{
"y": 2,
"y0": 18,
"month": "2018-06-02T00:00:00.000+03:00"
},
{
"y": 5,
"y0": 18,
"month": "2018-06-03T00:00:00.000+03:00"
},
{
"y": 10,
"y0": 20,
"month": "2018-06-04T00:00:00.000+03:00"
},
{
"y": 14,
"y0": 23,
"month": "2018-06-05T00:00:00.000+03:00"
},
{
"y": 18,
"y0": 29,
"month": "2018-06-06T00:00:00.000+03:00"
},
{
"y": 20,
"y0": 31,
"month": "2018-06-07T00:00:00.000+03:00"
},
{
"y": 20,
"y0": 29,
"month": "2018-06-08T00:00:00.000+03:00"
},
{
"y": 16,
"y0": 24,
"month": "2018-06-09T00:00:00.000+03:00"
},
{
"y": 10,
"y0": 19,
"month": "2018-06-10T00:00:00.000+03:00"
},
{
"y": 5,
"y0": 23,
"month": "2018-06-11T00:00:00.000+03:00"
},
{
"y": 3,
"y0": 20,
"month": "2018-06-12T00:00:00.000+03:00"
}
],
[{
"y": 12,
"y0": 24,
"month": "2018-06-01T00:00:00.000+03:00"
},
{
"y": 14,
"y0": 25,
"month": "2018-06-02T00:00:00.000+03:00"
},
{
"y": 13,
"y0": 31,
"month": "2018-06-03T00:00:00.000+03:00"
},
{
"y": 16,
"y0": 32,
"month": "2018-06-04T00:00:00.000+03:00"
},
{
"y": 18,
"y0": 33,
"month": "2018-06-05T00:00:00.000+03:00"
},
{
"y": 19,
"y0": 29,
"month": "2018-06-06T00:00:00.000+03:00"
},
{
"y": 20,
"y0": 27,
"month": "2018-06-07T00:00:00.000+03:00"
},
{
"y": 18,
"y0": 26,
"month": "2018-06-08T00:00:00.000+03:00"
},
{
"y": 20,
"y0": 31,
"month": "2018-06-09T00:00:00.000+03:00"
},
{
"y": 17,
"y0": 29,
"month": "2018-06-10T00:00:00.000+03:00"
},
{
"y": 18,
"y0": 26,
"month": "2018-06-11T00:00:00.000+03:00"
},
{
"y": 14,
"y0": 24,
"month": "2018-06-12T00:00:00.000+03:00"
}
],
[{
"y": 8,
"y0": 24,
"month": "2018-06-01T00:00:00.000+03:00"
},
{
"y": 14,
"y0": 26,
"month": "2018-06-02T00:00:00.000+03:00"
},
{
"y": 12,
"y0": 31,
"month": "2018-06-03T00:00:00.000+03:00"
},
{
"y": 15,
"y0": 33,
"month": "2018-06-04T00:00:00.000+03:00"
},
{
"y": 18,
"y0": 37,
"month": "2018-06-05T00:00:00.000+03:00"
},
{
"y": 16,
"y0": 29,
"month": "2018-06-06T00:00:00.000+03:00"
},
{
"y": 17,
"y0": 27,
"month": "2018-06-07T00:00:00.000+03:00"
},
{
"y": 19,
"y0": 25,
"month": "2018-06-08T00:00:00.000+03:00"
},
{
"y": 25,
"y0": 30,
"month": "2018-06-09T00:00:00.000+03:00"
},
{
"y": 23,
"y0": 31,
"month": "2018-06-10T00:00:00.000+03:00"
},
{
"y": 11,
"y0": 26,
"month": "2018-06-11T00:00:00.000+03:00"
},
{
"y": 12,
"y0": 23,
"month": "2018-06-12T00:00:00.000+03:00"
}
]
]
}
var parseDate = d3.time.format("%Y-%m-%d").parse;
dataset["categories"].forEach(function(d2) {
d = new Date(d2);
});
dataset["layers"].forEach(function(d) {
d.forEach(function(d2) {
d2.month = new Date(d2.month);
});
});
n = dataset["series"].length, // Number of Layers
m = dataset["layers"].length, // Number of Samples in 1 layer
yGroupMax = d3.max(dataset["layers"], function(layer) {
return d3.max(layer, function(d) {
return d.y0;
});
});
yGroupMin = d3.min(dataset["layers"], function(layer) {
return d3.min(layer, function(d) {
return d.y;
});
});
var pWidth = document.getElementById('chart1').offsetWidth;
var margin = {
top: 50,
right: 0,
bottom: 50,
left: 100
},
width = pWidth - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.time.scale()
.domain([new Date(dataset["categories"][0]), addDays(new Date(dataset["categories"][11]), 1)])
.range([0, width]);
var y = d3.scale.linear()
.domain([yGroupMin, yGroupMax])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(dataset["categories"].length)
.tickPadding(6)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var zoom = d3.behavior.zoom()
.x(x)
.scaleExtent([0, Infinity])
.on("zoom", zoomed);
var svg = d3.select("#chart1").append("svg")
.call(zoom)
.attr("class", "chart")
.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('x', 0)
.attr('y', 0)
.attr('width', width)
.attr('height', height);
svg = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var layer = svg.selectAll(".layer")
.data(dataset["layers"])
.enter().append("g")
.attr("class", "layer")
.attr('clip-path', 'url(#clip)');
var rect = layer.selectAll("rect")
.data(function(d, i) {
d.map(function(b) {
b.colorIndex = i;
return b;
});
return d;
})
.enter().append("rect")
.transition()
.duration(500)
.delay(function(d, i) {
return i * 10;
})
.attr("x", function(d, i, j) {
var qqq = x(d.month);
var qqq2 = x(d.month) + 30 / n * j;
return x(d.month) + 30 / n * j;
})
.attr("width", 30 / n)
.transition()
.attr("y", function(d) {
return y(d.y0);
})
.attr("height", function(d) {
return height - y(d.y0 - 0)
})
.attr("class", "bar")
.style("fill", function(d) {
return dataset["colors"][d.colorIndex];
})
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("text")
.attr("x", width / 3)
.attr("y", 0)
.attr("dx", ".71em")
.attr("dy", "-.71em")
.text("Min - Max Temperature (Month wise)");
svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var tooltip = d3.select("body")
.append('div')
.attr('class', 'tooltip');
tooltip.append('div')
.attr('class', 'month');
tooltip.append('div')
.attr('class', 'tempRange');
svg.selectAll("rect")
.on('mouseover', function(d) {
if (!d.month) return null;
tooltip.select('.month').html("<b>" + d.month + "</b>");
tooltip.select('.tempRange').html(d.y + "℃ to " + d.y0 + "℃");
tooltip.style('display', 'block');
tooltip.style('opacity', 2);
})
.on('mousemove', function(d) {
if (!d.month) return null;
tooltip.style('top', (d3.event.layerY + 10) + 'px')
.style('left', (d3.event.layerX - 25) + 'px');
})
.on('mouseout', function() {
tooltip.style('display', 'none');
tooltip.style('opacity', 0);
});
function zoomed() {
console.warn("zoom");
svg.select(".x.axis").call(xAxis);
console.log(d3.event.translate[0] + " " + d3.event.translate[1]);
svg.selectAll(".chart rect").attr("transform", "translate(" + d3.event.translate[0] + ",0)scale(" + d3.event.scale + ", 1)");
}
function addDays(startDate, numberOfDays) {
var returnDate = new Date(
startDate.getFullYear(),
startDate.getMonth(),
startDate.getDate() + numberOfDays,
startDate.getHours(),
startDate.getMinutes(),
startDate.getSeconds());
return returnDate;
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Multi Series Span Chart (Vertical)</title>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
<body>
<div id="chart1" class="chart" style="width: 100%;"></div>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
</body>
<script type="text/javascript" src="main.js"></script>
</html>

Related

plot not rendered under D3.js version 4

I am trying to port a d3 chart (which I found online) to v4 but for some reason only the axes are shown and nothing else. The code runs without any errors in the console of Chrome's developer tools and I have hit the wall as I am not an advanced d3 user. Any ideas are highly appreciated.
Here are two jsfiddles, in v3 and v4
https://jsfiddle.net/54mp286c/31/
https://jsfiddle.net/54mp286c/36/
and this is the d3 v4 code
var data = [{
"Cell_Num": 0,
"y": 3,
"x": 2
}, {
"Cell_Num": 1,
"y": 3,
"x": 6
}, {
"Cell_Num": 2,
"y": 7,
"x": 1
}, {
"Cell_Num": 3,
"y": 5,
"x": 5
}]
var margin = {
top: 10,
left: 50,
bottom: 30,
right: 10
},
width = 460 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var scale = {
x: d3.scaleLinear().range([0, width]).nice(),
y: d3.scaleLinear().range([height, 0]).nice()
};
var access = {
x: function(d) {
return d.x;
},
y: function(d) {
return d.y;
}
};
var value = {
x: function(d) {
return scale.x(access.x(d));
},
y: function(d) {
return scale.y(access.y(d));
}
};
var axis = {
x: d3.axisBottom(scale.x),
y: d3.axisLeft(scale.y)
};
var svg = d3.select(".chart").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 + ")");
svg.append("g").attr("class", "x axis");
svg.append("g").attr("class", "y axis");
svg.call(renderPlot, data);
function renderPlot(selection, data) {
updateScales(data);
selection.select(".x.axis").call(axis.x)
.attr("transform", "translate(0," + height + ")");
selection.select(".y.axis").call(axis.y);
selection
.call(renderVoronoi, data)
.call(renderPoints, data);
}
function renderVoronoi(selection, data) {
var voronoi = d3.voronoi()
.x(value.x)
.y(value.y)
.extent([
[0, 0],
[width, height]
]);
var polygons = selection.selectAll(".voronoi")
.data(voronoi(data));
polygons.enter().append("path")
.attr("class", "voronoi")
.on("mouseenter", function(d, i) {
var datum = selection.selectAll(".point").data()[i];
selection.call(renderCrosshair, datum);
})
.on("mouseleave", function(d, i) {
selection.selectAll(".crosshair").remove();
});
polygons
.attr("d", d3.line());
polygons.exit()
.remove();
}
function renderCrosshair(selection, datum) {
var lineData = [
// vertical line
[
[value.x(datum), height],
[value.x(datum), 0]
],
// horizontal line
[
[0, value.y(datum)],
[width, value.y(datum)]
]
];
var crosshairs = selection.selectAll(".crosshair.line").data(lineData);
crosshairs.enter().append("path")
.attr("class", "crosshair line");
crosshairs
.attr("d", d3.svg.line());
crosshairs.exit()
.remove();
var labelData = [{
x: -6,
y: value.y(datum) + 4,
text: Math.round(access.y(datum)),
orient: "left"
},
{
x: value.x(datum),
y: height + 16,
text: Math.round(access.x(datum)),
orient: "bottom"
}
];
var labels = selection.selectAll(".crosshair.label").data(labelData);
labels.enter().append("text")
.attr("class", "crosshair label");
labels
.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
})
.style("text-anchor", function(d) {
return d.orient === "left" ? "end" : "middle";
})
.text(function(d) {
return d.text;
});
labels.exit().remove();
}
function renderPoints(selection, data) {
var points = selection.selectAll(".point").data(data);
points.enter().append("circle")
.attr("class", "point")
.attr("cx", value.x)
.attr("cy", value.y)
.attr("r", 0)
.style("opacity", 0);
points
.transition().duration(1000)
.attr("cx", value.x)
.attr("cy", value.y)
.attr("r", 2)
.style("opacity", 1);
points.exit()
.transition().duration(1000)
.attr("r", 0)
.style("opacity", 0)
.remove();
}
function updateScales(data) {
var extent = {
x: d3.extent(data, access.x),
y: d3.extent(data, access.y)
};
scale.x.domain([extent.x[0] - 0.5, extent.x[1] + 0.5]);
scale.y.domain([extent.y[0] - 0.5, extent.y[1] + 0.5]);
}
Many thanks!

D3.js: Adding a time axis to a bar graph with negative values

I have a bar-graph where the x axis is the time. The bar graph supports negative values, I have an additional axis below for show the x-values. However, numeric values are being displayed instead of date.
Here is the code:
data = [{"value": 10, "date": "20150824"},
{"value": -21, "date": "20150924"},
{"value": 7, "date": "20151024"},
{"value": 12, "date": "20151124"},
{"value": 33, "date": "20151224"},
{"value": -10, "date": "20160124"},
{"value": -10, "date": "20160224"},
{"value": -2, "date": "20160324"},
{"value": 17, "date": "20160424"},
{"value": -4, "date": "20160524"},
{"value": 6, "date": "20160864"},
{"value": 23, "date": "20160724"},
{"value": 13, "date": "20160824"},
{"value": -19, "date": "20160924"},
{"value": -8, "date": "20161024"},
{"value": -2, "date": "20161124"},
{"value": 12, "date": "20161224"}
]
var margin = {top: 30, right: 10, bottom: 10, left: 30},
width = 960 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var y0 = Math.max(Math.abs(d3.min(data, function(d) { return +d.value; })), Math.abs(d3.max(data, function(d) { return +d.value; })));
var parseDate = d3.time.format("%Y%m%d").parse;
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var y = d3.scale.linear()
.domain([-y0, y0])
.range([height,0])
.nice();
var x = d3.scale.ordinal()
.domain(d3.range(data.length))
.rangeRoundBands([0, width], .2);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom + 100)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
bars = svg.selectAll(".bar")
.data(data);
bars.enter().append("rect")
.attr("class", function(d) { return d.value < 0 ? "bar negative" : "bar positive"; })
.attr("x", function(d, i) { return x(i); })
.attr("width", x.rangeBand())
.attr("fill", "#A9A9A9"});
bars.transition()
.duration(1000)
.delay(function (d, i) {
return i * 40;
})
.attr("y", function(d) { return y(Math.max(0, d.value)); })
.attr("height", function(d) {return Math.abs(y(d.value) - y(0)); });
svg.append("g")
.attr("class", "x axis")
.call(yAxis);
svg.append("g")
.attr("class", "y axis")
.append("line")
.attr("y1", y(0))
.attr("y2", y(0))
.attr("x1", 0)
.attr("x2", width);
svg.append("g")
.attr("class", "xx axis")
.attr("transform", "translate(0," + parseInt(height+ 30 ).toString() + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)" )
bars
.exit().remove();
This does not work. I've done some fair amount of googling but I am still unable to grasp the problem here.
It'd be great if someone could explain what's wrong with the code and how to fix it.
Use d3.time.scale() for time axis
var x = d3.time.scale()
.domain(d3.extent(data, function(d){
return d.date;
}))
.range([0, width]);
var xAxis = d3.svg.axis()
.scale(x)
.tickFormat(d3.time.format("%Y%m%d"))
.orient("bottom");

Adding nodes inside svg rect using d3 force layout

I have drawn four svg rect diagram...with different colors....
I'm trying to add node inside of those svg rect...but I'm getting only the svg rect without node inside them...I know that I have done something wrong with data...
But I'm not able to figure them out..I'm just one month old to D3..please advice me on this....
If you run the code below you can see my mistake....
<!DOCTYPE html>
<html>
<head>
<script src="../D3/d3.min.js"></script>
</head>
<body>
<style>
</style>
<script type="text/javascript">
var width = 500,
height = 500;
var nodes = [
{ x: width / 3, y: height / 2 }
//{ x: 2 * width / 3, y: height / 1 },
//{ x: 3 * width / 3, y: height / 2 },
//{ x: 4 * width / 3, y: height / 2 }
];
var force = d3.layout.force()
.size([width, height])
.nodes(nodes)
var svgcontainer = d3.select("body")
.append("svg")
.attr("width", 1000)
.attr("height", 900);
var rectdata = [{ "x": 50, "y": 70, "width": 600, "height": 150,"rx":80,"ry":80,"fill":"skyblue"},
{ "x": 50, "y": 260, "width": 200, "height": 400, "rx": 80, "ry": 90, "fill": "palegreen" },
{ "x": 440, "y": 260, "width": 200, "height": 400, "rx": 80, "ry": 90, "fill": "orange" },
{ "x": 50, "y": 700, "width": 600, "height": 150, "rx": 80, "ry": 80, "fill": "brown" }];
var svgrect = svgcontainer.selectAll("rect").data(rectdata).enter().append("rect");
var node = svgcontainer.selectAll('.node')
.data(nodes)
.enter().append('rect')
.attr('class', 'node');
force.on('end', function () {
svgrect.attr("x", function (d, i) { return d.x; })
.attr("y", function (d, i) { return d.y; })
.attr("rx", function (d, i) { return d.rx; })
.attr("ry", function (d, i) { return d.ry; })
.attr("width", function (d, i) { return d.width; })
.attr("height", function (d, i) { return d.height; })
.attr("fill", function (d, i) { return d.fill; });
});
force.start();
</script>
</body>
</html>
You aren't giving the node any attributes. I take it you want a circle and not a rect :
var node = svgcontainer.selectAll('.node')
.data(nodes)
.enter().append('circle')
.attr('class', 'node')
.attr('x', function(d){ console.log(d); return d.x})
.attr('y', function(d){ return d.y})
.attr('r', 10)
.attr('transform', function(d){
return 'translate(' + d.x + ', ' + d.y + ')'
})
Notice the translate at the bottom. If you are using the force layout in D3, the tick function should take care of this, but it looks like you don't have one, so you have to insert it after you create the nodes.
If you didn't want circles and wanted rectangles then this should do :
var nodeRect = svgcontainer.selectAll('.nodeRect')
.data(nodes)
.enter().append('rect')
.attr('class', 'nodeRect')
.attr('x', function(d){ console.log(d); return d.x})
.attr('y', function(d){ return d.y})
.attr('width', 100)
.attr('height', 50)
.attr('transform', function(d){
return 'translate(' + d.x + ', ' + d.y + ')'
})
Working fiddle with both : https://jsfiddle.net/reko91/n13kqvw9/
var width = 500,
height = 500;
var nodes = [
{ x: width / 3, y: height / 2 }
//{ x: 2 * width / 3, y: height / 1 },
//{ x: 3 * width / 3, y: height / 2 },
//{ x: 4 * width / 3, y: height / 2 }
];
var force = d3.layout.force()
.size([width, height])
.nodes(nodes)
var svgcontainer = d3.select("body")
.append("svg")
.attr("width", 1000)
.attr("height", 900);
var rectdata = [{ "x": 50, "y": 70, "width": 600, "height": 150,"rx":80,"ry":80,"fill":"skyblue"},
{ "x": 50, "y": 260, "width": 200, "height": 400, "rx": 80, "ry": 90, "fill": "palegreen" },
{ "x": 440, "y": 260, "width": 200, "height": 400, "rx": 80, "ry": 90, "fill": "orange" },
{ "x": 50, "y": 700, "width": 600, "height": 150, "rx": 80, "ry": 80, "fill": "brown" }];
var svgrect = svgcontainer.selectAll("rect").data(rectdata).enter().append("rect");
var node = svgcontainer.selectAll('.node')
.data(nodes)
.enter().append('circle')
.attr('class', 'node')
.attr('x', function(d){ console.log(d); return d.x})
.attr('y', function(d){ return d.y})
.attr('r', 10)
.attr('transform', function(d){
return 'translate(' + d.x + ', ' + d.y + ')'
})
var nodeRect = svgcontainer.selectAll('.nodeRect')
.data(nodes)
.enter().append('rect')
.attr('class', 'nodeRect')
.attr('x', function(d){ console.log(d); return d.x})
.attr('y', function(d){ return d.y})
.attr('width', 100)
.attr('height', 50)
.attr('transform', function(d){
return 'translate(' + d.x + ', ' + d.y + ')'
})
force.on('end', function () {
svgrect.attr("x", function (d, i) { return d.x; })
.attr("y", function (d, i) { return d.y; })
.attr("rx", function (d, i) { return d.rx; })
.attr("ry", function (d, i) { return d.ry; })
.attr("width", function (d, i) { return d.width; })
.attr("height", function (d, i) { return d.height; })
.attr("fill", function (d, i) { return d.fill; });
});
force.start();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

How to let the line fit into the axis

I am trying to plot a line using D3, but the line data only appears at the top left corner, could you please help find why the line can not fit into the axis?
var margin = { top: 20, right: 30, bottom: 30, left: 40 },
width = 680 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var lineData = [{ "x": 1, "y": 5 }, { "x": 20, "y": 20 },
{ "x": 40, "y": 10 }, { "x": 60, "y": 40 },
{ "x": 80, "y": 5 }, { "x": 100, "y": 60 }];
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1)
.domain(lineData.map(function (d) { return d.x; }));
var y = d3.scale.linear()
.range([height, 0])
.domain([0, d3.max(lineData, function (d) { return d.y; })]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svgContainer = 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 + ")");
var lineFunction = d3.svg.line()
.x(function (d) { return d.x; })
.y(function (d) { return d.y; });
var lineGraph = svgContainer.append("path")
.attr("d", lineFunction(lineData))
.attr("stroke", "blue")
.attr("stroke-width", 2)
.attr("fill", "none");
svgContainer.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svgContainer.append("g")
.attr("class", "y axis")
.call(yAxis);
var lineFunction = d3.svg.line()
.x(function (d) { return x(d.x); })
.y(function (d) { return y(d.y); });
You need to apply the scales you created to your line function.

d3.js - mouseover event not working properly on svg group

I have a graph for which I need a reference line everywhere the mouse-cursor is inside this graph. And this reference line will follow the mouse movements inside the graph.
But this doesn't seems to work fine. It works only on the axis and the ticks (.axis lines) of the axis. On debugging, I found that mouse event works fine when applied over SVG but not on the group, why so ?
Here is my code :
test.html
<html>
<head>
<script src="jquery.js">
</script>
<script src="d3.v2.js">
</script>
<script src="retest.js">
</script>
<style type="text/css">
.g_main {
cursor:pointer;
}
.axis path, .axis line {
stroke: #DBDBDB;
/*shape-rendering: crispEdges;
*/
}
.y g:first-child text {
display:none;
}
.y g:first-child line {
stroke: #989898 ;
stroke-width: 2.5px;
}
/*.x g:first-child line {
stroke: black ;
stroke-width: 2.5px;
}
*/
.y path {
stroke: #989898 ;
stroke-width: 2.5px;
}
</style>
</head>
<body>
<center>
<button id="reload" onclick="loadViz();">
load Graph
</button>
<div id="viz" class="viz">
</div>
</center>
<script>
loadViz();
</script>
</body>
</html>
retest.js
var series,
classifications,
minVal,
maxVal,
svgW = 600,
svgH = 600,
//w = 1200,
//h = 1200,
vizPadding = {
top: 120,
right: 30,
bottom: 120,
left: 50
},
yAxMin_PA = 0,
yAxMax_PA = 50,
xAxMin_PA = 2002,
xAxMax_PA = 2008,
areaStrokeColors = ['#FF6600', '#3366FF', '#B8860B', '#458B00', 'white'];
var loadViz = function () {
color = d3.scale.category10();
data = {
"lines": [{
"line": [{
"X": 2002,
"Y": 42
}, {
"X": 2003,
"Y": 45
},
{
"X": 2005,
"Y": 47
},
{
"X": 2007,
"Y": 41
}
]
}, {
"line": [{
"X": 2003,
"Y": 33
}, {
"X": 2005,
"Y": 38
}, {
"Y": 36,
"X": 2008
}
]
}, {
"line": [{
"X": 2004,
"Y": 13
}, {
"X": 2005,
"Y": 19
}, {
"X": 2008,
"Y": 21
}
]
}, {
"line": [{
"X": 2003,
"Y": 20
}, {
"X": 2005,
"Y": 27
}, {
"X": 2008,
"Y": 29
}
]
}
]
};
$("#viz").html("");
buildBase();
//setScales();
};
var buildBase = function () {
margin = {
top: 80,
right: 120,
bottom: 40,
left: 40
},
width = 960 - margin.left - margin.right,
height = 550 - margin.top - margin.bottom;
t2 = height + margin.top + margin.bottom;
x = d3.scale.linear()
.domain([xAxMin_PA, xAxMax_PA])
.range([0, width]);
y = d3.scale.linear()
.domain([yAxMin_PA, yAxMax_PA])
.range([height, 0]);
tickSizeToApplyX = 5;
tickSizeToApplyY = 10;
// Function to draw X-axis
xAxis = d3.svg.axis()
.scale(x)
.ticks(tickSizeToApplyX)
.tickSize(-height, 0, 0)
//.tickSize(10)
.orient("bottom")
.tickPadding(5);
// Function to draw Y-axis
yAxis = d3.svg.axis()
.scale(y)
.ticks(tickSizeToApplyY)
.tickSize(-width, 0, 0)
//.tickSize(0)
.orient("left")
.tickPadding(5);
// Define the line
var valueline = d3.svg.line()
.x(function (d) { /*console.log(d.X);*/
return x(d.X);
})
.y(function (d) { /*console.log(d.Y);*/
return y(d.Y);
});
// Define the line
var referline = d3.svg.line()
.x(function (dx) { /*console.log(d.X);*/
return dx;
})
.y(function (dy) { /*console.log(d.Y);*/
return dy;
});
// Append SVG into the html
var viz = d3.select("#viz")
.append("svg")
.attr("width", width + margin.left + margin.right + 10)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("class", "g_main")
.attr("transform", "translate(" + margin.left + "," + ((margin.top) - 30) + ")");
viz.on("mousemove", function () {
cx = d3.mouse(this)[0];
cy = d3.mouse(this)[1];
console.log("xx=>" + cx + "yy=>" + cy);
redrawline(cx, cy);
})
.on("mouseover", function () {
d3.selectAll('.line_over').style("display", "block");
})
.on("mouseout", function () {
d3.selectAll('.line_over').style("display", "none");
});
//console.log(this);
viz.append("line")
//d3.select("svg").append("line")
.attr("class", 'line_over')
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", x(xAxMax_PA))
.attr("y2", 0)
.style("stroke", "gray")
.attr("stroke-dasharray", ("5,5"))
.style("stroke-width", "1.5")
.style("display", "none");
// Draw X-axis
viz.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Draw Y-axis
viz.append("g")
.attr("class", function (d, i) {
return "y axis"
})
.call(yAxis);
function redrawline(cx, cy) {
d3.selectAll('.line_over')
.attr("x1", 0)
.attr("y1", cy)
.attr("x2", x(xAxMax_PA))
.attr("y2", cy)
.style("display", "block");
}
};
The g element is just an empty container which cannot capture click events (see documentation for pointer-events property for details).
However, mouse events do bubble up to it. Hence, the effect you desire can be achieved by first making sure that the g receives all pointer events:
.g_main {
// ..
pointer-events: all;
}
And then appending an invisible rectangle to it as a place to hover over:
viz.on("mousemove", function () {
cx = d3.mouse(this)[0];
cy = d3.mouse(this)[1];
redrawline(cx, cy);
})
.on("mouseover", function () {
d3.selectAll('.line_over').style("display", "block");
})
.on("mouseout", function () {
d3.selectAll('.line_over').style("display", "none");
})
.append('rect')
.attr('class', 'click-capture')
.style('visibility', 'hidden')
.attr('x', 0)
.attr('y', 0)
.attr('width', width)
.attr('height', height);
Working example: http://jsfiddle.net/H3W3k/
As for why they work when applied to the svg element (from the docs):
Note that the ‘svg’ element is not a graphics element, and in a Conforming SVG Stand-Alone File a rootmost ‘svg’ element will never be the target of pointer events, though events can bubble to this element. If a pointer event does not result in a positive hit-test on a graphics element, then it should evoke any user-agent-specific window behavior, such as a presenting a context menu or controls to allow zooming and panning of an SVG document fragment.

Categories

Resources