Nuevas tendencias: los SIG en la nube

Hoy se dispone de un abanico amplio de opciones para publicar información geográfica en Internet. Generalmente cuando un profesional se plantea esta tarea suele considerar alguna de estas vías

  • Preparar un servidor propio con software privativo, como el de la empresa ESRI (ArcGIS Server), de calidad alta pero modelos de licencia accesibles para pocos.
  • Montarlo con software libre, que ha ganado mucho en madurez y cuyo uso sigue extendiéndose, con productos como Geoserver y OpenLayers.
  • No utilizar software SIG y delegar en servicios web de mapas externos y sus APIs para programadores (como el archiconocido GoogleMaps).
  • Configurar un entorno híbrido, con elementos combinados de las opciones previas.

En general, las dos primeras opciones funcionan bien en organizaciones con personal cualificado, capaz de crear y mantener entornos tecnológicos complejos y siempre que se haga una inversión decidida de recursos, lo cual no siempre es fácil (grandes empresas, administraciones… los clientes SIG tradicionales, vaya). La tercera vía es mucho más ágil y con menos barreras de entrada para pequeñas empresas, asociaciones, particulares… que cada vez lo usan más y para usos más variados (es lo que algunos llaman “neogeografía”).

Sin embargo, hay más opciones a considerar… y es que en los últimos tiempos está emergiendo una tendencia denominada computación en la nube. Ésta implica la aparición de hardware, plataformas y servicios web mediante los que se externaliza a empresas especializadas parte de las labores informáticas de organizaciones e individuos. Y dentro de estas plataformas existen ya varias que permiten el almacenamiento y publicación de información geográfica.

Algunas de estas interesantes plataformas son: IkiMap, GeoCommons (recientemente adquirida por Esri), Google Fusion Tables o CartoDB. Se trata de espacios en la nube donde almacenar y publicar datos geográficos en forma de mapas, que además prestan especial atención a la facilidad de uso para el usuario, las redes sociales y el buen diseño. Además, son plataformas que generalmente incluyen un API para programadores, con lo que sus posibilidades de uso e integración con otras herramientas se amplían notablemente.

Sin duda se trata de plataformas potencialmente utilizables por un número muy alto y variado de individuos y organizaciones, especialmente en los casos en los que las “vías tradicionales” no sean aplicables. Por ejemplo, un medio de información local podría fácilmente ubicar en un mapa las noticias que suceden en su municipio (p.ej. obras en marcha o planificadas, actos culturales, animales perdidos…), incrustar ese mapa en su blog y permitir la participación de los ciudadanos y la difusión de sus mapas en redes sociales.

Por ejemplo, el siguiente mapa ha sido creado en la plataforma Ikimap, autodenominada la “red social de los mapas”. Crear un mapa como este es una labor rápida y sencilla. En páginas webs y blogs que lo admitan, también se puede embeber un mapa interactivo, a partir del código para crear un iframe que proporciona la plataforma (los iframe no son permitidos en la plataforma wordpress.com).

Sin ninguna duda, en los proyectos de publicación de información geográfica, las plataformas SIG en la nube son una opción muy a tener en cuenta …

Aplicaciones geográficas web móviles

Este es un tema que considero de gran interés y futuro: el desarrollo de aplicaciones web teniendo en mente los nuevos dispositivos móviles, tales como smartphones y tablets (iphones, teléfonos android, ipads, etc.).

Frente al desarrollo en entornos nativos (p.ej. con Java y eclipse para android), la programación web estándar (HTML, Javascript y CSS + un lenguaje de servidor como ASP o PHP) puede resultar una opción muy interesante.

Sobre la base de estas herramientas habituales de programación web están apareciendo varios complementos para el desarrollo móvil, sobre todo en forma de librerías javascript. De ellas, las dos que me parecen más reseñables son Sencha Touch [1] y jQuery Mobile [2].

Personalmente, dada la expansión de su hermana mayor jQuery, creo que la segunda es una opción que merece la pena probar.

Las capacidades de su API pueden estudiarse en la página oficial y varios ejemplos reales de su uso se pueden ver en la jqmgallery [3].

