Prevent D3 graph from scaling - javascript

I am using this as my base project which is scrollable and scalable in both x and y axis. I have modified it to only be scrollable and scalable in x axis. Now, I want to prevent it from being scalable (i.e the x-scale should not change) while keeping it scrollable (i.e click the graph and dragging it slides it to the left or right)
I have tried doing scale(1) where there's transformation to keep the scale from not changing but that doesn't help. Here's the snippet for that:
g.append("svg:rect")
.attr("class", "zoom x box")
.attr("width", width - margin.left - margin.right)
.attr("height", height - margin.top - margin.bottom)
.attr("transform", "translate(" + 0 + "," + 0 + ")")
.style("visibility", "hidden")
.attr("pointer-events", "all")
.call( d3.behavior.zoom().on("zoom", function(){
g.attr("transform", "translate(" + d3.event.translate + ") scale(1)")
}) ).append("g");
And here's the full code:
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.10/d3.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<title>Simpe Single Axis Zoom</title>
<script src="http://d3js.org/d3.v3.min.js"></script>
<style>
.axis path, .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<div id="chart"></div>
<script>
var ex_chart = example().zoom(true);
var data = [];
for (var i = 0; i < 100; i++) {
data.push([Math.random(), Math.random()]);
}
d3.select('#chart')
.append("svg").attr("width", window.innerWidth).attr("height",window.innerHeight)
.datum(data).call(ex_chart);
function example() {
var svg;
var margin = {
top: 60,
bottom: 80,
left: 60,
right: 0
};
var width = 500;
var height = 400;
var xaxis = d3.svg.axis();
var yaxis = d3.svg.axis();
var xscale = d3.scale.linear();
var yscale = d3.scale.linear();
var zoomable = true;
var xzoom = d3.behavior.zoom()
.x(xscale)
.on("zoom", zoomable ? draw : null);
function chart(selection) {
selection.each(function(data) {
svg = d3.select(this).selectAll('svg').data([data]);
svg.enter().append('svg');
var g = svg.append('g')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
g.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width - margin.left - margin.right)
.attr("height", height - margin.top - margin.bottom);
g.append("svg:rect")
.attr("class", "border")
.attr("width", width - margin.left - margin.right)
.attr("height", height - margin.top - margin.bottom)
.style("stroke", "black")
.style("fill", "none");
g.append("g").attr("class", "x axis")
.attr("transform", "translate(" + 0 + "," + (height - margin.top - margin.bottom) + ")");
g.append("g").attr("class", "y axis");
g.append("g")
.attr("class", "scatter")
.attr("clip-path", "url(#clip)");
g
.append("svg:rect")
.attr("class", "zoom x box")
.attr("width", width - margin.left - margin.right)
.attr("height", height - margin.top - margin.bottom)
.attr("transform", "translate(" + 0 + "," + 0 + ")")
.style("visibility", "hidden")
.attr("pointer-events", "all")
.call( d3.behavior.zoom().on("zoom", function(){
g.attr("transform", "translate(" + d3.event.translate + ") scale(1)")
}) ).append("g");
// Update the x-axis
xscale.domain(d3.extent(data, function(d) {
return d[0];
}))
.range([0, width - margin.left - margin.right]);
xaxis.scale(xscale)
.orient('bottom')
.tickPadding(10);
svg.select('g.x.axis').call(xaxis);
// Update the y-scale.
yscale.domain(d3.extent(data, function(d) {
return d[1];
}))
.range([height - margin.top - margin.bottom, 0]);
yaxis.scale(yscale)
.orient('left')
.tickPadding(10);
svg.select('g.y.axis').call(yaxis);
draw();
});
return chart;
}
function update() {
var gs = svg.select("g.scatter");
var circle = gs.selectAll("circle")
.data(function(d) {
return d;
});
circle.enter().append("svg:circle")
.attr("class", "points")
.style("fill", "steelblue")
.attr("cx", function(d) {
return X(d);
})
.attr("cy", function(d) {
return Y(d);
})
.attr("r", 4);
circle.attr("cx", function(d) {
return X(d);
})
.attr("cy", function(d) {
return Y(d);
});
circle.exit().remove();
}
function zoom_update() {
xzoom = d3.behavior.zoom()
.x(xscale)
.on("zoom", zoomable ? draw : null);
svg.select('rect.zoom.x.box').call(xzoom);
}
function draw() {
svg.select('g.x.axis').call(xaxis);
//svg.select('g.y.axis').call(yaxis);
update();
zoom_update();
};
// X value to scale
function X(d) {
return xscale(d[0]);
}
// Y value to scale
function Y(d) {
return yscale(d[1]);
}
chart.zoom = function (_){
if (!arguments.length) return zoomable;
zoomable = _;
return chart;
}
return chart;
}
</script>
</body>
</html>
Any sort of help is greatly appreciated. Thanks.

