D3 v4 pan with mouse wheel - javascript

How can I pan with the mouse wheel using d3.js version 4.
I found this example using v3, but it does not work with v4.
Example link
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<!DOCTYPE html>
<meta charset="utf-8">
<title>D3.js: Panning with mouse wheel</title>
<style>
.overlay {
fill: none;
pointer-events: all;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var width = 960,
height = 500;
var randomX = d3.random.normal(width / 2, 80),
randomY = d3.random.normal(height / 2, 80);
var data = d3.range(2000).map(function() {
return [
randomX(),
randomY()
];
});
var zoomer = d3.behavior.zoom()
.on("zoom", zoom)
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.call(zoomer)
.on("wheel.zoom",pan)
.append("g");
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height);
svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("r", 2.5)
.attr("transform", function(d) { return "translate(" + d + ")"; });
function zoom() {
console.log(d3.select(this))
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
function pan() {
current_translate = d3.transform(svg.attr("transform")).translate;
dx = d3.event.wheelDeltaX + current_translate[0];
dy = d3.event.wheelDeltaY + current_translate[1];
svg.attr("transform", "translate(" + [dx,dy] + ")");
d3.event.stopPropagation();
}
</script>

I had the same need and I figured out the changes required in D3v4 (below). I also posted to Blocks here.
var width = 960,
height = 500;
var randomX = d3.randomNormal(width / 2, 80),
randomY = d3.randomNormal(height / 2, 80);
var data = d3.range(2000).map(function() {
return [
randomX(),
randomY()
];
});
var zoomer = d3.zoom().scaleExtent([1 / 2, 4]).on("zoom", zoom)
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var g = svg.append("g");
svg.call(zoomer)
.on("wheel.zoom", null)
.on("wheel", pan);
g.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height);
g.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("r", 2.5)
.attr("transform", function(d) { return "translate(" + d + ")"; });
function zoom() {
g.attr("transform", d3.event.transform);
}
function pan() {
zoomer.translateBy(svg.transition().duration(100), d3.event.wheelDeltaX, d3.event.wheelDeltaY);
}
.overlay {
fill: none;
pointer-events: all;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.12.2/d3.min.js"></script>
<title>D3.js v4: Panning with mouse wheel</title>
<body></body>

Related

d3.js voronoi event. Mouseover seems to disappear when cursor is exactly above point

What am I doing wrong here please? I want to increase the point size when the mouse enters the associated voronoi cell, however the point goes back to its original size when the mouse is exaclty above that point; I have tried both the mouseover and mousemove events without any luck. Code in snippet, you can zoom in and you will be able to see what I just described.
Many thanks!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Chart</title>
<!-- Reference minified version of D3 -->
<script src='https://d3js.org/d3.v4.min.js' type='text/javascript'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js'></script>
</head>
<body>
<style>
.grid line {
stroke: #ddd;
}
</style>
<div id='scatter-plot'>
<svg width="700" height="500">
</svg>
</div>
<script>
var data = [];
for (let i = 0; i < 200; i++) {
data.push({
x: Math.random(),
y: Math.random(),
dotNum: i,
})
}
renderChart(data)
function renderChart(data) {
var totalWidth = 920,
totalHeight = 480;
var margin = {
top: 10,
left: 50,
bottom: 30,
right: 0
}
var width = totalWidth - margin.left - margin.right,
height = totalHeight - margin.top - margin.bottom;
// inner chart dimensions, where the dots are plotted
// var width = width - margin.left - margin.right;
// var height = height - margin.top - margin.bottom;
var tsn = d3.transition().duration(200);
// radius of points in the scatterplot
var pointRadius = 2;
var extent = {
x: d3.extent(data, function (d) {return d.x}),
y: d3.extent(data, function (d) {return d.y}),
};
var scale = {
x: d3.scaleLinear().range([0, width]),
y: d3.scaleLinear().range([height, 0]),
};
var axis = {
x: d3.axisBottom(scale.x).ticks(xTicks).tickSizeOuter(0),
y: d3.axisLeft(scale.y).ticks(yTicks).tickSizeOuter(0),
};
var gridlines = {
x: d3.axisBottom(scale.x).tickFormat("").tickSize(height),
y: d3.axisLeft(scale.y).tickFormat("").tickSize(-width),
}
var colorScale = d3.scaleLinear().domain([0, 1]).range(['#06a', '#06a']);
// select the root container where the chart will be added
var container = d3.select('#scatter-plot');
var zoom = d3.zoom()
.scaleExtent([1, 20])
.on("zoom", zoomed);
var tooltip = d3.select("body").append("div")
.attr("id", "tooltip")
.style("opacity", 0);
// initialize main SVG
var svg = container.select('svg')
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.call(zoom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Clip path
svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
// Heatmap dots
var dotsGroup = svg.append("g")
.attr("clip-path", "url(#clip)")
.append("g");
//Create X axis
var renderXAxis = svg.append("g")
.attr("class", "x axis")
//Create Y axis
var renderYAxis = svg.append("g")
.attr("class", "y axis")
// set up axis generating functions
var xTicks = Math.round(width / 50);
var yTicks = Math.round(height / 50);
function updateScales(data, scale){
scale.x.domain([extent.x[0], extent.x[1]]).nice(),
scale.y.domain([extent.y[0], extent.y[1]]).nice()
}
function zoomed() {
d3.event.transform.x = d3.event.transform.x;
d3.event.transform.y = d3.event.transform.y;
// update: rescale x axis
renderXAxis.call(axis.x.scale(d3.event.transform.rescaleX(scale.x)));
renderYAxis.call(axis.y.scale(d3.event.transform.rescaleX(scale.y)));
dotsGroup.attr("transform", d3.event.transform);
}
// add the overlay on top of everything to take the mouse events
dotsGroup.append('rect')
.attr('class', 'overlay')
.attr('width', width)
.attr('height', height)
.style('fill', 'red')
.style('opacity', 0)
.on('mouseover', mouseMoveHandler)
.on('mouseleave', () => {
// hide the highlight circle when the mouse leaves the chart
highlight(null);
});
renderPlot(data);
function renderPlot(data){
updateScales(data, scale);
svg.select('.y.axis')
.attr("transform", "translate(" + -pointRadius + " 0)" )
.call(axis.y);
var h = height + pointRadius;
svg.select('.x.axis')
.attr("transform", "translate(0, " + h + ")")
.call(axis.x);
svg.append("g")
.attr("class", "grid")
.call(gridlines.x);
svg.append("g")
.attr("class", "grid")
.call(gridlines.y);
//Do the chart
var update = dotsGroup.selectAll("circle").data(data)
update
.enter()
.append('circle')
.attr('r', pointRadius)
.attr('cx', d => scale.x(d.x))
.attr('cy', d => scale.y(d.y))
.attr('fill', d => colorScale(d.y))
};
// create a voronoi diagram
var voronoiDiagram = d3.voronoi()
.x(d => scale.x(d.x))
.y(d => scale.y(d.y))
.size([width, height])(data);
// add a circle for indicating the highlighted point
dotsGroup.append('circle')
.attr('class', 'highlight-circle')
.attr('r', pointRadius*2) // increase the size if highlighted
.style('fill', 'red')
.style('display', 'none');
// callback to highlight a point
function highlight(d) {
// no point to highlight - hide the circle and the tooltip
if (!d) {
d3.select('.highlight-circle').style('display', 'none');
//tooltip.style("opacity",0);
// otherwise, show the highlight circle at the correct position
} else {
d3.select('.highlight-circle')
.style('display', '')
.style('stroke', colorScale(d.y))
.attr('cx', scale.x(d.x))
.attr('cy', scale.y(d.y));
}
}
// callback for when the mouse moves across the overlay
function mouseMoveHandler() {
// get the current mouse position
var [mx, my] = d3.mouse(this);
var site = voronoiDiagram.find(mx, my);
// highlight the point if we found one, otherwise hide the highlight circle
highlight(site && site.data);
for (let i = 0; i < site.data.dotNum; i++) {
//do something....
}
}
}
</script>
</body>
</html>
you have to draw the overlay rect after the circles and the highlight circle. If not then hovering over a circle generates a mouse leave event and you see a flashing of the highlight circle
use the mousemove event not the mouseover, that is kind of a mouse-enter event
I have added logic to only update the highlight when it changes dots
the grid is not updated on zoom and translate (not fixed)
even when moving over the overlay there were still mouseleave events - they where caused by the grid lines. Moved the dots group after the grid line groups
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Chart</title>
<!-- Reference minified version of D3 -->
<script src='https://d3js.org/d3.v4.min.js' type='text/javascript'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js'></script>
</head>
<body>
<style>
.grid line { stroke: #ddd; }
</style>
<div id='scatter-plot'>
<svg width="700" height="500">
</svg>
</div>
<script>
var data = [];
for (let i = 0; i < 200; i++) {
data.push({
x: Math.random(),
y: Math.random(),
dotNum: i,
})
}
renderChart(data);
function renderChart(data) {
var totalWidth = 920,
totalHeight = 480;
var margin = {
top: 10,
left: 50,
bottom: 30,
right: 0
}
var width = totalWidth - margin.left - margin.right,
height = totalHeight - margin.top - margin.bottom;
// inner chart dimensions, where the dots are plotted
// var width = width - margin.left - margin.right;
// var height = height - margin.top - margin.bottom;
var tsn = d3.transition().duration(200);
// radius of points in the scatterplot
var pointRadius = 2;
var extent = {
x: d3.extent(data, function (d) {return d.x}),
y: d3.extent(data, function (d) {return d.y}),
};
var scale = {
x: d3.scaleLinear().range([0, width]),
y: d3.scaleLinear().range([height, 0]),
};
var axis = {
x: d3.axisBottom(scale.x).ticks(xTicks).tickSizeOuter(0),
y: d3.axisLeft(scale.y).ticks(yTicks).tickSizeOuter(0),
};
var gridlines = {
x: d3.axisBottom(scale.x).tickFormat("").tickSize(height),
y: d3.axisLeft(scale.y).tickFormat("").tickSize(-width),
}
var colorScale = d3.scaleLinear().domain([0, 1]).range(['#06a', '#06a']);
// select the root container where the chart will be added
var container = d3.select('#scatter-plot');
var zoom = d3.zoom()
.scaleExtent([1, 20])
.on("zoom", zoomed);
var tooltip = d3.select("body").append("div")
.attr("id", "tooltip")
.style("opacity", 0);
// initialize main SVG
var svg = container.select('svg')
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.call(zoom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Clip path
svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
//Create X axis
var renderXAxis = svg.append("g")
.attr("class", "x axis")
//Create Y axis
var renderYAxis = svg.append("g")
.attr("class", "y axis")
// set up axis generating functions
var xTicks = Math.round(width / 50);
var yTicks = Math.round(height / 50);
function updateScales(data, scale){
scale.x.domain([extent.x[0], extent.x[1]]).nice(),
scale.y.domain([extent.y[0], extent.y[1]]).nice()
}
function zoomed() {
d3.event.transform.x = d3.event.transform.x;
d3.event.transform.y = d3.event.transform.y;
// update: rescale x axis
renderXAxis.call(axis.x.scale(d3.event.transform.rescaleX(scale.x)));
renderYAxis.call(axis.y.scale(d3.event.transform.rescaleX(scale.y)));
dotsGroup.attr("transform", d3.event.transform);
}
var dotsGroup;
renderPlot(data);
function renderPlot(data){
updateScales(data, scale);
svg.select('.y.axis')
.attr("transform", "translate(" + -pointRadius + " 0)" )
.call(axis.y);
var h = height + pointRadius;
svg.select('.x.axis')
.attr("transform", "translate(0, " + h + ")")
.call(axis.x);
svg.append("g")
.attr("class", "grid")
.call(gridlines.x);
svg.append("g")
.attr("class", "grid")
.call(gridlines.y);
dotsGroup = svg.append("g")
.attr("clip-path", "url(#clip)")
.append("g");
//Do the chart
var update = dotsGroup.selectAll("circle").data(data)
update
.enter()
.append('circle')
.attr('r', pointRadius)
.attr('cx', d => scale.x(d.x))
.attr('cy', d => scale.y(d.y))
.attr('fill', d => colorScale(d.y))
};
// create a voronoi diagram
var voronoiDiagram = d3.voronoi()
.x(d => scale.x(d.x))
.y(d => scale.y(d.y))
.size([width, height])(data);
// add a circle for indicating the highlighted point
dotsGroup.append('circle')
.attr('class', 'highlight-circle')
.attr('r', pointRadius*2) // increase the size if highlighted
.style('fill', 'red')
.style('display', 'none');
// add the overlay on top of everything to take the mouse events
dotsGroup.append('rect')
.attr('class', 'overlay')
.attr('width', width)
.attr('height', height)
.style('fill', 'red')
.style('opacity', 0)
.on('mousemove', mouseMoveHandler)
.on('mouseleave', () => {
// hide the highlight circle when the mouse leaves the chart
console.log('mouse leave');
highlight(null);
});
var prevHighlightDotNum = null;
// callback to highlight a point
function highlight(d) {
// no point to highlight - hide the circle and the tooltip
if (!d) {
d3.select('.highlight-circle').style('display', 'none');
prevHighlightDotNum = null;
//tooltip.style("opacity",0);
// otherwise, show the highlight circle at the correct position
} else {
if (prevHighlightDotNum !== d.dotNum) {
d3.select('.highlight-circle')
.style('display', '')
.style('stroke', colorScale(d.y))
.attr('cx', scale.x(d.x))
.attr('cy', scale.y(d.y));
prevHighlightDotNum = d.dotNum;
}
}
}
// callback for when the mouse moves across the overlay
function mouseMoveHandler() {
// get the current mouse position
var [mx, my] = d3.mouse(this);
var site = voronoiDiagram.find(mx, my);
//console.log('site', site);
// highlight the point if we found one, otherwise hide the highlight circle
highlight(site && site.data);
for (let i = 0; i < site.data.dotNum; i++) {
//do something....
}
}
}
</script>
</body>
</html>

d3 US state map with markers, zooming transform issues

I've created a d3 map with US states, following this example:
http://bl.ocks.org/mbostock/4699541
and added markers following this SO question:
Put markers to a map generated with topoJSON and d3.js
The problem is that on zoom, the map markers stay in place. I believe I need to translate them into a new position, but not sure how to make that happen.
var width = 900,
height = 500,
active = d3.select(null);
var projection = d3.geo.albersUsa()
.scale(1000)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select(".rebates").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.on("click", reset);
var g = svg.append("g")
.style("stroke-width", "1.5px");
d3.json("/files/d3-geo/us.json", function(error, us) {
if (error) { throw error; }
g.selectAll("path")
.data(topojson.feature(us, us.objects.states).features)
.enter().append("path")
.attr("d", path)
.attr("class", function(item) {
return window.US_STATES[item.id].water_authorities > 0 ? 'avail' : 'unavail';
})
.on("click", clicked);
g.append("path")
.datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
.attr("class", "mesh")
.attr("d", path);
});
d3.json('/files/coordinates.json', function(error, coords) {
if (error) { throw error; }
svg.selectAll(".mark")
.data(coords)
.enter()
.append("image")
.attr('class','mark')
.attr('width', 20)
.attr('height', 20)
.attr("xlink:href",'assets/gmap_red.png')
.attr("transform", function(d) {
return "translate(" + projection([d[1],d[0]]) + ")";
});
});
function clicked(d) {
if (active.node() === this) { return reset(); }
if (window.US_STATES[d.id].water_authorities === 0) { return; }
active.classed("active", false);
active = d3.select(this).classed("active", true);
var bounds = path.bounds(d),
dx = bounds[1][0] - bounds[0][0],
dy = bounds[1][1] - bounds[0][1],
x = (bounds[0][0] + bounds[1][0]) / 2,
y = (bounds[0][1] + bounds[1][1]) / 2,
scale = .9 / Math.max(dx / width, dy / height),
translate = [width / 2 - scale * x, height / 2 - scale * y];
g.transition()
.duration(750)
.style("stroke-width", 1.5 / scale + "px")
.attr("transform", "translate(" + translate + ")scale(" + scale + ")");
}
function reset() {
active.classed("active", false);
active = d3.select(null);
rebatesTable.clear().draw();
g.transition()
.duration(750)
.style("stroke-width", "1.5px")
.attr("transform", "");
}
Step 1
Add all the points in the group and not in the svg.
This will ensure that the marker points translate with the main group.
g.selectAll(".mark")//adding mark in the group
.data(marks)
.enter()
.append("image")
.attr('class', 'mark')
.attr('width', 20)
.attr('height', 20)
.attr("xlink:href", 'https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/24x24/DrawingPin1_Blue.png')
.attr("transform", function(d) {
return "translate(" + projection([d.long, d.lat]) + ")";
});
Step2
Negate the scaling effect of the main group. else the markers will come zoomed up.
g.selectAll(".mark")
.transition()
.duration(750)
.attr("transform", function(d) {
var t = d3.transform(d3.select(this).attr("transform")).translate;//maintain aold marker translate
return "translate(" + t[0] +","+ t[1] + ")scale("+1/scale+")";//inverse the scale of parent
});
Step3
On zoom out make the marker scale back to 1.
g.selectAll(".mark")
.attr("transform", function(d) {
var t = d3.transform(d3.select(this).attr("transform")).translate;
console.log(t)
return "translate(" + t[0] +","+ t[1] + ")scale("+1+")";
});
Working code here
Hope this helps!

D3js d.x is undefined

I'm new to d3 and have to deal with an error here.
Can someone explain to me why my code always crashes at d.x and says "Cannot read property x of undefined"
I tried code from examples but the error still present. Here's my code:
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var elementWidth = 300,
elementHeight = 150,
screenWidth = 1000,
screenHeight = 800,
svg = null;
var drag = d3.behavior.drag()
.origin(function (d) { return d; })
.on("drag", dragged);
var zoom = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
var nodes = [{ id: 0, x: 10, y: 10 }, { id: 1, x: 600, y: 10 }];
var links = [{ source : 0 , target : 1 }];
function createView() {
alert();
d3.select("body")
.attr("width", screenWidth)
.attr("height", screenHeight);
svg = d3.select("body").append("svg")
.attr("width", screenWidth)
.attr("height", screenHeight)
.call(drag);
}
function addNewNodes() {
//change parameters
for (var i = 0; i < nodes.length; i++)
{
svg.append("rect")
.attr("x", nodes[i].x)
.attr("y", nodes[i].y)
.data([ {"x":nodes[i].x, "y":nodes[i].y} ])
.attr("width", elementWidth)
.attr("height", elementHeight).call(drag);
}
}
/* DRAG & ZOOM */
function zoomed()
{
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
function dragged(d) {
d3.select(this).attr("x", d.x = d3.event.x).attr("y", d.y = d3.event.y);
}
/* DRAG & ZOOM END*/
this.createView();
this.addNewNodes();
</script>
</body>
</html>
try this...
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="graph.js"></script>
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var elementWidth = 300,
elementHeight = 150,
screenWidth = 1000,
screenHeight = 800,
svg = null;
var drag = d3.behavior.drag()
.origin(function (d) {
return { x: d.x, y: d.y };
})
.on("drag", dragging);
var zoom = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", zoom);
var nodes = [{ id: 0, x: 10, y: 10 }, { id: 1, x: 600, y: 10 }];
var links = [{ source: 0, target: 1 }];
function createView() {
d3.select("body")
.attr("width", screenWidth)
.attr("height", screenHeight);
svg = d3.select("body").append("svg")
.attr("width", screenWidth)
.attr("height", screenHeight);
//.call(drag);
}
function addNewNodes() {
//change parameters
svg.selectAll("rect").data(nodes).enter().append("rect")
.attr("x", function (d) { return d.x })
.attr("y", function (d) { return d.y })
.attr("width", elementWidth)
.attr("height", elementHeight)
.call(drag);
}
/* DRAG & ZOOM */
function zoom() {
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
function dragging(d) {
d3.select(this).attr("x", d.x = d3.event.x).attr("y", d.y = d3.event.y);
}
/* DRAG & ZOOM END*/
this.createView();
this.addNewNodes();
</script>
</body>
</html>
First, you need to have a unique name for your drag handler, then you need to access the correct atributes (x and y not cx and cy)
function dragging(d) {
d3.select(this).attr("x", d.x = d3.event.x).attr("y", d.y = d3.event.y);
}
Then you need to connect it to the drag events...
var drag = d3.behavior.drag()
.origin(function (d) {
return { x: d.x, y: d.y };
})
.on("drag", dragging);
Then you need to bind your data. The standard d3 way to do this is...
svg.selectAll("rect").data(nodes).enter().append("rect")
You can't put .call(drag) on the svg element because it has no data bound so d will be undefined. So you have to remove that. (//.call(drag)).
Also, you don't need a loop in addNewNodes, d3 takes care of that.
You will get ( d , i ) arguments only when you assign there is data bound to object on which event is happening. So if you want ( d , i ) parameters when you drag rectangle, bind data x and y while creating them.
svg.append("rect")
.attr("x", nodes[i].x)
.attr("y", nodes[i].y)
.data([ {"x":nodes[i].x, "y":nodes[i].y} ])
.attr("width", elementWidth)
.attr("height", elementHeight).call(drag);

how do i add a zoom in and zoom out button in a graph on d3

I used
d3.select(".graph")
.call(d3.behavior.zoom().on("zoom", redraw)).on("dblclick.zoom", null).on("wheel.zoom", null);
function redraw() {
console.log("here", d3.event.translate, d3.event.scale);
g.attr("transform","translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}
to create pan, but when i create an .on event listener and call redraw() with anything other than the way i have it, it comes back with nothing for d3.event.translate and d3.event.scale, so i can't update the transform. And i've seen code out there for zooming with a button for maps, but not a graph. It must be possible some how, but i don't see how. The code i have so far is...
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
.node {
stroke: #000;
stroke-width: 0px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
.graphmap {
border: 1px solid black;
}
</style>
</head>
<body>
<div class="graph"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// start draw zoom buttons
var zoom = d3.select(".zoom").append("svg")
.attr("width", 40)
.attr("height", 40);
// start zoom behavior
var mapZoom = d3.behavior.zoom()
.on("zoom", redraw);
function zoomButton(zoomDirection) {
if (zoomDirection == "in") {
var newZoom = mapZoom.scale() * 1.5;
var newX =
((mapZoom.translate()[0] - (width / 2)) * 1.5) + width / 2;
var newY =
((mapZoom.translate()[1] - (height / 2)) * 1.5) + height / 2;
}
else if (zoomDirection == "out") {
var newZoom = mapZoom.scale() * .75;
var newX = ((mapZoom.translate()[0] - (width / 2)) * .75) + width / 2;
var newY = ((mapZoom.translate()[1] - (height / 2)) * .75) + height / 2;
}
mapZoom.scale(newZoom).translate([newX,newY])
redraw();
}
function zoomed() {
projection.translate(mapZoom.translate()).scale(mapZoom.scale());
d3.selectAll("path.graticule").attr("d", geoPath);
d3.selectAll("path.countries").attr("d", geoPath);
d3.selectAll("circle.cities")
.attr("cx", function(d) {return projection([d.x,d.y])[0]})
.attr("cy", function(d) {return projection([d.x,d.y])[1]});
}
d3.select(".zoomin").on("click", function (){
zoomButton("in");
console.log(d3.behavior.zoom().event(d3.select(".zoomin")))
});
d3.select(".graph")
.call(d3.behavior.zoom().on("zoom", redraw)).on("dblclick.zoom", null).on("wheel.zoom", null);
d3.select(".zoomin")
.call(d3.behavior.zoom().on("zoom", redraw)).on("dblclick.zoom", redraw);
function redraw() {
console.log("here", d3.event.translate, d3.event.scale);
g.attr("transform","translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}
// start svg
var width = 1100,
height = 900;
var color = d3.scale.category20();
var force = d3.layout.force()
.gravity(.05)
.charge(-700)
.linkDistance(150)
.size([width, height]);
var svg = d3.select(".graph").append("svg")
.attr("width", width)
.attr("height", height)
.attr("class", "graphmap");
zoomin = svg.append("g")
.attr("class", "zoomin");
zoomin.append("rect")
.attr("x", 10)
.attr("y", 10)
.attr("width", 30)
.attr("height", 30)
.attr("rx", 4)
.attr("ry", 4)
.attr("fill", "#dadae6");
var g = svg.append('g');
d3.json("miserables.json", function(error, graph) {
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = g.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = g.selectAll("g")
.data(graph.nodes)
.enter().append("g")
.attr("class","node")
.call(force.drag);
node.append("circle")
.attr("r", function(d) { return Math.sqrt(d.group * 20); })
.style("fill", function(d) { return color(d.group); })
.attr("pointer-events", "auto")
.attr("class", "circlenode");
node.append("text")
.attr("text-anchor", "right")
.attr("fill","black")
.style("pointer-events", "none")
.attr("font-size", function(d) { 20 + 'px'; })
.attr("font-weight", function(d) { return "bold"; })
.text( function(d) { return d.name + ' (' + d.group + ')';});
setTimeout(function() {
node.classed("fixed", function(d) { return d.fixed = true; });
}, 9000);
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")";});
});
});
function dump(obj) {
var out = '';
for (var i in obj) {
out += i + ": " + obj[i] + "\n";
}
// or, if you wanted to avoid alerts...
var pre = document.createElement('pre');
pre.innerHTML = out;
document.body.appendChild(pre)
}
</script>
</body>
</html>
You'll see i was playing around with making a zoomButton function but i don't know how to see how to set it up to make it work. I've seen some demos out there of different ideas with the zoom functionality but i don't really understand how they work and what the functions are for. And the d3 documentation doesn't seem to provide much insight. And i haven't found any tutorials that go over what each of the functions do and how to handle events. Any help an explanation of how the zoom functions actually work would be appreciated.
It turns out to be very simple.
var zoomfactor = 1;
var zoomlistener = d3.behavior.zoom()
.on("zoom", redraw);
d3.select(".zoomin").on("click", function (){
zoomfactor = zoomfactor + 0.2;
zoomlistener.scale(zoomfactor).event(d3.select(".graph"));
});
d3.select(".zoomout").on("click", function (){
zoomfactor = zoomfactor - 0.2;
zoomlistener.scale(zoomfactor).event(d3.select(".graph"));
});
function redraw() {
console.log("here", d3.event.translate, d3.event.scale);
g.attr("transform","translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}
scale() sets the amount you want to zoom and event() calls the portion of the page you want to update.

Aster Plot legend labels along with the arc

I am using aster plot of d3 in my project.
I want legend labels along with the arc radius outside the circle.
I could get an example of piechart showing labels along and outside the arc.
http://bl.ocks.org/Guerino1/2295263
But i am unable to implement the same in aster plot of d3.
http://bl.ocks.org/bbest/2de0e25d4840c68f2db1
Any help would be appreciated.
Thanks
Couple things to fix.
1.) You have to introduce margins into the aster plot for the labels.
2.) You then have to take the outer arcs, add a an svg g do you can group a path with a text:
var outerGroup = svg.selectAll(".solidArc")
.data(pie(data))
.enter()
.append("g")
outerGroup
.append("path")
.attr("fill", function(d) { return d.data.color; })
.attr("class", "solidArc")
.attr("stroke", "gray")
.attr("d", arc)
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
outerGroup
.append("text")
.attr("transform", function(d) {
return "translate(" + centroid(60, width, d.startAngle, d.endAngle) + ")";
})
.attr("text-anchor", "middle")
.text(function(d) { return d.data.label });
Note I had to create my own centroid function to move the labels outside the arc. The code in the pie chart example you linked did not work for me (it's using a old d3 version).
Here's my centroid function stolen from the d3 source:
function centroid(innerR, outerR, startAngle, endAngle){
var r = (innerR + outerR) / 2, a = (startAngle + endAngle) / 2 - (Math.PI / 2);
return [ Math.cos(a) * r, Math.sin(a) * r ];
}
Here's a working example.
Full code:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Testing Pie Chart</title>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?2.4.5"></script>
<style type="text/css">
.slice text {
font-size: 16pt;
font-family: Arial;
}
</style>
</head>
<body>
<script type="text/javascript">
var canvasWidth = 500, //width
canvasHeight = 500, //height
outerRadius = 150, //radius
//outerRadius = Math.min(canvasWidth, canvasHeight) / 2,
color = d3.scale.category20(); //builtin range of colors
innerRadius =0
var colorsArray = ['#0099ff','#cc00ff','#ff3366','#cc3300','#ff6600','#ffff33','#cccc00','#0066ff'];
var dataSet = [
{"legendLabel":"Testing Text Is", "magnitude":30,'score':4.8,width:20,color:colorsArray[0] },
{"legendLabel":"Two", "magnitude":8,'score':3.2,width:20,color:colorsArray[1] },
{"legendLabel":"Three", "magnitude":40,'score':3.9,width:20,color:colorsArray[2] },
{"legendLabel":"Four", "magnitude":50,'score':3.1,width:20,color:colorsArray[3] },
{"legendLabel":"Five", "magnitude":16,'score':4.2,width:20,color:colorsArray[4] },
{"legendLabel":"Six", "magnitude":50,'score':3.1,width:20,color:colorsArray[5] },
{"legendLabel":"Seven", "magnitude":30,'score':4.3,width:20,color:colorsArray[6] },
{"legendLabel":"Eight", "magnitude":20,'score':2.3,width:20,color:colorsArray[7] }
];
var vis = d3.select("body")
.append("svg:svg")
.data([dataSet])
.attr("width", canvasWidth)
.attr("height", canvasHeight)
.append("svg:g")
.attr("transform", "translate(" + 1.5*outerRadius + "," + 1.5*outerRadius + ")") // relocate center of pie to 'outerRadius,outerRadius'
var arc = d3.svg.arc()
.outerRadius(outerRadius);
var arc1 = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(function (d) {
return (outerRadius - innerRadius) * (d.data.score / 5.0) + innerRadius;
});
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.width; });
// Select all <g> elements with class slice (there aren't any yet)
var arcs = vis.selectAll("g.slice")
.data(pie)
.enter()
.append("svg:g")
.attr("class", "slice");
arcs.append("svg:path")
//set the color for each slice to be chosen from the color function defined above
.attr("fill", function(d, i) { return d.data.color; } )
//this creates the actual SVG path using the associated data (pie) with the arc drawing function
.attr("d", arc1);
var text = arcs.append("svg:text")
.attr("transform", function(d) {
d.outerRadius = outerRadius + 75;
d.innerRadius = outerRadius + 70;
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle") //center the text on it's origin
.style("fill", "black")
.style("font", "bold 12px Arial")
.each(function (d) {
var arr = d.data.legendLabel.split(" ");
if (arr != undefined) {
for (i = 0; i < arr.length; i++) {
d3.select(this).append("tspan")
.text(arr[i])
.attr("dy", i ? "1.2em" : 0)
.attr("x", 0)
.attr("text-anchor", "middle")
.attr("class", "tspan" + i);
}
}
});
//.text(function(d, i) { return dataSet[i].legendLabel; })
// .html(function(d, i) { return '<tspan>'+dataSet[i].legendLabel+'</tspan></n><tspan>'+dataSet[i].score+'</tspan>'})
/* arcs.append("foreignObject")
.attr("transform", function(d) {
d.outerRadius = outerRadius + 75;
d.innerRadius = outerRadius + 70;
return "translate(" + arc.centroid(d) + ")";
})
.attr("width", 50)
.attr("height", 50)
.append("xhtml:body")
.style("font", "14px 'Helvetica Neue'")
.html(function(d, i) { return dataSet[i].legendLabel+'<br>'+dataSet[i].score; });*/
</script>
</body>
</html>

Categories