Además, existe una buena capacidad de integración entre jquery mobile y OpenLayers en su última versión, como puede verse p.ej. en este enlace [4]. Esta es una gran combinación para realizar aplicaciones geográficas web móviles.

Recientemente hablamos de este tema en geoinquietos Cantabria [5]. Esta fue la presentación:

Si no conoces la iniciativa geoinquietos, infórmate en osgeo [6] y busca tu grupo más cercano.

Saludos a todos.

Enlaces:
[1] Sencha Touch: http://www.sencha.com/products/touch/
[2] jQuery Mobile: http://jquerymobile.com/
[3] jqmGallery: http://www.jqmgallery.com/
[4] Ejemplo de visor OpenLayers con jquerymobile: http://openlayers.org/dev/examples/mobile-jq.html#mappage
[5] Geoinquietos Cantabria: http://wiki.osgeo.org/wiki/Geoinquietos_Cantabria
[6] Capitulo local osgeo: http://wiki.osgeo.org/wiki/Cap%C3%ADtulo_Local_de_la_comunidad_hispano-hablante

OpenLayers y pestañas con jQuery

En este artículo vamos a ver cómo incluir pestañas de jquery (jqueryui tabs) dentro de un popup de OpenLayers. Si queréis ver el resultado podéis ir directamente al enlace de jsfiddle [1].
 jQuery [2]  se ha convertido en la librería por antonomasia para extender el javascript estándar en las páginas web modernas. Si desarrollas para la web, tienes que conocerla. Pero… ¿cómo trabajar con jQuery + OpenLayers?
  1. Hay algunos intentos de desarrollar un plugin para jQuery que permita trabajar con OpenLayers, como MapQuery [3], que antes se denominaba GeoJQuery. El proyecto lleva poco tiempo, pero habrá que seguir sus pasos; quizá pronto sea una alternativa a GeoExt [4].
  2. Mientras tanto es más frecuente que tengamos en marcha un proyecto con OpenLayers y queramos utilizar puntualmente jQuery, especialmente algún componente visual o widget de jqueryui, como las pestañas (tabs) o un acordeón (accordion) [5].

Vamos a ver cómo hacer en el caso 2, con un ejemplo sencillo.

a) Creamos el html básico para soportar el mapa, con las importaciones de librerías javacript y css necesarias. Hemos dejado un script vacío, que es donde iremos introduciendo el código.

<html>
    <head>
        <script src="http://www.openlayers.org/api/OpenLayers.js" type="text/javascript"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js" type="text/javascript"></script>
        <script src="http://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.9/jquery-ui.min.js" type="text/javascript"></script>
        <link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/themes/base/jquery-ui.css" rel="stylesheet" type="text/css"/>
        <title>Pestañas de jQuery en OpenLayers</title>
    </head>
    <body>
        <div id="mapa" style="width:512px; height:512px"></div>
        <script type="text/javascript"></script>
    </body>
</html>

b) Primero añadimos la función que generará el contenido html del popup. Éste debe seguir los convenios de estructura establecidos por jqueryui. Lo más importante son los ‘li’ y que los enlaces apunten al mismo id que tengan los ‘div’ posteriores.

// Html del popup con pestañas de jquery
function prepararFicha(ciudad) {
    var div = $("<div>");
    var contenido = $("<div id='popupConTabs' style='font-size:9px;'>");
    var titulos = $("<ul>" +
                        "<li><a href='#tab-1'>Atributos</a></li>" +
                        "<li><a href='#tab-2'>Wikipedia</a></li>" +
                    "</ul>");
    contenido.append(titulos);

    // Pestaña 1
    var p1 = $("<div id='tab-1'>");
    p1.append($("<p>" +
                    "<b>Nombre</b>: " + ciudad.attributes.Nombre + "<br/>" +
                    "<b>Habs.</b>: " + ciudad.attributes.Habitantes +
                "</p>"));
    contenido.append(p1);

    // Pestaña 2
    var p2 = $("<div id='tab-2'>");
    p2.append($("<p><a href='" + ciudad.attributes.Wikipedia + "'>Wikipedia</b></p>"));
    contenido.append(p2);

    div.append(contenido);
    return div.html();
}

