隐藏旋转的D3.Js地球上的点

时间:2019-05-09 11:16:33

标签: json d3.js

我正在尝试基于此块https://bl.ocks.org/sarah37/d8a1722f7d9f2a40eb07a3e99bf3c0be在D3.JS中制作一个可旋转且可缩放的地球仪。缩放和旋转功能可以正常工作,但我也希望为地球增加点。我已经弄清楚了如何从CSV文件中添加点,并将其放置在正确的经/纬度坐标上。但是,问题在于所有点都放置在地图上方,因此尽管这些国家/地区在全球范围内将消失,但这些点仍然可见。

很显然,我想让点在它们相关的大陆出现时消失。

我正在尝试根据点到地图中心的距离使它们消失。例如,如果某个点距地图中心的纬度大于90º,则填充将设置为none。

但是,我似乎无法计算出该中心点,并使其随着地球旋转而更新。我想知道是否有一个更简单的解决方案,或者是否有人对如何计算自转时地球中心的纬度/经度产生任何煽动。

HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Globe</title>
    <link rel="stylesheet" type="text/css" href="style.css">
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="https://d3js.org/topojson.v1.min.js"></script>
    <script src="mathsfunctions.js"></script>
  </head>

<body>

<div id="svgDiv"></div>

<script type="text/javascript" src="globe.js"></script>

</body>
</html>

Globe.JS

// width and height
// var w = document.documentElement.clientWidth;
// var h = document.documentElement.clientHeight;
var w = 960;
var h = 500;

// scale globe to size of window
var scl = Math.min(w, h)/2.5; 

// map projection
var projection = d3.geoOrthographic()
        .scale(scl)
        .translate([ w/2, h/2 ]);

// path generator
var path = d3.geoPath()
  .projection(projection);

// append svg
var svg = d3.select("#svgDiv")
  .append("svg")
  .attr("width", w)
  .attr("height", h);

// append g element for map
var map = svg.append("g");

// enable drag
var drag = d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged);

var gpos0, o0, gpos1, o1;
svg.call(drag);

// enable zoom
var zoom = d3.zoom()
    .scaleExtent([0.75, 50]) //bound zoom
    .on("zoom", zoomed);

svg.call(zoom);

