<!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>