Visores de mapas (parte 2): CesiumJS

La semana pasada empezamos a hablaros sobre las principales librerías de visores de mapas web. Hoy nos vamos a centrar en a hablar de la primera de estas librerías, la de CesiumJS.

Como ya dijimos, es una librería Open Source, bajo licencia Apache, diseñada para crear mapas en 2D, 2,5D y 3D a nivel global, con buen rendimiento, precisión, calidad visual y facilidad de uso.

Usando la librería

Para usar esta librería, tenemos tres posibles opciones:

  • Descargar e instalarla.
  • Utilizar el entorno Sandcastle.
  • Consumirla vía URL.

Descargar e instalarla

A la hora de escribir esta entrada, la versión disponible de Cesium es la 1.77, que podéis descargar desde este enlace. Sin importar la versión que sea, el funcionamiento será siempre el mismo, por lo que no os preocupéis por ello.

Para poder usarla, tenéis que montar un servidor web en la localización de la carpeta (o subir la carpeta a vuestro servidor web propio, claro). Para esto, normalmente se utiliza Node.js, el cual podéis descargar y montar en vuestro local, o en caso de que uséis Visual Studio, hacer uso de la extensión de Live Server, que lo incorpora y te permite simular el entorno web (así es como suelo trabajo yo).

Entorno Sandcastle

En la página web de Cesium existe un editor de código llamado Sandcastle, con multitud de ejemplos, que nos permite practicar y jugar con las distintas opciones disponibles. Lo mejor de todo es que no hay que preocuparse ni de librerías, ni de configuraciones ni nada.

Si bien esto está pensado para poner el práctica los ejemplos descritos, cambiando las opciones y parámetros de entrada, siempre es posible generar un visor completo para, seguidamente, exportar el código y utilizarlo en algún otro sitio, de manera similar a los visores GIS de la Onesait Platform.

Otra opción muy interesante que incluye, es la de generar un enlace que permita compartir con otra personas el visor generado, junto a su código.

Esto es algo muy práctico si necesitáis prototipar algo para enseñar rápidamente, sin tener que montaros el entorno de desarrollo.

Me gustaría indicar la obligatoriedad de usar la puntuación al final de cada declaración de código. Si venís de Java o similar no hay problema, pero si venís de Python…

Consumir la librería vía URL

Por último, tenemos la opción de generar nuestro visor web consumiendo la librería vía URL, como si se tratase de cualquier otra librería alojada en algún servidor.

Esta opción es muy práctica para tus desarrollos, ya sea utilizando las URLs de la página de Cesium, vía NPM o desde una carpeta local que tengas por ahí metida.

Entorno de trabajo

Para llevar a cabo este tutorial, vamos a hacer uso de la Onesait Platform como entorno web. Concretamente, vamos a hacer uso de CloudLab, el entorno de experimentación gratuito en el que se puede probar todas las capacidades de la Plataforma sin limitación.

La idea, por tanto, será generar un Dashboard con un Gadget, el cual contendrá el mapa y en donde podremos ir experimentando de una manera rápida y sencilla.

Cuando terminemos, tendremos algo como esto:

¿Que por qué parpadea? Ya lo veremos más adelante.

Si ya sabéis cómo montar el Dashboard, cargar las librerías y generar el Gadget, podéis saltaros este apartado y continuar al de «Generar el mapa de Cesium».

Carga de las librerías

Para simplificar las cosas, vamos a hacer uso de las librerías disponibles desde el servidor de Cesium, por lo que nos ahorramos el tener que estar instalando ni montando nada.

En concreto necesitamos dos librerías: una con las funcionalidades JavaScript y otra con los estilos de diseño:

https://cesium.com/downloads/cesiumjs/releases/1.77/Build/Cesium/Cesium.js
https://cesium.com/downloads/cesiumjs/releases/1.77/Build/Cesium/Widgets/widgets.css

Nota: es importante cargar siempre la librería de JS por encima de la de CSS, para evitar que sucedan errores extraños.

Con estas URLs copiadas en el portapapeles, continuamos.

Generación del Dashboard

Ya en la Plataforma, navegaremos al menú de Visualization & GIS > My Dashboards.

En la ventana que nos aparecerá, pulsaremos en el botón de «Nuevo Dashboard», situado arriba a la derecha, para crear un nuevo Dashboard.

