In d3.js v4 zoom functionality I am having an issue where when i zoom out and then pan the chart goes out of the viewport instantly - zooming

Here's my code:
var zoom = d3.zoom()
.scaleExtent([0.3, 10])
.on("zoom", zoomed);
function zoomed() {
svg.attr("transform", function(d){
var transform = d3.zoomTransform(this);
return transform;
});
}
var svg = d3.select("#app")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.call(zoom);
var rect = svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all");
There is no problem if the scaleExtent is [1,10]. The problem only occurs when it is below 1 for the x. If you pan after zooming below 1 the chart translates to some very high value and disappears from the viewport. And its not possible to bring it back.
I have gone through the api docs and didnt find any solution. Please help.
Your help will highly appreciated.

Instead of panning and zooming on the group which holds the rectangle.
svg.attr("transform", function(d){
var transform = d3.zoomTransform(this);
return transform;
});
do transform on the rectangle
function zoomed() {
rect.attr("transform", d3.event.transform);
}
working code here
Or
Better if you add another group(say layer). And in that group add the rectangle. Then apply transform on the group(layer) like this
EDIT
Since you dont want the rectangle to zoom and pan but be a reciever to all mouse events.
Add the rectangle to the svg group like below:
var rect = svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "red")
.style("pointer-events", "all");
Now create a layer
var layer = svg.append("g");
Add all that you want to zoom and pan in the new layer:
var circle = layer.append("circle")
.attr("cx", 100 )
.attr("cy", 10)
.attr("r", 10)
.style("fill", "blue")
.style("pointer-events", "all");
var circle = layer.append("circle")
.attr("cx", 100 )
.attr("cy", 50)
.attr("r", 10)
.style("fill", "blue")
.style("pointer-events", "all");
var circle = layer.append("circle")
.attr("cx", 300 )
.attr("cy", 50)
.attr("r", 10)
.style("fill", "blue")
.style("pointer-events", "all");
on zoom + pan apply the transform on the group like this
function zoomed() {
layer.attr("transform", d3.event.transform);
}
code is here

Related

Using Ajax for real time graphical display

I am doing a sample project using d3js sample from bl.ocks.org to plot graph. There is a csv file which is continuously updated. My task is to read it every second and update the plot. I tried to look up ajax example to add into the d3. However bl.ocks.org did not have what I needed. Can anyone please let me know a good ajax example which I can integrate with my application.
--> next task is to get csv off and have a restful service to read data. Then it is to be parsed and graph is to be updated periodically.
Thanks in advance!
<!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;
// Set the ranges
var x = d3.time.scale().range([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()
.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 + ")");
// Get the data
d3.csv("data.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
// Add the valueline path.
svg.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);
});
</script>
</body>

Legend text background change on mouse-hover over d3 doughnut chart

I have one doughnut chart whose values display on mouse-hover. But, I want to change the legend text background while hovering over the respective doughnut chart area.
return {
restrict: 'E',
scope: {
values: '='
},
link: function (scope, element, attrs) {
scope.$watch('values', function(values) {
var data = [];
if(values) {
console.log('values from directive: ', values);
var w = 700,
h = 700,
r = 290,
inner = 190,
color = d3.scale.category20();
data=values;
var total = d3.sum(data, function(d) {
return d3.sum(d3.values(d));
});
var vis = d3.select("#pieChartsD3")
.append("svg:svg")
.data([data])
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(" +320+ "," +320+ ")")
var textTop = vis.append("text")
.attr("dy", ".35em")
.style("text-anchor", "middle")
.attr("class", "textTop")
.text( "TOTAL" )
.attr("y", -10),
textBottom = vis.append("text")
.attr("dy", ".35em")
.style("text-anchor", "middle")
.attr("class", "textBottom")
.text(total.toFixed(0) )
.attr("y", 10);
var arc = d3.svg.arc()
.innerRadius(inner)
.outerRadius(r);
var arcOver = d3.svg.arc()
.innerRadius(inner + 20)
.outerRadius(r + 20);
var pie = d3.layout.pie()
.value(function(d) { return d.val; });
var arcs = vis.selectAll("g.slice")
.data(pie)
.enter()
.append("svg:g")
.attr("class", "slice")
.style('word-wrap', 'break-word')
.on("mouseover", function(d) {
d3.select(this).select("path").transition()
.duration(200)
.attr("d", arcOver)
textTop.text(d3.select(this).datum().data.name)
.style('fill', 'red')
.attr("y", -10);
textBottom.text(d3.select(this).datum().data.val.toFixed(0))
.style('fill', 'blue')
.attr("y", 10);
})
.on("mouseout", function(d) {
d3.select(this).select("path").transition()
.duration(100)
.attr("d", arc);
textTop.text( "TOTAL" )
.attr("y", -10);
textBottom.text(total.toFixed(0));
});
arcs.append("svg:path")
.attr("fill", function(d, i) { return color(i); } )
.attr("d", arc);
console.log("datas length: "+data.length)
var legend = d3.select("#legend").append("svg")
.attr("class", "legend")
.attr("width", 400)
.attr("height", 20*data.length)
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) { return color(i); });
legend.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
}
})
}
}
(BTW I'm inferring about your request because you haven't explicitly stated a question. With that said...)
Inside the .on("mouseover", function(d) { block, add another section:
legend.filter(function(l){
l === d; // remember d is the value bound to the donut segment
})
.attr("fill","yellow")
And be sure to write the logic to undo it, by setting fill back to grey or none, in .on("mouseout".

Unable to create bar chart from d3 using URL

I am trying to create bar chart from json file which is a URL but i am unable to do so please help me, as i am new to d3. The code is here please tell me where i am making mistake.
var width = 960,
height = 500;
var y = d3.scale.linear().range([height, 0]);
var chart = d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height);
generateChart();
function generateChart() {
d3.json('http://dataserver.org:8009/topic/domain/cancer',
function(error, data) {
y.domain([0, d3.max(data, function(d) {
return d.occurrence
})]);
var barWidth = width / data.length; //width of each bar
var bar = chart.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d, i) {
return "translate(" + i * barWidth + ", 0)";
});
bar.append("rect")
.attr("y", function(d) {
return y(d.occurrence);
})
.attr("height", function(d) {
return height - y(d.occurrence);
})
.attr("width", barWidth - 1);
bar.append("text")
.attr("x", barWidth / 2)
.attr("y", function(d) {
return y(d.occurrence) - 10;
})
.attr("dy", ".75em")
.text(function(d) {
return d.authorName;
});
});
};
It is not a problem of cross domain as I checked your API. Assuming that you are fetching d3.js from CDN, I am suggesting a change. Your chart will depend on "institute" as it is containing all the required values for chart. So I changed it as per structure your API's response.
**in some case data was coming as "undefined" so I am fetching d3.js from CDN
Here is whole modified file:-
function generateChart() {
d3.json('http://srvgal78.deri.ie:8009/topic/domain/cancer',
function(error, data) {
data = data.institute;
y.domain([0, d3.max(data, function(d) {
return d.occurrence
})]);
var barWidth = width / data.length; //width of each bar
var bar = chart.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d, i) {
return "translate(" + i * barWidth + ", 0)";
});
bar.append("rect")
.attr("y", function(d) {
return y(d.occurrence);
})
.attr("height", function(d) {
return height - y(d.occurrence);
})
.attr("width", barWidth - 1);
bar.append("text")
.attr("x", barWidth / 2)
.attr("y", function(d) {
return y(d.occurrence) - 10;
})
.attr("dy", ".75em")
.text(function(d) {
return d.authorName;
});
});
};