I found out that there was an easy fix for this. In the d3.behaviour.zoom() function, all I needed to add was .scaleExtent([1,1]), which doesn't scale anything more than 1 and hence preventing the zoom.
An example: https://jsfiddle.net/x97k4snj/1/
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.10/d3.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<title>Simpe Single Axis Zoom</title>
<script src="http://d3js.org/d3.v3.min.js"></script>
<style>
.axis path, .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<div id="chart"></div>
<script>
var ex_chart = example().zoom(true);
var data = [];
for (var i = 0; i < 100; i++) {
data.push([Math.random(), Math.random()]);
}
d3.select('#chart')
.append("svg").attr("width", window.innerWidth).attr("height",window.innerHeight)
.datum(data).call(ex_chart);
function example() {
var svg;
var margin = {
top: 60,
bottom: 80,
left: 60,
right: 0
};
var width = 500;
var height = 400;
var xaxis = d3.svg.axis();
var yaxis = d3.svg.axis();
var xscale = d3.scale.linear();
var yscale = d3.scale.linear();
var zoomable = true;
var xzoom = d3.behavior.zoom()
.scaleExtent([1,1])
.x(xscale)
.on("zoom", zoomable ? draw : null);
function chart(selection) {
selection.each(function(data) {
svg = d3.select(this).selectAll('svg').data([data]);
svg.enter().append('svg');
var g = svg.append('g')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
g.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width - margin.left - margin.right)
.attr("height", height - margin.top - margin.bottom);
g.append("svg:rect")
.attr("class", "border")
.attr("width", width - margin.left - margin.right)
.attr("height", height - margin.top - margin.bottom)
.style("stroke", "black")
.style("fill", "none");
g.append("g").attr("class", "x axis")
.attr("transform", "translate(" + 0 + "," + (height - margin.top - margin.bottom) + ")");
g.append("g").attr("class", "y axis");
g.append("g")
.attr("class", "scatter")
.attr("clip-path", "url(#clip)");
g
.append("svg:rect")
.attr("class", "zoom x box")
.attr("width", width - margin.left - margin.right)
.attr("height", height - margin.top - margin.bottom)
.attr("transform", "translate(" + 0 + "," + 0 + ")")
.style("visibility", "hidden")
.attr("pointer-events", "all")
.call( d3.behavior.zoom().on("zoom", function(){
g.attr("transform", "translate(" + d3.event.translate + ") scale(1)")
}) ).append("g");
// Update the x-axis
xscale.domain(d3.extent(data, function(d) {
return d[0];
}))
.range([0, width - margin.left - margin.right]);
xaxis.scale(xscale)
.orient('bottom')
.tickPadding(10);
svg.select('g.x.axis').call(xaxis);
// Update the y-scale.
yscale.domain(d3.extent(data, function(d) {
return d[1];
}))
.range([height - margin.top - margin.bottom, 0]);
yaxis.scale(yscale)
.orient('left')
.tickPadding(10);
svg.select('g.y.axis').call(yaxis);
draw();
});
return chart;
}
function update() {
var gs = svg.select("g.scatter");
var circle = gs.selectAll("circle")
.data(function(d) {
return d;
});
circle.enter().append("svg:circle")
.attr("class", "points")
.style("fill", "steelblue")
.attr("cx", function(d) {
return X(d);
})
.attr("cy", function(d) {
return Y(d);
})
.attr("r", 4);
circle.attr("cx", function(d) {
return X(d);
})
.attr("cy", function(d) {
return Y(d);
});
circle.exit().remove();
}
function zoom_update() {
xzoom = d3.behavior.zoom()
.scaleExtent([1,1])
.x(xscale)
.on("zoom", zoomable ? draw : null);
svg.select('rect.zoom.x.box').call(xzoom);
}
function draw() {
svg.select('g.x.axis').call(xaxis);
//svg.select('g.y.axis').call(yaxis);
update();
zoom_update();
};
// X value to scale
function X(d) {
return xscale(d[0]);
}
// Y value to scale
function Y(d) {
return yscale(d[1]);
}
chart.zoom = function (_){
if (!arguments.length) return zoomable;
zoomable = _;
return chart;
}
return chart;
}
</script>
</body>
</html>

Related

How to limit number of ticks to display in D3.js?