Aquí lo que haremos será rellenar las opciones que nos solicita el asistente de creación:

  • Identification: aquí le damos un nombre característico; en este caso le he puesto «tutorial_blog_Cesium».
  • Description: indicamos un texto que describa nuestro Dashboard.
  • Public: marcamos si queremos que el Dashboard sea de acceso libre para todo el mundo. Para poder incrustarlo ahí arriba, lo tengo como público.
  • Resto de opciones: las podemos dejar como está, que no nos afecta para este tutorial.

A continuación viene lo importante; incluir las librerías de Cesium. Esto lo haremos en la pantalla inferior, donde pone «Global Styles, Libraries and Scripts».

Como si se tratase del contenido del <body> de un HTML, lo incluiremos tal que así:

  <script src="https://cesium.com/downloads/cesiumjs/releases/1.77/Build/Cesium/Cesium.js"></script>

  <link href="https://cesium.com/downloads/cesiumjs/releases/1.77/Build/Cesium/Widgets/widgets.css" rel="stylesheet">

Una vez hecho esto, únicamente tendremos que darle al botón de «Nuevo», y tendremos el Dashboard listo para trabajar con él.

Preparación del Gadget

Ya tenemos el Dashboard preparado, pero está desierto, así que vamos a generar un Gadget que contendrá el mapa. Para ello pulsaremos en el botón de «+» situado en la parte superior derecha.

Aparecerá en la parte inferior de la pantalla un carrusel con distintos tipos de Gadgets. En nuestro caso, escogeremos el de nombre «Template», y lo arrastraremos hasta el fondo del Dashboard.

Hecho esto, nos saldrá una ventana preguntándonos el tipo de plantilla y si queremos usar una plantilla preexistente. Ignoramos todo esto, y empezamos creando una plantilla vacía.

Ya tenemos nuestro Gadget listo para prepararlo y montar el mapa.

Para poder configurar el Gadget, accederemos al menú pulsando en los tres puntos, situados en la parte superior derecha. De las distintas opciones que encontramos, seleccionaremos la de «Editar»

Esto nos abrirá una ventana con dos ventanas a su vez; la de la izquierda nos permitirá introducir el HTML y estilos del mapa, y la de la derecha a la parte lógica del mapa, con JavaScript.

Generar el mapa de Cesium

Bueno, pues tras tanto rollo tanta explicación, vamos a lo interesante, a preparar el mapa.

Lo primero que necesitamos es definir un contenedor HTML que vaya a contener el mapa. Para ello, en la ventana de la izquierda incluiremos la siguiente línea de código:

<div id="cesiumContainer"></div>

¿Sencillo, verdad? Vamos a darle un poco de estilos al tema, así que crearemos una etiqueta -por encima- de estilos, y añadiremos una altura del 100% y una anchura automática, para que el mapa ocupe todo el Gadget.

<style>
   #cesiumContainer {
      width: auto;
      height: 100%;
   }
</style>

<div id="cesiumContainer"></div>

Con esto ya tendríamos configurada la parte HTML. A continuación, vamos a programar la parte lógica en JavaScript. Por tanto, en la ventana de la derecha, localizaremos la función de «vm.initLiveComponent», y en su interior definimos una variable llamada «viewer» tal como os indico a continuación:

//This function will be call once to init components
vm.initLiveComponent = function() {

   let viewer = new Cesium.Viewer('cesiumContainer');
};

Pues ya estaría; pulsad en el botón de «Compile & Synchronize» y ved qué pasa.

Correcto, habemus chartam geographicam:

Como veis, crear un visor de mapa no es para nada complicado. El único detalle que tenéis que tener en cuenta es el parámetro de entrada que le metéis a la función «new Cesium.Viewer()».

Si os fijáis con cuidado, veréis que el string introducido, «cesiumContainer», coincide con el nombre de la clase del contenedor creado en la ventana de HTML. Esto significa que en la función estáis indicando el DIV donde crearéis el visor, por lo que si cambiáis el nombre del DIV por la razón que sea, acordaos de cambiarlo en el parámetro de entrada también.

Posibles configuraciones del visor del mapa

La función de «new Cesium.Viewer()» acepta de manera opcional un segundo parámetro: el de un objeto con las opciones de configuración del visor.

Existen un montón de opciones posibles, como podemos ver en la documentación de Cesium:

