<div id="container"></div>

<script src="https://cdn.rawgit.com/dataarts/webgl-globe/2d24ba30/globe/third-party/three.min.js"></script>
<!-- <script src="https://cdn.rawgit.com/dataarts/webgl-globe/2d24ba30/globe/globe.js"></script> -->

html {
  height: 100%;
}
body {
  margin: 0;
  padding: 0;
  height: 100%; 
} 
View Compiled
var DAT = DAT || {};

// blue 0x96ca6
// green 0x9de3cb

// green c.setHSL(  0.441, 0.56, 0.75 );
// blue c.setHSL(  0.558, 0.62, 0.75 );

DAT.Globe = function(container, opts) {
  opts = opts || {};

  var colorFn = opts.colorFn || function(x) {
    var c = new THREE.Color();
    
    c.setHSL(0.55 + (x / 2), 0.60, 0.75);
    return c;
    }; 

  var Shaders = {
    earth: {
      uniforms: {
        texture: { type: "t", value: null }
      },
      vertexShader: [
        "varying vec3 vNormal;",
        "varying vec2 vUv;",
        "void main() {",
        "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.05 );",
        "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( 0, 1.0, 1.0 ) * pow( intensity, 3.0 );",
        "gl_FragColor = vec4( diffuse + atmosphere, 0.3 );",
        "}"
      ].join("\n")
    },
    atmosphere: {
      uniforms: {},
      vertexShader: [
        "varying vec3 vNormal;",
        "void main() {",
        "vNormal = normalize( normalMatrix * normal );",
        "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 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( 1.0, 1.0, 1.0, 1.0 ) * intensity;",
        "}"
      ].join("\n")
    }
  };

  var camera, scene, renderer, w, h;
  var mesh, atmosphere, point;

  var overRenderer;

  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 = 1000000,
    distanceTarget = 100000;
  var padding = 40;
  var PI_HALF = Math.PI / 2;

  function init() {
    var shader, uniforms, material;
    w = container.offsetWidth || window.innerWidth;
    h = container.offsetHeight || window.innerHeight;

    camera = new THREE.PerspectiveCamera(30, w / h, 1, 10000);
    camera.position.z = distance;

    scene = new THREE.Scene();

    var geometry = new THREE.SphereGeometry(200, 40, 30);

    shader = Shaders["earth"];
    uniforms = THREE.UniformsUtils.clone(shader.uniforms);

    THREE.ImageUtils.crossOrigin = "";

    uniforms["texture"].value = THREE.ImageUtils.loadTexture(
      "http://cdn.rawgit.com/dataarts/webgl-globe/2d24ba30/globe/world.jpg"
    );

    material = new THREE.ShaderMaterial({
      uniforms: uniforms,
      vertexShader: shader.vertexShader,
      fragmentShader: shader.fragmentShader,
      transparent: true
    });

    mesh = new THREE.Mesh(geometry, material);
    mesh.rotation.y = Math.PI;
    scene.add(mesh);

    shader = Shaders["atmosphere"];
    uniforms = THREE.UniformsUtils.clone(shader.uniforms);

    material = new THREE.ShaderMaterial({
      uniforms: uniforms,
      vertexShader: shader.vertexShader,
      fragmentShader: shader.fragmentShader,
      side: THREE.BackSide,
      blending: THREE.AdditiveBlending,
      transparent: true
    });

    mesh = new THREE.Mesh(geometry, material);
    mesh.scale.set(1.1, 1.1, 1.1);
    scene.add(mesh);

    geometry = new THREE.BoxGeometry(0.75, 0.75, 1);
    geometry.applyMatrix(new THREE.Matrix4().makeTranslation(0, 0, -0.5));

    point = new THREE.Mesh(geometry);

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(w, h);
    
    renderer.setClearColor(0x111111, 1);

    renderer.domElement.style.position = "absolute";

    container.appendChild(renderer.domElement);

    container.addEventListener("mousedown", onMouseDown, 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
    );
  }

  function addData(data, opts) {
    var lat, lng, size, color, i, step, colorFnWrapper;

    step = 3;
    colorFnWrapper = function(data, i) {
      return colorFn(data[i + 2]);
    };
    
    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);
    }
    
    this._baseGeometry = subgeo;
  }
  
  // material texture
  var mapFront = new THREE.Texture( generateTexture('front') ),
    mapBack = new THREE.Texture( generateTexture('back') ),
    mapLeft = new THREE.Texture( generateTexture('left') ),
    mapRight = new THREE.Texture( generateTexture('right') ),
    mapTop = new THREE.Texture( generateTexture('top') );
  
  mapFront.needsUpdate = true;
  mapBack.needsUpdate = true;
  mapLeft.needsUpdate = true;
  mapRight.needsUpdate = true;
  mapTop.needsUpdate = true;
  
  function createPoints() {
    
    this.points = new THREE.Mesh(
      this._baseGeometry,
      new THREE.MeshFaceMaterial([
        new THREE.MeshBasicMaterial({
          map: mapLeft, 
          transparent: true,
          vertexColors: THREE.FaceColors
        }),
        new THREE.MeshBasicMaterial({
          map: mapRight, 
          transparent: true,
          vertexColors: THREE.FaceColors
        }),
        new THREE.MeshBasicMaterial({
          map: mapFront, 
          transparent: true,
          vertexColors: THREE.FaceColors
        }),
        new THREE.MeshBasicMaterial({
          map: mapBack, 
          transparent: true,
          vertexColors: THREE.FaceColors
        }),
        // Bottom
        new THREE.MeshBasicMaterial({
          vertexColors: THREE.FaceColors
        }),
        // Top
        new THREE.MeshBasicMaterial({
          transparent: true,
          alphaTest: 1
        })
      ])
    );
    
    scene.add(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 = Math.max(size, 0.1); // avoid non-invertible matrix
    point.updateMatrix();

    for (var i = 0; i < point.geometry.faces.length; i++) {
      point.geometry.faces[i].color = color;
    }
    
    if (point.matrixAutoUpdate) {
      point.updateMatrix();
    }
    
    subgeo.merge(point.geometry, point.matrix);
  }

  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 onDocumentKeyDown(event) {
    switch (event.keyCode) {
      case 38:
        zoom(100);
        event.preventDefault();
        break;
      case 40:
        zoom(-100);
        event.preventDefault();
        break;
    }
  }

  function onWindowResize(event) {
    camera.aspect = container.offsetWidth / container.offsetHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(container.offsetWidth, container.offsetHeight);
  }

  function zoom(delta) {
    distanceTarget -= delta;
    distanceTarget = distanceTarget > 1100 ? 1100 : distanceTarget;
    distanceTarget = distanceTarget < 350 ? 350 : distanceTarget;
  }

  function animate() {
    requestAnimationFrame(animate);
    render();
  }

  function render() {
    zoom(curZoomSpeed);

    rotation.x +=  0.005;
    rotation.y += (target.y - rotation.y) * 0.1;
    distance += (distanceTarget - distance) * 0.8;

    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);

    camera.lookAt(mesh.position);

    renderer.render(scene, 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;
};

var container = document.getElementById("container");
var globe = new DAT.Globe(container);

var xhr = new XMLHttpRequest();

xhr.open(
  "GET",
  "https://cdn.rawgit.com/dataarts/webgl-globe/2d24ba30/globe/population909500.json",
  true
);

xhr.onreadystatechange = function(e) {
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
      var data = JSON.parse(xhr.responseText);
      window.data = data;
      for (i = 0; i < data.length; i++) {
        globe.addData(data[i][1], {
          format: "magnitude",
          name: data[i][0],
          animated: false
        });
      }
      globe.createPoints();
      globe.animate();
    }
  }
};
xhr.send(null);

 

