In response to the previous tutorial I was asked how to make a pan and zoom control that included text. The previous code will work with text, but it will scale the text as you zoom in and out, which is not necessarily the behaviour you want.

This is the same map of Australia, but now with toponyms. As you zoom in and out the text maintains its position relative the drawing while maintaining its size. This can cause odd effects at extreme scales.

Western Australia Queensland Victoria New South Wales ACT Northern Territories South Australia

The problem is quite tricky and my solution is not particularly elegant. You can get the full code here. Briefly, it works by having separate groups for the drawing and for the text. The drawing moves as before, but the text elements have to be moved individually when you zoom since their positions relative to one another changes.

This code loops through all the text in the names group, getting the current (x, y) coordinate, passing them to a function, transformNames(), and using the returned values to set the new coordinates.

for (var i = 0; i < nameElements.length; i++) {
    var x = parseFloat(nameElements[i].getAttributeNS(null, "x"));
    var y = parseFloat(nameElements[i].getAttributeNS(null, "y"));
    var newCoords = transformNames(x, y);
    nameElements[i].setAttributeNS(null, "x", newCoords[0]);
    nameElements[i].setAttributeNS(null, "y", newCoords[1]);
}

When translating the function add (dx, dy) as before:

function(x, y) {
  return [x + dx, y + dy];
};

The function is a bit more complex when scaling. We have calculate the current distance from the center and then scale that distance.

function(x, y) {
  return [
    centerX - (centerX - x) * scale,
    centerY - (centerY - y) * scale
  ];
};

Another small point is that the text elements must have the style text-anchor: middle to ensure the centre of the text maintains its position relative to the drawing rather than the beginning.