This is the first step in an experiment on the problem of automatic positioning of labels on a map. You can drag de central label "Hello" and move over the other labels and you will see how the overlapped labels automatically change their positions. The function is called multiple times on each drag event, but the idea is to call only once on to a map.
xxxxxxxxxx
<html>
<head>
<style type="text/css">
.handler {
cursor: move;
cursor: -moz-grabbing;
cursor: -webkit-grabbing;
fill: red;
}
</style>
</head>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript">
var width = 960,
height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var drag = d3.behavior.drag()
.on("drag", function(d) {
d3.select(this).attr("dx", d3.event.x).attr("dy", d3.event.y);
alignLabels();
});
var label = svg.append("text")
.attr("dx", width/2)
.attr("dy", height/2)
.attr("class", "label handler")
.text("Hello")
.call(drag);
var data = [];
for (var i = 0; i < 200; i++) {
data.push(Math.round(Math.random() * 300));
}
svg.selectAll('.label')
.data(data)
.enter()
.append("text")
.attr("dx", function(d) { return width - d * Math.random() * 10; })
.attr("dy", function(d) { return height - d * Math.random() * 10; })
.attr("class", "label")
.text(function(d) { return d; });
var next = function(label) {
var box = getBox(label);
var pos = label.attr("data-pos") || 0;
pos++;
if (pos === 8) {
pos = 0;
}
label.attr("data-pos", pos);
switch (pos) {
case 0:
label.attr("transform", "translate(0,0)");
break;
case 1:
label.attr("transform", "translate(0, " + box.height + ")");
break;
case 2:
label.attr("transform", "translate(" + (-box.width/2) + ", " + box.height + ")");
break;
case 3:
label.attr("transform", "translate(" + (-box.width) + ", " + box.height + ")");
break;
case 4:
label.attr("transform", "translate(" + (-box.width) + ", 0)");
break;
case 5:
label.attr("transform", "translate(" + (-box.width) + ", " + (-box.height) + ")");
break;
case 6:
label.attr("transform", "translate(" + (-box.width/2) + ", " + (-box.height) + ")");
break;
case 7:
label.attr("transform", "translate(0, " + (-box.height) + ")");
break;
}
}
var overlaps = function(a, b) {
return (
(a.left <= b.left && b.left <= a.right)
||
(a.left <= b.right && b.right <= a.right)
)
&&
(
(a.top <= b.top && b.top <= a.bottom)
||
(a.top <= b.bottom && b.bottom <= a.bottom)
);
}
var getBox = function(d) {
return d[0][0].getBoundingClientRect();
}
var alignLabels = function() {
var texts = d3.selectAll(".label");
texts.each(function() {
var current = this;
var box_text = getBox(d3.select(current));
var overlappeds = texts[0].filter(function(d) {
if (d === current) return false;
return overlaps(box_text, getBox(d3.select(d)));
});
overlappeds.forEach(function(el) {
next(d3.select(el));
});
});
}
</script>
</body>
</html>
Modified http://d3js.org/d3.v3.min.js to a secure url
https://d3js.org/d3.v3.min.js