我正在寻找一种方法来创建一个全球图表,其中我有一个虚线世界和条形图显示某个区域的强度。
我通过使用世界人口数据(from globe site)并将所有点数量设置为0来实现这一点。但是从图像中可以看到一些点缺失(俄罗斯北部,撒哈拉等等)。
有没有更好的方法来达到预期的效果?
或者我在哪里可以找到带有完整点的数据集(我随处可见)?
我尽力将代码组合在一起:
var DAT = DAT || {};
DAT.Globe = function(container, colorFn) {
colorFn = colorFn || function(x) {
var c = new THREE.Color();
c.setHSV((0.6 - (x * 0.5)), 1.0, 1.0);
return c;
};
var Shaders = {
'earth': {
uniforms: {
'texture': {
type: 't',
value: 0,
texture: null
}
},
vertexShader: [
'varying vec3 vNormal;',
'varying vec2 vUv;',
'void main() {',
'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 0 );',
'vNormal = normalize( normalMatrix * normal );',
'vUv = uv;',
'}'
].join('\n'),
fragmentShader: [
'uniform sampler2D texture;',
'varying vec3 vNormal;',
'varying vec2 vUv;',
'void main() {',
'vec3 diffuse = texture2D( texture, vUv ).xyz;',
'float intensity = 1.05 - dot( vNormal, vec3( 0.0, 0.0, 1.0 ) );',
'vec3 atmosphere = vec3( 1.0, 1.0, 1.0 ) * pow( intensity, 1.0 );',
'gl_FragColor = vec4( diffuse + atmosphere, 1.0 );',
'}'
].join('\n')
},
'atmosphere': {
uniforms: {},
vertexShader: [
'varying vec3 vNormal;',
'void main() {',
'vNormal = normalize( normalMatrix * normal );',
'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
'}'
].join('\n'),
fragmentShader: [
'varying vec3 vNormal;',
'void main() {',
'float intensity = pow( 0.8 - dot( vNormal, vec3( 0, 0, 1.0 ) ), 12.0 );',
'gl_FragColor = vec4( 0.0, 0.0, 0.0, 0.0 );',
'}'
].join('\n')
}
};
var camera, scene, sceneAtmosphere, renderer, w, h;
var vector, mesh, atmosphere, point;
var overRenderer;
var imgDir = 'https://globe.chromeexperiments.com/world.jpg';
var curZoomSpeed = 0;
var zoomSpeed = 50;
var mouse = {
x: 0,
y: 0
},
mouseOnDown = {
x: 0,
y: 0
};
var rotation = {
x: 0,
y: 0
},
target = {
x: Math.PI * 3 / 2,
y: Math.PI / 6.0
},
targetOnDown = {
x: 0,
y: 0
};
var distance = 100000,
distanceTarget = 100000;
var padding = 40;
var PI_HALF = Math.PI / 2;
function init() {
var shader, uniforms, material;
w = container.offsetWidth || window.innerWidth * 0.85;
h = container.offsetHeight || window.innerHeight * 0.85;
camera = new THREE.Camera(
30, w / h, 1, 10000);
camera.position.z = distance;
vector = new THREE.Vector3();
scene = new THREE.Scene();
sceneAtmosphere = new THREE.Scene();
var geometry = new THREE.Sphere(200, 40, 30);
shader = Shaders['earth'];
uniforms = THREE.UniformsUtils.clone(shader.uniforms);
uniforms['texture'].texture = THREE.ImageUtils.loadTexture(imgDir);
material = new THREE.MeshShaderMaterial({
uniforms: uniforms,
vertexShader: shader.vertexShader,
fragmentShader: shader.fragmentShader
});
mesh = new THREE.Mesh(geometry, material);
mesh.matrixAutoUpdate = false;
scene.addObject(mesh);
shader = Shaders['atmosphere'];
uniforms = THREE.UniformsUtils.clone(shader.uniforms);
material = new THREE.MeshShaderMaterial({
uniforms: uniforms,
vertexShader: shader.vertexShader,
fragmentShader: shader.fragmentShader
});
mesh = new THREE.Mesh(geometry, material);
mesh.scale.x = mesh.scale.y = mesh.scale.z = 1.1;
mesh.flipSided = true;
mesh.matrixAutoUpdate = false;
mesh.updateMatrix();
sceneAtmosphere.addObject(mesh);
geometry = new THREE.Cube(0.75, 0.75, 1, 1, 1, 1, null, false, {
px: true,
nx: true,
py: true,
ny: true,
pz: false,
nz: true
});
for (var i = 0; i < geometry.vertices.length; i++) {
var vertex = geometry.vertices[i];
vertex.position.z += 0.5;
}
point = new THREE.Mesh(geometry);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.autoClear = false;
renderer.setClearColorHex(0x000000, 0.0);
renderer.setSize(w, h);
renderer.domElement.style.position = 'absolute';
container.appendChild(renderer.domElement);
container.addEventListener('mousedown', onMouseDown, false);
container.addEventListener('mousewheel', onMouseWheel, false);
document.addEventListener('keydown', onDocumentKeyDown, false);
window.addEventListener('resize', onWindowResize, false);
container.addEventListener('mouseover', function() {
overRenderer = true;
}, false);
container.addEventListener('mouseout', function() {
overRenderer = false;
}, false);
}
addData = function(data, opts) {
var lat, lng, size, color, i, step, colorFnWrapper;
opts.animated = opts.animated || false;
this.is_animated = opts.animated;
opts.format = opts.format || 'magnitude'; // other option is 'legend'
console.log(opts.format);
if (opts.format === 'magnitude') {
step = 3;
colorFnWrapper = function(data, i) {
return colorFn(data[i + 2]);
}
} else if (opts.format === 'legend') {
step = 4;
colorFnWrapper = function(data, i) {
return colorFn(data[i + 3]);
}
} else {
throw ('error: format not supported: ' + opts.format);
}
if (opts.animated) {
if (this._baseGeometry === undefined) {
this._baseGeometry = new THREE.Geometry();
for (i = 0; i < data.length; i += step) {
lat = data[i];
lng = data[i + 1];
// size = data[i + 2];
color = colorFnWrapper(data, i);
size = 0;
addPoint(lat, lng, size, color, this._baseGeometry);
}
}
if (this._morphTargetId === undefined) {
this._morphTargetId = 0;
} else {
this._morphTargetId += 1;
}
opts.name = opts.name || 'morphTarget' + this._morphTargetId;
}
var subgeo = new THREE.Geometry();
for (i = 0; i < data.length; i += step) {
lat = data[i];
lng = data[i + 1];
color = colorFnWrapper(data, i);
size = data[i + 2];
size = size * 200;
addPoint(lat, lng, size, color, subgeo);
}
if (opts.animated) {
this._baseGeometry.morphTargets.push({
'name': opts.name,
vertices: subgeo.vertices
});
} else {
this._baseGeometry = subgeo;
}
};
function createPoints() {
if (this._baseGeometry !== undefined) {
if (this.is_animated === false) {
this.points = new THREE.Mesh(this._baseGeometry, new THREE.MeshBasicMaterial({
color: 0xffffff,
vertexColors: THREE.FaceColors,
morphTargets: false
}));
} else {
if (this._baseGeometry.morphTargets.length < 8) {
console.log('t l', this._baseGeometry.morphTargets.length);
var padding = 8 - this._baseGeometry.morphTargets.length;
console.log('padding', padding);
for (var i = 0; i <= padding; i++) {
console.log('padding', i);
this._baseGeometry.morphTargets.push({
'name': 'morphPadding' + i,
vertices: this._baseGeometry.vertices
});
}
}
this.points = new THREE.Mesh(this._baseGeometry, new THREE.MeshBasicMaterial({
color: 0xffffff,
vertexColors: THREE.FaceColors,
morphTargets: true
}));
}
scene.addObject(this.points);
}
}
function addPoint(lat, lng, size, color, subgeo) {
var phi = (90 - lat) * Math.PI / 180;
var theta = (180 - lng) * Math.PI / 180;
point.position.x = 200 * Math.sin(phi) * Math.cos(theta);
point.position.y = 200 * Math.cos(phi);
point.position.z = 200 * Math.sin(phi) * Math.sin(theta);
point.lookAt(mesh.position);
point.scale.z = -size;
point.updateMatrix();
var i;
for (i = 0; i < point.geometry.faces.length; i++) {
point.geometry.faces[i].color = color;
}
GeometryUtils.merge(subgeo, point);
}
function onMouseDown(event) {
event.preventDefault();
container.addEventListener('mousemove', onMouseMove, false);
container.addEventListener('mouseup', onMouseUp, false);
container.addEventListener('mouseout', onMouseOut, false);
mouseOnDown.x = -event.clientX;
mouseOnDown.y = event.clientY;
targetOnDown.x = target.x;
targetOnDown.y = target.y;
container.style.cursor = 'move';
}
function onMouseMove(event) {
mouse.x = -event.clientX;
mouse.y = event.clientY;
var zoomDamp = distance / 1000;
target.x = targetOnDown.x + (mouse.x - mouseOnDown.x) * 0.005 * zoomDamp;
target.y = targetOnDown.y + (mouse.y - mouseOnDown.y) * 0.005 * zoomDamp;
target.y = target.y > PI_HALF ? PI_HALF : target.y;
target.y = target.y < -PI_HALF ? -PI_HALF : target.y;
}
function onMouseUp(event) {
container.removeEventListener('mousemove', onMouseMove, false);
container.removeEventListener('mouseup', onMouseUp, false);
container.removeEventListener('mouseout', onMouseOut, false);
container.style.cursor = 'auto';
}
function onMouseOut(event) {
container.removeEventListener('mousemove', onMouseMove, false);
container.removeEventListener('mouseup', onMouseUp, false);
container.removeEventListener('mouseout', onMouseOut, false);
}
function onMouseWheel(event) {
event.preventDefault();
if (overRenderer) {
zoom(event.wheelDeltaY * 0.3);
}
return false;
}
function onDocumentKeyDown(event) {
switch (event.keyCode) {
case 38:
zoom(100);
event.preventDefault();
break;
case 40:
zoom(-100);
event.preventDefault();
break;
}
}
function onWindowResize(event) {
console.log('resize');
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth * 0.85, window.innerHeight * 0.85);
}
function zoom(delta) {
distanceTarget -= delta;
distanceTarget = distanceTarget > 1000 ? 1000 : distanceTarget;
distanceTarget = distanceTarget < 350 ? 350 : distanceTarget;
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
zoom(curZoomSpeed);
rotation.x += (target.x - rotation.x) * 0.1;
rotation.y += (target.y - rotation.y) * 0.1;
distance += (distanceTarget - distance) * 0.3;
camera.position.x = distance * Math.sin(rotation.x) * Math.cos(rotation.y);
camera.position.y = distance * Math.sin(rotation.y);
camera.position.z = distance * Math.cos(rotation.x) * Math.cos(rotation.y);
vector.copy(camera.position);
renderer.clear();
renderer.render(scene, camera);
renderer.render(sceneAtmosphere, camera);
}
init();
this.animate = animate;
this.__defineGetter__('time', function() {
return this._time || 0;
});
this.__defineSetter__('time', function(t) {
var validMorphs = [];
var morphDict = this.points.morphTargetDictionary;
for (var k in morphDict) {
if (k.indexOf('morphPadding') < 0) {
validMorphs.push(morphDict[k]);
}
}
validMorphs.sort();
var l = validMorphs.length - 1;
var scaledt = t * l + 1;
var index = Math.floor(scaledt);
for (i = 0; i < validMorphs.length; i++) {
this.points.morphTargetInfluences[validMorphs[i]] = 0;
}
var lastIndex = index - 1;
var leftover = scaledt - index;
if (lastIndex >= 0) {
this.points.morphTargetInfluences[lastIndex] = 1 - leftover;
}
this.points.morphTargetInfluences[index] = leftover;
this._time = t;
});
this.addData = addData;
this.createPoints = createPoints;
this.renderer = renderer;
this.scene = scene;
return this;
};
/// HERE I INIT THE GLOBE
// Where to put the globe?
var container = document.getElementById( 'container' );
// Make the globe
var globe = DAT.Globe(container);
// We're going to ask a file for the JSON data.
var xhr = new XMLHttpRequest();
// Where do we get the data?
xhr.open( 'GET', 'https://gist.githubusercontent.com/ilicmarko/6f1781bfc3f72ec52b658d93b7fe8353/raw/90dadfdf3087c278d669c6e1a2411aef70503000/world-population-data-zeroed', true );
// What do we do when we have it?
xhr.onreadystatechange = function() {
// If we've received the data
if ( xhr.readyState === 4 && xhr.status === 200 ) {
// Parse the JSON
var data = JSON.parse( xhr.responseText );
// Tell the globe about your JSON data
for ( var i = 0; i < data.length; i ++ ) {
globe.addData( data[i][1], {format: 'magnitude', name: data[i][0]} );
}
// Create the geometry
globe.createPoints();
// Begin animation
globe.animate();
}
};
// Begin request
xhr.send( null );
&#13;
body {
background-color: #222;
}
&#13;
<script type="text/javascript" src="https://globe.chromeexperiments.com/third-party/Three/ThreeWebGL.js"></script>
<script type="text/javascript" src="https://globe.chromeexperiments.com/third-party/Three/ThreeExtras.js"></script>
<script type="text/javascript" src="https://globe.chromeexperiments.com/third-party/Three/RequestAnimationFrame.js"></script>
<script type="text/javascript" src="https://globe.chromeexperiments.com/third-party/Three/Detector.js"></script>
<div id="container" class="globe"></div>
&#13;