I am trying to do zoom on axis in scatter plot chart,this is what I have tried
var zoom = d3.zoom()
.scaleExtent([1, 5])
.extent([100, 100], [width - 100, height - 100])
.on("zoom", zoomed);
var gX;
var gY;
xScale = d3.scaleLinear().range([0, width])
yScale = d3.scaleLinear().range([height, 0])
function zoomed() {
svg.selectAll(".charts")
.attr("transform", d3.event.transform);
d3.selectAll('.dot').style("stroke-width", 2 / d3.event.transform.k);
gX.call(xAxis.scale(d3.event.transform.rescaleX(xScale)));
gY.call(yAxis.scale(d3.event.transform.rescaleY(yScale)));
}
and I appended gX and gY like below
gX = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
gY = svg.append("g")
.attr("class", "axis axis--y")
.call(yAxis)
When I do zoom on circle width is getting zoomed not axis, I am guessing something i am doing wrong on gX.call & gY.call which I couldn't figure. Any suggestions??
I did something like this....
Call zoom on select like this
var svg = d3.select("div#ScatterPlot svg").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(d3.zoom().on("zoom", zoom));
var gX = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
var gY = svg.append("g")
.attr("class", "y axis")
.call(yAxis)
var circles = svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")....
function zoom() {
gX.call(xAxis.scale(d3.event.transform.rescaleX(xScale)));
gY.call(yAxis.scale(d3.event.transform.rescaleY(yScale)));
var new_XScale = d3.event.transform.rescaleX(xScale);
var new_yScale = d3.event.transform.rescaleY(yScale);
circles.attr("cx", function (d) { return new_XScale(d.xAxisValue) });
circles.attr("cy", function (d) { return new_yScale(d.yAxisValue) });
}
Related
I've built a page with five d3.js charts. Four of them use standard linear axes (linear data over time), and the fifth chart uses log/log axes. So far, I can get either the linear charts to render correctly, or the log/log chart to render correctly, but not both.
Question -- how can I define log/log axes for the final chart while keeping the first four linear charts working correctly?
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
// Overall chart settings
var divwidth = $("#collapseOne").width();
var divheight = $(window).height();
var margin = {top: 25, right: 60, bottom: 35, left: 80},
width = divwidth - margin.left - margin.right,
height = 0.65*divheight - margin.top - margin.bottom;
var formatDate = d3.time.format("%Y-%m-%d");
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
</script>
Then I define the first chart:
<script>
// Chart1 - NumofCompanies chart settings
var line1 = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.cumcos); });
var chart1 = d3.select("#collapseOne")
.append("div")
.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 + ")");
d3.tsv("/monthly.tsv", type, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain(d3.extent(data, function(d) { return d.cumcos; }));
chart1.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart1.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("Number of companies");
chart1.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line1);
});
function type(d) {
d.date = formatDate.parse(d.date);
d.cumcos = +d.cumcos;
return d;
}
</script>
and then the log/log chart
<script>
// Chart5 - Log/log chart
var x = d3.scale.log()
.range([0, width]);
var y = d3.scale.log()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line5 = d3.svg.line()
.x(function(de) { return x(de.ordernum); })
.y(function(de) { return y(de.sumfunding); });
var chart5 = d3.select("#collapseFive").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 + ")");
d3.tsv("/loglog.tsv", type, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(de) { return de.ordernum; }));
y.domain(d3.extent(data, function(de) { return de.sumfunding; }));
chart5.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart5.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("Sum of Funding ($)");
chart5.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line5);
});
function type(de) {
de.ordernum = +de.ordernum;
de.sumfunding = +de.sumfunding;
return de;
}
</script>
edit - I've also tried renaming variables like this below and it still doesn't work; the axes are still linear. And when I try changing things like x.domain to xlog.domain and .x(function(de to .xlog(function(de then nothing on the chart renders at all.
<script>
// Chart5 - Log/log chart
var xlog = d3.scale.log()
.range([0, width]);
var ylog = d3.scale.log()
.range([height, 0]);
var xAxislog = d3.svg.axis()
.scale(xlog)
.orient("bottom");
var yAxislog = d3.svg.axis()
.scale(ylog)
.orient("left");
var line5 = d3.svg.line()
.x(function(de) { return x(de.ordernum); })
.y(function(de) { return y(de.sumfunding); });
var chart5 = d3.select("#collapseFive").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 + ")");
d3.tsv("/loglog.tsv", type, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(de) { return de.ordernum; }));
y.domain(d3.extent(data, function(de) { return de.sumfunding; }));
chart5.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxislog);
chart5.append("g")
.attr("class", "y axis")
.call(yAxislog)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Sum of Funding ($)");
chart5.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line5);
});
function type(de) {
de.ordernum = +de.ordernum;
de.sumfunding = +de.sumfunding;
return de;
}
</script>
You are mistaking the first x in the line generator with the variable x in your scale:
var line5 = d3.svg.line()
.x(function(de) { return x(de.ordernum); })
//^--this has to be "x" ^--this is the scale
So, it has to be:
var line5 = d3.svg.line()
.x(function(de) { return xLog(de.ordernum); })
.y(function(de) { return yLog(de.sumfunding); });
According to the API of version 3:
line.x - get or set the x-coordinate accessor.
line.y - get or set the y-coordinate accessor.
As a good practice, avoid variable names like x. Instead, use names like xScale, xAxis, xPosition etc.
I did a graph where my x-axis is out of the range in svg element.
I am using object structure ProgressGraph to save my options (because of another things...)
I would like to set x-axis to fixed size 720x450, but when I call zoom or I want to translate graph to right / left , the X-axis is going out of the svg element. The x-axis size width is changing from 720 to 920.53 and I do not know why. This width should be fixed to 720px.
Screenshots:
Good One good one before zoom / move to side
Bad One bad one after zoom / move to side
graphParams : {
//whole size
width : 1050,
height : 600,
//svg -g
svg_width: 720,
svg_height : 450
}
x = d3.time.scale().domain(ProgressGraph.xAxisDomain).range([0,graphParams.svg_width]);
xAxis =
d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(-graphParams.svg_height)
.tickPadding(12)
.ticks(12);
yAxis =
d3.svg.axis()
.scale(y)
.orient("left")
.tickSize(-graphParams.svg_width);
zoom =
d3.behavior.zoom()
.x(x)
.scaleExtent([1, 32])
.on("zoom", zoomed);
var svg = d3.select("#projectProgress-graph-div").append("svg")
.attr("width", graphParams.width)
.attr("height", graphParams.height)
.append("g")
.attr("id", "projectProgress-graph-svg")
.attr("transform", "translate(" + (graphMargin.left) + "," + graphMargin.top + ")")
.call(zoom);
svg.append("rect")
.attr("width", graphParams.svg_width )
.attr("height", graphParams.svg_height);
svg.append("g")
.attr("class", "x axis")
.attr("width", graphParams..svg_width)
.attr("transform", "translate(0, " + graphParams.svg_height + ")")
.call(xAxis)
.selectAll("text")
.attr("dx", "-2em")
.attr("dy", ".0em")
.attr("transform", "rotate(-65)" );
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
function zoomed() {
svg.select(".x.axis").call(xAxis)
.selectAll("text")
.attr("dx", "-2em")
.attr("dy", "0em")
.attr("transform", "rotate(-65)" );
}
Sollution is add a svg element befero we add "g element" for x axis
svg.append("svg")
.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0 , " + graphParams.svg_height + ")")
.call(xAxis)
.selectAll("text")
.attr("dx", "-2em")
.attr("dy", ".0em")
.attr("transform", "rotate(-65)" );
Fiddle Example:
Can anyone point me to the right direction on how to scale all the circles on the line graph correspondingly when panning and zooming on the chart? Can I use d3.event.scale to resize the circle and calculate the new cx and cy?
function zoomed() {
console.log(d3.event.translate);
console.log(d3.event.scale);
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
svg.select(".x.grid")
.call(make_x_axis()
.tickSize(-height, 0, 0)
.tickFormat(""));
svg.select(".y.grid")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat(""));
svg.select(".line")
.attr("class", "line")
.attr("d", line);
svg.selectAll("circle").attr("cy",function(){
??
}
.attr("cx",function(){
}
}
Here's the full code:
function line_chart(field,el){
margin = {
top: 20,
right: 20,
bottom: 20,
left: 45
};
tickno = 8;
width = 960 - margin.left - margin.right;
height = 400 - margin.top - margin.bottom;
var x = d3.scale.linear().domain(d3.extent(data, function (d) {
return d.item;
}))
.range([0, width]);
var ymax = d3.max(data,function(d){
return (parseInt(d[field])+1);
});
var ymin = d3.min(data,function(d){
return d[field];
});
var xmax = d3.max(data,function(d){
return d.name;
});
var y = d3.scale.linear()
.domain([ymin,ymax])
.range([height, 0]);
var line = d3.svg.line()
.x(function (d) {
return x(d.item);
})
.y(function (d) {
return y(d[field]);
});
var zoom = d3.behavior.zoom()
.x(x)
.scaleExtent([-1, 2])
.y(y)
.on("zoom", zoomed);
svg = d3.select(el)
.append("svg:svg")
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append("svg:g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
svg.append("svg:rect")
.attr("width", width)
.attr("height", height)
.attr("class", "plot");
var make_x_axis = function () {
return d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(tickno);
};
var make_y_axis = function () {
return d3.svg.axis()
.scale(y)
.orient("left")
.ticks(tickno);
};
svg.selectAll("dot").data(data).enter().append("circle").attr("r",3.5)
.attr("cx",function(d){return x(d.item);})
.attr("cy",function(d){return y(d[field]);})
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(tickno);
svg.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0, " + height + ")")
.call(xAxis);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(tickno);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "x grid")
.attr("transform", "translate(0," + height + ")")
.call(make_x_axis()
.tickSize(-height, 0, 0)
.tickFormat(""));
svg.append("g")
.attr("class", "y grid")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat(""));
var clip = svg.append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height);
var chartBody = svg.append("g")
.attr("clip-path", "url(#clip)");
chartBody.append("svg:path")
.datum(data)
.attr("class", "line")
.attr("d", line);
function zoomed() {
console.log(d3.event.translate);
console.log(d3.event.scale);
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
svg.select(".x.grid")
.call(make_x_axis()
.tickSize(-height, 0, 0)
.tickFormat(""));
svg.select(".y.grid")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat(""));
svg.select(".line")
.attr("class", "line")
.attr("d", line);
svg.select("circle").attr("cy",function
}
}
line_chart('diameter','#area')
First, wrap your "dots" in a g:
svg.append('g')
.attr('class','dots')
.selectAll(".dot")
.data(data)
.enter()
...
Then, transform that g in your zoom:
svg.select(".dots")
.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
To adjust the radius, multiply your base radius by scale:
svg.selectAll(".dots circle").attr("r", function(){
return (3.5 * d3.event.scale);
});
Updated fiddle here.
I am working on a multi-series line chart using d3.js and I am attempting to implement the focus and context zoom as seen in this example. I have converted the area chart in the example to a line chart with a single series but I cannot figure out how to extend it to allow multiple series.
Here is the code I am using:
var margin = {top: 10, right: 10, bottom: 100, left: 40},
margin2 = {top: 430, right: 10, bottom: 20, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
height2 = 500 - margin2.top - margin2.bottom;
var parseDate = d3.time.format("%b %Y").parse;
var x = d3.time.scale().range([0, width]),
x2 = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]),
y2 = d3.scale.linear().range([height2, 0]);
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", brushed);
var line = d3.svg.line()
.interpolate("monotone")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.price); }); // single series?
var line2 = d3.svg.line()
.interpolate("monotone")
.x(function(d) { return x2(d.date); })
.y(function(d) { return y2(d.price); });
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("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
d3.csv("trends.csv", type, function(error, data) {
x.domain(d3.extent(data.map(function(d) { return d.date; })));
y.domain([0, d3.max(data.map(function(d) { return d.price; }))]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "y axis")
.call(yAxis);
context.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line2);
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.select(".line").attr("d", line);
focus.select(".x.axis").call(xAxis);
}
function type(d) {
d.date = parseDate(d.date);
d.price = +d.price;
return d;
}
I was successful in building a multi-line chart without the focus+context zoom feature, but I would really like to get the zoom piece working if possible.
Any help is greatly appreciated!
Assuming from your data that you have multiple series for price, you would have to change d.price to d[price]. Did you check this stack overflow question? I think it's what you're looking for.
I have implemented the Drawing Focus + Context via Brushing diagram to display the chi-square results, but the drawing of the results in d3 are strange (check image below at 7 AM).
Also below the image I have included the code which draws the diagram.
ChiSquare Image
var chiSquare = function(config,data,d3){
var margin = {top: 10, right: 10, bottom: 100, left: 40},
margin2 = {top: 430, right: 10, bottom: 20, left: 40},
width = 1127 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
height2 = 500 - margin2.top - margin2.bottom;
var parseDate = d3.time.format("%b %Y").parse;
var x = d3.time.scale().range([0, width]),
x2 = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]),
y2 = d3.scale.linear().range([height2, 0]);
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", brushed);
var area = d3.svg.area()
.interpolate("monotone")
.x(function(d) { return x(d.timestamp_unix); })
.y0(height)
.y1(function(d) { return y(d.chiSquare); });
var area2 = d3.svg.area()
.interpolate("monotone")
.x(function(d) { return x2(d.timestamp_unix); })
.y0(height2)
.y1(function(d) {return y2(d.chiSquare); });
var svg = d3.select(config.selector).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("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
x.domain(d3.extent(data,(function(d) { return d.timestamp_unix; })));
y.domain([0, d3.max(data, (function(d) { return d.chiSquare; }))]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "y axis")
.call(yAxis);
context.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area2);
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.select(".area").attr("d", area);
focus.select(".x.axis").call(xAxis);
}
function type(d) {
d.timestamp_unix = parseDate(d.timestamp_unix);
d.chiSquare = +d.chiSquare;
return d;
}
//end class
}
I can't see anything wrong in the code, so I assume that the problem is from D3js but I don't know where to look, any ideas?