c) Lo siguiente  es la creación usual de un mapa, una capa vectorial con un solo registro y el control de selección. Nada particular.

// Creación del mapa y encuadre inicial
var WGS84 = new OpenLayers.Projection('EPSG:4326');
var MERCATOR = new OpenLayers.Projection('EPSG:900913');
var peninsula = new OpenLayers.Bounds(-18.0, 26.0, 6.0, 44.0).transform(WGS84, MERCATOR);
var mapa = new OpenLayers.Map("mapa");
mapa.addLayer(new OpenLayers.Layer.OSM());
mapa.zoomToExtent(peninsula);

// Creación de una capa vectorial con 1 registro puntual
var ciudades = new OpenLayers.Layer.Vector("Ciudades");
mapa.addLayer(ciudades);
var geometria = new OpenLayers.Geometry.Point(-3.8, 43.46).transform(WGS84, MERCATOR);
var atributos = {
    Nombre: 'Santander',
    Habitantes: 181589,
    Wikipedia: 'http://es.wikipedia.org/wiki/Santander_(Espa%C3%B1a)'
};
var santander = new OpenLayers.Feature.Vector(geometria, atributos);
ciudades.addFeatures([santander]);

// Creación del control de selección
var controlSeleccion = new OpenLayers.Control.SelectFeature(ciudades);
mapa.addControl(controlSeleccion);
controlSeleccion.activate();

d) Finalmente, la siguiente porción de código es la que establece las funciones manejadoras de eventos para crear el popup. En la función se produce la llamada clave al constructor ‘tabs’, para dotar del aspecto y funcionalidad deseados al html del popup.

// Mostrar el popup al seleccionar la ciudad
var popup;
ciudades.events.on({
    "featureselected": function(e) {
        var ciudad = e.feature;
        var html = prepararFicha(ciudad);
        popup = new OpenLayers.Popup.FramedCloud(
            "Info ciudad",
            ciudad.geometry.getBounds().getCenterLonLat(),
            null, html, null, true, function() {
                controlSeleccion.unselect(ciudad);
        });
        popup.minSize = new OpenLayers.Size(300, 100);
        ciudad.popup = popup;
        mapa.addPopup(popup);
        $("#popupConTabs").tabs(); /* << Creacion de Tabs */
    },
    "featureunselected": function(e) {
        var ciudad = e.feature;
        mapa.removePopup(ciudad.popup);
        ciudad.popup.destroy();
        ciudad.popup = null;
    }
});

Referencias:

[1] El código de este post en funcionamiento: http://jsfiddle.net/VictorVelarde/DbCgt/

[2] Librería jquery: http://jquery.com/

[3] Ejemplo de uso de MapQuery: http://blog.spaziogis.it/static/jquerymap/

[4] Proyecto GeoExt (OpenLayers + ExtJS): http://www.geoext.org/

[5] El control de pestañas de jqueryui: http://jqueryui.com/demos/tabs/

[6] Uso de jqueryui slider con OpenLayers: http://workshops.opengeo.org/openlayers-intro/integration/jqui-slider.html

Menu contextual sobre mapa – API Javascript ESRI

Lo siguiente es una pequeña guía de algo que me parece muy útil: generar un menu contextual asociado al botón derecho cuando estás sobre el mapa. Al margen de tener la típica barra superior de herramientas, con este tipo de menú podremos tener más a mano cualquier acción invocable desde javascript que nos interese, como p.ej. hacer zoom más en un punto o recoger las coordenadas del clic y usarlas para otra acción como p.ej. un geocoding.

El resultado final sería algo como:
Menu sobre Mapa con botón derecho

Para el menu vamos a usar la librería de Javascript dojo y el mapa estará realizado con el API de ESRI para Javascript [1].

Tomaremos como base un ejemplo de ESRI para mostrar un mapa topográfico [2]. Y ahora a ese codigo vamos a añadirle la funcionalidad del menu emergente.

Para ello:
a) Importamos el control de dojo para el menu.

      dojo.require("dijit.Menu");

