OpenLayers y Panoramio

Tras leer el excelente post de Pepe relativo a la integración de servicios de geo-localización en un ambiente Google Maps llegué a la conclusión de que resulta muy fácil aplicarlo con dicha API, pero mi inquietud me llevó a investigar en la posibilidad de integrarlo en otra API de tipo mapping de reconocido prestigio, como es el caso de OpenLayers, que además es 100% Open Source, lo que nos permite añadir múltiples fuentes de datos (wms, wfs, gml, xml, geojson, georss, conjunto de tiles, proveedores de mapa mundiales -OpenStreetMap, GMaps, Virtual Earth, Yahoo Maps, etc.), además de esa libertad de movimientos que le confieren una cierta ventaja sobre su homónima, sin que ello suponga una crítica hacia Google Maps, que entre otras virtudes ha popularizado el uso de los servicios de mapa vía web, acercándolo a un público genérico sin apenas conocimientos.

Openlayers con Panoramio

Openlayers con Panoramio


A la hora de “copiar” el ejemplo descrito en dicho post para OpenLayer, no he planteado integrar todos los geoservicios, entre otras cosas porque algunos de ellos forman parte de la API casi desde su inicio, y no supone un gran esfuerzo incorporarlo, salvo el de YouTube, que no tuve tiempo de ver, aunque tampoco creo que haya muchos videos geoposicionados. Ejemplos de integración de KML en OL hay muchos en la red, al igual que de GML, GeoJSON o GeoRSS, y también de Flickr, que en el fondo es acceder a un documento XML. Para más información consultas los ejemplos on-line del portal Openlayers.org

De todos los geoservicios, al que más atención ha prestado ha sido al de Panoramio, entre otras cosas por la ingente cantidad de fotografías georreferenciadas que integra, lo que lo hace muy atractivo para el webmaster. Como muchos sabéis este portal dispone de un servicio REST público que nos permite ejecutar una URL con ciertos parámetros, que finalmente nos devuelve un documento estructurado en formato JSON

{
"count": 110,
"photos": [
{
"upload_date": "20 May 2007",
"owner_name": "Carlos Sieiro del Nido",
"photo_id": 2313090,
"longitude": -0.516679,
"height": 75,
"width": 100,
"photo_title": "UNIVERSIDAD DE ALICANTE-Campus de San Vicente del Raspeig.",
"latitude": 38.386611000000002,
"owner_url": "http://www.panoramio.com/user/134716",
"owner_id": 134716,
"photo_file_url": "http://mw2.google.com/mw-panoramio/photos/thumbnail/2313090.jpg",
"photo_url": "http://www.panoramio.com/photo/2313090"
},
{
"upload_date": "16 January 2007",
"owner_name": "\u00a9 www.fotoseb.es - Sebastien Pigneur Jans",
"photo_id": 453228,
"longitude": -0.51438300000000003,
"height": 66,
"width": 100,
"photo_title": "Foto Aerea Universidad Alicante 2 (Foto_Seb)",
"latitude": 38.381700000000002,
"owner_url": "http://www.panoramio.com/user/55833",
"owner_id": 55833,
"photo_file_url": "http://mw2.google.com/mw-panoramio/photos/thumbnail/453228.jpg",
"photo_url": "http://www.panoramio.com/photo/453228"
}]

}

Como podéis ver por cada foto disponemos de un array con los datos que nos interesan, y de forma particular los siguientes:

  • owner_name: autor
  • photo_title: título
  • latitude y longitude: coordenadas geográficas de geoposicionamiento en datum WGS 84
  • photo_id: identificador de foto
  • photo_file_url: URL de la imagen
  • photo_url: URL del recurso en la web de Panoramio

Integrar Panoramio en OpenLayers no resulta tan elegante como en GM, donde sólo hay que escribir 2 líneas, pero con un poco de esfuerzo y el apoyo de la comunidad seguro que alguien es capaz de escribir una clase derivada de JSON que facilite las cosas. Por ahora me limitaré a desmenuzar el código de forma secuencial para que se entienda el proceso

Lo primero que debemos de hacer es preparar nuestro proxy para que admita peticiones al host de Panoramio. En este enlace explica como hacerlo

OpenLayers.ProxyHost= "/cgi-bin/proxy.cgi?url=";