I found an example. http://jsfiddle.net/aWJtJ/8/, for limiting number of ticks but it uses D3.js version 3. I converted the code to version 5 but I am not getting the same result. It shows only two bars. Complete Fiddle: http://jsfiddle.net/pmLf095y/
// Margins, width and height.
var margin = {top: 20, right: 20, bottom: 30, left: 10},
body_width = 500,
width = body_width - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
// Scales.
var x = d3.scaleTime().range([width/data.length/2, width-width/data.length/2]);
var y = d3.scaleLinear().range([height, 0]);
// Construct our SVG object.
var svg = d3.select(".system-efficiency").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 + ")");
// X-axis.
var xAxis = d3.axisBottom(x)
.ticks(d3.timeMonth.every(1))
.tickFormat(d3.timeFormat('%b %Y'));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Set scale domains.
x.domain(data.map(d => new Date(d.datestr)));
y.domain([0, d3.max(data, function(d) { return d.air_used; })]);
// Call x-axis.
d3.select(".x.axis")
.transition().duration(1000)
.call(xAxis);
// Draw bars.
var bars = svg.selectAll(".air_used")
.data(data, function(d) { return d.datestr; });
bars.exit().remove();
bars.transition().duration(1000)
.attr("x", function(d) { return x(d.date) - width/data.length/2; })
.attr("width", width / data.length)
.attr("y", function(d) { return y(d.air_used); })
.attr("height", function(d) { return height - y(d.air_used);});
bars.enter().append("rect")
.attr("class", "air_used")
.attr("width", width / data.length)
.attr("x", function(d) { return x(new Date(d.datestr)) - (width/data.length)/2; })
.attr("y", height)
.attr("height", 0)
.transition().duration(1000)
.attr("y", function(d) { return y(d.air_used); })
.attr("height", function(d) { return height - y(d.air_used);});
To set the x domain, you don't need to map the dates but to find the extent of the dates.
x.domain(d3.extent(data, (d => new Date(d.datestr))));
Changed code:
var data = [
{
"air_produced": 0.660985,
"air_used": 0.342706,
"datestr": "2012-12-01 00:00:00",
"energy_used": 0.106402
},
{
"air_produced": 0.824746,
"air_used": 0.400776,
"datestr": "2013-01-01 00:00:00",
"energy_used": 0.250462
},
{
"air_produced": 0.181898,
"air_used": 0.003541,
"datestr": "2013-02-01 00:00:00",
"energy_used": 0.000582
},
{
"air_produced": 1.096685,
"air_used": 0.97719,
"datestr": "2013-03-01 00:00:00",
"energy_used": 0.923212
},
{
"air_produced": 0.283379,
"air_used": 0.241088,
"datestr": "2013-04-01 00:00:00",
"energy_used": 0.23381
}
];
// Margins, width and height.
var margin = {top: 20, right: 20, bottom: 30, left: 10},
body_width = 500,
width = body_width - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
// Scales.
var x = d3.scaleTime().range([width/data.length/2, width-width/data.length/2]);
var y = d3.scaleLinear().range([height, 0]);
// Construct our SVG object.
var svg = d3.select(".system-efficiency").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 + ")");
// X-axis.
var xAxis = d3.axisBottom(x)
.ticks(d3.timeMonth.every(1))
.tickFormat(d3.timeFormat('%b %Y'));
// Set scale domains.
x.domain(d3.extent(data, (d => new Date(d.datestr))));
y.domain([0, d3.max(data, function(d) { return d.air_used; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")");
// Call x-axis.
d3.select(".x.axis")
.transition().duration(1000)
.call(xAxis);
// Draw bars.
var bars = svg.selectAll(".air_used")
.data(data, function(d) { return d.datestr; });
bars.exit().remove();
bars.transition().duration(1000)
.attr("x", function(d) { return x(d.date) - width/data.length/2; })
.attr("width", width / data.length)
.attr("y", function(d) { return y(d.air_used); })
.attr("height", function(d) { return height - y(d.air_used);});
bars.enter().append("rect")
.attr("class", "air_used")
.attr("width", width / data.length)
.attr("x", function(d) { return x(new Date(d.datestr)) - (width/data.length)/2; })
.attr("y", height)
.attr("height", 0)
.transition().duration(1000)
.attr("y", function(d) { return y(d.air_used); })
.attr("height", function(d) { return height - y(d.air_used);});
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.air_used {
fill: steelblue;
}
.x.axis path {
display: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div class="section system-efficiency">
<h4>SYSTEM EFFICIENCY</h4>
</div>
Hope this clears up.
The following line returns only one value [0] but I need to define range [0, ?]:
x.domain(data.map(d => new Date(d.datestr)));
I changed back to original line:
x.domain(d3.extent(data, function(d) { return d.date; }));
Complete code:
// Margins, width and height.
var margin = {top: 20, right: 20, bottom: 30, left: 10},
body_width = 600,
width = body_width - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
// Scales.
var x = d3.scaleTime().range([width / data.length / 2, width - width / data.length / 2]);
var y = d3.scaleLinear().range([height, 0]);
// Construct our SVG object.
var svg = d3.select(".system-efficiency").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 + ")");
// X-axis.
var xAxis = d3.axisBottom(x)
.ticks(d3.timeMonth.every(1))
.tickFormat(d3.timeFormat('%b %Y'));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Date parsing.
const parseDate = d3.timeParse("%Y-%m-%d %H:%M:%S");
data.forEach(function(d) {
d.date = parseDate(d.datestr);
});
console.log(data);
// Set scale domains.
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function (d) {
return d.air_used;
})]);
// Call x-axis.
d3.select(".x.axis")
.transition().duration(1000)
.call(xAxis);
// Draw bars.
var bars = svg.selectAll(".air_used")
.data(data, function (d) {
return d.datestr;
});
bars.exit().remove();
bars.transition().duration(1000)
.attr("x", function (d) {
return x(d.date) - width / data.length / 2;
})
.attr("width", width / data.length)
.attr("y", function (d) {
return y(d.air_used);
})
.attr("height", function (d) {
return height - y(d.air_used);
});
bars.enter().append("rect")
.attr("class", "air_used")
.attr("width", width / data.length)
.attr("x", function (d) {
return x(d.date) - (width / data.length) / 2;
})
.attr("y", height)
.attr("height", 0)
.transition().duration(1000)
.attr("y", function (d) {
return y(d.air_used);
})
.attr("height", function (d) {
return height - y(d.air_used);
});
Note: I took the v3 example from one of stackoverflow questions, but now I could not find it to reference.

