LoginSignup
4
7

More than 3 years have passed since last update.

D3 地球表示(マウス操作で回転)

Last updated at Posted at 2017-05-28

こんにちは。
"D3 3D globe trackball rotation" と言う記事を見つけたので、そっくり真似してみました(d3.js v4 利用)。マウス操作で地球を回転できます。

なお地軸を左右に傾ける必要が無い場合には、回転に関する関数定義はかなり簡素化できます(下記のtrackballAngles_simple(), composedRotation_simple())。

スクリーンショット 2017-05-28 18.38.56.jpg

d3_globe_rotation.html
<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg { border: 0.5px solid #eee; }
.title {
  display: inline-block;
  font-size: 48px;
  line-height: 90px;
  text-align: center;
}
.land {
  fill: #69D2E7;
  stroke: none;
}
.countries {
  stroke: #fff;
  stroke-width: 2px;
  fill: none;
}
.graticule {
  fill: none;
  stroke: #777;
  stroke-width: 0.5px;
  stroke-opacity: 0.5;
}
</style>
<body>
<div .title><a href="http://bl.ocks.org/patricksurry/5721459">D3 Globe Trackball</a></div>
<script src="http://d3js.org/d3.v4.min.js"></script>
<script src="http://d3js.org/topojson.v3.min.js"></script>
<script>
var radius = 220, scale = 1.0, scaleMin = 0.1;
var projection = d3.geoOrthographic()
    .scale(radius)
    .translate([radius, radius])
    .clipAngle(90);
var path = d3.geoPath()
    .projection(projection);
var graticule = d3.geoGraticule();

var svg = d3.select("body").append("svg")
    .attr("width", radius * 2)
    .attr("height", radius * 2)
    .on("mousedown", mousedown)
    .on("mousemove", mousemove)
    .on("mouseup", mouseup)
    .on("wheel", zoom);

var circle = svg.append("circle")
    .attr("cx", radius)
    .attr("cy", radius)
    .attr("r", radius)
    .style("fill", "none")
    .style("stroke", "black");

var url = "https://unpkg.com/world-atlas@1/world/50m.json"; // topojson

d3.json(url, function(error, world) {
  svg.insert("path")
      .datum(topojson.feature(world, world.objects.land))
      .attr("class", "land")
      .attr("d", path);
  svg.insert("path")
      .datum(topojson.mesh(world, world.objects.countries))
      .attr("class", "countries")
      .attr("d", path);
  svg.append("path")
      .datum(graticule)
      .attr("class", "graticule")
      .attr("d", path);
});

function zoom() {
  scale *= Math.exp(d3.event.deltaY*0.01);
  scale = Math.max(scale, scaleMin);
  circle.attr("r", radius*scale);
  projection.scale(radius*scale);
  svg.selectAll("path").attr("d", path);
}

function trackballAngles(pt) {
  var r = projection.scale();
  var c = projection.translate();
  var x = pt[0] - c[0], y = - (pt[1] - c[1]), ss = x*x + y*y;
  var z = r*r > 2 * ss ? Math.sqrt(r*r - ss) : r*r / 2 / Math.sqrt(ss);  
  var lambda = Math.atan2(x, z) * 180 / Math.PI; 
  var phi = Math.atan2(y, z) * 180 / Math.PI
  return [lambda, phi];
}

function composedRotation(λ, ϕ, γ, δλ, δϕ) {
    λ = Math.PI / 180 * λ;
    ϕ = Math.PI / 180 * ϕ;
    γ = Math.PI / 180 * γ;
    δλ = Math.PI / 180 * δλ;
    δϕ = Math.PI / 180 * δϕ;

    var  = Math.sin(λ),  = Math.sin(ϕ),  = Math.sin(γ), 
        sδλ = Math.sin(δλ), sδϕ = Math.sin(δϕ),
         = Math.cos(λ),  = Math.cos(ϕ),  = Math.cos(γ), 
        cδλ = Math.cos(δλ), cδϕ = Math.cos(δϕ);

    var m00 = -sδλ *  *  + ( *  *  +  * ) * cδλ,
            m01 = - * cδλ *  - sδλ * ,
                m02 = sδλ *  *  - ( *  *  -  * ) * cδλ,
        m10 = - sδϕ *  * cδλ *  - ( *  *  +  * ) * sδλ * sδϕ - ( *  *  -  * ) * cδϕ,
            m11 = sδλ * sδϕ *  *  - sδϕ *  * cδλ + cδϕ *  * ,
                 m12 = sδϕ * cδλ *  *  + ( *  *  -  * ) * sδλ * sδϕ + ( *  *  +  * ) * cδϕ,
        m20 = -  * cδλ * cδϕ *  - ( *  *  +  * ) * sδλ * cδϕ + ( *  *  -  * ) * sδϕ,
            m21 = sδλ *  * cδϕ *  - sδϕ *  *  -  * cδλ * cδϕ,
                 m22 = cδλ * cδϕ *  *  + ( *  *  -  * ) * sδλ * cδϕ - ( *  *  +  * ) * sδϕ;

    if (m01 != 0 || m11 != 0) {
         γ_ = Math.atan2(-m01, m11);
         ϕ_ = Math.atan2(-m21, Math.sin(γ_) == 0 ? m11 / Math.cos(γ_) : - m01 / Math.sin(γ_));
         λ_ = Math.atan2(-m20, m22);
    } else {
         γ_ = Math.atan2(m10, m00) - m21 * λ;
         ϕ_ = - m21 * Math.PI / 2;
         λ_ = λ;       
    }

    return([λ_ * 180 / Math.PI, ϕ_ * 180 / Math.PI, γ_ * 180 / Math.PI]);
}

// simple rotation:
//   function trackballAngles_simple(pt) {return pt};
//   function composedRotation_simple(λ, ϕ, γ, δλ, δϕ) {return [λ + δλ/3, ϕ - δϕ/3]};

var o0, m0 = null;

function mousedown() {
  m0 = trackballAngles(d3.mouse(this));
  o0 = projection.rotate();
  d3.event.preventDefault();
}

function mousemove() {
  if (m0) {
    var m1 = trackballAngles(d3.mouse(this));
    o1 = composedRotation(o0[0], o0[1], o0[2], m1[0] - m0[0], m1[1] - m0[1])
    projection.rotate(o1);
    svg.selectAll("path").attr("d", path); 
  }
}

function mouseup() {
  m0 = null;
}
</script>

なお同様なものも多数あるようです。

4
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7