Posteriormente creamos un par de variables con la URL del servicio REST y los parámetros

url = "http://www.panoramio.com/map/get_panoramas.php";
parametros = {
   order:'popularity',
   set:'full',
   from:0,
   to:20,
   minx: minx,
   miny: miny,
   maxx: maxx,
   maxy: maxy,
   size:'thumbnail'
}

Consulta la documentación de la API de Panoramio para más información. Por ahora los parámetros que más nos interesan son:

  • to”: indica el nº de fotografías que queremos cargar. Máximo 50
  • minx”,”miny”,”maxx”,”maxy”: coordenadas de la BBOX donde buscará fotos. Debén de estar en coordenadas geográficas (EPSG: 4326). En mi caso los he obtenido utilizando este código:
// Obtener extensiones.
ext = map.getExtent();
var minx = ext.left;
var miny = ext.bottom;
var maxx = ext.right;
var maxy = ext.top; //alert (minx + " " + miny + " " + maxx + " " + maxy);

Por último sólo nos queda invocar a un HttpRequest de AJAX que ejecute la URL con los parámetros y los envíe a nuestra función: mostrarfotos

OpenLayers.loadURL(url, parametros, this, mostrarfotos);

Función mostrarfotos
Es la encargada de procesar el documento JSON que recibe como argumento

function mostrarfotos(response) {

El documento JSON, que todavía es una cadena de texto es parseada con OpenLayers.Format.JSON, que la convierte en un objeto que ya podemos tratar:

var json = new OpenLayers.Format.JSON();
var panoramio = json.read(response.responseText);

Creamos un array de featutes con el total de fotos (panoramio.photos.length)

var features = new Array(panoramio.photos.length);

Aplicamos un bucle “for” para recorrer cada fotografía.

for (var i = 0; i < panoramio.photos.length; i++)
{

Declaramos y almacenamos cada dato de la foto en una variable para recorrerla con comodidad.

var upload_date = panoramio.photos[i].upload_date;
var owner_name = panoramio.photos[i].owner_name;
var photo_id = panoramio.photos[i].photo_id;
var longitude =panoramio.photos[i].longitude;
var latitude = panoramio.photos[i].latitude;
var pheight = panoramio.photos[i].height;
var pwidth = panoramio.photos[i].width;
var photo_title = panoramio.photos[i].photo_title;/
var owner_url = panoramio.photos[i].owner_url;
var owner_id = panoramio.photos[i].owner_id;
var photo_file_url = panoramio.photos[i].photo_file_url;
var photo_url = panoramio.photos[i].photo_url;

Opcionalmente podemos recoger todas las fotografías en código HTML para ser ubicadas en el BODY con un DIV, en forma de “galería de fotos”

OpenLayers.Util.getElement('galeria').innerHTML +=
     "<a href='"+ photo_url +
     "'><img src='"+photo_file_url+"' title='"+
     photo_title +"' /></a>&nbsp;";

Posteriormente en el BODY para representar esta galería sólo hay que escribir:

<div id="galeria"></div>

Creamos un objeto point con las coordenadas de la foto

var fpoint = new OpenLayers.Geometry.Point(longitude,latitude);

Creo un array de atributos

var atributos = {
   'upload_date' : upload_date,
   'owner_name':owner_name,
   'photo_id':photo_id,
   'longitude':longitude,
   'latitude':latitude,
   'pheight':pheight,
   'pwidth':pwidth,
   'pheight':pheight,
   'photo_title':photo_title,
   'owner_url':owner_url,
   'owner_id':owner_id,
   'photo_file_url':photo_file_url,
   'photo_url':photo_url
}

Por cada foto creo un elemento (feature) que contiene su posición y sus atributos

features[i] = new OpenLayers.Feature.Vector(fpoint,atributos);

Terminamos el bucle

}

Fuera del bucle, todavía en la función mostrar foto nos queda:

Definir el estilo para representar la foto. Tenemos dos opciones
a) Crear un icono único para cada fotografía

// estilo punto
var panoramio_style1 = new OpenLayers.StyleMap(OpenLayers.Util.applyDefaults({
   pointRadius: 15,
   fillColor: "red",
   fillOpacity: 1,
   strokeColor: "black",
   externalGraphic: "${photo_file_url}"
}, OpenLayers.Feature.Vector.style["default"]));