How to set d3 brush extent on load

Here I'm trying to draw a brushable bar chart. Following are the lines of code I have used.
var barchart = function(width,height,id,data,d3){
var focusGraph;
var margin = {top: 10, right: 10, bottom: (height*.2), left: 40},
margin2 = {top: (height*.86), right: 10, bottom: 20, left: 40},
width = width - margin.left - margin.right,
height2 = height - margin2.top - margin2.bottom,
height = height - margin.top - margin.bottom;
var max1 = d3.max(data, function(d) { return d.Metric1; });
var max2 = d3.max(data, function(d) { return d.Metric2; });
var maxValue = d3.max([max1, max2]);
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], 0.1, 0.2);
var x2 = d3.scale.ordinal()
.rangeRoundBands([0, width], 0.1, 0.2);
var y = d3.scale.linear()
.range([height,0]);
var y2 = d3.scale.linear()
.range([height2,0]);
var dimensions = data.map(function(d){
return d.Dim1;
});
var barWidth = x.rangeBand();
x.domain(data.map(function(d) { return d.Dim1; }));
y.domain([0, d3.max(data.map(function(d) { return d.Metric1; }))]);
x2.domain(x.domain());
y2.domain(y.domain());
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").tickFormat(d3.format("s"));
var svg = d3.select("#"+id).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
var brush = d3.svg.brush()
.x(x2)
.extent([0,50])
.on("brush", brushed);
var area = d3.svg.area()
.x(function(d) { return x(d.Dim1); })
.y0(height)
.y1(function(d) { return y(d.Metric1); });
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 barsGroup = focus.append("g")
.attr('clip-path', 'url(#clip)');
var context = svg.append("g")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var barWidth = x.rangeBand();
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "y axis")
.call(yAxis);
focusGraph = barsGroup.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d, i) { return x(d.Dim1); })
.attr("y", function(d) { return y(d.Metric1); })
.attr("width", barWidth)
.attr("height", function(d) { return height-y(d.Metric1); });
context.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d, i) { return x2(d.Dim1); })
.attr("y", function(d) { return y2(d.Metric1); })
.attr("width", barWidth)
.attr("height", function(d) { return height2-y2(d.Metric1); });
// 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);
var test = 0;
function brushed() {
var selected = x2.domain().filter(function(d){return (brush.extent()[0] <= x2(d)) && (x2(d) <= brush.extent()[1])});
x.domain(selected);
var b =0;
var w =0;
if(selected.length < 2){
b=barWidth;
w=b*2;
}
else{
b = x(selected[1])-x(selected[0]);
var w = b/2;
}
var j = 0;
focusGraph.attr("width", w);
focusGraph.attr("id",function(d, i) { return d.Index;});
focusGraph.attr("height", function(d,i) {
for(j=0;j<selected.length;j++)
{
if(d.Dim1 == selected[j]){
console.log(d.Dim1 +"=="+selected[j])
return d.Metric1;
}
}
});
console.log(j);
focusGraph.attr("x", function(d,i) {
for(j=0;j<selected.length;j++)
{
if(d.Dim1 == selected[j]){
console.log(d.Dim1 +"=="+selected[j])
return (x(selected[j]))+(barWidth/2);
}
}
});
focus.select(".x.axis").call(xAxis);
svg.append("g")
}
};
The output:
Here I want to display the bar chart to a fixed brush extent. as shown in the below image:
I have tried applying default extent value as brush.extent([0,50]) it set up the extent width but it does not clip the bar chart by default.
I have tried using brush.on("start",brushstart). But its not working
Any help is greatly appreciated.
I have successfully solved the issue by adding a line of code brush.call(brush.event)
var barchart = function(width,height,id,data,d3){
var focusGraph;
var margin = {top: 10, right: 10, bottom: (height*.2), left: 40},
margin2 = {top: (height*.86), right: 10, bottom: 20, left: 40},
width = width - margin.left - margin.right,
height2 = height - margin2.top - margin2.bottom,
height = height - margin.top - margin.bottom;
var max1 = d3.max(data, function(d) { return d.Metric1; });
var max2 = d3.max(data, function(d) { return d.Metric2; });
var maxValue = d3.max([max1, max2]);
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], 0.1, 0.2);
var x2 = d3.scale.ordinal()
.rangeRoundBands([0, width], 0.1, 0.2);
var y = d3.scale.linear()
.range([height,0]);
var y2 = d3.scale.linear()
.range([height2,0]);
var dimensions = data.map(function(d){
return d.Dim1;
});
var barWidth = x.rangeBand();
x.domain(data.map(function(d) { return d.Dim1; }));
y.domain([0, d3.max(data.map(function(d) { return d.Metric1; }))]);
x2.domain(x.domain());
y2.domain(y.domain());
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").tickFormat(d3.format("s"));
var svg = d3.select("#"+id).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
var brush = d3.svg.brush()
.x(x2)
.extent([0,50])
.on("brush", brushed);
var area = d3.svg.area()
.x(function(d) { return x(d.Dim1); })
.y0(height)
.y1(function(d) { return y(d.Metric1); });
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 barsGroup = focus.append("g")
.attr('clip-path', 'url(#clip)');
var context = svg.append("g")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var barWidth = x.rangeBand();
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "y axis")
.call(yAxis);
focusGraph = barsGroup.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d, i) { return x(d.Dim1); })
.attr("y", function(d) { return y(d.Metric1); })
.attr("width", barWidth)
.attr("height", function(d) { return height-y(d.Metric1); });
context.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d, i) { return x2(d.Dim1); })
.attr("y", function(d) { return y2(d.Metric1); })
.attr("width", barWidth)
.attr("height", function(d) { return height2-y2(d.Metric1); });
// context.append("g")
// .attr("class", "x axis")
// .attr("transform", "translate(0," + height2 + ")")
// .call(xAxis2);
context.append("g")
.attr("class", "x brush")
.call(brush)
.call(brush.event)
.selectAll("rect")
.attr("y", -6)
.attr("height", height2);
var test = 0;
function brushed() {
var selected = x2.domain().filter(function(d){return (brush.extent()[0] <= x2(d)) && (x2(d) <= brush.extent()[1])});
x.domain(selected);
var b =0;
var w =0;
if(selected.length < 2){
b=barWidth;
w=b*2;
}
else{
b = x(selected[1])-x(selected[0]);
var w = b/2;
}
var j = 0;
focusGraph.attr("width", w);
focusGraph.attr("id",function(d, i) { return d.Index;});
focusGraph.attr("height", function(d,i) {
for(j=0;j<selected.length;j++)
{
if(d.Dim1 == selected[j]){
console.log(d.Dim1 +"=="+selected[j])
return d.Metric1;
}
}
});
console.log(j);
focusGraph.attr("x", function(d,i) {
for(j=0;j<selected.length;j++)
{
if(d.Dim1 == selected[j]){
console.log(d.Dim1 +"=="+selected[j])
return (x(selected[j]))+(barWidth/2);
}
}
});
focus.select(".x.axis").call(xAxis);
svg.append("g")
}
};