b) añadimos al HTML de la pagina el código de marcado del diálogo (también se podría crear al vuelo con javascript). Los atributos son importantes para vincularlo correctamente al mapa y cada entrada del menu tiene asociada la función de javascript que queramos:

<div dojotype="dijit.Menu" id="menu" contextmenuforwindow="false" style="display: none;" targetnodeids="map">
    <div dojotype="dijit.MenuItem" onclick="Foo();">Foo</div>
    <div dojotype="dijit.MenuSeparator"></div>
    <div dojotype="dijit.MenuItem" onclick="Bar();">Bar</div>
</div> 

c) Y finalmente la captura del evento ‘clic botón derecho’ y el guardado del punto del mapa. De manera automática y justo a continuación de la gestion del evento, dojo se encargará de mostrar el menu emergente y ya podremos usar en nuestras funciones el último valor de la variable lastPoint.

dojo.connect(map, 'onMouseDown', this, function(evt){
            if(evt.button !== 2) return; // only right button
            lastPoint = evt.mapPoint;
        });

El código completo sería algo como:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title> Topographic Map</title>
    <link rel="stylesheet" type="text/css" href="http://serverapi.arcgisonline.com/jsapi/arcgis/1.6/js/dojo/dijit/themes/tundra/tundra.css">
    <style>
      html, body { height: 100%; width: 100%; margin: 0; padding: 0; }
    </style>
    <script type="text/javascript">var djConfig = {parseOnLoad: true};</script>
    <script type="text/javascript" src="http://serverapi.arcgisonline.com/jsapi/arcgis/?v=1.6"></script>
    <script type="text/javascript">
      dojo.require("dijit.layout.BorderContainer");
      dojo.require("dijit.layout.ContentPane");
      dojo.require("esri.map");
      dojo.require("dijit.Menu");

      var map;
      var lastPoint;

      function Foo(){
           alert("x: " + lastPoint.x + " y: " + lastPoint.y);
      }
      function Bar(){
           alert('Bar');       
      }

      function init() {
        var initExtent = new esri.geometry.Extent({"xmin":-13635568,"ymin":4541606,"xmax":-13625430,"ymax":4547310,"spatialReference":{"wkid":102100}});
        map = new esri.Map("map",{extent:initExtent}); 
        var basemap = new esri.layers.ArcGISTiledMapServiceLayer("http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer");
        map.addLayer(basemap);
       
        var resizeTimer;
        dojo.connect(map, 'onLoad', function(theMap) {
          dojo.connect(dijit.byId('map'), 'resize', function() {  //resize the map if the div is resized
            clearTimeout(resizeTimer);
            resizeTimer = setTimeout( function() {
              map.resize();
              map.reposition();
            }, 500);
          });
        });

        dojo.connect(map, 'onMouseDown', this, function(evt){
            if(evt.button !== 2) return; // only right button
            lastPoint = evt.mapPoint;
        });
      }

      dojo.addOnLoad(init);
    </script>
  </head>
  
  <body class="tundra">
    <div dojotype="dijit.layout.BorderContainer" design="headline" gutters="false" style="width: 100%; height: 100%; margin: 0;">
        <div id="map" dojotype="dijit.layout.ContentPane" region="center" style="overflow:hidden;"></div>
    </div>
        <!-- Right-button menu -->
        <div dojotype="dijit.Menu" id="menu" contextmenuforwindow="false" style="display: none;" targetnodeids="map">
            <div dojotype="dijit.MenuItem" onclick="Foo();">Foo</div> 
            <div dojotype="dijit.MenuSeparator"></div> 
            <div dojotype="dijit.MenuItem" onclick="Bar();">Bar</div> 
        </div> 
  </body>

</html>

La última versión del API para JS disponible con la que lo hemos probado es la v2.1 pero no debería ser un problema usar el código en otras versiones.

Enlaces:

[1] API ESRI de Javascript

[2] Ejemplo de mapa con api js de Esri

Material de introducción a OpenLayers