Pincha sobre la imagen para ver el listado, que aquí no se ve nada.

Como la idea de esta entrada es ser una pequeña introducción, os comentaré sólo algunas de las más interesantes/solicitadas:

  • Cambiar el visor a 2D, 2.5D o 3D: si queréis que por defecto el mapa aparezca en una de esas dimensiones, tendréis que configurar la propiedad de «sceneMode» según:
    • 2D: sceneMode: Cesium.SceneMode.SCENE2D
    • 2.5D: sceneMode: Cesium.SceneMode.COLUMBUS_VIEW
    • 3D: sceneMode: Cesium.SceneMode.SCENE3D
Representación 2.5D
  • Terreno en 3D: para que el terreno no sea un plano perfecto, sino que presente elevaciones, tendréis que indicar: terrainProvider: Cesium.createWorldTerrain()

Algo que también está muy demandado es «limpiar» el visor, ya que como veis aparecen un montón de elementos que quizás no nos sirvan para nada (selector de mapas base, geocodificador, línea temporal, etc.).

Para ello, únicamente tendremos que añadir las siguientes opciones:

  • Ocultar el selector de mapa base: baseLayerPicker: false
  • Ocultar el geocodificador: geocoder: false
  • Ocultar el botón Home: homeButton: false
  • Ocultar el infoBox (el popup que sale al pinchar en una entidad): infoBox: false
  • Ocultar la línea temporal: timeline: false
  • Ocultar el reloj: animation: false
  • Ocultar el selector de modos 2D/2.5D/3D: sceneModePicker: false
  • Ocultar ayuda de navegación: navigationHelpButton: false

Todas estas opciones representadas a nivel de código quedarían de esta forma:

vm.initLiveComponent = function(){
   let viewer = new Cesium.Viewer('cesiumContainer', {
      sceneMode: Cesium.SceneMode.COLUMBUS_VIEW,
      terrainProvider: Cesium.createWorldTerrain(),
      baseLayerPicker: false,
      geocoder: false,
      homeButton: false,
      infoBox: false,
      timeline: false,
      animation: false,
      sceneModePicker: false,
      navigationHelpButton: false
   });
};

Si aplicáis estas opciones, el visor de mapa se transformará en algo similar a esto:

Bastante mejor, ¿verdad?

El logotipo de Cesium Ion (la plataforma de datos geoespaciales) y la nota del token por defecto no se puede eliminar vía estas opciones de visor de mapa.

Como habréis visto, hay muchas otras opciones disponibles a configurar, así que si os interesa echad un ojo a las que hay e ir probando, a ver qué pasa.

Cargar una capa

Lamento si hemos tardado tanto en llegar a lo principal, el cargar capas, pero considero que era importante conocer un poco cómo configurar nuestro visor antes de meterle contenido.

Cesium, al igual que el resto de visores que estamos viendo, hace uso entre otros formatos de GeoJSON, que es un archivo JSON con datos referidos a la localización espacial.

Para este tutorial, he creado un sencillo GeoJSON con unos cuantos puntos que podéis usar como ejemplo:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "id": "Work"
      },
      "geometry": {
        "type": "Point",
        "coordinates": [
          -3.641436696052551,
          40.529066557739924
        ]
      }
    },
    {
      "type": "Feature",
      "properties": {
        "id": "Lunch"
      },
      "geometry": {
        "type": "Point",
        "coordinates": [
          -3.6394304037094116,
          40.53058739857294
        ]
      }
    },
    {
      "type": "Feature",
      "properties": {
        "id": "Beer"
      },
      "geometry": {
        "type": "Point",
        "coordinates": [
          -3.639392852783203,
          40.530179669832364
        ]
      }
    }
  ]
}

Aunque lo normal es que este tipo de archivos lo tengamos alojado en otro sitio y lo consumamos vía API o similar, vamos a incrustarlo a lo bestia en nuestro código, definiéndolo como una variable tras declarar el «viewer» con sus opciones:

vm.initLiveComponent = function(){
   let viewer = new Cesium.Viewer('cesiumContainer', {
      sceneMode: Cesium.SceneMode.COLUMBUS_VIEW,
      terrainProvider: Cesium.createWorldTerrain(),
      baseLayerPicker: false,
      geocoder: false,
      homeButton: false,
      infoBox: false,
      timeline: false,
      animation: false,
      sceneModePicker: false,
      navigationHelpButton: false
   });

   const myJSON = {... aquí vendría el GeoJSON ...}
};