line chart with mouseover tooltip is not working in d3.js

I've created a line chart using this http://bl.ocks.org/d3noob/6eb506b129f585ce5c8a code example. I've managed to recreate it.
that code is
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
body { font: 12px Arial;}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>
<body>
<!-- load the d3.js library -->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%d-%b-%y").parse,
formatDate = d3.time.format("%d-%b"),
bisectDate = d3.bisector(function(d) { return d.date; }).left;
// Set the ranges
// var x = d3.time.scale().range([0, width]);
var x = d3.scale.ordinal().rangePoints([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
// Define the line
var valueline = d3.svg.line()
.interpolate('basis')
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
// Adds the svg canvas
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var lineSvg = svg.append("g");
var focus = svg.append("g")
.style("display", "none");
// Get the data
var data = [
{
"date": "w1",
"close": 629.32
},
{
"date": "w2",
"close": 124.31
},
{
"date": "w3",
"close": 333.68
},
{
"date": "w4",
"close": 236.23
}
]
// data.forEach(function(d) {
// // d.date = parseDate(d.date);
// d.date = +d.date;
// d.close = +d.close;
// });
// Scale the range of the data
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
// Add the valueline path.
lineSvg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// append the x line
focus.append("line")
.attr("class", "x")
.style("stroke", "blue")
.style("stroke-dasharray", "3,3")
.style("opacity", 0.5)
.attr("y1", 0)
.attr("y2", height);
// append the y line
focus.append("line")
.attr("class", "y")
.style("stroke", "blue")
.style("stroke-dasharray", "3,3")
.style("opacity", 0.5)
.attr("x1", width)
.attr("x2", width);
// append the circle at the intersection
focus.append("circle")
.attr("class", "y")
.style("fill", "none")
.style("stroke", "blue")
.attr("r", 4);
// place the value at the intersection
focus.append("text")
.attr("class", "y1")
.style("stroke", "white")
.style("stroke-width", "3.5px")
.style("opacity", 0.8)
.attr("dx", 8)
.attr("dy", "-.3em");
focus.append("text")
.attr("class", "y2")
.attr("dx", 8)
.attr("dy", "-.3em");
// place the date at the intersection
focus.append("text")
.attr("class", "y3")
.style("stroke", "white")
.style("stroke-width", "3.5px")
.style("opacity", 0.8)
.attr("dx", 8)
.attr("dy", "1em");
focus.append("text")
.attr("class", "y4")
.attr("dx", 8)
.attr("dy", "1em");
// append the rectangle to capture mouse
svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all")
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemove);
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
focus.select("circle.y")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")");
focus.select("text.y1")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.text(d.close);
focus.select("text.y2")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.text(d.close);
focus.select("text.y3")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.text(formatDate(d.date));
focus.select("text.y4")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.text(formatDate(d.date));
focus.select(".x")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.attr("y2", height - y(d.close));
focus.select(".y")
.attr("transform",
"translate(" + width * -1 + "," +
y(d.close) + ")")
.attr("x2", width + width);
}
</script>
</body>
line is coming correctly but i m not able to set mouseover tooltip like above example..
I m facing error like invert fuction is not defined...
In the chart you linked to, he is using a time-scale for the x-axis (which has an invert-function). You are using an ordinal-scale (which do not have an invert-function).The invert-function is used to calculate the value on the x-axis for a given mouse-position.
A time-scale always has a corresponding x-value for each mouse-position (since it is continous, so no matter where your mouse is, you have a date-time for that position) while an ordinal scale does not have a corresponding x-value for all mouse-positions since it is discrete, i.e. what is the x-value when you have the mouse in-beteen for example w1 and w2?
So your solution would be to change to a time-scale (in which case you have to convert w1,w2,w3 e.t.c. to a date-time object).
Or, if you want to stick with your ordinal-scale, you have to remove the invert function. Since the invert-function is used to calculate the x-value for a given mouse-position, you have to create this logic by your self. Inspiration can be found in Inversion with ordinal scale. So replace var x0 = x.invert(d3.mouse(this)[0]) with
var xPos = d3.mouse(this)[0];
console.log("hovering at " + xPos);
var leftEdges = x.range();
var width = x.rangeBand();
var j;
for(j=0; xPos > (leftEdges[j] + width); j++) {}
//do nothing, just increment j until case fails
console.log("Clicked on " + x.domain()[j]);
var x0 = x.domain()[j];

