block by enjalot 31168147b88a1748bc8b

d3.geo with d3.geo.zoom

Full Screen

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<html>
<head>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script src="d3.geo.zoom.js"></script>
<style>
.land {
  fill: green;

}
.graticule {
  fill-opacity: 0;
  stroke: gray;
  stroke-opacity: 0.5;
}
.point {
  stroke: red;
  fill-opacity: 0.2;
  fill: white;
}
</style>
</head>

<body>
<svg class="myworld"></svg>

<script>

d3.json("world110.json", function(err, world) {
  var countries = topojson.feature(world, world.objects.land);

  var width = 420
  var height = 400
  var projection = d3.geo.orthographic()
  //var projection = d3.geo.albers()
  //var projection = d3.geo.mercator()
    .scale(170)
    .rotate([100,0,0])
    .translate([width/2, height/2])
    .clipAngle(90);

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

  var svg = d3.select(".myworld");

  var graticule = d3.geo.graticule()
  svg.append("path")
    .datum(graticule)
    .attr("class", "graticule")
    .attr("d", path);

  svg.append("path")
    .datum(countries)
    .attr("d", path)
    .classed("land", true);


  var zoom = d3.geo.zoom()
    .projection(projection)
    //.scaleExtent([projection.scale() * .7, projection.scale() * 10])
    .on("zoom.redraw", function() {
      d3.event.sourceEvent.preventDefault();
      svg.selectAll("path").attr("d", path);
      svg.selectAll("circle")
      .attr({
        cx: function(d) { return projection(d)[0] },
        cy: function(d) { return projection(d)[1] },
      })

    })
  d3.selectAll("path").call(zoom);

  var lonlat = [-109, 37.7833];

  var xy = projection(lonlat)
  svg.append("circle")
  .datum(lonlat)
  .classed("point", true)
  .attr({
    cx: function(d) { return projection(d)[0] },
    cy: function(d) { return projection(d)[1] },
    r: 10
  })

  navigator.geolocation.getCurrentPosition(function(pos){
    console.log(pos);
    var coords = [pos.coords.longitude, pos.coords.latitude]
    svg.append("circle")
    .datum(coords)
    .attr({
      cx: function(d) { return projection(d)[0] },
      cy: function(d) { return projection(d)[1] },
      r: 15,
    })
    .classed("point", true)
    .style("stroke", "blue");
  })

})

</script>
</body>
</html>

d3.geo.zoom.js

// Copyright (c) 2013, Jason Davies, http://www.jasondavies.com
// See LICENSE.txt for details.
(function() {

var radians = Math.PI / 180,
    degrees = 180 / Math.PI;

// TODO make incremental rotate optional

d3.geo.zoom = function() {
  var projection,
      zoomPoint,
      event = d3.dispatch("zoomstart", "zoom", "zoomend"),
      zoom = d3.behavior.zoom()
        .on("zoomstart", function() {
          var mouse0 = d3.mouse(this),
              rotate = quaternionFromEuler(projection.rotate()),
              point = position(projection, mouse0);
          if (point) zoomPoint = point;

          zoomOn.call(zoom, "zoom", function() {
                projection.scale(d3.event.scale);
                var mouse1 = d3.mouse(this),
                    between = rotateBetween(zoomPoint, position(projection, mouse1));
                projection.rotate(eulerFromQuaternion(rotate = between
                    ? multiply(rotate, between)
                    : multiply(bank(projection, mouse0, mouse1), rotate)));
                mouse0 = mouse1;
                event.zoom.apply(this, arguments);
              });
          event.zoomstart.apply(this, arguments);
        })
        .on("zoomend", function() {
          zoomOn.call(zoom, "zoom", null);
          event.zoomend.apply(this, arguments);
        }),
      zoomOn = zoom.on;

  zoom.projection = function(_) {
    return arguments.length ? zoom.scale((projection = _).scale()) : projection;
  };

  return d3.rebind(zoom, event, "on");
};

function bank(projection, p0, p1) {
  var t = projection.translate(),
      angle = Math.atan2(p0[1] - t[1], p0[0] - t[0]) - Math.atan2(p1[1] - t[1], p1[0] - t[0]);
  return [Math.cos(angle / 2), 0, 0, Math.sin(angle / 2)];
}

function position(projection, point) {
  var t = projection.translate(),
      spherical = projection.invert(point);
  return spherical && isFinite(spherical[0]) && isFinite(spherical[1]) && cartesian(spherical);
}

function quaternionFromEuler(euler) {
  var λ = .5 * euler[0] * radians,
      φ = .5 * euler[1] * radians,
      γ = .5 * euler[2] * radians,
      sinλ = Math.sin(λ), cosλ = Math.cos(λ),
      sinφ = Math.sin(φ), cosφ = Math.cos(φ),
      sinγ = Math.sin(γ), cosγ = Math.cos(γ);
  return [
    cosλ * cosφ * cosγ + sinλ * sinφ * sinγ,
    sinλ * cosφ * cosγ - cosλ * sinφ * sinγ,
    cosλ * sinφ * cosγ + sinλ * cosφ * sinγ,
    cosλ * cosφ * sinγ - sinλ * sinφ * cosγ
  ];
}

function multiply(a, b) {
  var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3],
      b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
  return [
    a0 * b0 - a1 * b1 - a2 * b2 - a3 * b3,
    a0 * b1 + a1 * b0 + a2 * b3 - a3 * b2,
    a0 * b2 - a1 * b3 + a2 * b0 + a3 * b1,
    a0 * b3 + a1 * b2 - a2 * b1 + a3 * b0
  ];
}

function rotateBetween(a, b) {
  if (!a || !b) return;
  var axis = cross(a, b),
      norm = Math.sqrt(dot(axis, axis)),
      halfγ = .5 * Math.acos(Math.max(-1, Math.min(1, dot(a, b)))),
      k = Math.sin(halfγ) / norm;
  return norm && [Math.cos(halfγ), axis[2] * k, -axis[1] * k, axis[0] * k];
}

function eulerFromQuaternion(q) {
  return [
    Math.atan2(2 * (q[0] * q[1] + q[2] * q[3]), 1 - 2 * (q[1] * q[1] + q[2] * q[2])) * degrees,
    Math.asin(Math.max(-1, Math.min(1, 2 * (q[0] * q[2] - q[3] * q[1])))) * degrees,
    Math.atan2(2 * (q[0] * q[3] + q[1] * q[2]), 1 - 2 * (q[2] * q[2] + q[3] * q[3])) * degrees
  ];
}

function cartesian(spherical) {
  var λ = spherical[0] * radians,
      φ = spherical[1] * radians,
      cosφ = Math.cos(φ);
  return [
    cosφ * Math.cos(λ),
    cosφ * Math.sin(λ),
    Math.sin(φ)
  ];
}

function dot(a, b) {
  for (var i = 0, n = a.length, s = 0; i < n; ++i) s += a[i] * b[i];
  return s;
}

function cross(a, b) {
  return [
    a[1] * b[2] - a[2] * b[1],
    a[2] * b[0] - a[0] * b[2],
    a[0] * b[1] - a[1] * b[0]
  ];
}

})();