I'm tying to implement semantic zoom with d3.js v4. Most examples and questions on Stackoverflow are for v3. So i tried to alter one of them, like from this answer. Example from the answer: bl.ocks.org example
I tried to adept the example for d3 v4:
var xOld, yOld;
var width = document.querySelector('body').clientWidth,
height = document.querySelector('body').clientHeight;
var randomX = d3.randomNormal(width / 2, 80),
randomY = d3.randomNormal(height / 2, 80);
var data = d3.range(2000).map(function() {
return [
randomX(),
randomY()
];
});
var xScale = d3.scaleLinear()
.domain(d3.extent(data, function (d) {
return d[0];
}))
.range([0, width]);
var yScale = d3.scaleLinear()
.domain(d3.extent(data, function (d) {
return d[1];
}))
.range([0, height]);
var xExtent = xScale.domain();
var yExtent = yScale.domain();
var zoomer = d3.zoom().scaleExtent([1, 8]).on("zoom", zoom);
var svg0 = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
var svg = svg0.append('g')
.attr("width", width)
.attr("height", height)
var circle = svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("r", 2.5)
.attr("transform", transform_);
svg0
.call(zoomer)
.call(zoomer.transform, d3.zoomIdentity);
function zoom(e) {
var transform = d3.zoomTransform(this);
var x = 0;
var y = 0;
if(d3.event.sourceEvent) {
var x = d3.event.sourceEvent.layerX;
var y = d3.event.sourceEvent.layerY;
}
var scale = Math.pow(transform.k, .8);
xScale = d3.scaleLinear()
.domain([xExtent[0], xExtent[1] / scale])
.range([0, width]);
yScale = d3.scaleLinear()
.domain([yExtent[0], yExtent[1] / scale])
.range([0, height]);
circle.attr('transform', transform_)
svg.attr("transform", "translate(" + d3.event.transform.x + "," + d3.event.transform.y + ")");
}
function transform_(d) {
var x = xScale(d[0]);
var y = yScale(d[1]);
return "translate(" + x + "," + y + ")";
}
The zoom itself works - basically. Like the normal zoom it should zoom to the position of the mouse pointer, which it doesn't. Also the panning looks a little bit unsmooth.
I tried to use the mouse position from the d3.event.sourceEvent as offset for the translation, but it didn't work.
So, how could the zoom use the mouse position? It would be also great to get smoother panning gesture.
The zoom on mouse pointer can be added using pointer-events attribute.
Also, I have an example for a semantic zoom for d3 version 4 with the mouse pointer and click controls and also displaying the scale value for reference.[enter link description here][1]
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var randomX = d3.randomNormal(width / 2, 80),
randomY = d3.randomNormal(height / 2, 80),
data = d3.range(20).map(function() {
return [randomX(), randomY()];
});
var scale;
console.log(data);
var circle;
var _zoom = d3.zoom()
.scaleExtent([1, 8])
.on("zoom", zoom);
circle = svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("r", 5)
.attr("transform", transform(d3.zoomIdentity));
svg.append("rect")
.attr("fill", "none")
.attr("pointer-events", "all")
.attr("width", width)
.attr("height", height)
.call(_zoom);
function zoom() {
circle.attr("transform", transform(d3.event.transform));
scale = d3.event.transform.k;
console.log(scale);
document.getElementById('scale').value = scale;
}
function transform(t) {
return function(d) {
return "translate(" + t.apply(d) + ")";
}
}
var gui = d3.select("#gui");
gui.append("span")
.classed("zoom-in", true)
.text("+")
.on("click", function() {
_zoom.scaleBy(circle, 1.2);
});
gui.append("span")
.classed("zoom-out", true)
.text("-")
.on("click", function() {
_zoom.scaleBy(circle, 0.8);
});
please find the link to fiddle:
[1]: https://jsfiddle.net/sagarbhanu/5jLbLpac/3/
Related
I want to calculate the distance while drawing the line on the vertical axis or horizontal axis. It could show next to the line of how much distance drawn from mousedraw or any other ways. Can someone help me?
var width = 400,
height = 500;
var data = [10, 15, 20, 25, 30];
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.on("mousedown", mousedown)
.on("mouseup", mouseup);
var xscale = d3.scale.linear()
.domain([0, d3.max(data)])
.range([0, width - 100]);
var yscale = d3.scale.linear()
.domain([0, d3.max(data)])
.range([height / 2, 0]);
svg.on("mousemove", function() {
console.log("x - using invert", xscale.invert(d3.mouse(this)[0] - 50));
console.log("y- using invert", yscale.invert(d3.mouse(this)[1] - 10));
});
var x_axis = d3.svg.axis()
.scale(xscale).orient("bottom");
var y_axis = d3.svg.axis()
.scale(yscale).orient("left");;
svg.append("g")
.attr("transform", "translate(50, 10)")
.call(y_axis);
var xAxisTranslate = height / 2 + 10;
svg.append("g")
.attr("transform", "translate(50, " + xAxisTranslate + ")")
.call(x_axis)
function mousedown() {
var m = d3.mouse(this);
line = svg.append("line")
.attr("x1", m[0])
.attr("y1", m[1])
.attr("x2", m[0])
.attr("y2", m[1]);
svg.on("mousemove", mousemove);
}
function mousemove() {
var m = d3.mouse(this);
line.attr("x2", m[0])
.attr("y2", m[1]);
}
function mouseup() {
svg.on("mousemove", null);
}
Please check the live output I have now: https://jsfiddle.net/anojansith/m38b5fnp/1/
Image expected output
You might already know the Pythagorean theorem for determining the distance of a straight line; you could implement it like this:
Math.sqrt((m[0] - line.attr("x1")) ** 2 + (m[1] - line.attr("y1")) ** 2)
I'm referencing your global variable line and the local variable m for the mouse position. To display this next to the mouse cursor? Well, in mousedown you can add a text variable in addition to the line:
text = svg.append("text")
.attr("x", m[0])
.attr("y", m[1])
.style("user-select", "none")
Then you update the text in mousemove, shoving it over to the right of the cursor a little. Also in this case the distance is expressed in the same units as the data, by inverting the scale functions and thereby converted from pixels back into the data units.
var dist = Math.sqrt(
(xscale.invert(m[0]) - xscale.invert(line.attr("x1"))) ** 2 +
(yscale.invert(m[1]) - yscale.invert(line.attr("y1"))) ** 2)
text
.text(dist.toFixed(2))
.attr("x", m[0] + 10)
.attr("y", m[1])
and remove it in mouseup
text.remove()
I have been trying to upgrade this code to v4 of d3 with no luck.
function xnr(selector) {
var coordinates = [
{"id": 1, "x": 120.16353869437225, "y": 160.974180892502466},
{"id": 2, "x": 190.285414932883366, "y": 259.116836781737214},
{"id": 3, "x": 310.218762385111142, "y": 170.033297729284202}
];
var x = d3.scale.linear()
.domain([0, 100])
.range([0, 100]);
var y = d3.scale.linear()
.domain([0, 100])
.range([0, 100]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select(selector).append("svg:svg")
.attr("width", 400)
.attr("height", 400)
.call(d3.behavior.zoom().x(x).y(y).scaleExtent([1, 100]).on("zoom", zoom));
var groups = svg.selectAll("g.leaf")
.data(coordinates)
.enter()
.append("svg:g")
.attr("class", "leaf")
.attr("transform", transform)
.append("svg:circle")
.attr("r", 5)
.attr('stroke', '#aaaaaa')
.attr('stroke-width', '2px');
function zoom() {
svg.selectAll("g.leaf").attr("transform", transform);
}
function transform(d) {
return "translate(" + x(d.x) + "," + y(d.y) + ")";
}
}
Old code jsfiddle - works as i need it (semantic zoom).
New code jsfiddle - only works as geometic zoom.
Mike Bostock's examples for SVG geometric and semantic zooming worked for me in d3 v3, but since the zoom.x() and zoom.y() functions had been removed, i am lost.
Mike Bostock's Pan & Zoom III example can be modified to use semantic zooming as per this jsfiddle. The important bits are:
var radius = 3;
var circles = g.selectAll("circle")
.data(points)
.enter().append("circle")
.attr("cx", function(d) { return d[0]; })
.attr("cy", function(d) { return d[1]; })
.attr("r", radius);
svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all")
.call(d3.zoom()
.scaleExtent([1 / 2, 4])
.on("zoom", zoomed));
function zoomed() {
var transform = d3.zoomTransform(this);
circles.attr("transform", transform);
circles.attr("r", radius/transform.k)
}
zoomTransform is a linear transformation of the plane which increases area as it increases distances. If we want the circles to have the same area after zooming, we need to rescale their radii by the scale factor of the transformation, transform.k.
I have an example fiddle for semantic zoom for d3 version 4 with click controls. also displaying scale for the reference. you can use this scale to add any semantic information for the same.
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var randomX = d3.randomNormal(width / 2, 80),
randomY = d3.randomNormal(height / 2, 80),
data = d3.range(200).map(function() {
return [randomX(), randomY()];
});
var scale;
var circle;
var _zoom = d3.zoom()
.scaleExtent([1, 8])
.on("zoom", zoom);
circle = svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("r", 5)
.attr("transform", transform(d3.zoomIdentity));
svg.append("rect")
.attr("fill", "none")
.attr("pointer-events", "all")
.attr("width", width)
.attr("height", height)
.call(_zoom);
function zoom() {
circle.attr("transform", transform(d3.event.transform));
scale = d3.event.transform.k;
console.log(scale);
document.getElementById('scale').value = scale;
}
// semantic zoom
function semanticZoom() {
circle.attr("transform", transform(d3.event.transform));
}
function transform(t) {
return function(d) {
return "translate(" + t.apply(d) + ")";
}
}
var gui = d3.select("#gui");
gui.append("span")
.classed("zoom-in", true)
.text("+")
.on("click", function() {
_zoom.scaleBy(circle, 1.2);
});
gui.append("span")
.classed("zoom-out", true)
.text("-")
.on("click", function() {
_zoom.scaleBy(circle, 0.8);
});
below is the js fiddle link:
https://jsfiddle.net/sagarbhanu/5jLbLpac/36/
I am working with D3 v4 and JS. I have a scatter plot with a predefined set of data loaded along with axes with the ability to pan and zoom. I need to be able to then dynamically add points and eventually output them in data space not pixel space. I am using the "rescaleX" and "rescaleY" methods of the zoom object. They work fine for rescaling the axes but, when I try to add new points, the location of the plotted point does correspond to the mouse location. Here is a simplified version of the code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>
var data = [{x:17,y:3},
{x:20,y:16},
{x:2,y:13},
{x:19,y:10},
{x:13,y:15},
{x:2,y:2},
{x:5,y:8},
{x:11,y:19},
{x:20,y:12},
{x:10,y:20}];
var width = 600;
var height = 600;
var padding = 50;
var newXscale, newYscale;
var dataScale = d3.scaleLinear()
.domain([0,21])
.range([0, width]);
var svg = d3.select('body').append('svg')
.attr('width', width+2*padding)
.attr('height', height+2*padding)
.on('click', clicked);
var xAxis = d3.axisTop()
.scale(dataScale);
var gX = svg.append('g')
.attr('transform','translate(50,50)')
.call(xAxis);
var yAxis = d3.axisLeft()
.scale(dataScale);
var gY = svg.append('g')
.attr('transform','translate(50,50)')
.call(yAxis);
var canvas = svg.append('g')
var points = canvas.append('g');
points.selectAll('circle').data(data)
.enter().append('circle')
.attr('cx', function(d) {return dataScale(d.x)+padding})
.attr('cy', function(d) {return dataScale(d.y)+padding})
.attr('r', 5);
var zoom
var zoomOn = false;
window.addEventListener('keydown', function (event) {
if (event.key=='z') {
if (zoomOn) {
d3.select('#zoomBox').remove();
zoomOn = false;
} else {
zoom = d3.zoom()
.scaleExtent([0.1, 10])
.on('zoom', zoomed);
svg.append("rect")
.attr('cursor','move')
.attr("width", width+padding*2)
.attr("height", height+padding*2)
.attr('id','zoomBox')
.style("fill", "none")
.style("pointer-events", "all")
.call(zoom);
zoomOn = true;
}
}
});
function zoomed() {
canvas.attr("transform", d3.event.transform)
newXscale = d3.event.transform.rescaleX(dataScale);
newYscale = d3.event.transform.rescaleY(dataScale);
gX.call(xAxis.scale(newXscale));
gY.call(yAxis.scale(newYscale));
}
function clicked() {
var coords = d3.mouse(this);
points.append('circle')
.attr('cx',coords[0])
.attr('cy',coords[1])
.attr('r',5);
var x = newXscale.invert(coords[0]-padding);
var y = newYscale.invert(coords[1]-padding);
console.log(x+' '+y);
}
</script>
</body>
</html>
Create a variable to store the zoom level:
newZscale = d3.event.transform.k;
And, in your clicked function, use the dateScale to plot the new circles, dividing the padding by the zoom level:
function clicked() {
var coords = d3.mouse(this);
if (newXscale && newYscale) {
var x = newXscale.invert(coords[0] - padding);
var y = newYscale.invert(coords[1] - padding);
};
console.log(newZscale);
points.append('circle')
.attr('cx', (!x) ? coords[0] : dataScale(x) + (padding / newZscale))
.attr('cy', (!y) ? coords[1] : dataScale(y) + (padding / newZscale))
.attr('r', 5);
console.log(x + ' ' + y);
}
Here is the demo:
var data = [{
x: 17,
y: 3
}, {
x: 20,
y: 16
}, {
x: 2,
y: 13
}, {
x: 19,
y: 10
}, {
x: 13,
y: 15
}, {
x: 2,
y: 2
}, {
x: 5,
y: 8
}, {
x: 11,
y: 19
}, {
x: 20,
y: 12
}, {
x: 10,
y: 20
}];
var width = 600;
var height = 600;
var padding = 50;
var newXscale, newYscale, newZscale;
var dataScale = d3.scaleLinear()
.domain([0, 21])
.range([0, width]);
var svg = d3.select('body').append('svg')
.attr('width', width + 2 * padding)
.attr('height', height + 2 * padding)
.on('click', clicked);
var xAxis = d3.axisTop()
.scale(dataScale);
var gX = svg.append('g')
.attr('transform', 'translate(50,50)')
.call(xAxis);
var yAxis = d3.axisLeft()
.scale(dataScale);
var gY = svg.append('g')
.attr('transform', 'translate(50,50)')
.call(yAxis);
var canvas = svg.append('g')
var points = canvas.append('g');
points.selectAll('circle').data(data)
.enter().append('circle')
.attr('cx', function(d) {
return dataScale(d.x) + padding
})
.attr('cy', function(d) {
return dataScale(d.y) + padding
})
.attr('r', 5);
var zoom
var zoomOn = false;
window.addEventListener('keydown', function(event) {
if (event.key == 'z') {
if (zoomOn) {
d3.select('#zoomBox').remove();
zoomOn = false;
} else {
zoom = d3.zoom()
.scaleExtent([0.1, 10])
.on('zoom', zoomed);
svg.append("rect")
.attr('cursor', 'move')
.attr("width", width + padding * 2)
.attr("height", height + padding * 2)
.attr('id', 'zoomBox')
.style("fill", "none")
.style("pointer-events", "all")
.call(zoom);
zoomOn = true;
}
}
});
function zoomed() {
canvas.attr("transform", d3.event.transform)
newXscale = d3.event.transform.rescaleX(dataScale);
newYscale = d3.event.transform.rescaleY(dataScale);
newZscale = d3.event.transform.k;
gX.call(xAxis.scale(newXscale));
gY.call(yAxis.scale(newYscale));
}
function clicked() {
var coords = d3.mouse(this);
if (newXscale && newYscale) {
var x = newXscale.invert(coords[0] - padding);
var y = newYscale.invert(coords[1] - padding);
};
points.append('circle')
.attr('cx', (!x) ? coords[0] : dataScale(x) + (padding / newZscale))
.attr('cy', (!y) ? coords[1] : dataScale(y) + (padding / newZscale))
.attr('r', 5);
}
<script src="https://d3js.org/d3.v4.min.js"></script>
I figured it out. The problem lied in the fact that I was removing the zoom box when toggling the zoom. I switched the event listener to just hide the box and unbind the pointer-events. Here is the final code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>
var data = [{x:17,y:3},
{x:20,y:16},
{x:2,y:13},
{x:19,y:10},
{x:13,y:15},
{x:2,y:2},
{x:5,y:8},
{x:11,y:19},
{x:20,y:12},
{x:10,y:20}];
var width = 600;
var height = 600;
var padding = 50;
var newXscale, newYscale;
var zoomOn = false;
var xScale = d3.scaleLinear()
.domain([0,21])
.range([0, width]);
var yScale = d3.scaleLinear()
.domain([0,21])
.range([0, width]);
var svg = d3.select('body').append('svg')
.attr('width', width+2*padding)
.attr('height', height+2*padding)
.on('click', clicked)
.attr('cursor','crosshair');
var xAxis = d3.axisTop()
.scale(xScale);
var gX = svg.append('g')
.attr('transform','translate(50,50)')
.call(xAxis);
var yAxis = d3.axisLeft()
.scale(yScale);
var gY = svg.append('g')
.attr('transform','translate(50,50)')
.call(yAxis);
var canvas = svg.append('g')
var points = canvas.append('g');
points.selectAll('circle').data(data)
.enter().append('circle')
.attr('cx', function(d) {return xScale(d.x)+padding})
.attr('cy', function(d) {return yScale(d.y)+padding})
.attr('r', 5);
var zoom = d3.zoom()
.scaleExtent([0.1, 10])
.on('zoom', zoomed);
var zoombox = svg.append("rect")
.attr("width", width+padding*2)
.attr("height", height+padding*2)
.attr('id','zoomBox')
.style("fill", "none")
.style("pointer-events", "none")
.style('visibility','off')
.call(zoom);
window.addEventListener('keydown', function (event) {
if (event.key=='z') {
if (zoomOn) {
d3.select('#zoomBox')
.attr('cursor','auto')
.style('pointer-events','none')
.style('visibility','off');
zoomOn = false;
} else {
d3.select('#zoomBox')
.attr('cursor','move')
.style('pointer-events','all')
.style('visibilty','on')
zoomOn = true;
}
}
});
function zoomed() {
canvas.attr("transform", d3.event.transform)
newXscale = d3.event.transform.rescaleX(xScale);
newYscale = d3.event.transform.rescaleY(yScale);
gX.call(xAxis.scale(newXscale));
gY.call(yAxis.scale(newYscale));
newZscale = d3.event.transform.k;
}
function clicked() {
var coords = d3.mouse(this);
if (newXscale && newYscale) {
var x = newXscale.invert(coords[0] - padding);
var y = newYscale.invert(coords[1] - padding);
};
console.log(newZscale);
points.append('circle')
.attr('cx', (!x) ? coords[0] : xScale(x) + (padding / newZscale))
.attr('cy', (!y) ? coords[1] : yScale(y) + (padding / newZscale))
.attr('r', 5);
console.log(x + ' ' + y);
}
</script>
</body>
</html>
I have a bar chart see plunker the problem is that I would like to move the y-axis ticks to be at the middle left side of the rects but they appear on the top and end. and I cannot seem to move them without destroying the chart.
my code
var info = [{
name: "Walnuts",
value: 546546
}, {
name: "Almonds",
value: 456455
}
];
/* Set chart dimensions */
var width = 960,
height = 500,
margin = {
top: 10,
right: 10,
bottom: 20,
left: 60
};
//subtract margins
width = width - margin.left - margin.right;
height = height - margin.top - margin.bottom;
//sort data from highest to lowest
info = info.sort(function(a, b) {
return b.value - a.value;
});
//Sets the y scale from 0 to the maximum data element
var max_n = 0;
var category = []
for (var d in info) {
max_n = Math.max(info[d].value, max_n);
category.push(info[d].name)
}
var dx = width / max_n;
var dy = height / info.length;
var y = d3.scale.ordinal()
.domain(category)
.range([0, height]);
var yAxis = d3.svg.axis()
.scale(y)
.orient('left')
var svg = d3.select("#chart")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr('preserveAspectRatio', 'xMidYMin')
.attr("viewBox", '0 0 ' + parseInt(width + margin.left + margin.right) + ' ' + parseInt(height + margin.top + margin.bottom))
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.selectAll(".bar")
.data(info)
.enter()
.append("rect")
.attr("class", function(d, i) {
return "bar" + d.name;
})
.attr("x", function(d, i) {
return 0;
})
.attr("y", function(d, i) {
return dy * i;
})
.attr("width", function(d, i) {
return dx * d.value
})
.attr("height", dy)
.attr("fill", function(d, i) {
if (d.name == 'Walnuts') {
return 'red'
} else {
return 'green'
}
});
var y_xis = svg.append('g')
.attr('id', 'yaxis')
.call(yAxis);
You are using range in y axis like this:
var y = d3.scale.ordinal()
.domain(category)
.range([0, height]);
You should be using 'rangeRoundBands' since the y scale is ordinal
var y = d3.scale.ordinal()
.domain(category)
.rangeRoundBands([0, height], .1);
working code here
For d3 versions like v4/v5.
Defining height as the graph/plot height, and max as the maximum value of y.
import { parseSvg } from 'd3-interpolate/src/transform/parse'
const yScale = d3
.scaleLinear()
.domain([0, max])
.rangeRound([height, 0])
const yAxis = d3.axisLeft(yScale)
svg
.append('g')
.call(yAxis)
.selectAll('.tick')
.each(function(data) {
const tick = d3.select(this)
const { translateX, translateY } = parseSvg(tick.attr('transform'))
tick.attr(
'transform',
translate(translateX, translateY + height / (2 * max))
)
})
Recently I needed something very very similar and I solved this with a call with selecting all text elements in the selection and moving their dy upwards. I will give an example with OP's code:
var y_xis = svg.append('g')
.attr('id','yaxis')
.call(yAxis)
.call(selection => selection
.selectAll('text')
.attr('dy', '-110') // this moves the text labels upwards
.attr('x', '110')); // this does the same job but horizontally
I'm trying to learn how to use the d3.js hexbin plugin.
I started with the example: http://bl.ocks.org/mbostock/4248145 , and I'm adapting it.
I have a data set of points between [0,0] and [600,600]. I want to output them to a 300,300 graph.
My graph doesn't look right. It looks like the data isn't being scaled properly and the graph is only showing 1/4 of the data. Can someone tell me what's wrong? I've read a book about using d3, but I don't have very much experience using it.
Jsfiddle of my hexbin
var graph_width = 300;
var graph_height = 300;
var data_width = 600;
var data_height = 600;
var randomX = d3.random.normal(data_width / 2, 80),
randomY = d3.random.normal(data_height / 2, 80),
points = d3.range(2000).map(function() { return [randomX(), randomY()]; });
var color = d3.scale.linear()
.domain([0, 20])
.range(["white", "steelblue"])
.interpolate(d3.interpolateLab);
var hexbin = d3.hexbin()
.size([graph_width, graph_height])
.radius(20);
var x = d3.scale.linear()
.domain([0, data_width])
.range([0, graph_width]);
var y = d3.scale.linear()
.domain([0, data_height])
.range([0, graph_height]);
var svg = d3.select("body").append("svg")
.attr("width", graph_width)
.attr("height", graph_height)
.append("g");
svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("class", "mesh")
.attr("width", graph_width)
.attr("height", graph_height);
svg.append("g")
.attr("clip-path", "url(#clip)")
.selectAll(".hexagon")
.data(hexbin(points))
.enter().append("path")
.attr("class", "hexagon")
.attr("d", hexbin.hexagon())
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.style("fill", function(d) { return color(d.length); });
I think I understand. You have data values in in the range of 0 to 600 but want those mapped to x/y positions in the range of 0 to 300.
If that's it then scale the points:
var x = d3.scale.linear()
.domain([0, data_width])
.range([0, graph_width]);
var y = d3.scale.linear()
.domain([0, data_height])
.range([0, graph_height]);
var randomX = d3.random.normal(data_width / 2, 80),
randomY = d3.random.normal(data_height / 2, 80),
points = d3.range(2000).map(function() { return [x(randomX()), y(randomY())]; });
Updated fiddle.