How to implement a jointplot using d3js

I'm working on a a visualisation project using d3js. I have some distributed dataset that I need to visualise like that :
this visualisation was done using seaborn.jointplot in python, but I need this to be implemented using d3js, is there any alternative for that in d3js? how can this be done in d3js ?
seaborn.jointplot API Documentation
Cool looking plot and I had some time this morning, so here's a d3 v4 minimal implementation:
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
// set the dimensions and margins of the graph
var margin = {
top: 80,
right: 80,
bottom: 20,
left: 20
},
width = 400 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
var g = svg.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleLinear().range([0, width]).domain([0, 10]),
y = d3.scaleLinear().range([height, 0]).domain([0, 10]);
// Add the X Axis
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add the Y Axis
g.append("g")
.call(d3.axisLeft(y));
var random = d3.randomNormal(0, 1.2),
data = d3.range(100).map(function() {
return [random() + 5, random() + 5];
});
g.selectAll(".point")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return x(d[0]);
})
.attr("cy", function(d) {
return y(d[1]);
})
.attr("r", 7)
.style("fill", "steelblue")
.style("stroke", "lightgray");
// top histogram
var gTop = svg.append("g")
.attr("transform",
"translate(" + margin.left + "," + 0 + ")");
var xBins = d3.histogram()
.domain(x.domain())
.thresholds(x.ticks(10))
.value(function(d) {
return d[0];
})(data);
var xy = d3.scaleLinear()
.domain([0, d3.max(xBins, function(d) {
return d.length;
})])
.range([margin.top, 0]);
var xBar = gTop.selectAll(".bar")
.data(xBins)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) {
return "translate(" + x(d.x0) + "," + xy(d.length) + ")";
});
var bWidth = x(xBins[0].x1) - x(xBins[0].x0) - 1;
xBar.append("rect")
.attr("x", 1)
.attr("width", bWidth)
.attr("height", function(d) {
return margin.top - xy(d.length);
})
.style("fill", "steelblue");
xBar.append("text")
.attr("dy", ".75em")
.attr("y", 2)
.attr("x", bWidth / 2)
.attr("text-anchor", "middle")
.text(function(d) {
return d.length < 4 ? "" : d.length;
})
.style("fill", "white")
.style("font", "9px sans-serif");
// right histogram
var gRight = svg.append("g")
.attr("transform",
"translate(" + (margin.left + width) + "," + margin.top + ")");
var yBins = d3.histogram()
.domain(y.domain())
.thresholds(y.ticks(10))
.value(function(d) {
return d[1];
})(data);
var yx = d3.scaleLinear()
.domain([0, d3.max(yBins, function(d) {
return d.length;
})])
.range([0, margin.right]);
var yBar = gRight.selectAll(".bar")
.data(yBins)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) {
return "translate(" + 0 + "," + y(d.x1) + ")";
});
var bWidth = y(yBins[0].x0) - y(yBins[0].x1) - 1;
yBar.append("rect")
.attr("y", 1)
.attr("width", function(d) {
return yx(d.length);
})
.attr("height", bWidth)
.style("fill", "steelblue");
yBar.append("text")
.attr("dx", "-.75em")
.attr("y", bWidth / 2 + 1)
.attr("x", function(d) {
return yx(d.length);
})
.attr("text-anchor", "middle")
.text(function(d) {
return d.length < 4 ? "" : d.length;
})
.style("fill", "white")
.style("font", "9px sans-serif");
</script>
</body>
</html>
d3.js version 3 sample:
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
</style>
</head>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
// set the dimensions and margins of the graph
var margin = {
top: 80,
right: 80,
bottom: 20,
left: 20
},
width = 400 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
var g = svg.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var x = d3.scale.linear().range([0, width]).domain([0, 10]),
y = d3.scale.linear().range([height, 0]).domain([0, 10]);
// Add the X Axis
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.svg.axis().orient("bottom").scale(x));
// Add the Y Axis
g.append("g")
.attr("class", "axis")
.call(d3.svg.axis().orient("left").scale(y));
var random = d3.random.normal(0, 1.2),
data = d3.range(100).map(function() {
return [random() + 5, random() + 5];
});
g.selectAll(".point")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return x(d[0]);
})
.attr("cy", function(d) {
return y(d[1]);
})
.attr("r", 7)
.style("fill", "steelblue")
.style("stroke", "lightgray");
// top histogram
var gTop = svg.append("g")
.attr("transform",
"translate(" + margin.left + "," + 0 + ")");
var xBins = d3.layout.histogram()
.range(x.domain())
.bins(x.ticks(10))
.value(function(d) {
return d[0];
})(data);
var xy = d3.scale.linear()
.domain([0, d3.max(xBins, function(d) {
return d.length;
})])
.range([margin.top, 0]);
var xBar = gTop.selectAll(".bar")
.data(xBins)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) {
return "translate(" + x(d.x) + "," + xy(d.length) + ")";
});
var bWidth = x(xBins[0].dx) - 1;
xBar.append("rect")
.attr("x", 1)
.attr("width", bWidth)
.attr("height", function(d) {
return margin.top - xy(d.length);
})
.style("fill", "steelblue");
xBar.append("text")
.attr("dy", ".75em")
.attr("y", 2)
.attr("x", bWidth / 2)
.attr("text-anchor", "middle")
.text(function(d) {
return d.length < 4 ? "" : d.length;
})
.style("fill", "white")
.style("font", "9px sans-serif");
// right histogram
var gRight = svg.append("g")
.attr("transform",
"translate(" + (margin.left + width) + "," + margin.top + ")");
var yBins = d3.layout.histogram()
.range(y.domain())
.bins(y.ticks(10))
.value(function(d) {
return d[1];
})(data);
console.log(yBins);
var yx = d3.scale.linear()
.domain([0, d3.max(yBins, function(d) {
return d.length;
})])
.range([0, margin.right]);
var yBar = gRight.selectAll(".bar")
.data(yBins)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) {
return "translate(" + 0 + "," + y(d.x + d.dx) + ")";
});
yBar.append("rect")
.attr("y", 1)
.attr("width", function(d) {
return yx(d.length);
})
.attr("height", bWidth)
.style("fill", "steelblue");
yBar.append("text")
.attr("dx", "-.75em")
.attr("y", bWidth / 2 + 1)
.attr("x", function(d) {
return yx(d.length);
})
.attr("text-anchor", "middle")
.text(function(d) {
return d.length < 4 ? "" : d.length;
})
.style("fill", "white")
.style("font", "9px sans-serif");
</script>
</body>
</html>