b) Por cada posición añadimos una miniatura de la foto.

var panoramio_style2 = new OpenLayers.StyleMap(OpenLayers.Util.applyDefaults({
   pointRadius: 7,
   fillColor: "red",
   fillOpacity: 1,
   strokeColor: "black",
   externalGraphic: "panoramio-marker.png"
}, OpenLayers.Feature.Vector.style["default"]));

Imagen: Ejemplos de estilos

Estilos para Panoramio

Estilos para Panoramio

Ahora creamos la vector layer asignándole el estilo escogido

var vectorPano = new OpenLayers.Layer.Vector("Panoramio fotos", {
   styleMap: panoramio_style2
});

Añadimos los elementos:

vectorPano.addFeatures(features);

Añadimos la layer al mapa

map.addLayer(vectorPano);

Definimos para terminar la función el comportamiento del evento que se desencadenará cuando seleccionemos ( click con el ratón) cada fotografía

selectControl = new OpenLayers.Control.SelectFeature(vectorPano,
   {onSelect: onFeatureSelect, onUnselect: onFeatureUnselect});
map.addControl(selectControl);
selectControl.activate();
}

Como ves, se ejecutará la función “onFeatureSelect” cada vez que se selecciona un feature, y “onFeatureUnselect” cuando no hay nada seleccionado.

Con este código tenemos toda la lógica para obtener un vector layer con su posición y atributos. Ahora sólo nos queda definir el comportamiento de los eventos. En este caso vamos a crear un “popup” por cada elemento.

// popups
function onPopupClose(evt) {
   selectControl.unselect(selectedFeature);
}
function onFeatureSelect(feature) {
   selectedFeature = feature;

   // HTML del PopUp
      mensaje = "<a href='http://www.panoramio.com'><img src='./panoramio_header-logo.png' alt='Panoramio' width='146' height='27' /></a><br>"+
	      "<h2>"+ feature.attributes.photo_title + "</h2><p>" +
	      "<a href='"+ feature.attributes.photo_url+
		  "'><img src='http://mw2.google.com/mw-panoramio/photos/small/" +
		  feature.attributes.photo_id + ".jpg' border='0' alt=''></a><br>" +
	      "autor: <a href='"+ feature.attributes.owner_url+"'>"+feature.attributes.owner_name +"</a>";

   popup = new OpenLayers.Popup.FramedCloud("chicken",
      feature.geometry.getBounds().getCenterLonLat(),
      null,
      mensaje,
      null, true, onPopupClose);
   feature.popup = popup;
   map.addPopup(popup);
}
function onFeatureUnselect(feature) {
   map.removePopup(feature.popup);
   feature.popup.destroy();
   feature.popup = null;
}

Ya lo tenemos todo listo. Sólo nos queda verlo en un navegador. ¿A que esperas?

Ver demo en proyección Mercator con fondo de OpenStreetMap
Ver demo en WGS84

Nota de interés:

Por último nos gustaría hacer una consideración de especial relevancia. Este código tiene sentido cuando todas las layers (WMS y vector layer de Panoramio) se encuentran en la misma proyección, en este caso se utiliza el datum WGS84 en coordenadas geográficas (EPSG: 4326). En el caso de que utilices en el mapa otra proyección debes de recordar que has de pasarle a la URL de Panoramio los parámetros de caja en WGS84, y lo mismo ocurre cuando el servicio te devuelve los puntos de cada foto, que ahora debes de hacer lo contrario, es decir, convertirla de EPSG:4326 a la proyección que estés utilizando.

    • jose luis
    • July 7th, 2011 12:55am

    No consigo hacer funcionar el proxyhost, me da in error 500 Internal server error
    A que se puede deber, que mas puedo comenetar para describir el error

    • Salva
    • December 5th, 2011 5:07pm

    Yo tengo el mismo problema. Es un problema de permisos, creo que Apache no tiene permisos para abrir la carpeta donde esta el proxy. Estuve intentándolo hace un tiempo y me dijeron que se debia a los permisos de la carpeta.

  1. Un tutorial excelente. Muchas gracias

  1. May 5th, 2010