Pintar poligonos en WorldWind

De ChuWiki
Saltar a: navegación, buscar

Después de Empezar con WorldWind vamos a modificar un poco el código para añadir tres tipos de polígonos sobre nuestra bola terrestre: polígonos normales, prismas o polígonos con altura ( los de WorldWind lo llaman ExtrudedPolygon ) y polígnos de superficie. El código está disponible aquí. Vamos a ello.

Añadimos algunas cosas útiles al mapa

Puesto que los polígonos se dibujan a una altura determinada sobre la tierra, vamos a poner a nuestro mapa un par de herramientas que vienen con WorldWind y que nos permiten ver mejor las alturas del terreno.

Una es la barra de estado, en la que veremos la latitud y longitud donde tenemos el ratón y la altura del terreno en ese punto. El código para poner esta barra de estado es el siguiente

      StatusBar statusBar = new StatusBar();
      this.getContentPane().add(statusBar, BorderLayout.PAGE_END);
      statusBar.setEventSource(wwd);

donde wwd es nuestro canvas de dibujo WorldWindowGLCanvas que habíamos instanciado anteriormente y el this que aparece por ahí es el JFrame principal de nuestra aplicación. Con esto, en nuestro mapa veremos la barra de estado en la parte inferior que se muestra en la siguiente figura

Worldwind-statusbar.png

La otra cosa que vamos a poner son un conjunto de botones que nos permitirán hacer determinadas operaciones sobre el mapa. Una de ellas, que es la que nos interesa, es la de exagerar la altura de las montañas, de forma que veremos más fácilmente el relieve. El código para poner este juego de botones es el siguiente

      ViewControlsLayer viewControlsLayer = new ViewControlsLayer();
      ApplicationTemplate.insertBeforeCompass(wwd, viewControlsLayer);
      wwd.addSelectListener(new ViewControlsSelectListener(wwd,
            viewControlsLayer));
      panelCapas.update(wwd);