D3 Zooming in graph

i have tried using D3.zoom behavior in my scatterplot graph.
But the problem i am having now is that, i can only zoom my graph ONLY when i mouseover my DOT.
Any reasons why? I want it to be able to zoom in anywhere as long as my mouse is pointing inside the graph.
And also, my X axis doesn't stays at the bottom... it gets zoomed in as well
Is it possible to just let the X Axis stay constant at the bottom?
here are my codes...
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 650 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S").parse;
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var zoom = d3.behavior.zoom()
.scaleExtent([0.95, 10])
.on("zoom", zoomed);
var drag = d3.behavior.drag()
.origin(function(d) { return d; })
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var valueline = d3.svg.line()
.x(function(d) { return x(d.ArtDateTime); })
.y(function(d) { return y(d.Ranking); });
var svg = d3.select(".graph")
.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 + ")")
.call(zoom);
function zoomed() {
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
svg.selectAll("g")
.attr("width", width)
.attr("height", height);
}
function make_x_axis() {
return d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(4)
}
function make_y_axis() {
return d3.svg.axis()
.scale(y)
.orient("left")
.ticks(4)
}
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.select(this).classed("dragging", true);
}
function dragged(d) {
d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
}
function dragended(d) {
d3.select(this).classed("dragging", false);
}
d3.csv("FinalCSVFile.csv", function(error, data) {
data.forEach(function(d) {
d.ArtDateTime = parseDate(d.ArtDateTime);
d.Ranking = +d.Ranking;
});
x.domain(d3.extent(data, function(d) { return d.ArtDateTime; }));
y.domain([0, d3.max(data, function(d) { return d.Ranking; })]);
// Add the X Axis
svg.append("g")
.transition()
.duration(300)
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append('g')
.transition()
.duration(300)
.attr('class', 'grid')
.attr('transform', 'translate(0,' + height + ")")
.call(make_x_axis()
.tickSize(-height, 0, 0)
.tickFormat("")
)
svg.append('g')
.transition()
.duration(300)
.attr("class", "grid")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat("")
)
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 5)
.attr("cx", function(d) { return x(d.ArtDateTime);})
.attr("cy", function(d) { return y(d.Ranking * 3); })
.on("mouseover", function(d) {
var xPosition = parseFloat(d3.select(this).attr("cx"));
var yPosition = parseFloat(d3.select(this).attr("cy"));
d3.select(this)
.transition()
.duration(20)
.style("fill", "red");
d3.select("#box")
.transition()
.duration(300)
.style("left", xPosition + 80 + "px")
.style("top", yPosition + 140 +"px" )
.select("#ranking")
.text(d.Ranking)
d3.select("#box")
.select("#startDT")
.text(d.startDateTime)
d3.select("#box")
.select("#senCONT")
.text(d.sentenceContent)
d3.select("#tooltip")
.classed("hidden", false);
})
.on("mouseout", function() {
d3.select("#tooltip")
.classed("hidden", true);
});
});
The reason you can't zoom in on the white areas is because there is actually nothing there to zoom in on. Paste this code below into your jsfiddle example at line 39 (after you've created the svg variable) to append a background rectangle behind your graph that you can zoom in on.
svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "#fff");

Categories

Resources