Recientemente he tenido el placer de participar como profesor dentro de la primera edición del curso Experto en Desarrollo y Gestión de Sistemas de Información Geográfica de la Universidad de Cantabria.

El módulo que impartí era una introducción rápida a OpenLayers. La mayoría de los alumnos eran geógrafos, pero también había biólogos, historiadores, sociólogos… sin una base previa de programación pero con interés en aprender.

OpenLayers

Cuelgo a continuación el documento de apuntes que se les entregó a los alumnos para apoyar y complementar los ejercicios prácticos hechos en clase.

Apuntes ‘Curso OpenLayers’

El documento pretende recoger los fundamentos básicos útiles (sobre neogeografía, mashups, html, javascript…) para que una persona sin conocimientos previos de programación se sienta cómoda al empezar a trabajar con OpenLayers.

Saludos y que sea útil.

Convertir rasters de un directorio a otro formato con gdal

Script sencillito pero práctico, para convertir todos los ficheros raster de un directorio a otro formato raster (dentro de los soportados por gdal)

Teniendo acceso desde línea de comandos a la utilidad gdal_translate (distribuida p.ej. en FWTools), podemos convertir todos los ficheros .ecw de una carpeta a formato .tif, mediante el siguiente código en Python:

import os, sys
directorioActual = os.getcwd()
directorioSalida = os.path.join(directorioActual, "resultados")

for root, dirs, files in os.walk(directorioActual):
    for fichero in files:
        (nombreFichero, extension) = os.path.splitext(fichero)
        if(extension == ".ecw"):
            comando = "gdal_translate %s %s\\%s" %(fichero, directorioSalida, nombreFichero + ".tif")
            print comando
            os.system(comando)

Copiamos el código a un fichero llamado p.ej. ‘ejecutar.py’ dentro de la carpeta que nos interese, creamos una carpeta dentro para los ficheros transformados (‘resultados’) y ejecutamos… Más fácil imposible.

Se pueden introducir fácilmente otras variantes, p.ej. con otros formatos de raster, viendo las opciones disponibles en la herramienta gdal_translate

EDIT: Editado para añadir mejoras de WordPress al código fuente

Geoproceso interactivo / geoproceso en batch

Geoprocesos ESRIEn ocasiones, con geoprocesos que tienen como parámetro de entrada entidades vectoriales, es interesante que podamos ejecutarlos de dos modos:

(1) de manera interactiva (dibujando nosotros las geometrías) o
(2) suministrando una capa ya existente que tenga las entidades  (p.ej. un shapefile).

Dentro del entorno de geoprocesos de ESRI, esto se corresponde con la definición de un parámetro de tipo FeatureSet (1) y un parámetro como FeatureLayer (2), respectivamente.

[Esto en la versión 9.3 de ArcGIS Desktop viene incorporado 'de serie', así que no hay problema. Lo que se comenta a continuación en este post es aplicable solo para la versión 9.2]

Una manera sencilla de que el mismo script pueda trabajar con los dos modos es como se muestra a continuación:

gp = arcgisscripting.create()
gp.overwriteoutput = 1
if gp.ScratchWorkspace is None:
    gp.ScratchWorkspace = gp.GetSystemEnvironment("TEMP")
# Parametros de entrada.........
fs = gp.GetParameter(0)   # (1) FeatureSet [opcional]
fLayer = gp.GetParameter(1)   #(2) FeatureLayer [opcional]
# Detección del MODO DE USO
if (str(fLayer).strip() != ""):
    gp.AddMessage("Trabajando con modo batch...")
else:
    gp.AddMessage("Trabajando con modo interactivo...")
    #Adaptación del FeatureSet a Feature Layer
    tmp = str(int(math.ceil(random.random()*1000000)))
    fLayer = "%s\\%s.shp" %(gp.ScratchWorkspace, tmp)
    fs.Save(fLayer)
# A partir de aquí, el tratamiento puede ser igual
registros = gp.SearchCursor(fLayer)
registro = registros.Next()
while registro:
    # tratamiento del registro [...]
    registro = registros.Next()

En la línea tmp = … también puede usarse gp.CreateScratchName.
Que sea útil.

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 323 seguidores