La introducción de técnicas gráficas WebGL para la visualización de datos es una técnica que se va extendiendo gracias al poder visual que conlleva y facilidad de expresión sobre la complejidad de los datos que podemos manejar.
El salto al 3D ofrece de hecho un nuevo eje donde poder crear nuevas formas de expresar y comprender el dato, y para poder expresarlo he creado un ligero ejemplo en GitHub donde podéis seguir esta experiencia, donde podréis acceder, copiar y comentar.
Estructura HTML
El ejemplo consta de una estructura básica de HTML:
- Un sencillo index.html
- Un Javascript terrain.js
- Una carpeta que carga las librerías WebGL que en este caso son de ThreeJS.
ThreeJS es la librería WebGL más importante para el desarrollo en sistemas WebGL y 3D, que de hecho es equipada en proyectos tan relevantes en geo como Mapbox, Potree o Terre3.
En esta carpeta encontramos 3 archivos:
- Detector.js: Nos permite averiguar la disponibilidad de WebGL del navegador.
- OrbitControls.js: Habilita los movimientos de cámara en 3D con el ratón.
- Three.min.js: Es el corazón de la librería
Código de la aplicación web 3D
HTML
EL HTML no tiene mucha miga ya que será el Javascript quien haga el trabajo principalmente.
Encontramos en la cabecera la llamada a nuestro Javascript terrain.js y una imagen en el cuerpo que será la que utilicemos para crear nuestro mapa, y finalmente en la zona de script dos llamadas a nuestra librería, una para crear propiamente el mapa 3D y la otra para insertar los pilares con las magnitudes tal como se ve en la imagen del ejemplo.
<!DOCTYPE html>
<html>
<head>
<title>OGC Maps 3D - TR3</title>
<script src="terrain.js"></script>
</head>
<body>
<img
id="imgOri"
src="spain-01.png"
width="892"
height="632"
style="display: none"
/>
<div id="contMeshMap" style="width: 600px; height: 600px"></div>
<script type="text/javascript">
function initMap() {
var imgOriObj = document.getElementById("imgOri");
TR3.setMeshMap(imgOriObj, "contMeshMap");
TR3.loadFile([{ point: [30, 3, -70, "Madrid"] }], [[20, 1, 20]]);
}
initMap();
</script>
</body>
</html>
Una estructura muy sencilla que consiste en cargar una imagen “imgOriObj” que será la base de nuestro mapa, para transformarla y llevarla al DIV de destino “contMeshMap”.
Terrain.js
Aquí haremos nuestra magia WebGL con la librería ThreeJS.
Inicialización
Desde el HTML comenzamos llamando a la función “setMeshMap” y esta nos lleva a la inicialización del entorno WebGL que gira entorno a la escena que va a contener todos los elementos que queramos visualizar.
TR3.scene = new THREE.Scene();
Seguidamente llamamos a la cámara para que la escena se visualice con los parámetros de su campo visual.
TR3.camera = new THREE.PerspectiveCamera(TR3.fov, TR3.canvasDestW / TR3.canvasDestH, 1, 20000);
Para poder manejar la cámara mediante el ratón y cursor llamamos a orbitControls con la cámara y el lugar de destino como parámetros.
TR3.controls = new THREE.OrbitControls( TR3.camera, canvasDest );
Finalmente seguimos con la función que creará el mapa “makeWorld” con la imagen como parámetro, también se llama a la función de la animación para que ocurra el movimiento en la escena y unos eventos básicos.
TR3.makeWorld( imgOri );
El mapa
En la función makeWorld, hacemos que la imagen se convierta en un elemento 3D con una simple función, y colocamos la cámara en su posición.
TR3.camera.position.y = Math.cos(radianFOV/2)*(TR3.widthImg/2)/Math.sin(radianFOV/2)/1.5;
TR3.camera.position.z = TR3.camera.position.y*Math.sin(Math.PI/4);
/*Texture-Material*/
var texture = new THREE.Texture(imgConteint);
texture.needsUpdate = true;
TR3.material = new THREE.MeshBasicMaterial({map: texture});
/*Image-Mesh*/
var geometry = new THREE.PlaneBufferGeometry( TR3.widthImg, TR3.heightImg );
TR3.mesh = new THREE.Mesh( geometry, TR3.material);
Posicionamos la cámara con razones trigonométricas basadas en el ángulo de cámara o FOV, y creamos la malla 3D que contiene la imagen del mapa. La malla, se crea mediante un material y una geometría, obviamente la geometría es un plano del tamaño de la imagen “PlaneBufferGeometry( Width, Height )” y el material es la textura que tiene la imagen “MeshBasicMaterial({ map: texture })”, con lo que el plano se crea en base a este binomio de textura y geometría.
Los pilares
Para finalizar, incluimos los pilares que representan las magnitudes, que como en el caso del mapa se componen de una geometría y un material, que es lo habitual.
var gltf = new Array();
var geometry = new THREE.BoxBufferGeometry( 1, 1, 1 );
var material = new THREE.MeshNormalMaterial();
var objGLTF = new THREE.Mesh( geometry, material );
gltf.scene = objGLTF;
objGLTF.position.set( pos[0].point[0], pos[0].point[1], pos[0].point[2] );
objGLTF.scale.set( scale[0][0], scale[0][1], scale[0][2] );
geometry.translate( 0, 0.5, 0 );
TR3.scene.add( objGLTF );
En esta ocasión creamos el pilar mediante una geometría de caja “BoxBufferGeometry(1,1,1)” de tamaño 1 y un material predeterminado “MeshNormalMaterial()” Que posicionamos y escalamos según los parámetros que le estemos dando desde la llamada en el HTML, que finalmente se añade a la escena.
Conclusiones
Este pequeño ejemplo es un reflejo de lo que se pude hacer con un puñado de líneas y un par de nuevos conceptos, siendo un comienzo para la curiosidad y la exploración. Así que os animo a descargar el ejemplo modificar y experimentar en este entorno, donde hay todo un mundo que explorar creando nuevas habilidades sorprendentes.
Autor: Agustín Costa Cimadevilla
Tutor del Curso de Desarrollo de aplicaciones de mapas web en 3D con tecnologías de código abierto (ThreeJS)
¿Quieres comentarnos algo? Adelante!