lenyapugachev
lenyapugachev

Reputation: 81

D3 map visible area coordinates

here is my simple code for d3 scalable map:

  winWidth = $(window).width();
  winHeight = $(window).height();

  projection = d3.geo.mercator()
      .translate([0, 0])
      .scale(winWidth / 2 / Math.PI);

  zoom = d3.behavior.zoom()
      .scaleExtent([1, 50])
      .on("zoom", manageMap);

  path = d3.geo.path()
      .projection(projection);

  svg = d3.select("#map").append("svg")
      .attr("viewBox", "0 0 " + winWidth + " " + winHeight)
      .attr("preserveAspectRatio", "xMidYMid meet")
      .append("g")
      .attr("transform", "translate(" + winWidth / 2 + "," + winHeight / 2 + ")")
      .call(zoom);

  g = svg.append("g");

  d3.json("world-50m.json", function(error, world) {
    g.append("path")
        .datum(topojson.feature(world, world.objects.countries))
        .attr("class", "land")
        .attr("d", path);

    g.append("path")
        .datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
        .attr("class", "boundary")
        .attr("d", path);
  });

function manageMap() {

  var t = d3.event.translate,
      s = d3.event.scale;

  t[0] = Math.min(winWidth / 2 * (s - 1), Math.max(winWidth / 2 * (1 - s), t[0]));
  t[1] = Math.min(winHeight / 2 * (s - 1) + 230 * s, Math.max(winHeight / 2 * (1 - s) - 230 * s, t[1]));

  zoom.translate(t);
  g.style("stroke-width", 1/s).attr("transform", "translate(" + t + ")scale(" + s + ")");
  svg.select("g").selectAll("circle")
      .attr("cx", function(d) { return projection([d.lng, d.lat])[0]; })
      .attr("cy", function(d) { return projection([d.lng, d.lat])[1]; })
      .attr("r", 11/s);

}

Is there any simple way to have current visible area coordinates, when map is scaled and translated? I'm already try to project translation of map, but just got some strange numbers.

Upvotes: 1

Views: 1530

Answers (1)

Orwellophile
Orwellophile

Reputation: 13933

This will do it. I've also put it up on http://bl.ocks.org/sfinktah/1d38c8a268d893d769ed

Even if you have found your solution, this may be useful for future googlers.

function getScreenBounds() {
  return [getPoint(0,0), getPoint()];
}

function getPoint(x,y) {
  if (x == null) x = winWidth;
  if (y == null) y = winHeight;
  var container = g.node();
  var svg = container.ownerSVGElement || container;
  var point = svg.createSVGPoint();
  point.x = x, point.y = y;
  point = point.matrixTransform(container.getScreenCTM().inverse());
  return formatLocation(projection.invert([point.x, point.y]), zoom.scale());
}

function formatLocation(p, k) {
  var format = d3.format("." + Math.floor(Math.log(k) / 2 - 2) + "f");
  return (p[1] < 0 ? format(-p[1]) + "°S" : format(p[1]) + "°N") + " "
       + (p[0] < 0 ? format(-p[0]) + "°W" : format(p[0]) + "°E");
}

Upvotes: 2

Related Questions