我正在尝试基于此块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
我希望这些要点会消失,我将非常感谢您的帮助。谢谢!