D3 chart with JSON web service

I am trying to make the following bar chart from this tutorial. The tutorial utilizes TSV files, but I have modified the code for JSON. I have checked that the endpoint http://localhost:3000/graphs/data from my Node / Express service I created is indeed returning JSON, which can be seen below. The proper D3 libraries are also being included. After checking all this, I cannot get the chart to render.
The goal is to have route on the x-axis and count on the y-axis. Any suggestions would be greatly appreciated.
JSON Response:
[{"route":"9","count":273},{"route":"49","count":242},{"route":"151","count":221},{"route":"8","count":220},{"route":"3","count":213},{"route":"82","count":209},{"route":"79","count":206},{"route":"N5","count":206},{"route":"62","count":206},{"route":"4","count":202}]
Bar Chart Code
<script>
var margin = {top: 40, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var formatPercent = d3.format(".0%");
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
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");
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Count:</strong> <span style='color:red'>" + d.count + "</span>";
})
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 + ")");
svg.call(tip);
d3.json('http://localhost:3000/graphs/data', type, function(error, data) {
x.domain(data.map(function(d) { return d.route; }));
y.domain([0, d3.max(data, function(d) { return d.count; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.route); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.count); })
.attr("height", function(d) { return height - y(d.count); })
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
});
function type(d) {
d.count = +d.count;
return d;
}
</script>
d3.json(), unlike d3.csv(), takes only two arguments, with the second being the callback function. Your call
d3.json('http://localhost:3000/graphs/data', type, function(error, data) {
passes the result of the call to type, not to the anonymous function after that, which is never executed. The call should be
d3.json('http://localhost:3000/graphs/data', function(error, data) {

D3 update line graph

I created a line graph using a csv file and now I am trying to update this graph using new data from the same csv. However when I open the file in firefox I am getting an error: ReferenceError: updateData is not defined.
Here is my code:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="d3/d3.min.js"></script>
</head>
<body>
<div id="option">
<input name="updateButton"
type="button"
value="Update"
onclick="updateData()" />
</div>
<script type="text/javascript">
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 150},
width = 1000 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.tickFormat(d3.format(".0f"))
.orient("bottom");
var yAxis = d3.svg.axis().scale(y)
.orient("left")
.ticks(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.Year); })
.y(function(d) { return y(d.AttendancePerG); });
// 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 + ")");
// Get the data
d3.csv("piratesAttendance.csv", function(error, data) {
data.forEach(function(d) {
d.Year = +d.Year;
d.AttendancePerG = +d.AttendancePerG;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.Year; }));
y.domain([0, d3.max(data, function(d) { return d.AttendancePerG; })]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data))
.attr("stroke", "black")
.attr("stroke-width",2)
.attr("fill","none");
// 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);
function updateData() {
// Get the data again
d3.tsv("pittsburghAttendance", function(error, data) {
data.forEach(function(d) {
d.Year = +d.Year;
d.Attendance = +d.Attendance;
});
// Scale the range of the data again
x.domain(d3.extent(data, function(d) { return d.Year; }));
y.domain([0, d3.max(data, function(d) { return d.Attendance; })]);
// Select the section we want to apply our changes to
var svg = d3.select("body").transition();
// Make the changes
svg.select(".line") // change the line
.duration(750)
.attr("class", "line")
.attr("d", valueline(data));
svg.select(".x.axis") // change the x axis
.duration(750)
.call(xAxis);
svg.select(".y.axis") // change the y axis
.duration(750)
.call(yAxis);
});
}
});
</script>
</body>
</html>
Your function updateData is defined in a callback, so it will not be in the "global" scope of your page, hence you cannot invoke it from the HTML.
Try to simply move its declaration to the beginning (or end) of your <script> tag.
Oh and also your usage of d3.csv seems incorrect.