Mapas OSM en 3D con three.js

Three.js es una librería de Javascript (creada por @mrdoob) que permite usar WebGL – 3D en el navegador de forma fácil, sin necesidad de saber realmente WebGL, solo hay que conocer cuatro conceptos básicos (cámara, luces, materiales, objetos). Además es OpenSource, con ella podemos visualizar mapas en 3D con datos, por ejemplo, sacados de OpenStreetMap. 

¿Y que es WebGL (Web Graphics Library)? es una API de JavaScript que renderiza graficos 3D interactivos en los navegadores compatibles sin necesidad de instalar plug-ins. Mozilla, Chome, Safari y Opera lo permiten,  Intenet Explorer lo hace solo a partir de la versión 11.

Primero hay que descargar los datos OpenStreetMap de la zona que te interese, mejor que sea una zona pequeña o no se renderizara bien en el navegador. Una vez descargado tendrás un archivo del tipo “map.osm”

Luego hay que descargar OSM2world, puedes usar la última versión: latest build/ OSM2World-latest-bin.zip

OSM2world es un converter open source que transforma los datos 2D (map.osm) de OpenStreetMap a objetos 3D (map.obj)

En este wiki tienes explicado su funcionamiento y los comandos básicos. Antes de seguir asegúrate de que tienes Java actualizado, una vez descomprimido el archivo (te sitúas dentro de la carpeta) y ejecutas en consola:

./osm2world.sh -i ../bcn_osm/map.osm -o ../bcn_osm/map.obj

Una vex obtenido el archivo map.obj hay que usar otro converter  (convert_obj_three.py ) para pasarlo a un formato JSON que pueda leer three.js (map.js)

De nuevo desde la consola:

python convert_obj_three.py -i map.obj -o map.js

Una vez tenemos los datos en el formato adecuado solo hay que  añadir las librerias adecuadas a nuestro código:

http://threejs.org/build/three.min.js

http://threejs.org/examples/js/controls/OrbitControls.js

“lights, camera, action”

Para poder visualizar algo en el navegador hay que crear los elementos básicos necesarios una escena, una cámara y un renderizado.

var scene, camera, renderer, loader, light, controls;

cámara

Primero se selecciona la cámara (PerspectiveCamera ) el tamaño de la escena que se vera con esa cámara, y el angulo (45º), y un ratio de aspecto  (anchura del elemento dividido por la altura).  Luego se seleccionan otros dos atributos: “near” y “far” que indican que si el objeto se aleja o se acerca de la cámara mas que esos valores, ese objeto no se renderizara.

Hay tres tipos de cámaras:
- PerspectiveCamera (perspectiva normal, objetos más lejanos se ven de menor tamaño)
- OrthographicCamera (proyección paralela, el tamaño de los objetos no cambia)
- CombinedCamera (permite cambiar entre uno y otro durante la ejecución del programa)

La posición por defecto de la cámara es (0, 0, 0), en este caso la hemos situado en la posición y = 1000. El eje Y es la altura y no Z, al contrario de lo que cabría esperar. El plano x es el horizontal, el plano y es vertical y el plano z es la profundidad.

ejes en un 3D grid

var WIDTH = window.innerWidth,
    HEIGHT = window.innerHeight;
var VIEW_ANGLE = 45, 
    ASPECT = WIDTH / HEIGHT, 
    NEAR = 1, 
    FAR = 10000;

scene = new THREE.Scene(); 
camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR); 
camera.position.y = 1000; 

// add the camera to the scene
scene.add(camera);

Lighting Methods

Se añade la luz, en este caso se ha escogido Directional light, que es una luz que imita la luz solar.

light = new THREE.DirectionalLight(0xffffff);

Se añaden los controles de la órbita, que nos permiten orbitar con botón izquierdo del  ratón, desplazarnos con el derecho y hacer zoom con la rueda:

controls = new THREE.OrbitControls(camera);

Otro parámetro que se puede controlar es el angulo máximo de rotación de la órbita, este por defecto se sitúa entre 0 y Pi Radianes (0-180º).  Si queremos que no se pueda ver por debajo de la línea del horizonte hay que añadir esta línea para que el máximo sea Pi/2 (90º).

controls.maxPolarAngle = Math.PI/2;

Se cargan los datos de la geometría creados anteriormente (map.js) gracias a el JSONLoader.

loader = new THREE.JSONLoader();

loader.load('./obj/mapas/map.js', function (geometry, materials) {
var mesh, material;

material = new THREE.MeshFaceMaterial(materials);
mesh = new THREE.Mesh(geometry, material);

mesh.scale.set(1, 1, 1);
mesh.receiveShadow = true;
mesh.castShadow = true;

scene.add(mesh);
});

A la hora renderizar se crea un bucle llamado “render loop”, este bucle lo que hace es dibujar la escena 60 veces por segundo.  El requestAnimationFrame tiene la ventaja de que se para cuando el usuario cambia de pestaña, ahorrando de esta manera recursos y bateria.

function render() { 
    requestAnimationFrame(render);
    controls.update();
    renderer.render(scene, camera); 
} 
render();

El resultado:

bcn3D

Se puede encontrar el código en github y además podeis ver el mapa3d

Tutoriales de Three.js:

Ejemplo basado en: https://gist.github.com/roman01la/5794160