function generateTexture(alt) {

  var size = 16;

  // create canvas
  canvas = document.createElement( 'canvas' );
  canvas.width = size;
  canvas.height = size;

  // get context
  var context = canvas.getContext( '2d' );

  // draw gradient
  context.rect( 0, 0, size, size );
  var gradient;
  
  if (alt == 'front' || alt == 'back') {
    gradient = context.createLinearGradient( 0, 0, 0, size );
  } else {
    gradient = context.createLinearGradient( 0, 0, size, 0 );
  }
  
  if (alt == 'front') {
    gradient.addColorStop(0, 'transparent');
    gradient.addColorStop(1, 'white');
  } else if (alt == 'back') {
    gradient.addColorStop(1, 'transparent');
    gradient.addColorStop(0, 'white');
  } else if (alt == 'left') {
    gradient.addColorStop(1, 'transparent');
    gradient.addColorStop(0, 'white');
  } else if (alt == 'right') {
    gradient.addColorStop(0, 'transparent');
    gradient.addColorStop(1, 'white');
  } else {
    gradient.addColorStop(0, 'transparent');
    gradient.addColorStop(1, 'white');
  }
  context.fillStyle = gradient;
  context.fill();

  return canvas;

}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdn.rawgit.com/dataarts/webgl-globe/2d24ba30/globe/third-party/Detector.js
  2. https://cdn.rawgit.com/dataarts/webgl-globe/2d24ba30/globe/third-party/Tween.js