// load topojson
d3.json("world-110m.json", function(json) {
    map.append("path")
    .datum({type: "Sphere"})
    .attr("class", "ocean")
    .attr("d", path);

    map.append("path")
    .datum(topojson.merge(json, json.objects.countries.geometries))
    .attr("class", "land")
    .attr("d", path);

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

//Add the city point data
d3.csv("us-cities2.csv", function(data) {
  map.selectAll("circle")
     .data(data)
     .enter()
     .append("circle")
     .attr("cx", function(d) {
       return projection([d.lon, d.lat])[0];
     })
     .attr("cy", function(d) {
       return projection([d.lon, d.lat])[1];
     })
     .attr("r", 2)
     .attr("class","city")
     .style("fill", "yellow")
     .style("stroke", "gray")
     .style("stroke-width", 0.25)
     .style("opacity", 0.75);         
});


// functions for dragging
function dragstarted() {
    gpos0 = projection.invert(d3.mouse(this));
    o0 = projection.rotate();
}

function dragged() {
    gpos1 = projection.invert(d3.mouse(this));
    // console.log("gpos0: "+gpos0+"and gpos1: "+gpos1)

    o0 = projection.rotate();
    o1 = eulerAngles(gpos0, gpos1, o0);
    projection.rotate(o1);

    map.selectAll("path").attr("d", path);
    map.selectAll('circle')
          .attr('cx', function(d){
            return projection([d.lon, d.lat])[0]
          })
          .attr('cy', function(d){
            return projection([d.lon, d.lat])[1]
          });
}

// functions for zooming
function zoomed() {
    projection.scale(d3.event.transform.translate(projection).k * scl)
    map.selectAll("path").attr("d", path);
        map.selectAll('circle')
          .attr('cx', function(d){
            return projection([d.lon, d.lat])[0]
          })
          .attr('cy', function(d){
            return projection([d.lon, d.lat])[1]
          });
}

mathsfunctions.js

// all functions are from http://bl.ocks.org/ivyywang/7c94cb5a3accd9913263

var to_radians = Math.PI / 180;
var to_degrees = 180 / Math.PI;


// Helper function: cross product of two vectors v0&v1
function cross(v0, v1) {
    return [v0[1] * v1[2] - v0[2] * v1[1], v0[2] * v1[0] - v0[0] * v1[2], v0[0] * v1[1] - v0[1] * v1[0]];
}

//Helper function: dot product of two vectors v0&v1
function dot(v0, v1) {
    for (var i = 0, sum = 0; v0.length > i; ++i) sum += v0[i] * v1[i];
    return sum;
}

// Helper function: 
// This function converts a [lon, lat] coordinates into a [x,y,z] coordinate 
// the [x, y, z] is Cartesian, with origin at lon/lat (0,0) center of the earth
function lonlat2xyz( coord ){

  var lon = coord[0] * to_radians;
  var lat = coord[1] * to_radians;

  var x = Math.cos(lat) * Math.cos(lon);

  var y = Math.cos(lat) * Math.sin(lon);

  var z = Math.sin(lat);

  return [x, y, z];
}

// Helper function: 
// This function computes a quaternion representation for the rotation between to vectors
// https://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions#Euler_angles_.E2.86.94_Quaternion
function quaternion(v0, v1) {

  if (v0 && v1) {

      var w = cross(v0, v1),  // vector pendicular to v0 & v1
          w_len = Math.sqrt(dot(w, w)); // length of w     

        if (w_len == 0)
          return;

        var theta = .5 * Math.acos(Math.max(-1, Math.min(1, dot(v0, v1)))),

          qi  = w[2] * Math.sin(theta) / w_len; 
          qj  = - w[1] * Math.sin(theta) / w_len; 
          qk  = w[0]* Math.sin(theta) / w_len;
          qr  = Math.cos(theta);

      return theta && [qr, qi, qj, qk];
  }
}

// Helper function: 
// This functions converts euler angles to quaternion
// https://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions#Euler_angles_.E2.86.94_Quaternion
function euler2quat(e) {

  if(!e) return;

    var roll = .5 * e[0] * to_radians,
        pitch = .5 * e[1] * to_radians,
        yaw = .5 * e[2] * to_radians,

        sr = Math.sin(roll),
        cr = Math.cos(roll),
        sp = Math.sin(pitch),
        cp = Math.cos(pitch),
        sy = Math.sin(yaw),
        cy = Math.cos(yaw),

        qi = sr*cp*cy - cr*sp*sy,
        qj = cr*sp*cy + sr*cp*sy,
        qk = cr*cp*sy - sr*sp*cy,
        qr = cr*cp*cy + sr*sp*sy;

    return [qr, qi, qj, qk];
}

// This functions computes a quaternion multiply
// Geometrically, it means combining two quant rotations
// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/arithmetic/index.htm
function quatMultiply(q1, q2) {
  if(!q1 || !q2) return;

    var a = q1[0],
        b = q1[1],
        c = q1[2],
        d = q1[3],
        e = q2[0],
        f = q2[1],
        g = q2[2],
        h = q2[3];

    return [
     a*e - b*f - c*g - d*h,
     b*e + a*f + c*h - d*g,
     a*g - b*h + c*e + d*f,
     a*h + b*g - c*f + d*e];

}

// This function computes quaternion to euler angles
// https://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions#Euler_angles_.E2.86.94_Quaternion
function quat2euler(t){

  if(!t) return;

  return [ Math.atan2(2 * (t[0] * t[1] + t[2] * t[3]), 1 - 2 * (t[1] * t[1] + t[2] * t[2])) * to_degrees, 
       Math.asin(Math.max(-1, Math.min(1, 2 * (t[0] * t[2] - t[3] * t[1])))) * to_degrees, 
       Math.atan2(2 * (t[0] * t[3] + t[1] * t[2]), 1 - 2 * (t[2] * t[2] + t[3] * t[3])) * to_degrees
      ]
}

/*  This function computes the euler angles when given two vectors, and a rotation
  This is really the only math function called with d3 code.

  v0 - starting pos in lon/lat, commonly obtained by projection.invert
  v1 - ending pos in lon/lat, commonly obtained by projection.invert
  o0 - the projection rotation in euler angles at starting pos (v0), commonly obtained by projection.rotate
*/

function eulerAngles(v0, v1, o0) {

  /*
    The math behind this:
    - first calculate the quaternion rotation between the two vectors, v0 & v1
    - then multiply this rotation onto the original rotation at v0
    - finally convert the resulted quat angle back to euler angles for d3 to rotate
  */

  var t = quatMultiply( euler2quat(o0), quaternion(lonlat2xyz(v0), lonlat2xyz(v1) ) );
  return quat2euler(t); 
}

包含CSV的城市:

rank,place,population,lat,lon
13,San Francisco,864816,37.777125,-122.419644
1000, Addis Adaba,20000,9.005401,38.763611

我希望这些要点会消失,我将非常感谢您的帮助。谢谢!

0 个答案:

没有答案