Vale, y ahora la magia; mediante la función de «Cesium.GeoJsonDataSource.load()» vamos a proceder a cargar ese JSON en el visor de mapas como una capa. Lo que tendríamos que poner sería:

vm.initLiveComponent = function(){
   let viewer = new Cesium.Viewer('cesiumContainer', {
      sceneMode: Cesium.SceneMode.COLUMBUS_VIEW,
      terrainProvider: Cesium.createWorldTerrain(),
      baseLayerPicker: false,
      geocoder: false,
      homeButton: false,
      infoBox: false,
      timeline: false,
      animation: false,
      sceneModePicker: false,
      navigationHelpButton: false
   });

   const myJSON = {... aquí vendría el GeoJSON ...}

   viewer.dataSources.add(Cesium.GeoJsonDataSource.load(myJSON));
};

Si volvemos a sincronizar y compilar el Gadget, ya tendremos nuestra primera capa cargada:

Sin embargo… es un poco feo, ¿no os parece? Así el mapa centrado en América del Norte, con una vista a nivel mundial mientras mis datos son a nivel local… Bueno, hay solución para todo.

Si en la línea donde cargamos el JSON, lo metemos todo como parámetro de otra función llamada «viewer.zoomTo()», conseguiremos modificar la vista del visor hasta nuestro datos:


viewer.zoomTo((Cesium.GeoJsonDataSource.load(myJSON)));

Mejor, ¿verdad?

Ahora hablemos de buenas prácticas; aunque esto que hemos hecho es correcto, es mejor definir una variable con esta capa, y pasar dicha variable a la función de zoom:

let layer = (Cesium.GeoJsonDataSource.load(myJSON));

viewer.zoomTo(layer);

Eso tiene una ventaja, y es que al definir la capa como una variable, nos permite hacerle más cosas. Por ejemplo, podemos ocultarla y mostrarla haciendo uso de la propiedad de «show» de dicha capa.

Para que veamos cómo funciona, metamos un intervalo para que la muestre y oculte cada segundo. Puesto que aquí todo va por promesas, es necesario programarlo teniendo esto en cuenta:

layer.then(lyr => {
   setInterval(() => {
      // Check if the show property is true
      if (lyr.show) {
         lyr.show = false
      } else {
         lyr.show = true
      }
   }, 1000);
})

Quizás no sea el ejemplo más bonito, pero resume perfectamente la idea.

Ya para ir terminando, vamos a eliminar la capa del visor, que es algo que no se comenta normalmente mucho pero que también es útil.

Si recordáis, para añadir la capa hemos usado la función de «viewer.dataSources.add()»; en este caso, usaremos una muy parecida: «viewer.dataSources.remove()», pasando como parámetro la capa que hemos declarado.

Modificando el invento anterior de la promesa, vamos a meter una espera de tres segundos para, seguidamente, eliminar la capa; así os dará tiempo a verla aparecer, y luego desaparecer:

    layer.then(lyr => {
        // setInterval(() => {
        //     if (lyr.show) {
        //         lyr.show = false
        //     } else {
        //         lyr.show = true
        //     }
        // }, 1000);

        setTimeout(() => {
            viewer.dataSources.remove(lyr)
        }, 3000);
    })

Pues ya está, con esto damos terminado este sencillo tutorial de cómo cargar las librerías de Cesium, generar un pequeño visor, configurarlo y hacer algunas operaciones básicas.

Esto apenas es una pincelada de las posibilidades que tienen estos visores, pero para hacerse una idea es un buen comienzo. El potencial de este visor de mapas es enorme, y por eso le damos soporte en la Onesait Platform.

Si os interesa generar vuestro propio visor, pero sin tener que programar nada, siempre os queda la opción de utilizar nuestros visores de la Plataforma, los cuales podéis encontrar en el menú de Visualization & GIS > My GIS Viewers.

Si os interesa hacerlo de este otro modo, tenemos otro tutorial que seguro que os será de utilidad.


Esperamos que os haya resultado interesante la entrada, y nos vemos la semana que viene para hablar de OpenLayers.

Si algo no os ha quedado claro, o queréis añadir alguna cosa más, por favor dejadnos un comentario.

3 Comments

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *