• Examples
  • API
  • Code
  • Source

Three.js globe example

Three.js globe example.

This page's try to Three.js globe example. Ref

Tags: three.js, webgl

Copy
<!DOCTYPE html>
<html>
  <head>
    <title>Three.js globe example</title>
    <link rel="stylesheet" href="http://www.3daysofprogramming.com/playground/pg.css" type="text/css">
    <!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
    <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL,Map,Set,Promise"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.min.js"></script>
    <script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Turf.js/5.1.5/turf.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.5.0/proj4.js"></script>
    <script src="http://www.3daysofprogramming.com/playground/examples/resources/js/stats.js"></script>
    <style>
      #container {
        position: relative;
        overflow: hidden;
      }
    </style>
  </head>
  <body>
    <div id="container" class="main">
    </div>
    <script>
      // three
      var VIEW_ANGLE = 45;
      var NEAR = 0.1;
      var FAR = 2000;

      // Earth
      var RADIUS = 200;

      var renderer;
      var scene;
      var camera;
      var width;
      var height;
      var container;
      var globe;
      var capitals = [];

      var raycaster = new THREE.Raycaster();
      var mouse = new THREE.Vector2();

      // Proj4
      proj4.defs([
        [
          'Globe',
          '+proj=longlat +a=6367470 +b=6367470 +ellps=sphere +datum=WGS84 +units=degrees'
        ]
      ]);
      // console.log(proj4('Globe', 'EPSG:4326', [126.982512, 37.564174]));
      // console.log(proj4('EPSG:4326', 'EPSG:3857', [126.982512, 37.564174]));

      init();
      animate();

      function init() {
        container = document.getElementById('container');
        width = container.clientWidth;
        height = container.clientHeight;

        // initEvent(container);

        // scene
        scene = new THREE.Scene({antialias: true});
        scene.background = new THREE.Color(0x000);
        scene.fog = new THREE.FogExp2(0x000000, 0.0003);

        // camera
        camera = new THREE.PerspectiveCamera(VIEW_ANGLE, width / height, NEAR, FAR);
        camera.position.set(0, 0, 1000);
        // camera.lookAt(0, 0, 0);

        // light
        var pointLight = new THREE.PointLight(0xFFFFFF);
        pointLight.position.set(10, 50, 400);
        scene.add(pointLight);

        // globe
        var radius = RADIUS;
        var segments = 50;
        var rings = 50;

        globe = new THREE.Group();
        scene.add(globe);

        var loader = new THREE.TextureLoader();
        loader.load(
          'http://i.imgur.com/puZgGjm.jpg',
          function (texture) {
            // Create the sphere
            var sphere = new THREE.SphereGeometry(radius, segments, rings);

            // Map the texture to the material.
            var material = new THREE.MeshBasicMaterial({
              map: texture,
              overdraw: 0.5,
              transparent: true,
              opacity: 0.5
            });

            // Create a new mesh with sphere geometry.
            var mesh = new THREE.Mesh(sphere, material);

            // Add mesh to globe
            globe.add(mesh);
          }
        );

        // stars
        var starSize = 45000;
        var geometry = new THREE.SphereGeometry(1000, 100, 50);

        var materialOptions = {
          size: 1.0, //I know this is the default, it's for you.  Play with it if you want.
          opacity: 0.7
        };

        var material = new THREE.PointsMaterial(materialOptions);

        // The wizard gaze became stern, his jaw set, he creates the cosmos with a wave of his arms

        for (var i = 0; i < starSize; i++) {
          var vector = new THREE.Vector3();
          vector.x = (Math.random() * 1000 + 1000) * (Math.round(Math.random()) ? 1 : -1);
          vector.y = (Math.random() * 1000 + 1000) * (Math.round(Math.random()) ? 1 : -1);
          vector.z = (Math.random() * 1000 + 1000) * (Math.round(Math.random()) ? 1 : -1);

          geometry.vertices.push(vector);
        }

        var stars = new THREE.Points(geometry, material);
        scene.add(stars);

        // renderer
        renderer = new THREE.WebGLRenderer({
          alpha: true,
          antialias: true
        });
        renderer.setClearColor(0xFFFFFFF, 0);
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(width, height);

        container.appendChild(renderer.domElement);

        // controls
        var controls = new THREE.OrbitControls(camera, renderer.domElement);
        controls.screenSpacePanning = true;
        controls.minDistance = 300;
        controls.maxDistance = FAR;

        window.addEventListener('resize', onWindowResize, false);
        window.addEventListener('click', onClick);
        window.addEventListener('dblclick', onDBClick);

        loadGeojson();
      }

      function onWindowResize() {
        width = container.clientWidth;
        height = container.clientHeight;
        camera.aspect = width / height;
        camera.updateProjectionMatrix();
        renderer.setSize(width, height);
      }

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

      function render() {
        renderer.render(scene, camera);
        capitals.forEach(function (capital) {
          if (
            globe.position.distanceTo(camera.position) < capital.position.distanceTo(camera.position)
          ) {
            capital.sphere.visible = false;
            capital.text.visible = false;
          } else {
            capital.sphere.visible = true;
            capital.text.visible = true;
          }
        });
      }

      function loadGeojson() {
        fetch('data/geojson/capitals.geojson')
          .then(function (response) { return response.json(); })
          .then(function (geojson) {
            capitals = geojson.features
              .map(function (feature) {
                return Object.assign(
                  {},
                  feature.properties,
                  {
                    coordinates: turf.getCoord(feature)
                  }
                );
              })
              .filter(function (capital) { return capital.city; });

            capitals.forEach(function (capital) {
              var ref = capital.coordinates;
              var lng = ref[0];
              var lat = ref[1];
              // const [lng, lat] = [126.982512, 37.564174]; // Seoul
              // const [lng, lat] = [126.529541, 33.364209]; // Jeju
              // const [lng, lat] = [-73.959961, 40.725275]; // New York
              // const [lng, lat] = [-74.132116, 20.215811];
              // const [lng, lat] = proj4('Globe', 'EPSG:4326', [-74.132116, 20.215811]);

              var geometry = new THREE.SphereGeometry(1);
              var material = new THREE.MeshBasicMaterial({color: '#ff0000'});
              var sphere = new THREE.Mesh(geometry, material);
              var position = convertLngLatToVector3(lng, lat, RADIUS);

              sphere.position.copy(position);
              scene.add(sphere);
              capital.position = position;
              capital.sphere = sphere;

              var text = makeTextSprite(capital.city, {fontsize: 50});

              text.position.copy(convertLngLatToVector3(lng, lat, RADIUS + 10));
              scene.add(text);
              capital.text = text;
            });
          });

        fetch('data/geojson/countries.geojson')
          .then(function (response) { return response.json(); })
          .then(function (geojson) {
            var countries = geojson.features;
            countries.forEach(function (country) {
              var type = country.geometry.type;
              if (type === 'Polygon') {
                scene.add(getLineMesh(country.geometry.coordinates[0]));
              } else if (type === 'MultiPolygon') {
                country.geometry.coordinates.forEach(function (coordinates) { return scene.add(getLineMesh(coordinates[0])); });
              }
            });

          });
      }

      function getLineMesh(coordinates) {
        var material = new THREE.LineBasicMaterial({color: 0xffffff});
        var geometry = new THREE.Geometry();

        coordinates.forEach(function (coordinate) {
          var lng = coordinate[0];
          var lat = coordinate[1];
          var position = convertLngLatToVector3(lng, lat, RADIUS);
          geometry.vertices.push(position);
        });
        return new THREE.Line(geometry, material);
      }

      function makeTextSprite(message, parameters) {
        if (parameters === undefined) { parameters = {}; }

        var fontface = parameters.hasOwnProperty('fontface') ? parameters['fontface'] : 'Arial';
        var fontsize = parameters.hasOwnProperty('fontsize') ? parameters['fontsize'] : 18;
        var borderThickness = parameters.hasOwnProperty('borderThickness') ? parameters['borderThickness'] : 4;
        var borderColor = parameters.hasOwnProperty('borderColor') ? parameters['borderColor'] : {r: 0, g: 0, b: 0, a: 1.0};
        var backgroundColor = parameters.hasOwnProperty('backgroundColor') ? parameters['backgroundColor'] : {
          r: 255,
          g: 255,
          b: 255,
          a: 1.0
        };
        var textColor = parameters.hasOwnProperty('textColor') ? parameters['textColor'] : {r: 0, g: 0, b: 0, a: 1.0};

        var canvas = document.createElement('canvas');
        var context = canvas.getContext('2d');
        context.font = 'Bold ' + fontsize + 'px ' + fontface;
        var metrics = context.measureText(message);
        var textWidth = metrics.width;

        context.fillStyle = 'rgba(' + backgroundColor.r + ',' + backgroundColor.g + ',' + backgroundColor.b + ',' + backgroundColor.a + ')';
        context.strokeStyle = 'rgba(' + borderColor.r + ',' + borderColor.g + ',' + borderColor.b + ',' + borderColor.a + ')';

        context.lineWidth = borderThickness;
        roundRect(context, borderThickness / 2, borderThickness / 2, (textWidth + borderThickness) * 1.1, fontsize * 1.4 + borderThickness, 8);

        context.fillStyle = 'rgba(' + textColor.r + ', ' + textColor.g + ', ' + textColor.b + ', 1.0)';
        context.fillText(message, borderThickness, fontsize + borderThickness);

        var texture = new THREE.Texture(canvas);
        texture.needsUpdate = true;

        var spriteMaterial = new THREE.SpriteMaterial({map: texture});
        var sprite = new THREE.Sprite(spriteMaterial);
        sprite.scale.set(0.5 * fontsize, 0.25 * fontsize, 0.75 * fontsize);
        return sprite;
      }

      function roundRect(ctx, x, y, w, h, r) {
        ctx.beginPath();
        ctx.moveTo(x + r, y);
        ctx.lineTo(x + w - r, y);
        ctx.quadraticCurveTo(x + w, y, x + w, y + r);
        ctx.lineTo(x + w, y + h - r);
        ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
        ctx.lineTo(x + r, y + h);
        ctx.quadraticCurveTo(x, y + h, x, y + h - r);
        ctx.lineTo(x, y + r);
        ctx.quadraticCurveTo(x, y, x + r, y);
        ctx.closePath();
        ctx.fill();
        ctx.stroke();
      }

      function onClick(event) {
        // mouse.x = (event.clientX / renderer.domElement.clientWidth) * 2 - 1;
        // mouse.y = -(event.clientY / renderer.domElement.clientHeight) * 2 + 1;
        mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
        mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

        raycaster.setFromCamera(mouse, camera);

        var intersects = raycaster.intersectObject(globe, true);

        for (var i = 0; i < intersects.length; i++) {
          console.log(convertVector3ToLngLat(intersects[i].point));
        }
      }

      function onDBClick(event) {
        mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
        mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

        raycaster.setFromCamera(mouse, camera);

        var intersects = raycaster.intersectObject(globe, true);

        for (var i = 0; i < intersects.length; i++) {
          var geometry = new THREE.SphereGeometry(0.5);
          var material = new THREE.MeshBasicMaterial({color: '#ff0000'});
          var sphere = new THREE.Mesh(geometry, material);
          sphere.position.copy(intersects[i].point);
          scene.add(sphere);
        }
      }

      function convertLngLatToVector3(lng, lat, radius) {
        var phi = deg2rad(90 - lat);
        var theta = deg2rad(lng + 180);

        var x = -((radius) * Math.sin(phi) * Math.cos(theta));
        var z = ((radius) * Math.sin(phi) * Math.sin(theta));
        var y = ((radius) * Math.cos(phi));

        return new THREE.Vector3(x, y, z);
      }

      function convertVector3ToLngLat(vector) {
        vector.normalize();
        var lng = (270 + rad2deg(Math.atan2(vector.x, vector.z))) % 360;

        var p = new THREE.Vector3(vector.x, 0, vector.z);
        p.normalize();

        var lat = rad2deg(Math.acos(p.dot(vector)));

        if (vector.y < 0) {
          lat *= -1;
        }

        return [lng, lat];
      }

      function deg2rad(degree) {
        return degree * (Math.PI / 180);
      }

      function rad2deg(radians) {
        return radians * (180 / Math.PI);
      }
    </script>
  </body>
</html>