Vamos a extendernos un poco en la explicación de este código. Como se mencionó en Empezar con WorldWind el mapa básico contiene varias capas que podemos ocultar y visualizar. En estas capas están los mapas de satélite de la nasa, de open street map, etc. Pero también hay algunas capas que sólo contienen "cosas" que se dibujan sobre el mapa, como una brújula para indicar el norte o un pequeño mapa mundi para ir directamente a una zona concreta (puedes verlo en las imágenes de Empezar con WorldWind. Una capa con herramientas de este tipo y que no se incluye por defecto es ViewControlsLayer. Esta clase contiene botones para exagerar el relieve del terreno, para cambiar nuestro punto de vista sobre el terreno, para desplazarnos por él, etc. La siguiente figura muestra este conjunto de botones, que es precisamente el que queremos poner

Worldwind-controles.png

En la foto hemos destacado los dos botones que nos interesan para destacar el relieve del terreno, pero la clase ViewControlsLayer son los 9 botones que se ven.

Una forma fácil de añadir una capa en el mapa es usar el método insertBeforeCompass() de la clase ApplicationTemplate. Esto insertará la capa delante de la capa del compas (la brújula). Nuestra capa no tiene por qué estar delante de la brújula, simplemente es un método cómodo para hacerlo.

Una vez hecho esto, sólo nos queda indicarle a ViewControlsLayer cual es el canvas de mapa que debe controlar, para ello se añade a wwd (nuestro canvas de mapa), un listener o controlador que use este viewControlsLayer. La llamada

wwd.addSelectListener(new ViewControlsSelectListener(wwd,viewControlsLayer));

es un poco rebuscada, pero es la que se encarga de asociar nuestros controles con nuestro mapa. Una vez hecho esto, ya tenemos los controles en el mapa y funcionando.

Un último detalle es que en Empezar con WorldWind teníamos un panel donde se visualizaban todas las capas que había en el mapa. Para que este panel se actualice con la nueva capa que hemos añadido, debemos llamar a su método update

panelCapas.update(wwd);

Listo, ya tenemos todo preparado. Vamos ahora, por fin, a pintar polígonos

Crear una capa para los polígonos

Para pintar polígonos (u otras figuras), necesitamos añadir una nueva capa al mapa en la que podamos dibujar. La capa corresponde a una clase RenderableLayer. Para crearla y añadirla al mapa sólo tenemos que hacer el siguiente código

      RenderableLayer capa = new RenderableLayer();
      capa.setName("Poligonos");
      ApplicationTemplate.insertBeforeCompass(wwd, capa);
      panelCapas.update(wwd);

Es decir, creamos la capa simplemente con un new de la clase RenderableLayer. Con el método setName() le ponemos un nombre que será el nombre visible en el panel de capas. Como comentamos antes, añadimos la capa al mapa utilizando el método insertBeforeCompass() de la clase ApplicationTemplate. Finalmente, para que se actualice el panel que muestra todas las capas disponibles, llamamos al método panelCapas.update(). Por supuesto, wwd es la instancia de WorldWindowGLCanvas, que es nuestro canvas mapa.

Pintar polígonos normales

Un polígono normal corresponde a la clase Polygon. A este Polygon le damos varios vértices cada uno con latitud, longitud y altura. El polígono unirá por medio de líneas rectas todos estos vértices y también unirá automáticamente el último vértice con el primero, puesto que un polígono es cerrado. De hecho, si el último punto que le demos no coincide con el primero, la clase Polygon automáticamente añadirá un nuevo punto final coincidente con el primero.

Así que la forma de construir el polígono puede ser algo como esto

      List<Position> vertices = new LinkedList<>();
      vertices.add(Position.fromDegrees(40.0, -3.0, 700.0));
      vertices.add(Position.fromDegrees(40.1, -3.0, 700.0));
      vertices.add(Position.fromDegrees(40.1, -3.1, 700.0));
      vertices.add(Position.fromDegrees(40.0, -3.1, 700.0));
      
      Polygon poligono = new Polygon(vertices);

Hemos creado una lista de vértices. Cada vértice es una instancia de la clase Position. Una forma fácil de obtener estas Position es por medio de su método fromDegrees() al que se pasan tres parámetros

  • Latitud en grados (40.0 o 40.1 en nuestro ejemplo). Negativo indicaría al Sur del ecuador
  • Longitud en grados (-3.0 o -3.1 en nuestro ejemplo). Negativo indica al Oeste del meridiano de Greenwich.
  • Altura en metros, opcional. Si no ponemos este parámetro la altura por defecto será 0.

Las alturas pueden ser relativas a nivel del mar o relativas al nivel del terreno. Por defecto son relativas al nivel del mar. Si queremos que sean sobre el terreno, una vez creado el polígono, debemos llamar a su método

poligono.setAltitudeMode(WorldWind.RELATIVE_TO_GROUND);

Es importante el tema de alturas, puesto que si parte del polígono queda por debajo de tierra, no se verá en el mapa. También es importante tener en cuenta que las alturas indicadas son de los vértices y que la línea que une los vértices será una línea recta que no seguirá los desniveles del terreno. Por ello, si tenemos dos vértices por encima de tierra y hay una montaña entre ellos, el lado correspondiente del polígono puede atravesar la montaña y quedar oculto parcialmente.

Una vez fijados los vértices del polígono, tenemos que definir qué colores queremos. Hay dos partes claras del polígono, por un lado su línea exterior (los vértices y lados, que WorldWind llama Outline), a los que podemos fijar color, grosor, opacidad, etc. Y por otro lado el interior del polígono (a la que WorldWind llama Interior), al que también podemos fijar color, opacidad, si debe o no pintarse ... e incluso una imagen para que haga de textura del interior del polígono.

Para fijar todo esto, debemos crear una instancia de la interface ShapeAttributes y hacer que sus métodos devuelvan todo este tipo de cosas. Para facilitar el asunto, la clase BasicShapeAttributes implementa esta interface ofreciéndonos métodos set y get para todas estas cosas. Por defecto, pintará un polígono negro relleno de blanco totalmente opaco. Podemos cambiar aquello que nos interese, como se ve en el siguiente código

      ShapeAttributes atributos = new BasicShapeAttributes();
      atributos.setOutlineMaterial(Material.RED);
      atributos.setInteriorMaterial(Material.RED);
      atributos.setInteriorOpacity(0.2);
      poligono.setAttributes(atributos);

Con esto el borde del polígono será rojo y su interior será rojo casi transparente (opacidad 0.2). Una vez creado, sólo hay que pasárselo al polígono por medio de su método setAttributes().

Ya tenemos el polígono, sólo queda añadirlo a la capa y decirle al mapa que se repinte

      capa.addRenderable(poligono);
      wwd.redraw();

El resultado puedes verlo en la siguiente imagen. Fíjate que hemos puesto este polígono a posta para que quede parcialmente oculto entre las montañas. En la imagen hemos exagerado el relieve usando el botón marcado en rojo, para que sea más evidente el cómo el polígono desaparece por debajo de las montañas.

Worldwind-poligono-normal.png


Prismas o ExtrudedPolygon

Este polígono es una especie de prisma con una base y una altura. El polígono base se construye igual que un polígono normal, dando los vértices y altura de cada uno de los vértices. La única diferencia es que el constructor de ExtrudedPolygon tiene un parámetro más para indicar la altura del prisma y un método setHeight() para poder modificarla posteriormente. El código de creación del prisma sería este

      List<Position> vertices = new LinkedList<>();
      vertices.add(Position.fromDegrees(40.2, -3.0, 600.0));
      vertices.add(Position.fromDegrees(40.3, -3.0, 600.0));
      vertices.add(Position.fromDegrees(40.3, -3.1, 600.0));
      vertices.add(Position.fromDegrees(40.2, -3.1, 600.0));
      
      ExtrudedPolygon poligono = new ExtrudedPolygon(vertices,700.0);

Es decir. la base del prisma estaría a una altura de 600 metros y la altura vertical del prisma llegaría hasta los 700 metros, es decir, 100 metros de altura.

El resto es igual que al anterior, podemos fijar colores y añadirlo a la capa correspondiente. Para este ejemplo no hemos rellenado el interior de ningún color, por lo que nuestro prisma quedará sin las "tapas" superior o inferior, pareciendo más bien una "cerca", como se puede ver en la siguiente foto

Worldwind-extrudedpolygon.png

Polígonos de superficie

La tercera clase de polígono que vamos a ver son los de superficie. Estos sí, se dibujan totalmente sobre el terreno, siguiendo su geografía, por lo que no quedarán flotando sobre él ni quedarán ocultos por la geografía. La clase a utilizar es SurfacePolygon, que se utiliza exactamente igual que las anteriores pero con la salvedad de que no tiene sentido darle alturas a los vértices, las alturas serán automáticamente las del terreno.

El código para crearlo sería el siguiente

      List<Position> vertices = new LinkedList<>();
      vertices.add(Position.fromDegrees(40.0, -3.2));
      vertices.add(Position.fromDegrees(40.1, -3.2));
      vertices.add(Position.fromDegrees(40.1, -3.3));
      vertices.add(Position.fromDegrees(40.0, -3.3));
      
      SurfacePolygon poligono = new SurfacePolygon(vertices);

No hemos puesto alturas. Los colores se ponen igual que en los casos anteriores, le hemos dejado el interior rojo transparente, como al primer polígono. El resultado es el de la siguiente imagen

Surface-polygon-worlwind.png