<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>GIS &#38; Chips &#187; jose</title>
	<atom:link href="http://www.gisandchips.org/author/jose/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.gisandchips.org</link>
	<description>Geografía útil para llevar</description>
	<lastBuildDate>Tue, 29 Nov 2011 10:38:56 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Proyección segura desde UTM ED50 con interfaz gráfico</title>
		<link>http://www.gisandchips.org/2011/11/28/proyeccion-segura-desde-utm-ed50-con-interfaz-grafico/</link>
		<comments>http://www.gisandchips.org/2011/11/28/proyeccion-segura-desde-utm-ed50-con-interfaz-grafico/#comments</comments>
		<pubDate>Mon, 28 Nov 2011 19:52:39 +0000</pubDate>
		<dc:creator>jose</dc:creator>
				<category><![CDATA[Consejo práctico]]></category>
		<category><![CDATA[ed50]]></category>
		<category><![CDATA[epsg]]></category>
		<category><![CDATA[etrs89]]></category>
		<category><![CDATA[ogr2ogr]]></category>

		<guid isPermaLink="false">http://www.gisandchips.org/?p=1963</guid>
		<description><![CDATA[Seguramente muchos de vosotros habéis estado en la tesitura de tener que proyectar vuestras capas de datos gis que usan la vetusta y a extinguir proyección UTM con datum ED50. Lo de que dicha proyección &#8220;no esta de moda&#8221; no es algo causal, puesto que hay un Real Decreto, el 1071, que define como sistema [...]]]></description>
			<content:encoded><![CDATA[<p>Seguramente muchos de vosotros habéis estado en la tesitura de tener que proyectar vuestras capas de datos gis que usan la vetusta y a extinguir proyección UTM con datum ED50. Lo de que dicha proyección &#8220;no esta de moda&#8221; no es algo causal, puesto que hay un Real Decreto, el 1071, que define como sistema geodésico oficial de referencia para España aquel con el datum ETRS89 y el elipsoide SGR80. Bien es cierto que el decreto permite la ambivalencia de ambos sistemas (ETRS89 y ED50) hasta el año 2015; fecha a partir del cual diremos adiós definitivamente a ED50 (Elipsoide Hayford).<br />
Por otra parte, con el auge de los wikimaps es bastante habitual tener que realizar reproyecciones de las capas gis con ED50 &#8220;de toda la vida&#8221; al sistema geodésico mundial WGS84, que es lo que admiten casi todas las geowikis (OpenStreetMap, ikiMaps, geoNames, etc.).<br />
<div id="attachment_1989" class="wp-caption aligncenter" style="width: 496px"><p class="wp-caption-text">Reproyección mal realizada</p></div><a href="http://www.gisandchips.org/?attachment_id=1989"><img src="http://www.gisandchips.org/wp-content//ua_etrs89_mal.png" alt="Reproyección mal realizada" width="550" class="aligncenter size-full wp-image-1991" /></a><br />
<span id="more-1963"></span><br />
Pero, ¿que es lo que ocurre cuando utilizamos de forma genérica las utilidades de reproyección de los programas de GIS?. Casi todos, generan datos erróneos, que de ninguna manera se corresponden con la realidad. He encontrado translaciones en la X de más de 100 metros, y unos 50 en la Y respecto a la coordenada donde debería estar. </p>
<p>He probado diferentes métodos (sin transformación, transformación EPSG) y el resultado es muy dispar. Sólo un de los métodos funciona bien, aquel cuya transformación utiliza la rejilla en formato NTv2 del IGN, que fue creado precisamente para la transformación de datos desde ED50.<br />
<div id="attachment_1991" class="wp-caption aligncenter" style="width: 496px"><p class="wp-caption-text">Reproyección con rejilla del IGN</p></div><a href="http://www.gisandchips.org/?attachment_id=1991"><img src="http://www.gisandchips.org/wp-content//sigua_ed50.png" alt="Reproyección con rejilla IGN" width="500" class="alignnone size-full wp-image-1991" /></a><br />
Para este artículo he creído conveniente proponer un método de reprojección que no haga uso de ningún programa GIS, y que podamos lanzarlo cómodamente desde la consola (eso de &#8220;cómodamente&#8221; a alguno le sonará a coña). Lo que quería era simplemente un programa que le indicase mi dato en UTM ED50 y me lo proyecte a lo que quiera. Finalmente he decidido crear un híbrido que esté a medio camino entre el &#8220;terminal&#8221; Linux y un interfaz gráfico (en este caso &#8220;GTK&#8221;).<br />
Como no se trata de reinventar la rueda, he utilizado una herramienta con la que me siento muy cómodo y me ha sacado más de una vez de un apuro. Se trata de &#8220;ogr2ogr&#8221;, un comando que forma parte de la suite de GDAL. Este comando está diseñado para cambiar de formato nuestros datos, pero también para reproyectar. Si sintaxis está llena de modificadores que lo hacen a veces un poco críptico.<br />
Entrando en harina, sí deseamos reproyectar un fichero en ED50 a otra proyección la sintaxis es:</p>
<pre class="brush: bash; title: ; notranslate">ogr2ogr -s_srs &quot;EPSG:23030&quot; -t_srs epsg_destino destino origen</pre>
<p>Este comando tampoco solucionaría los problemas para ED50, por lo que habría que retocar el comando de esta forma para que utilice la rejilla del IGN:</p>
<pre class="brush: bash; title: ; notranslate">ogr2ogr -s_srs '+init=epsg:23030 +nadgrids=./peninsula.gsb +wktext' -t_srs epsg_destino origen destino</pre>
<p>Coincidiréis conmigo que este comando con todas sus opciones no es muy intuitivo para recordar. Por esta razón he decidido utilizar un interfaz gráfico para el terminal (sí, no habéis oído mal) que permite lanzar los típicos formularios gráficos para rellenar los datos. En definitiva, no se hace nada más que encapsular dicho comando con cuadros de diálogo que se lanzan al X-windows. Hay algunas utilidades para ello: Xdialog (x-windows), dialog (para el terminal), kdialog (para KDE). Yo voy a utilizar uno llamado &#8220;Zenity&#8221;, que está en todas las distribuciones linux.<br />
Finalmente he creado un script BASH que empotra las peticiones (selección del EPSG, de ficheros, alertas, comprobaciones, etc.) en diálogos hechos con Zenity.<br />
Este es el codigo:</p>
<pre class="brush: bash; title: ; notranslate">
#!/bin/sh
zenity --info --text=&quot;OGR2OGR Dialog \n
Autor: José Manuel Mira \n
GISandCHIPS 2011 \n
Aplicación basada en OGR2OGR para convertir \n
shapefile proyectado en UTM con datum ED50 en huso 30 (EPSG:23030) a  \n
Geodésica mundial con datum WGS84 (epsg:4326) o \n
UTM ETRS89 (epsg:25830) utilizando GDAL. \n
NOTA: Esta utilidad necesita la rejilla del IGN \n
descargable en: \n

http://www.gisandchips.org/demos/j3m/ogr/peninsula.gsb&quot;

# Comprobar que está instalado OGR2OGR
if which ogr2ogr &gt; /dev/null; then
echo &quot;GDAL está instalado&quot;
else
echo &quot;No está instalado GDAL&quot; | zenity --error --text=&quot;No está instalado GDAL. Salir \n ¿Ubuntu? sudo apt-get install gdal-bin&quot;
exit
fi
#Comprobamos que tienes la rejilla instalada en el directorio del script
if [ -f ./peninsula.gsb ]
then
  echo &quot;OK, tienes la rejilla&quot; | zenity --info --text=&quot;OK, tienes la rejilla&quot;
else
  echo &quot;Descargando rejilla ...&quot;
  wget http://www.gisandchips.org/demos/j3m/ogr/peninsula.gsb 2&gt;=1 | sed -u 's/.*\ \([0-9]\+%\)\ \+\([0-9.]\+\ [KMB\/s]\+\)$/\1\n# Downloading \2/' | zenity --progress --title=&quot;Descargando rejilla IGN ...&quot;
fi

origen=$(zenity --file-selection --title=&quot;Selecciona un shapefile en proyección ED50&quot;)
case $? in
  0)
    echo &quot;\&quot;$origen\&quot; selected.&quot;;;
  1)
    exit;;
   -1)
    exit;;
esac

# obtener extensión del fichero
ext=${origen#*.}
echo $ext
if [ $ext != 'shp' ]
then
   echo &quot;No es un shapefile. Salir&quot; | zenity --error --text=&quot;NO es un shapefile. Salir&quot;
   exit
fi

destino=$(zenity --entry --text &quot;Nombre del Shapefile de salida (sin .shp)?&quot; --entry-text &quot;destino&quot;); echo $destino
if [ $? = 1 ]
then
 exit
else
 echo $?
fi

epsg=$(zenity  --list  --text &quot;Selecciona la proyección de salida&quot; --radiolist  --column &quot;Sel&quot; --column &quot;EPSG&quot; --column &quot;Nombre&quot; TRUE 4326 &quot;Geodésica WGS84&quot; FALSE 25830 &quot;UTM ETS89 HUSO 30&quot;  FALSE 25831 &quot;UTM ETS89 HUSO 31&quot;  FALSE 25829 &quot;UTM ETS89 HUSO 29&quot;); 

echo &quot;ogr2ogr -s_srs '+init=epsg:23030 +nadgrids=./peninsula.gsb +wktext' -t_srs EPSG:&quot;${epsg} ${destino}-${epsg}.shp $origen
ogr2ogr -s_srs '+init=epsg:23030 +nadgrids=./peninsula.gsb +wktext' -t_srs EPSG:${epsg} ${destino}-${epsg}.shp $origen
zenity --info --text &quot;Shapefile ${destino}-${epsg}.shp creado satisfactoriamente&quot;
</pre>
<p>Guardamos el script como conversor.sh, le damos permisos de ejecución y lo lanzamos con </p>
<pre class="brush: bash; title: ; notranslate">sh ./conversor.sh</pre>
<p> Estos son algunos de los diálogos que aparecen:<br />
<img src="http://www.gisandchips.org/wp-content//dialogo_shapefile.png" alt="Indicar shapefile" /><br />
<img src="http://www.gisandchips.org/wp-content//dialogo_rejilla.png" alt="Descarga rejilla IGN" /><br />
<img src="http://www.gisandchips.org/wp-content//dialogo_ok.png" alt="Fin diálogo" /><br />
<img src="http://www.gisandchips.org/wp-content//dialogo_epsg.png" alt="Lista SRS" /><br />
Sí, ya se lo que estáis pensando (tanto rollo para una sola línea de comando). Es cierto, pero resulta útil y sencillo para aquellos que no se quieren complicar la vida. Además, con ligeras modificaciones lo puedo hacer extensible a las islas, otros husos, escribir un log con comandos para reutilizarlos en el futuro en modo BATCH, etc)<br />
He realizado algunas pruebas para comprobar la validez de los datos reproyectados utilizando coordenadas de vértices geodésicos y visualizadas sobre un fondo con el WMS del PNOA. El resultado salta a la vista: &#8220;lo ha cuadrado&#8221;<br />
<div id="attachment_1990" class="wp-caption aligncenter" style="width: 496px"><p class="wp-caption-text">Comparación original (EPSG:23030) y proyectada a WGS84 (EPSG:4326)</p></div><a href="http://www.gisandchips.org/?attachment_id=1990"><img src="http://www.gisandchips.org/wp-content//comparacion_23030-4326.png" alt="comparación 23030-4326" width="570" class="alignnone size-full wp-image-1990" /></a></p>
<p>Sí deseas probar el script <a href="/demos/j3m/ogr/vgeo.zip" title="Descarga vértices geodésicos">aquí</a> tienes unas shapes de prueba (vértices geodésicos en 23030) de la Comunidad Valenciana)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gisandchips.org/2011/11/28/proyeccion-segura-desde-utm-ed50-con-interfaz-grafico/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Digitalización web con OpenLayer y WFS-T (Geoserver)</title>
		<link>http://www.gisandchips.org/2010/09/16/digitalizacion-web-con-openlayer-y-wfs-t-geoserver/</link>
		<comments>http://www.gisandchips.org/2010/09/16/digitalizacion-web-con-openlayer-y-wfs-t-geoserver/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 16:06:40 +0000</pubDate>
		<dc:creator>jose</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[Digitalización]]></category>
		<category><![CDATA[OpenLayers]]></category>
		<category><![CDATA[WFS-T]]></category>

		<guid isPermaLink="false">http://www.gisandchips.org/?p=1654</guid>
		<description><![CDATA[OBJETIVO: En el contexto de la Web 2.0. cada vez son más los casos de cartografía interactiva donde continuamente se está actualizando la información. El caso más espectacular es el de OpenStreetMap, donde una legión de &#8220;mappers&#8221; interactuan con el sistema. Nuestro objetivo es mucho más modesto, pero también más fácil de implementar, y todo [...]]]></description>
			<content:encoded><![CDATA[<p>OBJETIVO:<br />
En el contexto de la Web 2.0. cada vez son más los casos de cartografía interactiva donde continuamente se está actualizando la información. El caso más espectacular es el de OpenStreetMap, donde una legión de &#8220;mappers&#8221; interactuan con el sistema. Nuestro objetivo es mucho más modesto, pero también más fácil de implementar, y todo gracias al uso de estándares y servicios de mapa libres. Perseguimos, en definitiva, una digitalización <em>on-line</em>, en todos los aspectos: creación de nuevos elementos, modificación geométrica de los ya existentes, actualización de atributos, etc. Todo ello con casi todas las ventajas de las aplicaciones de escritorio, pero con la singularidad de que cualquier usuario pueda intervenir sin apenas conocimientos. </p>
<p>Estos son los ingredientes para la receta:</p>
<ul>
<li>capas geográficas almacenadas en tablas PostgreSQL/PostGIS</li>
<li>servicio de mapas WFS-T con GeoServer</li>
<li>Diseño web con OpenLayers</li>
</ul>
<div id="attachment_1677" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.gisandchips.org/wp-content/digitalizacion.png"><img src="http://www.gisandchips.org/wp-content/digitalizacion-300x298.png" alt="Digitalizacion WFS-T" width="300" height="298" class="size-medium wp-image-1677" /></a><p class="wp-caption-text">Digitalizacion WFS-T</p></div><br />
<span id="more-1654"></span><br />
En definitiva, nuestro objetivo es ofrecer un servicio de digitalización de elementos geométricos en Internet, utilizando como framework a OpenLayers, que carga una serie de capas servidas por un servicio de mapas de elementos transaccional (WFS-T) almacenadas en una geodatabase PostGIS.</p>
<p>Tengo que reconocer que en un primer momento intenté realizar este juego con un servidor WFS-T poco conocido, pero muy ligero (funciona como CGI, al igual que MapServer), y trabaja con PostGIS, con lo que me las prometía felices, pero las pruebas fueron satisfactorias  sólo mientras  modificase o crease geometría, pero en ningún momento pude subir o modificar atributos. Para los estudiosos este motor se llama TinyOWS . Seguro que más de uno lo habrá usado satisfactoriamente, con lo que se agradecerían comentarios de como hacerlo . Me gustaba porque la instalación era muy sencilla (colocar el ejecutable en el directorio donde se ejecutan los scripts CGI, generalmente en &#8220;cgi-bin&#8221;, sin necesidad de tener instaladas otras herramientas. En este enlace tienes un precioso ejemplo de esta implementación: <a href="http://dev4.mapgears.com/bdga/bdgaWFS-T.html">http://dev4.mapgears.com/bdga/bdgaWFS-T.html</a></p>
<p>Ante este dilema opté por instalar GeoServer (versión 2.0.1) cuya experiencia está más que demostrada para tener servicios de mapa interoperables. Sí quieres ver las características de nuestro servicio consúltalas aquí</p>
<p><a href="http://www.gisandchips.org:8080/geoserver/wfs?service=wfs&amp;VERSION=1.1.1&amp;REQUEST=GetCapabilities">http://www.gisandchips.org:8080/geoserver/wfs?service=wfs&amp;VERSION=1.1.1&amp;REQUEST=GetCapabilities</a></p>
<p>De esta manera me propuse crear un servicio WFS-T sobre tres capas, cada una de una tipología distinta ( puntos, polígonos y líneas), almacenadas en sus correspondientes tablas de PostGIS:</p>
<ul>
<li>tabla &#8220;golf&#8221; para puntos de campos de golf. Ver <a href="http://www.gisandchips.org:8080/geoserver/wfs?service=WFS&amp;version=1.0.0&amp;request=DescribeFeatureType&amp;TypeName=cite:golf">características.</a></li>
<li>tabla &#8220;municipios&#8221; para polígonos de términos municipales Ver <a href="http://www.gisandchips.org:8080/geoserver/wfs?service=WFS&amp;version=1.0.0&amp;request=DescribeFeatureType&amp;TypeName=cite:municipios">características.</a></li>
<li>tabla &#8220;vias&#8221; para línea de una red de carreteras Ver <a href="http://www.gisandchips.org:8080/geoserver/wfs?service=WFS&amp;version=1.0.0&amp;request=DescribeFeatureType&amp;TypeName=cite:vias">características.</a></li>
</ul>
<p>Para ver su potencial desarrollé tres páginas web para cada uno de los tipos de digitalización</p>
<ul>
<li>demo 1: <a href="http://www.gisandchips.org/demos/j3m/wfs/wfs_golf.html">Digitalización de Campos de golf</a></li>
<li>demo2: <a href="http://www.gisandchips.org/demos/j3m/wfs/wfs_municipios.html">Digitalización de municipios</a></li>
<li>demo 3: <a href="http://www.gisandchips.org/demos/j3m/wfs/wfs_vias.html">Digitalización de vías</a></li>
</ul>
<p><em>NOTA: Puedes probar sin miedo cualquiera de estas demos. Los datos se vuelven a cargar cada día. No te desesperes si es un poco lento el servidor web. No da más de sí.</em></p>
<p>En cada una de las demos he querido incorporar una barra de iconos para hacer uso de las siguientes herramientas:</p>
<p><div id="attachment_1673" class="wp-caption aligncenter" style="width: 212px"><a href="http://www.gisandchips.org/wp-content/panel.png"><img class="size-full wp-image-1673" src="http://www.gisandchips.org/wp-content/panel.png" alt="" width="202" height="29" /></a><p class="wp-caption-text">panel digitalización</p></div>
<ul>
<li>Consulta y edición de atributos</li>
<li>Digitalización</li>
<li>Mover elementos (o vértices en el caso de municipios y vías)</li>
<li>Borrar elementos</li>
<li>Guardar las transacciones</li>
<li>Navegación en modo Pan-zoom</li>
</ul>
<p>El código está implementado de tal forma que la digitalización forma parte de una sesión, y  sólo se validan los cambios (transacción) cuando hacemos clic en el botón &#8220;salvar&#8221;. De esta forma tengo recogido todo el abanico de posibilidades de la digitalización.</p>
<p>OPENLAYERS<br />
Este framework, de sobra conocido es el responsable de articular todos los flujos entre el cliente y la base de datos utilizando WFS-T servido por GeoServer. El código fuente de cada ejemplo lo puedes obtener del propio navegador, por lo que me centraré en desmenuzar las partes:</p>
<p>ESTILO HTML PARA MENÚS<br />
Estilo para la barra de iconos</p>
<pre class="brush: xml; title: ; notranslate">
    &lt;style&gt;
        .customEditingToolbar {
            float: right;
            right: 0px;
            height: 30px;
            width: 220px;
        }
        .customEditingToolbar div {
            float: right;
            margin: 5px;
            width: 24px;
            height: 24px;
        }
        .olControlNavigationItemActive {
            background-image: url(&quot;../../js/OpenLayers-2.8/theme/default/img/editing_tool_bar.png&quot;);
            background-repeat: no-repeat;
            background-position: -103px -23px;
        }
        .olControlNavigationItemInactive {
            background-image: url(&quot;../../js/OpenLayers-2.8/theme/default/img/editing_tool_bar.png&quot;);
            background-repeat: no-repeat;
            background-position: -103px -0px;
        }
        .olControlDrawFeaturePointItemInactive {
            background-image: url(&quot;../../js/OpenLayers-2.8/theme/default/img/editing_tool_bar.png&quot;);
            background-repeat: no-repeat;
            background-position: -77px 0px;
        }
        .olControlDrawFeaturePointItemActive {
            background-image: url(&quot;../../js/OpenLayers-2.8/theme/default/img/editing_tool_bar.png&quot;);
            background-repeat: no-repeat;
            background-position: -77px -23px ;
        }
        .olControlModifyFeatureItemActive {
            background-image: url(../../js/OpenLayers-2.8/theme/default/img/move_feature_on.png);
            background-repeat: no-repeat;
            background-position: 0px 1px;
        }
        .olControlModifyFeatureItemInactive {
            background-image: url(../../js/OpenLayers-2.8/theme/default/img/move_feature_off.png);
            background-repeat: no-repeat;
            background-position: 0px 1px;
        }
        .olControlDeleteFeatureItemActive {
            background-image: url(../../js/OpenLayers-2.8/theme/default/img/remove_point_on.png);
            background-repeat: no-repeat;
            background-position: 0px 1px;
        }
        .olControlDeleteFeatureItemInactive {
            background-image: url(../../js/OpenLayers-2.8/theme/default/img/remove_point_off.png);
            background-repeat: no-repeat;
            background-position: 0px 1px;
        }
        .olControlSelectFeatureItemInactive {
            background-image: url(&quot;../../js/OpenLayers-2.8/theme/default/img/info_off.png&quot;);
            background-repeat: no-repeat;
            background-position: 0px 1px;
        }
        .olControlSelectFeatureItemActive {
            background-image: url(&quot;../../js/OpenLayers-2.8/theme/default/img/info_on.png&quot;);
            background-repeat: no-repeat;
            background-position: 0px 1px;
        }

    &lt;/style&gt;
</pre>
<p>Aplico en el código de OpenLayers los estilos que tendrá la capa WFS que vamos a cargar</p>
<pre class="brush: jscript; title: ; notranslate">
	    // Styles
            var styles = new OpenLayers.StyleMap({
                &quot;default&quot;: new OpenLayers.Style(null, {
                    rules: [
                        new OpenLayers.Rule({
                            symbolizer: {
                                &quot;Point&quot;: {
                                    pointRadius: 5,
                                    graphicName: &quot;square&quot;,
                                    fillColor: &quot;red&quot;,
                                    fillOpacity: 0.25,
                                    strokeWidth: 1,
                                    strokeOpacity: 1,
                                    strokeColor: &quot;#333333&quot;
                                }
                            }
                        })
                    ]
                }),
                &quot;select&quot;: new OpenLayers.Style({
                    strokeColor: &quot;#00ccff&quot;,
                    strokeWidth: 4
                }),
                &quot;temporary&quot;: new OpenLayers.Style(null, {
                    rules: [
                        new OpenLayers.Rule({
                            symbolizer: {
                                &quot;Point&quot;: {
                                    pointRadius: 5,
                                    graphicName: &quot;square&quot;,
                                    fillColor: &quot;white&quot;,
                                    fillOpacity: 0.25,
                                    strokeWidth: 1,
                                    strokeOpacity: 1,
                                    strokeColor: &quot;#333333&quot;
                                }

                            }
                        })
                    ]
                })
            });
</pre>
<p>En definitivo asigno un estilo para el elemento geométrico, y su comportamiento cuando es seleccionado, cuando está temporal (digitalización), o su valor por defecto.</p>
<p>CAPA WFS<br />
Definimos una capa WFS con Strategy (para indicar que conjunto de datos deben de ser cargados -ej. todos los datos, por BBOX, en clusters, por página, por filtro-)</p>
<pre class="brush: jscript; title: ; notranslate">
// Definimos una capa WFS con St
           var saveStrategy = new OpenLayers.Strategy.Save();

           wfs = new OpenLayers.Layer.Vector(&quot;golf&quot;, {
               //isBaseLayer:true,
               strategies: [new OpenLayers.Strategy.BBOX(), saveStrategy],
               projection: new OpenLayers.Projection(&quot;EPSG:23030&quot;),
               styleMap: styles,
               protocol: new OpenLayers.Protocol.WFS({
                   version: &quot;1.0.0&quot;,
                   srsName: &quot;EPSG:23030&quot;,
                   url: &quot;http://www.gisandchips.org:8080/geoserver/wfs&quot;,
                   featureType: &quot;golf&quot;,
                   featureNS: &quot;http://www.opengeospatial.net/cite&quot;,
                   geometryName: &quot;geometria&quot;,
                   schema: &quot;http://www.gisandchips.org:8080/geoserver/wfs?service=WFS&amp;version=1.0.0&amp;request=DescribeFeatureType&amp;TypeName=cite:golf&quot;,
               })
            });
 // Añadimos la capa anterior y los WMS que queramos de fondo
            map.addLayers([tiled,wfs]);
</pre>
<p>RESALTADO DE ELEMENTOS</p>
<p>El siguiente código controla el resaltado de los elementos cuando se lanzan eventos del ratón (mouseover, selección)</p>
<pre class="brush: jscript; title: ; notranslate">
		/* HIGHLIGHT */

	    // inicio highlight mouseover
            var report = function(e) {
                OpenLayers.Console.log(e.type, e.feature.id);
            };

            var highlightCtrl = new OpenLayers.Control.SelectFeature(wfs, {
                hover: true,
                highlightOnly: true,
                renderIntent: &quot;temporary&quot;,
                eventListeners: {
                    beforefeaturehighlighted: report,
                    featurehighlighted: report,
                    featureunhighlighted: report
                }
            });

            var selectCtrl = new OpenLayers.Control.SelectFeature(wfs,
                {clickout: true}
            );

            map.addControl(highlightCtrl);
            map.addControl(selectCtrl);

            highlightCtrl.activate();
            selectCtrl.activate();

	    /* END HIGHLIGHT */
</pre>
<p>SNAP O TOLERANCIAS DE CAZADO Y SPLIT (CORTES)</p>
<p>En ocasiones puede ser útil que la digitalización no sea a mano alzada (donde hagamos clic con el ratón), sino que tenga establecidas tolerancias de cazado para que cuando nos acerquemos a una línea o un polígono pueda pillar el nodo, vértice o eje. En este <a href="http://openlayers.org/dev/examples/snapping.html">enlace</a> verás un ejemplo más completo de este tema.<br />
En el caso del Split sólo tiene sentido cuando los elementos son polígonos o líneas. Esto nos permite por ejemplo crear una ruta (R1) desde cualquier punto de otra ruta existente (R2), y mientras con el snapping obtenemos la sujección al vértice o eje, la ruta ya existente (R2) se parte en dos partes.</p>
<pre class="brush: jscript; title: ; notranslate">
		/* SNAPPING */
            // Configuración del Snapping
            var snap = new OpenLayers.Control.Snapping({layer: wfs});
            map.addControl(snap);
            snap.activate();

            // Configuración del Split
            var split = new OpenLayers.Control.Split({
                layer: wfs,
                source: wfs,
                tolerance: 1000,
                deferDelete: true,
                eventListeners: {
                    aftersplit: function(event) {
                        var msg = &quot;Split resulted in &quot; + event.features.length + &quot; features.&quot;;
                        flashFeatures(event.features);
                    }
                }
            });
            map.addControl(split);
            split.activate();
</pre>
<p>PANEL Y CONTROLES</p>
<p>Como ya hemos comentado es preferible tener los iconos a mano en el mapa en vez de los típicos radio button, ya que resultan más elegantes.</p>
<pre class="brush: jscript; title: ; notranslate">
/* PANEL */
// crear un panel y añadirle iconos con funcionalidad
var panel = new OpenLayers.Control.Panel(
    {displayClass: 'customEditingToolbar'}
);

// Control para digitalizar puntos (draw)
var draw = new OpenLayers.Control.DrawFeature(
    wfs, OpenLayers.Handler.Point,
    {
        title: &quot;Draw Feature&quot;,
        displayClass: &quot;olControlDrawFeaturePoint&quot;,
        handlerOptions: {freehand: false, multi: false},
        // Muy importante: Si la capa postGIS es POINT poner multi:false.
        // Si es MULTIPOINT poner multi:true
        featureAdded: onFeatureInsert,
        //onFeatureInsert es el método que se ejecuta una vez que hemos terminado de digitalizar
    }
);

/*
En el caso de digitalizar polígonos este sería el código
            var draw = new OpenLayers.Control.DrawFeature(
                wfs, OpenLayers.Handler.Polygon,
                {
                    title: &quot;Draw Feature&quot;,
                    displayClass: &quot;olControlDrawFeaturePolygon&quot;,
                    handlerOptions: {multi: true},
                    featureAdded: onFeatureInsert,
                }
            );
*/

// Control para modificar elementos. En el caso de puntos sólo se pueden desplazar
// a otra posición
modify = new OpenLayers.Control.ModifyFeature(
    wfs, {displayClass: &quot;olControlModifyFeature&quot;, title: &quot;Modify Feature&quot;}
);

// Control para borrar elementos
var del = new DeleteFeature(wfs, {title: &quot;Delete Feature&quot;});

// Control para salvar la sesión de digitalización
var save = new OpenLayers.Control.Button({
    title: &quot;Save Changes&quot;,
    trigger: function() {
        if(modify.feature) {
            modify.selectControl.unselectAll();
        }
        saveStrategy.save();
    },
    displayClass: &quot;olControlSaveFeatures&quot;
});

/*
Control de Información. Es el típico control que una vez que seleccionas un elementos obtenemos los atributos, en este caso englobado en un popUp. Aquí hemos pensado reutilizar este formulario para actualizar datos de cada elemento (UPDATE de atributos).
*/
 selectControl = new OpenLayers.Control.SelectFeature(wfs,
 {
   onSelect: onFeatureInsert,
   onUnselect: onFeatureUnselect,
   displayClass: &quot;olControlSelectFeature&quot;,
   title: &quot;Info&quot;,
});

// Añadimos los controles al panel
panel.addControls([
    new OpenLayers.Control.Navigation({title: &quot;Pan and zoom&quot;}),
    save, del, modify, draw, selectControl
]);

// Definimos el control de navegación como el activo por defecto
panel.defaultControl = panel.controls[0];
map.addControl(panel);
</pre>
<p>FUNCIONES DE CADA CONTROL<br />
Borrar elementos</p>
<pre class="brush: jscript; title: ; notranslate">
 var DeleteFeature = OpenLayers.Class(OpenLayers.Control, {
      initialize: function(layer, options) {
          OpenLayers.Control.prototype.initialize.apply(this, [options]);
          this.layer = layer;
          this.handler = new OpenLayers.Handler.Feature(
              this, layer, {click: this.clickFeature}
          );
      },
      clickFeature: function(feature) {
          // sí no tiene fid eliminarlo
          if(feature.fid == undefined) {
              this.layer.destroyFeatures([feature]);
          } else {
              feature.state = OpenLayers.State.DELETE;
              this.layer.events.triggerEvent(&quot;afterfeaturemodified&quot;,
                                             {feature: feature});
              feature.renderIntent = &quot;select&quot;;
              this.layer.drawFeature(feature);
          }
      },
      setMap: function(map) {
          this.handler.setMap(map);
          OpenLayers.Control.prototype.setMap.apply(this, arguments);
      },
      CLASS_NAME: &quot;OpenLayers.Control.DeleteFeature&quot;
  });
</pre>
<p>Información de los elementos, actualización de atributos e inserción de nuevos elementos (geometría + atributos):</p>
<pre class="brush: jscript; title: ; notranslate">
// Funciones para cerrar el popUp
  function onPopupClose(evt) {
      selectControl.unselect(selectedFeature);
  }

// Función para cuando se quita la selección
 function onFeatureUnselect(feature) {
      map.removePopup(feature.popup);
      feature.popup.destroy();
      feature.popup = null;
  }
</pre>
<p>Función para insertar un nuevo elemento o actualizar atributos en uno ya existente</p>
<pre class="brush: jscript; title: ; notranslate">
// PopUp para insert/update
  function onFeatureInsert(feature)
  {
  		selectedFeature = feature;
                // mapeo de los atributos
		var fid = selectedFeature.id;
     	        var nombre = selectedFeature.attributes['nombre'];
     	        var municipio = selectedFeature.attributes['municipio'];
		var codive = selectedFeature.attributes['codive'];
		var hoyos = selectedFeature.attributes['hoyos'];
                // Compruebo si existe un valor para el atributo nombre.
                // Sí no tiene atributos es un &quot;INSERT&quot; y muestro el formulario para cumplimentar
		if (nombre == null)
		{
 			// formulario
	            htmlForm = &quot;&lt;div style='font-size:.8em'&gt;&quot;+
	            &quot;&lt;H2&gt;&lt;b&gt;A&amp;Ntilde;ADIR CAMPO DE GOLF&lt;/b&gt;&lt;/h2&gt;\n&quot; +
	            &quot;FID: &quot;+ fid + &quot;&lt;br/&gt;\n&quot; +
	            &quot;&lt;input type='hidden' name='fid' id='fid' value='&quot;+ fid +&quot;'&gt;&quot; +
	            //&quot;GEOMETRIA: &quot;+ feature.geometry + &quot;&lt;br/&gt;\n&quot; +
	            &quot;UBICACION: &quot;+ feature.geometry + &quot;&lt;br/&gt;\n&quot; +
	            &quot;Rellene los siguientes datos: &lt;br/&gt; \n&quot; +
	            &quot;NOMBRE:&lt;input type='text' name='nombre' id='nombre' value='Test Campo de Golf' size=20 style='background-color: #E6E6FA; color: #7F7F7F; font-style: italic;' &gt; &lt;br/&gt;\n&quot; +
	            &quot;HOYOS:&lt;input type='text' name='hoyos' id='hoyos' value='18' size=2 style='background-color: #E6E6FA; color: #7F7F7F; font-style: italic;' &gt; &lt;br/&gt;\n&quot; +
	            &quot;MUNICIPIO:&lt;input type='text' name='municipio' id='municipio' value='Test Municipio' size=20 style='background-color: #E6E6FA; color: #7F7F7F; font-style: italic;' &gt; &lt;br/&gt;\n&quot; +
		    &quot;IVE:&lt;input type='text' name='codive' id='codive' value='12345' size=5 style='background-color: #E6E6FA; color: #7F7F7F; font-style: italic;' &gt; &lt;br/&gt;\n&quot; +
	            &quot;&lt;button onclick='onTriggerInsertar()'&gt;A&amp;ntilde;adir&lt;/button&gt;&quot; +
	            &quot;&lt;button onclick='onTriggerDelNewPoint()'&gt;Eliminar&lt;/button&gt;&lt;/div&gt;&quot;;
             }
/* Sí nombre tiene valor ofrezco los atributos en el formulario para modificarlos, añadiendo al popUp
    un botón para &quot;Actualizar&quot; el registro.
*/
                 else
            {
	         htmlForm = &quot;&lt;div style='font-size:.8em'&gt;&quot;+
	         &quot;&lt;H2&gt;&lt;b&gt;ACTUALIZAR CAMPO DE GOLF&lt;/b&gt;&lt;/h2&gt;\n&quot; +
	         &quot;FID: &quot;+ fid + &quot;&lt;br/&gt;\n&quot; +
	         &quot;&lt;input type='hidden' name='fid' id='fid' value='&quot;+ fid +&quot;'&gt;&quot; +
	         &quot;Rellene los siguientes datos: &lt;br/&gt; \n&quot; +
	         &quot;NOMBRE:&lt;input type='text' name='nombre' id='nombre' value='&quot;+ nombre + &quot;' size=20 style='background-color: #E6E6FA;color: #7F7F7F; font-style: italic;' &gt; &lt;br/&gt;\n&quot; +
	         &quot;HOYOS:&lt;input type='text' name='hoyos' id='hoyos' value='&quot; + hoyos + &quot;' size=2 style='background-color: #E6E6FA; color:#7F7F7F; font-style: italic;' &gt; &lt;br/&gt;\n&quot; +
	         &quot;MUNICIPIO:&lt;input type='text' name='municipio' id='municipio' value='&quot; + municipio +&quot;' size=20 style='background-color:#E6E6FA; color: #7F7F7F; font-style: italic;' &gt; &lt;br/&gt;\n&quot; +
		 &quot;IVE:&lt;input type='text' name='codive' id='codive' value='&quot; + codive +&quot;' size=5 style='background-color:#E6E6FA; color: #7F7F7F; font-style: italic;' &gt; &lt;br/&gt;\n&quot; +
	         &quot;&lt;button onclick='onTriggerUpdate()'&gt;Actualizar&lt;/button&gt;&lt;/div&gt;&quot;;
      }
// Defino el objeto popUp
      popup = new OpenLayers.Popup.FramedCloud(&quot;info&quot;,
    		feature.geometry.getBounds().getCenterLonLat(),
         null,
         htmlForm,
         null,
         true,
         onPopupClose);

      feature.popup = popup;
      map.addPopup(popup);

  }

   // Trigger y función para pasar atributos del formulario
	var btnInsert = new OpenLayers.Control.Button({trigger: onTriggerInsertar});

	function onTriggerInsertar(fid)
	// se le pasa el fid como parametro para seleccionarlo mediante getFeatureById
	{
      //alert(&quot;has llegado a trigger insertar&quot;);
      var fid =  OpenLayers.Util.getElement('fid').value;// recojo el fid del formulario
      var miFeature = wfs.getFeatureById(fid);// hago la selección de la feature a partir del fid del form
      //alert(miFeature.id);
      // Paso atributos del formulario al elemento
		miFeature.attributes.nombre = OpenLayers.Util.getElement('nombre').value;
		miFeature.attributes.municipio = OpenLayers.Util.getElement('municipio').value;
		miFeature.attributes.hoyos = OpenLayers.Util.getElement('hoyos').value;
		miFeature.attributes.codive = OpenLayers.Util.getElement('codive').value;
		// elimina el popup
		onFeatureUnselect(miFeature);
	}
	// fin trigger insert

   // Trigger y función para pasar atributos del formulario en el update
	var btnUpdate = new OpenLayers.Control.Button({trigger: onTriggerUpdate});

	function onTriggerUpdate()
	{
      // defino miFeature como el array de elementos seleccionados, miFeature[0] será el único elemento del array
      miFeature = [selectedFeature]; //alert(miFeature.length);
      // Paso atributos del formulario al elemento
                var fid =  OpenLayers.Util.getElement('fid').value;
                miFeature[0].id = fid;
		miFeature[0].attributes.nombre = OpenLayers.Util.getElement('nombre').value;
		miFeature[0].attributes.municipio = OpenLayers.Util.getElement('municipio').value;
		miFeature[0].attributes.hoyos = OpenLayers.Util.getElement('hoyos').value;
		miFeature[0].attributes.codive = OpenLayers.Util.getElement('codive').value;
		// comprobar que se le pasan los atributos al feature. Descomentar para comprobación
		/*alert (  miFeature[0].id + &quot;,&quot; +
				   miFeature[0].attributes.nombre + &quot;,&quot; +
					miFeature[0].attributes.municipio  + &quot;,&quot; +
					miFeature[0].attributes.hoyos  + &quot;,&quot; +
					miFeature[0].attributes.codive );
               */

		miFeature[0].state = OpenLayers.State.UPDATE;
		// elimina el popup
		map.removePopup(miFeature[0].popup);
                selectControl.unselectAll();
                miFeature[0].popup = null;
	}
	// fin trigger
</pre>
<p>Faltan algunos elementos más, pero en esencia aquí está la lógica de la digitalización. Observa cada caso, puntos, polígonos y líneas para ver las diferencias existentes.</p>
<p>CONCLUSIÓN:<br />
Quizás lo más relevante de este tema es que con un sólo fichero HTML hemos abordado la complejidad de la digitalización, utilizando siempre servicios y tecnologías estándar y abiertas. Con este tipo de soluciones tan sencillas podemos acercar a un número mayor de usuarios, menos especializados quizás, pero que de otra forma se hubieran visto imposibilitados de actuar por razones técnicas, económicas o de formación. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.gisandchips.org/2010/09/16/digitalizacion-web-con-openlayer-y-wfs-t-geoserver/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>OpenLayers y Panoramio</title>
		<link>http://www.gisandchips.org/2010/05/04/openlayers-y-panoramio/</link>
		<comments>http://www.gisandchips.org/2010/05/04/openlayers-y-panoramio/#comments</comments>
		<pubDate>Tue, 04 May 2010 09:59:47 +0000</pubDate>
		<dc:creator>jose</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[OpenLayers]]></category>
		<category><![CDATA[Panoramio]]></category>

		<guid isPermaLink="false">http://www.gisandchips.org/?p=1601</guid>
		<description><![CDATA[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 [...]]]></description>
			<content:encoded><![CDATA[<p>Tras leer el excelente <a href="/2010/03/24/api-de-google-openstreetmap-y-otros-servicios-georreferenciados/">post</a> de <a href="http://www.gisandchips.org/author/pepe/">Pepe</a> relativo a la integración de servicios de geo-localización en un ambiente <a href="/2010/03/24/api-de-google-openstreetmap-y-otros-servicios-georreferenciados/">Google Maps</a> 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 <a href="http://openlayers.org">OpenLayers</a>, 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.<br />
<div id="attachment_1618" class="wp-caption aligncenter" style="width: 496px"><a href="http://www.gisandchips.org/demos/j3m/panoramio/panoramio.html"><img src="http://www.gisandchips.org/wp-content/captura_panoramio2.png" alt="Openlayers con Panoramio" width="486" height="228" class="size-full wp-image-1618" /></a><p class="wp-caption-text">Openlayers con Panoramio</p></div><br />
<span id="more-1601"></span></p>
<p>A la hora de &#8220;copiar&#8221; 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 <a href="http://dev.openlayers.org/releases/OpenLayers-2.9/examples/">ejemplos</a> on-line del portal Openlayers.org</p>
<p>De todos los geoservicios, al que más atención ha prestado ha sido al de <a href="http://www.panoramio.com">Panoramio</a>, 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 <a href="http://www.panoramio.com/api/">servicio REST</a> público que nos permite ejecutar una URL con ciertos parámetros, que finalmente nos devuelve un documento estructurado en formato JSON</p>
<pre>
{
"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"
}]</pre>
<p>}</p>
<p>Como podéis ver por cada foto disponemos de un array con los datos que nos interesan, y de forma particular los siguientes:</p>
<ul>
<li> <strong>owner_name</strong>: autor</li>
<li> <strong>photo_title</strong>: título</li>
<li> <strong>latitude</strong> y <strong>longitude</strong>: coordenadas geográficas de geoposicionamiento en datum WGS 84</li>
<li> <strong>photo_id</strong>: identificador de foto</li>
<li> <strong>photo_file_url</strong>: URL de la imagen</li>
<li> <strong>photo_url</strong>: URL del recurso en la web de Panoramio</li>
</ul>
<p>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 <a href="http://docs.openlayers.org/library/formats.html#creating-custom-formats">clase derivada</a> de JSON que facilite las cosas. Por ahora me limitaré a desmenuzar el código de forma secuencial para que se entienda el proceso</p>
<p>Lo primero que debemos de hacer es preparar nuestro proxy para que admita peticiones al host de Panoramio.  En este <a href="http://trac.openlayers.org/wiki/FrequentlyAskedQuestions#ProxyHost">enlace</a> explica como hacerlo</p>
<pre class="brush: jscript; title: ; notranslate">
OpenLayers.ProxyHost= &quot;/cgi-bin/proxy.cgi?url=&quot;;
</pre>
<p>Posteriormente creamos un par de variables con la URL del servicio REST y los parámetros</p>
<pre class="brush: jscript; title: ; notranslate">
url = &quot;http://www.panoramio.com/map/get_panoramas.php&quot;;
parametros = {
   order:'popularity',
   set:'full',
   from:0,
   to:20,
   minx: minx,
   miny: miny,
   maxx: maxx,
   maxy: maxy,
   size:'thumbnail'
}
</pre>
<p>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:</p>
<ul>
<li> “<strong>to</strong>”: indica el nº de fotografías que queremos cargar. Máximo 50</li>
<li> “<strong>minx</strong>”,”<strong>miny</strong>”,”<strong>maxx</strong>”,”<strong>maxy</strong>”: 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:</li>
</ul>
<pre class="brush: jscript; title: ; notranslate">
// Obtener extensiones.
ext = map.getExtent();
var minx = ext.left;
var miny = ext.bottom;
var maxx = ext.right;
var maxy = ext.top; //alert (minx + &quot; &quot; + miny + &quot; &quot; + maxx + &quot; &quot; + maxy);
</pre>
<p>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</p>
<pre class="brush: jscript; title: ; notranslate">
OpenLayers.loadURL(url, parametros, this, mostrarfotos);
</pre>
<p><strong>Función mostrarfotos</strong><br />
Es la encargada de procesar el documento JSON que recibe como argumento</p>
<pre class="brush: jscript; title: ; notranslate">
function mostrarfotos(response) {
</pre>
<p>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:</p>
<pre class="brush: jscript; title: ; notranslate">
var json = new OpenLayers.Format.JSON();
var panoramio = json.read(response.responseText);
</pre>
<p>Creamos un array de featutes con el total de fotos  (panoramio.photos.length)</p>
<pre class="brush: jscript; title: ; notranslate">
var features = new Array(panoramio.photos.length);
</pre>
<p>Aplicamos un bucle “for” para recorrer cada fotografía.</p>
<pre class="brush: jscript; title: ; notranslate">
for (var i = 0; i &lt; panoramio.photos.length; i++)
{
</pre>
<p>Declaramos y almacenamos cada dato de la foto en una variable para recorrerla con comodidad.</p>
<pre class="brush: jscript; title: ; notranslate">
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;
</pre>
<p>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”</p>
<pre class="brush: jscript; title: ; notranslate">
OpenLayers.Util.getElement('galeria').innerHTML +=
     &quot;&lt;a href='&quot;+ photo_url +
     &quot;'&gt;&lt;img src='&quot;+photo_file_url+&quot;' title='&quot;+
     photo_title +&quot;' /&gt;&lt;/a&gt;&amp;nbsp;&quot;;
</pre>
<p>Posteriormente en el BODY para representar esta galería sólo hay que escribir:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;div id=&quot;galeria&quot;&gt;&lt;/div&gt;
</pre>
<p>Creamos un objeto point con las coordenadas de la foto</p>
<pre class="brush: jscript; title: ; notranslate">
var fpoint = new OpenLayers.Geometry.Point(longitude,latitude);
</pre>
<p>Creo un array de atributos</p>
<pre class="brush: jscript; title: ; notranslate">
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
}
</pre>
<p>Por cada foto creo un elemento (feature) que contiene su posición y sus atributos</p>
<pre class="brush: jscript; title: ; notranslate">
features[i] = new OpenLayers.Feature.Vector(fpoint,atributos);
</pre>
<p>Terminamos el bucle</p>
<pre class="brush: jscript; title: ; notranslate">
}
</pre>
<p>Fuera del bucle, todavía en la función mostrar foto nos queda:</p>
<p>Definir el estilo para representar la foto. Tenemos dos opciones<br />
a) Crear un icono único para cada fotografía</p>
<pre class="brush: jscript; title: ; notranslate">
// estilo punto
var panoramio_style1 = new OpenLayers.StyleMap(OpenLayers.Util.applyDefaults({
   pointRadius: 15,
   fillColor: &quot;red&quot;,
   fillOpacity: 1,
   strokeColor: &quot;black&quot;,
   externalGraphic: &quot;${photo_file_url}&quot;
}, OpenLayers.Feature.Vector.style[&quot;default&quot;]));
</pre>
<p>b) Por cada posición añadimos una miniatura de la foto.</p>
<pre class="brush: jscript; title: ; notranslate">
var panoramio_style2 = new OpenLayers.StyleMap(OpenLayers.Util.applyDefaults({
   pointRadius: 7,
   fillColor: &quot;red&quot;,
   fillOpacity: 1,
   strokeColor: &quot;black&quot;,
   externalGraphic: &quot;panoramio-marker.png&quot;
}, OpenLayers.Feature.Vector.style[&quot;default&quot;]));
</pre>
<p>Imagen: Ejemplos de estilos</p>
<div id="attachment_1613" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.gisandchips.org/wp-content/captura_panoramio_styles.png"><img class="size-medium wp-image-1613" src="http://www.gisandchips.org/wp-content/captura_panoramio_styles-300x123.png" alt="Estilos para Panoramio" width="300" height="123" /></a><p class="wp-caption-text">Estilos para Panoramio</p></div>
<p>Ahora creamos la vector layer asignándole el estilo escogido</p>
<pre class="brush: jscript; title: ; notranslate">
var vectorPano = new OpenLayers.Layer.Vector(&quot;Panoramio fotos&quot;, {
   styleMap: panoramio_style2
});
</pre>
<p>Añadimos los elementos:</p>
<pre class="brush: jscript; title: ; notranslate">
vectorPano.addFeatures(features);
</pre>
<p>Añadimos la layer al mapa</p>
<pre class="brush: jscript; title: ; notranslate">
map.addLayer(vectorPano);
</pre>
<p>Definimos para terminar la función el comportamiento del evento que se desencadenará cuando seleccionemos ( click con el ratón) cada fotografía</p>
<pre class="brush: jscript; title: ; notranslate">
selectControl = new OpenLayers.Control.SelectFeature(vectorPano,
   {onSelect: onFeatureSelect, onUnselect: onFeatureUnselect});
map.addControl(selectControl);
selectControl.activate();
}
</pre>
<p>Como ves, se ejecutará la función “onFeatureSelect” cada vez que se selecciona un feature, y “onFeatureUnselect” cuando no hay nada seleccionado.</p>
<p>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.</p>
<pre class="brush: jscript; title: ; notranslate">
// popups
function onPopupClose(evt) {
   selectControl.unselect(selectedFeature);
}
function onFeatureSelect(feature) {
   selectedFeature = feature;

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

   popup = new OpenLayers.Popup.FramedCloud(&quot;chicken&quot;,
      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;
}
</pre>
<p>Ya lo tenemos todo listo. Sólo nos queda verlo en un navegador. ¿A que esperas?</p>
<p><a href="http://www.gisandchips.org/demos/j3m/panoramio/panoramio.html">Ver demo en proyección Mercator con fondo de OpenStreetMap</a><br />
<a href="http://www.gisandchips.org/demos/j3m/panoramio/panoramio_4326.html">Ver demo en WGS84</a></p>
<p><strong>Nota de interés:</strong></p>
<blockquote><p>
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.
</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.gisandchips.org/2010/05/04/openlayers-y-panoramio/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Cartografía temática II: Cálculo de intervalos de clase con PL/R</title>
		<link>http://www.gisandchips.org/2010/02/04/cartografia-tematica-ii-calculo-de-intervalos-de-clase-con-plr/</link>
		<comments>http://www.gisandchips.org/2010/02/04/cartografia-tematica-ii-calculo-de-intervalos-de-clase-con-plr/#comments</comments>
		<pubDate>Thu, 04 Feb 2010 14:53:22 +0000</pubDate>
		<dc:creator>jose</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[cartografía temática]]></category>
		<category><![CDATA[mapscript]]></category>
		<category><![CDATA[pl/R]]></category>
		<category><![CDATA[R]]></category>

		<guid isPermaLink="false">http://www.gisandchips.org/?p=1380</guid>
		<description><![CDATA[Como ya vimos en la primera parte de esta mini-serie dedicada a la cartografía temática, hasta ahora sólo tenemos resuelta una parte, la asignación de paletas de colores para nuestros mapas o semiología cartográfica, pero queda otra parte por resolver, la lógica estadística, es decir, calcular cuales serán los valores de cada uno de los [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_1416" class="wp-caption aligncenter" style="width: 310px"><a href="/demos/j3m/plr/test5.php"><img class="size-medium wp-image-1416" src="http://www.gisandchips.org/wp-content/madrid-300x228.png" alt="Población por municipios de Madrid (2008)" width="300" height="228" /></a><p class="wp-caption-text">Población por municipios de Madrid (2008) </p></div>
<p>Como ya vimos en la <a href="http://www.gisandchips.org/2010/02/04/cartografia-tematica-i-phpcolorbrewer/">primera parte</a> de esta mini-serie dedicada a la cartografía temática, hasta ahora sólo tenemos resuelta una parte, la asignación de paletas de colores para nuestros mapas o semiología cartográfica, pero queda otra parte por resolver, la lógica estadística, es decir, calcular cuales serán los valores de cada uno de los intervalos de clase que intervienen en una distribución numérica.<br />
<span id="more-1380"></span><br />
Como ya es habitual en<a href="http://www.gisandchips.org"> </a><a href="http://www.gisandchips.org">www.gisandchips.org</a> tenemos una especial predilección por almacenar nuestra fuente de datos en tablas espaciales contenidas en una base de datos PostgreSQL-PostGIS, y esta vez no va a ser menos.</p>
<p>Generalmente, el cálculo de los puntos de ruptura de una distribución ha sido tratado habitualmente desde el punto de vista del cliente (<em>gvSIG, Quantum GIS, ArcGIS</em>, etc), descargándose a la aplicación  los datos oportunos (shapefiles, PostGIS, etc) para escoger posteriormente algún método de agrupamiento o clustering. Esta solución resulta apropiada cuando los datos a representar proceden de diferentes fuentes, cambios de geometría constantes realizamos muchos temáticos distintos, o cualquier otra variable a considerar. Pero sin embargo, cuando necesitamos representar la misma realidad espacial (municipios, provincias, comunidades autónomas, continentes, etc) desde diferentes aspectos (demografía, economía, agriculturas, etc) no resulta tán cómodo delegar esta función a la aplicación cliente.</p>
<p>Pongamos un ejemplo para clarificar el tema: Imaginemos que disponemos de datos de población agregados a nivel de municipios, donde existen tantas columnas como censos de población se hayan hecho, y deseamos generar en un portal de Internet mapas temáticos a requerimiento del usuario (año del censo, nº de clases, método de agrupamiento o <em>clustering </em>y paleta de colores). Crear un mapa coroplético de la población lleva implícito un conocimiento de la distribución de los datos (valores mínimos y máximos de la serie, media), y sobre todo las medidas de dispersión o de variabilidad: desviación típica, varianza, co-varianza, coeficiente de correlación de Pearson. Con estos parámetros estamos en condiciones de definir cual será el método de cálculo de intervalo de clase más apropiado para nuestra distribución, para posteriormente asignar la paleta de colores acorde al fenómeno a representar (este último aspecto ya lo tenemos solucionado con nuestra clase PHPcolorBrewer). Con una aplicación de GIS de escritorio sólo podemos realizar un mapa para visualizarlo en pantalla o imprimirlo, pero en un ambiente web(GIS) este forma de actuar carece de lógica, pues estamos limitando al usuario la posibilidad de elegir estos parámetros (método de clustering, número de clases y paleta de colores), ya que es muy complicado obtener datos de un GIS y realizar los cálculos de los intervalos de clases en la própia página web (<em>JavaScript, PHP</em>).</p>
<div id="attachment_1432" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.gisandchips.org/wp-content/plr.png"><img class="size-medium wp-image-1432" src="http://www.gisandchips.org/wp-content/plr-300x175.png" alt="Flujo cartografía temática" width="300" height="175" /></a><p class="wp-caption-text">Flujo cartografía temática</p></div>
<p>En este artículo vamos a solucionar esta cuestión proponiendo un método que nos permita desde el servidor, es decir, desde el gestor de base de datos, calcular los intervalos de clase. Para ello no nos queda más remedio que recurrir a la creación de funciones o procedimientos almacenados que contemplen dicha lógica. Realizarlo con <em>PLPg/SQL</em> es una solución, pero programar estas funciones requiere de mucho esfuerzo al no formar parte de las funciones nativas del servidor (por ejemplo, no existe una función que calcule la desviación típica). ¿Por qué no utilizamos un lenguaje especializado en el manejo estadístico? En este sentido, <em><strong>PL/R</strong></em> reúne todas las condiciones para convertirse en el lenguaje de programación elegido. Una prueba de la potencia de este lenguaje, en este caso <strong>R</strong> lo podemos ver con este ejemplo.</p>
<p><strong>Implementación en R</strong><br />
Tenemos la población activa (valores en miles) de todas las provincias españolas, obtenidas del INE, en el año 1996, disponibles en un objeto vector:</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">c(126.4,137.9,570.9,192.1,409.7,60.2,251.3,325.9,2109.1,145.8,159.4,425.5,207.8,194.3,<br />
172.5,301.5,443,69,252.4,300,55.2,296.4,158.2,84.5,244.3,200.3,141.7,174.2,2180.7,<br />
475.8,438,224.6,161.4,71.2,320.8,390,104.7,144.3,328.3,56.5,646.5,36.3,242,52.8,196.5,<br />
919.6,211.6,473.9,70.2,356.7,50)</td>
</tr>
</tbody>
</table>
<p>En una sesión de R se crearía de esta manera</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">poblacion.1996 &lt;- c(126.4,137.9,570.9,192.1,409.7,60.2,251.3,325.9,2109.1,145.8,159.4,<br />
425.5,207.8,194.3,172.5,301.5,443,69,252.4,300,55.2,296.4,158.2,84.5,244.3,200.3,141.7,<br />
174.2,2180.7,475.8,438,224.6,161.4,71.2,320.8,390,104.7,144.3,328.3,56.5,646.5,36.3,<br />
242,52.8,196.5,919.6,211.6,473.9,70.2,356.7,50)</td>
</tr>
</tbody>
</table>
<p>Y se podría ver simplemente escribiendo:</p>
<pre class="brush: bash; title: ; notranslate">poblacion.1996</pre>
<p>Resultado</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">[1]  126.4  137.9  570.9  192.1  409.7   60.2  251.3  325.9 2109.1  145.8<br />
[11]  159.4  425.5  207.8  194.3  172.5  301.5  443.0   69.0  252.4  300.0<br />
[21]   55.2  296.4  158.2   84.5  244.3  200.3  141.7  174.2 2180.7  475.8<br />
[31]  438.0  224.6  161.4   71.2  320.8  390.0  104.7  144.3  328.3   56.5<br />
[41]  646.5   36.3  242.0   52.8  196.5  919.6  211.6  473.9   70.2  356.7<br />
[51]   50.0</td>
</tr>
</tbody>
</table>
<p>Para ver un sumario estadístico básico de dicho objeto escribimos:</p>
<pre class="brush: bash; title: ; notranslate">summary(poblacion.1996)</pre>
<p>Con este resultado:</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">Min. 1st Qu.  Median    Mean 3rd Qu.    Max.<br />
36.3   139.8   207.8   320.8   342.5  2181.0</td>
</tr>
</tbody>
</table>
<p>Entrando ya en materia, vamos a calcular 5 clases distribuidas por el método de intervalos iguales, es decir, <em>(valor máximo- valor mínimo) / nº intervalos</em>:</p>
<pre class="brush: bash; title: ; notranslate">seq(min(poblacion.1996),max(poblacion.1996),length.out=(5+1))</pre>
<p>Resultado</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">[1]   36.30  465.18  894.06 1322.94 1751.82 2180.70</td>
</tr>
</tbody>
</table>
<p>Es decir, las clases quedarían agrupadas de esta forma:</p>
<ul>
<li>clase 1: 36.30 &#8211; 465.18</li>
<li>clase 2: 465.18 &#8211; 894.06</li>
<li>clase 3: 894.06 &#8211; 1322.94</li>
<li>clase 4: 1322.94 &#8211; 1751.82</li>
<li>clase 5: 1751.82 &#8211; 2180.70</li>
</ul>
<p>Este método, aunque sencillo de implementar (incluso desde la parte cliente) ofrece una clasificación muy distorsionada cuando los valores son muy dispares, por lo que necesitamos recurrir a otros métodos más eficientes, como son los cuantiles. En R este cálculo queda reducido a la mínima expressión:</p>
<pre class="brush: bash; title: ; notranslate">quantile(poblacion.1996)</pre>
<p>Resultado:</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">0%    25%    50%    75%   100%<br />
36.3  139.8  207.8  342.5 2180.7</td>
</tr>
</tbody>
</table>
<p>Esta sería nuestra clasificación</p>
<ul>
<li>1º quantil: 36.3 &#8211; 139.8</li>
<li>2º quantil: 139.8 &#8211; 207.8</li>
<li>3º quantil: 207.8 &#8211; 342.5</li>
<li>4º quantil: 342.5 &#8211; 2180.7</li>
</ul>
<p>¡Más sencillo imposible!</p>
<p>El problema de trabajar directamente en R estriba en la dificultad para obtener datos desde otras fuentes, aunque siempre podemos recurrir a paquetes externos para traer los datos de shapefiles o tablas de PostgreSQL a R</p>
<p><strong>Implementación en PostgreSQL</strong></p>
<p>Como ya hemos visto en anteriores artículos sobre PL/R, esta lenguaje procedural es muy sencillo de utilizar. Sólo hay que copiar el código de R y realizar pequeños cambios para adaptarlo a la sintaxis de PL/R.</p>
<p>Un aspecto que debemos tener muy claro es que casi todos los cálculos en R de una distribución deben de estar dispuestos en forma de un vector. Como es lógico, PostgreSQL no soporta este tipo de dato, pero el lenguaje PL/R si que nos permite una conversión hacia el tipo de datos &#8220;array&#8221;, ofreciéndonos además una función que realiza todo el trabajo (<em>array_accum</em>)</p>
<p>Por ejemplo, si queremos obtener un array con los datos de la poblacion activa en 1996 (columna t1996t1) de la tabla ine.poblacion_activa, esta es la forma de conseguirlo con SQL:</p>
<pre class="brush: sql; title: ; notranslate">SELECT array_accum(t1996t1) FROM ine.poblacion_activa;</pre>
<p>Y este el resultado:</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">{126.4,137.9,570.9,192.1,409.7,60.2,251.3,325.9,2109.1,145.8,<br />
159.4,425.5,207.8,194.3,172.5,301.5,443,69,252.4,300,55.2,296.4,<br />
158.2,84.5,244.3,200.3,141.7,174.2,2180.7,475.8,438,224.6,161.4,<br />
71.2,320.8,390,104.7,144.3,328.3,56.5,646.5,36.3,242,52.8,196.5,<br />
919.6,211.6,473.9,70.2,356.7,50}</td>
</tr>
</tbody>
</table>
<p>Con esta pequeña aclaración ya podemos utilizar cualquier columna con datos numéricos para realizar funciones de clustering. Las siguientes funciones de PL/R reproducen los cálculos realizados anteriormente con R:</p>
<p>Función para el método de intervalos iguales:</p>
<pre class="brush: sql; title: ; notranslate">
CREATE OR REPLACE FUNCTION r_equal(double precision[], integer)
RETURNS double precision[] AS
$BODY$
sort(seq(min(arg1),max(arg1),length.out=(arg2+1)))

$BODY$
LANGUAGE 'plr' VOLATILE STRICT
COST 100;
</pre>
<p>Vamos a analizar esta función.</p>
<p>Tiene dos parámetros de entrada:</p>
<ul>
<li>double precision[]: El array con los datos de entrada que queremos utilizar para calcular los intervalos, utilizando la función array_accum</li>
<li>integer: el nº de intervalos de clase que deseamos obtener</li>
</ul>
<p>El resultado (RETURNS) será siempre una estructura de tipo array.<br />
El cuerpo de la función se reduciría a una sola línea que recibe los dos argumentos de la función (arg1 y arg2):<br />
<em>sort(seq(min(arg1),max(arg1),length.out=(arg2+1)))</em></p>
<p>El uso de esta función es muy sencillo:</p>
<pre class="brush: sql; title: ; notranslate">SELECT r_equal( array_accum(t1996t1), 5 ) FROM ine.poblacion_activa;</pre>
<p>Nos devolverá el siguiente array:</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">{36.3,465.18,894.06,1322.94,1751.82,2180.7}</td>
</tr>
</tbody>
</table>
<p>Para aquellos más versados en PLPg/SQL habrán advertido que realizar la función en este lenguaje requiere de algunas líneas más de código, que pueden ser bastantes más si queremos utilizar otros métodos, como el de cuantiles, que reproducimos a continuación.</p>
<pre class="brush: sql; title: ; notranslate">
CREATE OR REPLACE FUNCTION r_quantile(double precision[], integer)
RETURNS double precision[] AS
$BODY$
quantile(arg1,probs = seq(0, 1, 1/arg2))
$BODY$
LANGUAGE 'plr' VOLATILE STRICT
COST 100;
</pre>
<p>Ejemplo de uso para 5 intervalos de clase:</p>
<pre class="brush: sql; title: ; notranslate">
SELECT r_quantile(array_accum(t1996t1),5) FROM ine.poblacion_activa;</pre>
<p>Devuelve:</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">{36.3,104.7,174.2,251.3,409.7,2180.7}</td>
</tr>
</tbody>
</table>
<p>Ahora que tenemos implementado un método para calcular los intervalos de clase ya estamos en condiciones de desarrollar un auténtico motor de cartografía temática (corológica) en un ambiente web que combine la capacidad de cálculo de PL/R con la semiología cartográfica implementada en PHPcolorBrewer que sea capaz de &#8220;pintar&#8221; geometrias de una tabla espacial de PostGIS. El resultado de esta coctelera explosiva lo podeis ver en las siguientes demostraciones on-line:</p>
<p><a name="demos"><strong>EJEMPLOS</strong></a></p>
<p><strong>Test 1: <a href="/demos/j3m/plr/test1.php">Distribución de la población activa por provincias (valores en miles de personas)</a></strong><br />
Este sencillo ejemplo nos permite ver las posibilidades de integración de varias piezas de software (PostGIS, PL/R. PHP MapScripts y PHPcolorBrewer). Los cálculos de los intervalos de clase utilizan el método &#8220;Kmeans&#8221;, y el usuario tiene la posibilidad de cambiar la paleta de colores y el nº de clases, así como el año que desea consultar.</p>
<p><strong>Test 2: <a href="/demos/j3m/plr/test2.php">Distribución de la tasa de ocupación/desempleo por Comunidades Autónomas(valores en miles de personas)</a></strong><br />
Similar al anterior, pero aplicado a CC.AA. Ofrece la posibilidad de múltiples consultas (número de ocupados/desempleados diferenciando hombre, mujeres y total)</p>
<p><strong>Test 3: <a href="/demos/j3m/plr/test3.php">Distribución de la población activa por provincias (valores en miles de personas) según diferentes métodos de <em>clustering</em></a></strong><br />
Este ejemplo es similar al primero, pero hemos añadido la posibilidad de que el usuario selecciones el método de cálculo de intervalos de clase desea. Es sin duda, un buen ejemplo de la potencia que tiene PL/R al permitirnos elegir hasta 8 métodos diferentes:</p>
<ul>
<li><a href="/demos/j3m/plr/test3.php?paletas=YlOrRd&amp;numInt=5&amp;field=t2000t1&amp;method=quantile">quantile</a></li>
<li><a href="/demos/j3m/plr/test3.php?paletas=YlOrRd&amp;numInt=5&amp;field=t2000t1&amp;method=equal">equal</a></li>
<li><a href="/demos/j3m/plr/test3.php?paletas=YlOrRd&amp;numInt=5&amp;field=t2000t1&amp;method=kmeans">kmeans</a></li>
<li><a href="/demos/j3m/plr/test3.php?paletas=YlOrRd&amp;numInt=5&amp;field=t2000t1&amp;method=hclust.complete">hclust.complete</a></li>
<li><a href="/demos/j3m/plr/test3.php?paletas=YlOrRd&amp;numInt=5&amp;field=t2000t1&amp;method=hclust.single">hclust.single</a></li>
<li><a href="/demos/j3m/plr/test3.php?paletas=YlOrRd&amp;numInt=5&amp;field=t2000t1&amp;method=bclust">bclust</a></li>
<li><a href="/demos/j3m/plr/test3.php?paletas=YlOrRd&amp;numInt=5&amp;field=t2000t1&amp;method=fisher">fisher</a></li>
<li><a href="/demos/j3m/plr/test3.php?paletas=YlOrRd&amp;numInt=5&amp;field=t2000t1&amp;method=jenks">jenks</a></li>
</ul>
<p><strong>Test 4: <a href="/demos/j3m/plr/test4.php">Varios mapas temáticos en una sóla página con todos los métodos anteriores</a></strong><br />
El objeto de este ejemplo es demostrar de una sóla vez como se representa un mismo fenómeno desde diferentes métodos de clustering, para ver cual es el que mejor se adapta a nuestras necesidades.</p>
<p><strong>Test 5: <a href="/demos/j3m/plr/test5.php">Mapa temático municipal sensible con HTML dinámico</a></strong><br />
Se trata del ejemplo más completo de todos, que nos permite seleccionar una provincia y ver la distribución de la población (total, hombres o mujeres) por municipio. También permite seleccionar todos los métodos indicados, así como la paleta de colores y nº de intervalos.</p>
<p><strong>Test 6: <a href="/demos/j3m/plr/test6.php">Todas las paletas PHPcolorBrewer aplicadas a un mapa temático</a></strong><br />
El objetivo de este ejemplo es demostrar en una sóla página como un mismo fenómeno es representado con todas las paleta de PHPcolorBrewer, para que el usuario seleccione la que más le convenga.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gisandchips.org/2010/02/04/cartografia-tematica-ii-calculo-de-intervalos-de-clase-con-plr/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Cartografía temática I: PHPcolorBrewer</title>
		<link>http://www.gisandchips.org/2010/02/04/cartografia-tematica-i-phpcolorbrewer/</link>
		<comments>http://www.gisandchips.org/2010/02/04/cartografia-tematica-i-phpcolorbrewer/#comments</comments>
		<pubDate>Thu, 04 Feb 2010 14:51:30 +0000</pubDate>
		<dc:creator>jose</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[cartografía coroplética]]></category>
		<category><![CDATA[PHPcolorBrewer]]></category>

		<guid isPermaLink="false">http://www.gisandchips.org/?p=1360</guid>
		<description><![CDATA[La creación de cartografía coroplética es quizás uno de los aspectos más recurrentes que podemos encontrar en un ambiente webgis. Todos hemos visto o realizado alguna vez con nuestro programa de GIS favorito un mapa de distribución de la población o cualquier otra variable por municipios, provincias, autonomías o estados. En su preparación siempre intervienen [...]]]></description>
			<content:encoded><![CDATA[<p>La creación de cartografía coroplética es quizás uno de los aspectos más recurrentes que podemos encontrar en un ambiente webgis. Todos hemos visto o realizado alguna vez con nuestro programa de GIS favorito un mapa de distribución de la población o cualquier otra variable por municipios, provincias, autonomías o estados. En su preparación siempre intervienen tres elementos: una base cartográfica con datos asociados para representar, un método estadístico para agrupar los datos en intervalos de clase o conjunto de datos, y finalmente una simbología cartográfica aplicada a dichos grupos que represente de una manera clara el fenómeno que deseamos destacar. Los datos a tratar siempre serán de tipo numérico y harán referencia a datos cuantitativos.</p>
<div id="attachment_1363" class="wp-caption aligncenter" style="width: 310px"><a title="PHPcolorBrewer palette" href="http://www.gisandchips.org/demos/j3m/plr/PHPcolorBrewer/examples.palettes.PHPcolorBrewer.php" target="_blank"><img class="size-medium wp-image-1363" src="http://www.gisandchips.org/wp-content/paletas.phpColorBrewer-300x204.jpg" alt="" width="300" height="204" /></a><p class="wp-caption-text">PHPcolorBrewer palettes</p></div>
<p><strong>NOTA</strong>: La segunda parte de este artículo lo puede consultar en: <a href="/2010/02/04/cartografia-tematica-ii-calculo-de-intervalos-de-clase-con-plr/"> Cartografía temática II: Cálculo de intervalos de clase con Pl/R<br />
</a><br />
<span id="more-1360"></span></p>
<p>Sobre este tipo de cartografía siempre han circulado muchos tópicos del tipo: <em>&#8220;la mejor manera de representar los datos es con un mapa&#8221;</em>, con una clara alusión al uso de mapas en detrimento de largos listados o tablas cuya lectura resulta menos intuitiva que un mapa con colores degradados. También encontramos declaraciones con una percepción negativa sobre los mapas temáticos, <em>&#8220;la mejor forma de ocultar datos es con un mapa&#8221;</em>, aludiendo esta vez al hecho incuestionable de que un fenómeno dado puede dar diferentes lecturas en función del mayor o menor acierto en la elección de los intervalos de clase, en el sentido de que muchos intervalos dificultan la lectura y pocos generalizan demasiado los datos.</p>
<p>Cuando trabajamos con aplicaciones de escritorio GIS nuestra labor se reduce a indicar el fenómeno a representar, el número de intervalos y como mucho elegir una de las gamas de paletas, para finalmente obtener una distribución más o menos aceptable donde es raro que el usuario intervenga en el proceso estadístico que implica decidir las rupturas de clase. De igual forma ocurre con la gama de colores, donde solemos aceptar los predefinidos, o como mucho definimos una degradación de colores.</p>
<p>Quizás sea por deformación profesional, pero suelo advertir una cierta dejadez del usuario respecto al tratamiento de los colores que intervienen en una distribución normalizada en mucha de la cartografía coroplética existente, utilizando, en la mayoría de los casos, los valores que por defecto nos proporciona la aplicación. Para cualquiera que haya estudiado cartografía, sabe que la simbolización del color es uno de los temás más complejos, que muchas veces delegamos en la aplicación de escritorio, en la que intervienen factores tales como la representatividad de los colores, legibilidad, idoneidad en función de su uso (por ejemplo en un mapa pensado para su impresión no todos los colores son imprimibles), adecuación del número de intervalos al fenómeno estudiado, capacidad del usuario para distinguir colores, y por supuesto la elección de un sistema de agrupamiento (clustering) adecuado.</p>
<p>La doctora Cynthia A. Brewer, profesora del Departamento de Geografía en la universidad estatal de Pennsylvania (<a href="http://www.personal.psu.edu/cab38/">http://www.personal.psu.edu/cab38/</a>) ha dedicado su faceta profesional al estudio del color en la cartografía coroplética, dejando como legado una serie de paletas de colores adecuadas para la representación cartográfica que han sido bautizadas con su nombre y utilizadas en muchas publicaciones de renombre, como el Census Atlas of the United States (<a href="http://www.census.gov/population/www/cen2000/censusatlas/">http://www.census.gov/population/www/cen2000/censusatlas/</a>) e implementadas en muchas otras aplicaciones de GIS (ArcGIS)</p>
<p>Basándonos en su trabajo hemos elaborado una sencilla clase en PHP que nos permite hacer uso de dichas paletas de una manera sencilla para el usuario, susceptibles de ser utilizadas en la elaboración de cartografía coroplética.</p>
<p><a title="Descarga PHPcolorBrewer" href="http://www.gisandchips.org/demos/j3m/plr/PHPcolorBrewer.tar.gz">Descargar PHPcolorBrewer</a></p>
<p>Esta utilidad está sujeta a la <a href="http://www.gisandchips.org/demos/j3m/plr/PHPcolorBrewer/LICENSE-2.0.txt">Apache License 2.0</a></p>
<p>Esta clase consta de los siguientes métodos:</p>
<ul>
<li>listColors: Listar colores de una paleta y un nº de intervalos o clases</li>
<li>getPalettes: Obtiene un array de paletas</li>
<li>getPaletteIntervals: Obtiene un array de intervalos de una paleta. Los valores siempre estarán entre de 3 a 9</li>
<li>getPalIntColors: Obtiene un array de intervalos de una paletas. Siempre será de 3 a 9</li>
<li>getColor: Obtiene un array con el color de una paleta, un intervalo y un número.</li>
</ul>
<p>Dada la vocación web de esta clase se han creado estos métodos:</p>
<ul>
<li>comboPalettes: Utilidad HTML para listar paletas en forma de combo</li>
<li>comboNumInt: Utilidad HTML para listado del nº de intervalos de una paleta en forma de combo</li>
<li>rgb2html:  Convierte un color en formato RGB a hexadecimal para su uso en web.</li>
<li>tableColor: Crea una tabla HTML dada una paleta y un intervalo (de 3 a 9)</li>
</ul>
<p>Con el objeto de facilitar al usuario su implementación se han añadido dos scripts con ejemplos de uso:<br />
1. Listado de todas las paletas de colores: <a title="Paletas" href="/demos/j3m/plr/PHPcolorBrewer/examples.palettes.PHPcolorBrewer.php">examples.palettes.colorBrewer.php</a><br />
2. Ejemplos de uso: <a title="Ejemplos de uso" href="http://www.gisandchips.org/demos/j3m/plr/PHPcolorBrewer/examples.PHPcolorBrewer.php">examples.colorBrewer.php</a></p>
<p>Finalmente, y como ejemplo de cartografía temática les mostramos algunas demostraciones que utilizan esta utilidad, incluidos en el siguiente post: <a href="/2010/02/04/cartografia-tematica-ii-calculo-de-intervalos-de-clase-con-plr/#demos">Cartografía temática II: Cálculos de intervalos de clase con PL/R</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.gisandchips.org/2010/02/04/cartografia-tematica-i-phpcolorbrewer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Thumbnails con Mapnik y OpenStreetMap</title>
		<link>http://www.gisandchips.org/2009/11/04/thumbnails-con-mapnik-y-openstreetmap/</link>
		<comments>http://www.gisandchips.org/2009/11/04/thumbnails-con-mapnik-y-openstreetmap/#comments</comments>
		<pubDate>Wed, 04 Nov 2009 20:12:13 +0000</pubDate>
		<dc:creator>jose</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[Mapnik]]></category>
		<category><![CDATA[OpenStreetMap]]></category>
		<category><![CDATA[thumbnails]]></category>

		<guid isPermaLink="false">http://www.gisandchips.org/?p=543</guid>
		<description><![CDATA[¿Alguna vez te has visto en la tesitura de conocer el estado en OpenStreetMap de una determinada provincia, municipio, comunidad autónoma, región etc. en una fecha concreta? Pues bien, en este artículo intentaremos afrontar esta cuestión desde una perspectiva más pedagógica que profesional Objetivo: Crear thumbnails (imágenes reducidas) de cada provincia que serán generadas cada [...]]]></description>
			<content:encoded><![CDATA[<p>¿Alguna vez te has visto en la tesitura de conocer el estado en OpenStreetMap de una determinada provincia, municipio, comunidad autónoma, región etc. en una fecha concreta? Pues bien, en este artículo intentaremos afrontar esta cuestión desde una perspectiva más pedagógica que profesional</p>
<p><strong>Objetivo</strong>:<br />
Crear thumbnails (imágenes reducidas) de cada provincia que serán generadas cada semana para ver la evolución histórica de las aportaciones de usuarios a OpenStreetMap. Cada imagen tendrá como nombre la fecha de su creación, y se almacenarán cada una en su carpeta de provincia correspondiente. Estas imágenes serán visibles en un servicio web para su consulta pública.</p>
<p>Fases:</p>
<p>Para llevar a cabo este objetivo precisamos de:</p>
<ol>
<li>Actualizar cada semana la base de datos con el último &#8220;planet osm&#8221;</li>
<li>Generar las imágenes de cada provincia (mediante un script en Python)</li>
<li>Colocarlas en el lugar adecuado de nuestro servidor web</li>
<li>Programar la tarea para que se ejecute de forma automática cada semana, sin intervención del usuario.</li>
</ol>
<p>Figura: <em>Ejemplo del renderizado de una imagen de la provincia de Alicante</em></p>
<p style="text-align: center">
<div id="attachment_585" class="wp-caption alignnone" style="width: 410px"><img class="size-full wp-image-585" src="http://www.gisandchips.org/wp-content/2009-11-03.png" alt="Tile de Alicante" width="400" height="400" /><p class="wp-caption-text">Tile de Alicante el 3 de noviembre de 2009</p></div>
<p><span id="more-543"></span></p>
<p><strong>Descripción:</strong><br />
A lo largo de este pequeño artículo mostraremos las posibilidades conjuntas de OSM, PostGIS y Mapnik para que, con muy pocas líneas de código, seamos capaces de generar un repositorio de imágenes de cada provincia (en nuestro caso, aunque se puede adaptar a comunidades autónomas, municipios, etc.) que serán generadas por un script en python programado para ejecutarse una vez a la semana. Dicho script utilizará los módulos de Mapnik para generar imágenes a partir de un mapfile, y PsycoPg para llamar a sentencias SQL de PostgreSQL.</p>
<p><!--more--></p>
<p><strong>Punto de partida:</strong><br />
Tenemos instalado un servidor con las siguientes aplicaciones instaladas y configuradas:</p>
<ul>
<li>Python</li>
<li>PostgreSQL y PostGIS</li>
<li>Mapnik y utilidad Mapnik de OSM</li>
<li>Una tabla de PostGIS con las provincias</li>
<li>Un servidor web para mostrar los resultados</li>
</ul>
<p>Actualizar la base de datos de OSM cada semana</p>
<p>Para realizar esta fase necesitamos un script que automatice todas las funciones:</p>
<ul>
<li>Borrar la bbdd existente</li>
<li>Descarga del planet</li>
<li>Creación de la bbdd con PostGIS</li>
<li>Carga de datos con osm2pgsql</li>
</ul>
<p><strong>Script Bash para recargar la base de datos de OSM</strong></p>
<pre class="brush: bash; title: ; notranslate">
#!/bin/sh
echo &quot;Borrando spain.osm.bz2&quot;
rm spain.osm.bz2
echo &quot;Descargando fichero osm de Spain desde Cloudmade.com&quot;
wget http://downloads.cloudmade.com/europe/spain/spain.osm.bz2
echo &quot;Descomprimiendo&quot;
bzip2 -d spain.osm.bz2
echo &quot;Borrando bbdd osm&quot;
dropdb -h localhost -U postgres osm
echo &quot;Creando bbdd osm&quot;
createdb -U postgres -E UTF8 osm
echo &quot;Instalar lenguaje pl/pgsql y Postgis en bbdd osm&quot;
createlang -U postgres plpgsql osm
psql -U postgres -f /usr/share/pgsql/contrib/postgis.sql osm
psql -U postgres -f /usr/share/pgsql/contrib/spatial_ref_sys.sql osm
echo &quot;Añadir datos d espain.osm a la bbdd osm&quot;
echo &quot;Tiempo aproximado: 11 minutos&quot;
/home/&lt;user&gt;/compilar/osm2pgsql/osm2pgsql --slim -H localhost -U postgres -d osm ./spain.osm
echo &quot;fin&quot;
exit 0
</pre>
<p><strong>Programar tarea con CRONTAB</strong><br />
Como ya sabes, los &#8220;crones&#8221; son tareas (<em>deaemon</em>) que se programan para ejecutarse en el momento (tiempo) que decidamos. Sí deseas más información aquí tienes un buen enlace: <a href="http://dns.bdat.net/documentos/cron/x50.html">http://dns.bdat.net/documentos/cron/x50.html</a></p>
<p><strong>Generar el árbol de directorios</strong>.<br />
Creamos un script bash para generar las carpetas de cada provincia. Vamos a utilizar el código de cada provincia por cuestión de comodidad</p>
<pre class="brush: bash; title: ; notranslate">
#!/bin/bash
# Crear directorios para provincias.

for provincias in 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
do
  echo $provincias
  mkdir -p $provincias
done

exit 0
</pre>
<p>Este sencillo script, que seguro podrás mejorar, debemos de ejecutarlo en el directorio de nuestro website donde se alojarán el servicio para ver la evolución de OSM</p>
<p><strong>Generar imágenes de OSM por provincia</strong><br />
Este script en Python hace uso de los módulos de Mapnik para generar la imagen, y de Psycopg para ejecutar sentencias SQL. Fíjate que la clave consiste en calcular por cada provincia las columnas con los valores de la extensión (xmin, ymin, xmax, ymax). Opcionalmente puedes añadirle una tolerancia para que el límite no se circunscriba a la caja de la provincia, sino que sea como una especie de buffer.<br />
En nuestro caso, la tabla &#8220;provincias&#8221; está en la proyección UTM-ED50 del uso 30 (epsg: 23030), por lo que para utilizarlo con los datos de OSM necesitamos proyectar los valores de la extensión a Mercator (EPSG: 900913)</p>
<p>NOTA: Si no tienes instalago psycopg (o psycopg2 según la distro) lo puedes instalar con</p>
<pre class="brush: bash; title: ; notranslate">yum install python-psycopg2.x86_64</pre>
<p>Script de Python</p>
<pre class="brush: python; title: ; notranslate">
#!/usr/bin/env python
## DNS de la bbdd que contiene las provincias
DSN = 'host=localhost user=postgres dbname=demos'

import sys, psycopg, mapnik
## Nota: Si obtienes algun error aqui, cambia psycopg por psycopg2
from datetime import date

## mapnik
mapfile = 'osm.xml'
## tamaño de la imagen
m = mapnik.Map(400, 400)

if len(sys.argv) &gt; 1:
    DSN = sys.argv[1]

print 'Abrir conexion usando dns', DSN
conn = psycopg.connect(DSN)
## Nota: Si obtienes algun error aqui, cambia psycopg por psycopg2
curs = conn.cursor()

curs.execute('select codigo, st_xmin(st_extent(st_transform(geometria,900913))) as xmin, st_ymin(st_extent(st_transform(geometria,900913))) as ymin, st_xmax(st_extent(st_transform(geometria,900913))) as xmax, st_ymax(st_extent(st_transform(geometria,900913))) as ymax from ine.provincias group by codigo')

row = curs.fetchone()

rows = curs.fetchall()
print 'Creando imagenes...';
for row in rows:
    now = date.today()
    ## A cada imagen le asignamos como nombre la fecha de creación de la imagen
    map_output = 'miniaturas/'+row[0]+'/'+now.strftime(&quot;%Y-%m-%d&amp;&quot;)+'.png'
    mapnik.load_map(m, mapfile)
    bbox = mapnik.Envelope(mapnik.Coord(row[1],row[2]), mapnik.Coord(row[3],row[4]))
    m.zoom_to_box(bbox)
    mapnik.render_to_file(m, map_output)

conn.commit()
</pre>
<p>Ejecutamos el script</p>
<pre class="brush: bash; title: ; notranslate">
python miniatura_provincias.py
</pre>
<p>Por último sólo nos queda hacer visible a nuestro servidor web las tiles. Para ello tenemos dos opciones:</p>
<p>a) Copiamos el directorio miniaturas a nuestra carpeta de publicación web</p>
<pre class="brush: bash; title: ; notranslate">cp /home/mapnik/miniaturas /var/www/&lt;tu_ruta&gt;/</pre>
<p>b) Crear un enlace rígido en nuestra web que apunte al directorio &#8220;miniaturas&#8221;</p>
<pre class="brush: bash; title: ; notranslate">ln -d /home/mapnik/miniaturas /var/www/&amp;lt;tu_ruta&amp;gt;/miniaturas</pre>
<p>Este segundo método es más recomendable, puesto que nos permite aislar la lógica de nuestra aplicación del servicio web.</p>
<p><strong>Test</strong><br />
Probamos el ejemplo<br />
<a href="http://www.gisandchips.org/demos/j3m/miniaturas/">http://www.gisandchips.org/demos/j3m/miniaturas/</a></p>
<p><strong>Automatización:</strong></p>
<p>Sí queremos automatizar estas tareas, de forma que cada semana obtengamos una nueva imagen, cuyo nombre de imagen es la fecha en que se lanza el <a href="http://www.telefonica.net/web2/dinsalpa/crontab/crontab.htm">cron</a>, debemos de lanzar un script que, como root, realice estos pasos. A modo de ejemplo, este sería el Bash:</p>
<pre class="brush: bash; title: ; notranslate">
#!/bin/sh
cd &lt;ruta_directorio_volcado_bbdd_osm&gt;
python ./&lt;fichero python para generar miniaturas&gt;
cp /home/mapnik/miniaturas /var/www/&lt;tu_ruta&gt;
</pre>
<p>Este artículo es el tercero de una serie formada por:</p>
<ul>
<li><a href="http://www.gisandchips.org/2009/10/22/renderizado-de-osmcon-mapnik-para-usar-en-openlayers/#more-387">Renderizado con Mapnik</a></li>
<li><a href="http://www.gisandchips.org/2009/10/23/openstreetmap-y-mapnik/">Mapnik y OpenStreetMap</a></li>
<li>Thumbnails con Mapnik y OpenStreetMap  (este post)</li>
<li>Uso de TMS de OpenStreetMap con OpenLayers</li>
<li>TMS de OpenStreetMap con Mod_tile</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.gisandchips.org/2009/11/04/thumbnails-con-mapnik-y-openstreetmap/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>OpenStreetMap y Mapnik</title>
		<link>http://www.gisandchips.org/2009/10/23/openstreetmap-y-mapnik/</link>
		<comments>http://www.gisandchips.org/2009/10/23/openstreetmap-y-mapnik/#comments</comments>
		<pubDate>Fri, 23 Oct 2009 11:42:23 +0000</pubDate>
		<dc:creator>jose</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[mapfile]]></category>
		<category><![CDATA[Mapnik]]></category>
		<category><![CDATA[OpenStreetMap]]></category>

		<guid isPermaLink="false">http://www.gisandchips.org/?p=420</guid>
		<description><![CDATA[Objetivo: Siguiendo con la serie de artículos sobre OSM vamos a introducirnos en las posibilidades que nos ofrece Mapnik para renderizar datos de OSM contenidos en una geodatabase que hemos creado con &#8220;osm2pgsql&#8221; (ver artículo previo). El objetivo final es crear un fichero de mapa (mapfile) en XML para utilizarlo con Python y los bindings [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Objetivo</strong>:<br />
Siguiendo con la serie de artículos sobre OSM vamos a introducirnos en las posibilidades que nos ofrece <a href="http://mapnik.org/">Mapnik </a>para renderizar datos de OSM contenidos en una geodatabase que hemos creado con &#8220;osm2pgsql&#8221; (ver artículo <a href="http://www.gisandchips.org/2009/10/22/renderizado-de-osmcon-mapnik-para-usar-en-openlayers/#more-387">previo</a>).<br />
El objetivo final es crear un fichero de mapa (mapfile) en XML para utilizarlo con Python y los bindings de Mapnik. Una vez realizado este proceso podremos generar tanto imágenes como conjuntos de teselas (tiles)</p>
<p><img src="http://www.gisandchips.org/wp-content/ali_zoom14-300x300.png" alt="" width="300" height="300" /></p>
<p><span id="more-420"></span></p>
<p><strong>Descarga de utilidades OSM para Mapnik</strong><br />
En el repositorio subversion de OSM tenemos todo lo necesario para montar nuestro propio motor de renderizado de imágenes y conjuntos de tiles.<br />
Para descargar estas utilidades nos situamos en la raíz de nuestro &#8220;home&#8221; y procedemos a la descarga:</p>
<pre class="brush: bash; title: ; notranslate">
svn co http://svn.openstreetmap.org/applications/rendering/mapnik/ mapnik
cd mapnik
</pre>
<p>Este es el contenido de la carpeta &#8220;mapnik&#8221;:</p>
<ul>
<li> <em>symbols</em>: carpeta con los iconos utilizados &#8220;oficialmente&#8221; en el &#8220;slippy map&#8221; de www.openstreetmap.org</li>
<li><em>osm_template.xml</em>:  plantilla por defecto para generar un mapfile de OSM</li>
<li><em>set_mapnik.env</em>:  script de configuración de directorios, datasources, etc.</li>
<li><em>generate_image.py</em>:  script para generar una imagen</li>
<li><em>generate_tiles.py</em>:  script para generar tiles</li>
<li>otros archivos</li>
</ul>
<p><strong>Descarga de shapefiles</strong>:</p>
<p>OSM necesita una serie de shapefiles para poder renderizar aspectos como la costa, paises, masas de agua, etc.<br />
Lo ideal es que dentro del directorio &#8220;mapnik&#8221; creemos una carpeta donde almacenarlos</p>
<pre class="brush: bash; title: ; notranslate">
mkdir world_boundaries
</pre>
<p>Ahora descargamos los shapefiles y los descomprimimos</p>
<pre class="brush: bash; title: ; notranslate">
echo &quot;Tamaño 50 Mb&quot;
wget http://tile.openstreetmap.org/world_boundaries-spherical.tgz
echo &quot;processed_p: Tamaño 227 Mb&quot;
wget http://tile.openstreetmap.org/processed_p.tar.bz2
echo &quot;shoreline_300: Tamaño 46 Mb&quot;
$ wget http://tile.openstreetmap.org/shoreline_300.tar.bz2
cd world_boundaries
echo &quot;Descomprimir&quot;
tar -xvzf world_boundaries-spherical.tgz
tar jxvf processed_p.tar.bz2
tar jxvf shoreline_300.tar.bz2
</pre>
<p>De todas las shapes de estos archivos nos interesan fundamentalmente tres:</p>
<ul>
<li><em>shoreline_300</em> : polígonos con los contienentes y en cuadrículas, utilizado para el renderizado de la costa. El detalle es muy tosco, por lo que se utiliza para escalas superiores a 1/600.000</li>
<li><em>processed_p</em>:  polígonos con los contienentes y en cuadrículas, utilizado para el renderizado de la costa. Tienen gran detalle, por lo que se utiliza para escalas inferiores a 1/600.000</li>
<li><em>builtup_area</em>:  polígonos con masas de agua continentales (lagos)</li>
</ul>
<p><strong>Configuración de Mapnik para OSM</strong></p>
<p>El siguiente paso es configurar el fichero &#8220;set_mapnik.env&#8221; donde especificaremos las rutas a las carpetas de símbolos, shapefiles y conexiones a PostGIS.<br />
Este fichero será utilizado por la plantilla de OSM (&#8220;osm_template.xml&#8221;) para sustituir las variables contenidas entre &#8220;%&#8221;, como por ejemplo &#8220;%HOST%&#8221;, por nuestros valores personalizados.</p>
<p>Contenido del fichero &#8220;set_mapnik.env&#8221;</p>
<pre class="brush: python; title: ; notranslate">
#!/bin/sh
# Nombre del mapfile
export MAPNIK_MAP_FILE=/home/&lt;usuario&gt;/mapnik/osm.xml

# carpeta de símbolos
export MAPNIK_SYMBOLS_DIR=/home/&lt;usuario&gt;/mapnik/symbols

# carpeta de shapefiles
export MAPNIK_WORLD_BOUNDARIES_DIR=/home/&lt;usuario&gt;/mapnik/world_boundaries

# Carpeta donde se generarán las tiles
export MAPNIK_TILE_DIR=/home/&lt;usuario&gt;/mapnik/tiles/

# Host de PostgreSQL
export MAPNIK_DBHOST='localhost';

# Puerto de PostgreSQL
export MAPNIK_DBPORT=''

# Nombre de la base de datos del planet OSM
export MAPNIK_DBNAME='osm'

# Nombre del usuario de Postgres con derechos sobre la base de datos OSM
export MAPNIK_DBUSER='postgres'

# Password del usuario de PostgreSQL
export MAPNIK_DBPASS='';

# Prefijo utilizado en las tablas. Este prefijo es generado si se utiliza
# el parámetro &quot;-p &lt;nombre&gt;&quot; en la utilidad &quot;osm2pgsql&quot;. Si no lo utilizamos
# se entiende que será &quot;planet_&quot;;
export MAPNIK_PREFIX=''
$*
#-----------------------------------------------------------------------------
</pre>
<p>NOTA: En el script sustituye &#8220;&#8221; por tu nombre de usuario.</p>
<p><strong>Configuración de la plantilla de OSM</strong></p>
<p>Como ya hemos indicado, entre los ficheros proporcionados en la carpeta &#8220;mapnik&#8221;, tenemos uno muy especial, &#8220;osm_template.xml&#8221;. Como habeis deducido se trata de la plantilla que utiliza Mapnik para generar el mapfile (&#8220;osm.xml&#8221;). Sí no tocamos nada de este archivo obtendremos un renderizado de imágenes o tiles igual que el utilizado por el &#8220;slippy map&#8221; de <a href="http://www.openstreetmap.org">www.openstreetmap.org</a>. Es aquí, por tanto, donde debemos de agudizar nuestro ingenio para establecer una simbología acorde con nuestros intereses: cambiar colores, retocar las SQL de las layers, añadir o cambiar iconos, etc.</p>
<p>En lo referente a este artículo, sólo vamos a realizar pequeños cambios.</p>
<p>1. Vamos a resaltar las paradas del tranvía para que aparezca el logotipo &#8220;oficial&#8221; por el que se conoce en Alicante a este medio de transporte (<a href="http://www.fgvalicante.com">TRAM</a>)</p>
<p>En el documento cambiamos este texto:</p>
<pre class="brush: xml; title: ; notranslate">
     &lt;Rule&gt;
      &lt;MaxScaleDenominator&gt;100000&lt;/MaxScaleDenominator&gt;
      &lt;MinScaleDenominator&gt;25000&lt;/MinScaleDenominator&gt;
      &lt;Filter&gt;[railway]='station' and not [disused]='yes'&lt;/Filter&gt;
      &lt;PointSymbolizer file =  &quot;%SYMBOLS_DIR%/station_small.png&quot; type=&quot;png&quot; width=&quot;6&quot; height=&quot;6&quot; /&gt;
    &lt;/Rule&gt;
</pre>
<p>Por este otro</p>
<pre class="brush: xml; title: ; notranslate">
    &lt;Rule&gt;
      &lt;MaxScaleDenominator&gt;100000&lt;/MaxScaleDenominator&gt;
      &lt;MinScaleDenominator&gt;25000&lt;/MinScaleDenominator&gt;
      &lt;Filter&gt;[railway]='station' and not [disused]='yes'&lt;/Filter&gt;
      &lt;PointSymbolizer file =  &quot;%SYMBOLS_DIR%/tram2.png&quot; type=&quot;png&quot; width=&quot;20&quot; height=&quot;20&quot; /&gt;
    &lt;/Rule&gt;
</pre>
<p>2. Por otra parte vamos a cambiar el color de fondo para las áreas residenciales<br />
Color: <span style="color: #F6CEE3">♦♦♦♦♦</span></p>
<pre class="brush: xml; title: ; notranslate">
    &lt;Rule&gt;
      &lt;MaxScaleDenominator&gt;1000000&lt;/MaxScaleDenominator&gt;
      &lt;MinScaleDenominator&gt;1000&lt;/MinScaleDenominator&gt;
      &lt;Filter&gt;[landuse] = 'residential'&lt;/Filter&gt;
      &lt;PolygonSymbolizer&gt;
        &lt;CssParameter name=&quot;fill&quot;&gt;#F6CEE3&lt;/CssParameter&gt;
      &lt;/PolygonSymbolizer&gt;
    &lt;/Rule&gt;
</pre>
<p><strong>Crear imágenes</strong></p>
<p>Una vez configurado nuestro mapfile (&#8220;osm.xml&#8221;) pasamos a generar imágenes utilizando el script &#8220;generate_image.py&#8221;. Te recomendamos que hagas una copia de ese archivo para no trabajar con el original. Con un editor de textos tienes que modificar los siguientes parámetros:</p>
<pre class="brush: python; title: ; notranslate">
#!/bin/sh
# ... resto de código
    mapfile = &quot;osm.xml&quot;
    map_uri = &quot;image.png&quot;

    #---------------------------------------------------
    #  Change this to the bounding box you want
    #
    # Área metropolitana de Alicante
    ll = (-0.5534, 38.3245, -0.4267, 38.4071)
    #---------------------------------------------------
    z = 18
    imgx = 50 * z
    imgy = 50 * z
# ... resto de código
</pre>
<p>Cómo ves sólo es necesario modificar tres aspectos:</p>
<ol>
<li>Extensión de la imagen en grados decimales: xmin,ymin,xmax,ymax</li>
<li>Nivel de zoom: del 1 (mundo) al 18 (máximo de detalle)</li>
<li>Dimensiones de la imagen en píxeles</li>
</ol>
<p>NOTA:</p>
<p>Para evitar resultados desagradables intenta ajustar el BBOX y las dimensiones de la imagen.</p>
<p>Por último debemos de realizar los siguientes pasos para generar la imagen</p>
<p>1. Leer las variables que hemos definido en &#8220;set_mapnik.env&#8221;</p>
<pre class="brush: bash; title: ; notranslate">source set-mapnik-env</pre>
<p>2.  Utilizar la plantilla &#8220;osm-template.xml&#8221; para generar el fichero &#8220;oxm.xml&#8221;</p>
<pre class="brush: bash; title: ; notranslate">
./customize-mapnik-map &gt;$MAPNIK_MAP_FILE
</pre>
<p>3. Lanzar el script:</p>
<pre class="brush: bash; title: ; notranslate">python mi_genera_imagen.py</pre>
<p>Este es el resultado:</p>
<p>Zoom 14<br />
<img src="http://www.gisandchips.org/wp-content/ali_zoom14-300x300.png" alt="" width="300" height="300" /></p>
<p>Zoom 18 (fíjate en el renderizado de las estaciones del TRAM)<br />
<img src="http://www.gisandchips.org/wp-content/ali_zoom18.png" alt="" width="300" height="300" /></p>
<p><strong>Crear las tiles</strong></p>
<p>Esta es la parte de Mapnik que tiene más interés para nosotros, puesto que nos permitirá generar nuestro propio TMS para ser utilizado con independencia del &#8220;Slippy map&#8221; de OpenStreetMap.</p>
<p>1. Creamos el directorio donde se guardarán las tiles creadas:</p>
<pre class="brush: bash; title: ; notranslate">mkdir -p $MAPNIK_TILE_DIR</pre>
<p>2. Editamos el fichero &#8220;generate_tiles.py&#8221; para definir las zonas a renderizar</p>
<pre class="brush: python; title: ; notranslate">
#!/usr/bin/python
# resto de código
# directorio donde se almacenan las imágenes
        tile_dir = home + &quot;/osm/tiles/&quot;

    #-------------------------------------------------------------------------
    #
    # Change the following for different bounding boxes and zoom levels
    #
    # Start with an overview
    # World
    bbox = (-180.0,-90.0, 180.0,90.0)

    render_tiles(bbox, mapfile, tile_dir, 0, 5, &quot;World&quot;)

    # Europa
    minZoom = 6
    maxZoom = 8
    bbox = (1.0,10.0, 20.6,50.0)
    render_tiles(bbox, mapfile, tile_dir, minZoom, maxZoom)

    # Provincia de Alicante
    bbox = (-1.498,37.622,0.529,38.946)
    render_tiles(bbox, mapfile, tile_dir, 9, 11 , &quot;Europe+&quot;)

    # Área metropolitana de la ciudad de Alicante
    bbox = (-0.6171,38.295,-0.3637,38.4602)
    render_tiles(bbox, mapfile, tile_dir, 12, 18 , &quot;Alicante&quot;)
</pre>
<p>Cómo habrás podido deducir vamos a generar tiles en cuatro intervalos de escala:</p>
<ul>
<li>Escalas pequeñas: Zoom del 1 al 5</li>
<li>Escalas medias: 6 al 8</li>
<li>Escalas grandes (más detalles): 9 al 11</li>
<li>Escalas de máximo detalle): 12 al 18</li>
</ul>
<p>3. Ejecutamos el script</p>
<pre class="brush: bash; title: ; notranslate">python ./mi_genera_tiles.py</pre>
<p>4. Comprobación:<br />
Fíjate que en la carpeta &#8220;tiles&#8221; tendremos una carpeta por cada nivel de zoom. En los niveles superiores es donde habrán más imágenes (las de mayor detalle)</p>
<pre class="brush: bash; title: ; notranslate">ls tiles</pre>
<p>Por fin, ya hemos creado nuestro directorio de tiles listo para ser utilizado por OpenLayers como un TMS.</p>
<p>Esto es todo. Espero que os haya gustado. Comentarios y críticas serán bien recibidas</p>
<p>Este artículo es el segundo de una serie formada por:</p>
<ul>
<li><a href="http://www.gisandchips.org/2009/10/22/renderizado-de-osmcon-mapnik-para-usar-en-openlayers/#more-387">Renderizado con Mapnik</a></li>
<li>Mapnik y OpenStreetMap (este post)</li>
<li>Thumbnails con Mapnik y OpenStreetMap</li>
<li>Uso de TMS de OpenStreetMap con OpenLayers</li>
<li>TMS de OpenStreetMap con Mod_tile</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.gisandchips.org/2009/10/23/openstreetmap-y-mapnik/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Renderizado con Mapnik</title>
		<link>http://www.gisandchips.org/2009/10/22/renderizado-de-osmcon-mapnik-para-usar-en-openlayers/</link>
		<comments>http://www.gisandchips.org/2009/10/22/renderizado-de-osmcon-mapnik-para-usar-en-openlayers/#comments</comments>
		<pubDate>Thu, 22 Oct 2009 08:43:27 +0000</pubDate>
		<dc:creator>jose</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[Mapnik]]></category>
		<category><![CDATA[OpenStreetMap]]></category>
		<category><![CDATA[render]]></category>

		<guid isPermaLink="false">http://www.gisandchips.org/?p=387</guid>
		<description><![CDATA[En el planteamiento que presenté en el primer artículo sobre OSM y PgRouting no estaba incluido el tema del renderizado, pero tras indagar el tema de Mapnik me he dado cuenta de las magníficas posibilidades que ofrece este renderizador, de ahí que haga un inciso para introducir esta cuestión. Mapnik es un toolkit en C++ [...]]]></description>
			<content:encoded><![CDATA[<p>En el planteamiento que presenté en el primer artículo sobre OSM y PgRouting no estaba incluido el tema del renderizado, pero tras indagar el tema de <a href="http://mapnik.org/">Mapnik</a> me he dado cuenta de las magníficas posibilidades que ofrece este renderizador, de ahí que haga un inciso para introducir esta cuestión.</p>
<p>Mapnik es un toolkit en C++ para renderizar varios orígenes de datos, que tiene bindings para Python. La calidad de este paquete, a mi juicio, está por encima de otros renderizadores como Osmarender o Kosmos. Profundizar en él me ha permitido aprender algo de Python, que nunca viene mal y sumergirme en las entrañas de la edición cartográfica. En cualquiera de los casos, los resultados estarán limitados a nuestra destreza para la composición de mapas, pero nunca a las limitaciones del programa.</p>
<p><span id="more-387"></span></p>
<p>Mapnik entiende los siguientes orígenes de datos:</p>
<ul>
<li>Shapefiles</li>
<li>PostGIS</li>
<li>Raster (generalmente TIFF, aunque si se compila Mapnik con GDAL podemos ampliar el espectro de formatos)</li>
</ul>
<p><span style="font-weight: bold">OBJETIVO</span>:</p>
<p>En muchas de nuestras aplicaciones web incluimos una layer del proveedor OSM, utilizando OpenLayers, Mapfish u otras. El resultado es fácil de implementar, pero tiene el problema de depender del estado de salud del servidor oficial de www.openstreetmap.org. Además, puede que en un momento dado nos planteemos utilizar nuestra propia simbolización, cambiando los colores de las vias, o añadiendo unos iconos diferentes a los oficiales Aquí nos planteamos crear un TMS (Tile Map Service), o lo que es lo mismo, un conjunto de imágenes de mapa (tiles) de reducido tamaño (256 * 256) , personalizadas con nuestros criterios de simbolización gráfica, que están ordenadas en carpetas, cada una de las cuales recibe el nombre del nivel de zoom utilizado, que oscila entre 1 y 18, siguiendo la estructura del &#8220;<a href="http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames">mapnik slippy map</a>&#8220;, que es una forma de ordenar las tiles en función de la proyección utilizada (Mercator).</p>
<p>REFERENCIAS WEB:</p>
<ul>
<li><a href="http://weait.com/content/build-your-own-openstreetmap-server">http://weait.com/content/build-your-own-openstreetmap-server</a> Interesante portal con artículos sobre OSM y Mapnik. Os lo recomiendo para instalar Mapnik y realizar pruebas</li>
</ul>
<p><span style="font-weight: bold">DESARROLLO</span>:</p>
<ol>
<li><span style="background-color: #ffffff"> Compilación de librerías necesarias (Mapnik y osm2pgsql)</span></li>
<li><span style="background-color: #ffffff">Creación de una geodatabase con datos OSM utilizando Osm2Pgsql</span></li>
<li><span style="background-color: #ffffff">Configuración de la plantilla de mapa (XML mapfile)</span></li>
<li><span style="background-color: #ffffff">Pruebas de renderizado</span></li>
<li><span style="background-color: #ffffff">Creación del TMS</span></li>
<li><span style="background-color: #ffffff">Implementación del TMS en OpenLayers</span></li>
</ol>
<p><span style="font-weight: bold">COMPILACIÓN DE LIBRERÍAS</span>:</p>
<p>Entrando en faena vamos a compilar el paquete Mapnik, que como no podía ser de otra manera tiene una dependencia insalvable, BOOST, que ya vimos en el anterior artículo. No obstante indico rápidamente su compilación:</p>
<p><span style="font-weight: bold">COMPILACIÓN DE BOOST</span></p>
<p>1. Añadir dependencias. Según la distro necesitaremos una u otra (python-devel, freetype-devel, etc). En este paso vamos a incluir una librería que me ha dado varios quebraderos de cabeza: EXPAT. No sólo es necesaria instalarla, sino que además debemos de indicar algunas variables de configuración. Empezamos por su instalación desde la paquetería</p>
<p>Ubuntu: $  sudo apt-get install libexpat1-dev</p>
<p>$ sudo apt-get install libltdl7-dev libpng12-dev libtiff4-dev libicu-dev libboost-regex-dev libboost-iostreams-dev libboost-filesystem-dev libboost-thread-dev libboost-python1.34.1 libboost-python-dev libfreetype6-dev libcairo2-dev libcairomm-1.0-dev libboost-program-options-dev python-cairo-dev libboost-serialization-dev imagemagick</p>
<p>Centos: $ yum install expat expat-devel.x86_64</p>
<p>NOTA: Fíjate que para expat le indico específicamente la plataformas de 64 bits. Es importante indicarla. Si ponemos &#8220;yum install expat-devel&#8221; instalará las dos, y nos proporcionará problemas en el futuro (por ejemplo compilando nuestra querida GDAL). Indícale cual es tu plataforma (x86_64 / i386)</p>
<p>2. Define variables para expat:</p>
<pre class="brush: bash; title: ; notranslate">&lt;br&gt;export EXPAT_INCLUDE=/usr/include&lt;br&gt;export EXPAT_LIBPATH=/usr/lib&lt;br&gt;</pre>
<p>3. Descargar la version 1.39 y descomprimir en /usr/local/</p>
<pre class="brush: bash; title: ; notranslate">
mkdir /usr/local/include/boost
wget http://downloads.sourceforge.net/project/boost/boost/1.39.0/boost_1_39_0.tar.bz2
tar -xjvf boost_1_39_0.tar.bz2&lt;br&gt;cd boost_1_39_0
</pre>
<p>4. Compilar:</p>
<pre class="brush: bash; title: ; notranslate">./bootstrap.sh --prefix=/usr/local/
./bjam install --includedir=/usr/local/include/boost/
</pre>
<p>Como root:</p>
<pre class="brush: bash; title: ; notranslate">/sbin/ldconfig</pre>
<p>Este comando es mano de santo para que los compiladores encuentren las librerías.<br />
<span style="font-weight: bold">COMPILAR MAPNIK</span><br />
Para compilar en Ubuntu os recomiendo seguir las indicaciones de este <a href="http://weait.com/content/build-your-own-openstreetmap-server">enlace</a>. Para Centos estos son los pasos a seguir:</p>
<pre class="brush: bash; title: ; notranslate">cd /home/&lt;usuario&gt;/compilar</pre>
<p>1. Descargar y descomprimir la fuente de Mapnik  (versión 0.6.1)</p>
<pre class="brush: bash; title: ; notranslate">wget http://download.berlios.de/mapnik/mapnik-0.6.1.tar.bz2
tar -xjvf mapnik-0.6.1.tar.bz2
cd mapnik-0.6.1&lt;br&gt;
</pre>
<p>2. Compilar:</p>
<pre class="brush: bash; title: ; notranslate">python scons/scons.py configure
python scons/scons.py BOOST_INCLUDES=/usr/local/include/boost/boost-1_39/ BOOST_LIBS=/usr/local/lib BOOST_TOOLKIT=gcc41
</pre>
<p>Como root:</p>
<pre class="brush: bash; title: ; notranslate">
python scons/scons.py BOOST_INCLUDES=/usr/local/include/boost/boost-1_39/ BOOST_LIBS=/usr/local/lib BOOST_TOOLKIT=gcc41 install
</pre>
<p>3. Actualizar rutas de librerías (como root):</p>
<pre class="brush: bash; title: ; notranslate">/sbin/ldconfig</pre>
<p>4. Comprobación de la instalación:</p>
<p>Las fuentes contienen una demo para comprobar la instalación. Se trata de un script en Python que tras su ejecución llama a algunos shapefiles y genera unas imágenes en PNG. También lo hará en otros formatos (PDF, PS, etc.)  sí has compilado Mapnik con Cairo y PyCairo. En Ubuntu es muy fácil ya que estas librerías se encuentran en el repositorio, pero en Centos he sido incapaz de utilizarlas (si alguno lo consigue su comentario será de agradecer).</p>
<p>Mapnik utiliza la librería <a href="http://www.antigrain.com/">AGG</a> para crear las imágenes, siendo ésta la librería de uso por defecto, aunque también puede utilizar Cairo.<br />
1. Cambia al directorio de pruebas</p>
<pre class="brush: bash; title: ; notranslate">
cd &lt;fuente&gt;/demo/python
python rundemo.py
</pre>
<p>Obtendrás este mensaje:</p>
<pre>Skipping cairo examples as Pycairo not available
3 maps have been rendered in the current directory:
- demo.png
- demo256.png
- demo.jpg
Have a look!</pre>
<p>Bueno, pues ya está. Si las imágenes se han generado Mapnik funciona. Sí te da algun error es que te falta alguna librería. Otra manera de ver que errores hay es utilizando la propia consola de python. Escribe lo que está en negrita:</p>
<p>jose@jose:~/compilar$ <span style="font-weight: bold">python</span></p>
<p>Python 2.6.2 (release26-maint, Apr 19 2009, 01:56:41)</p>
<p><span style="background-color: #ffffff">[GCC 4.3.3] on linux2</span><span style="background-color: #ffffff">Type &#8220;help&#8221;, &#8220;copyright&#8221;, &#8220;credits&#8221; or &#8220;license&#8221; for more information.</span><br />
<span style="background-color: #ffffff">&gt;&gt;&gt;<span style="font-weight: bold"> import mapnik</span></span><br />
&gt;&gt;&gt; <span style="font-weight: bold">exit()</span></p>
<p><span style="background-color: #ffffff">Sí tras escribir &#8220;import mapnik&#8221; obtienes algunas líneas de error es que algo te falta.  De lo contrario el intérprete de Python entiende que la llamada a la librería funciona y no devuelve nada.</span><br />
<span style="font-weight: bold">CREACIÓN DE UNA GEODATABASE CON EL PLANET DE OSM</span></p>
<p>Nuestro objetivo es ahora crear una geodatabase de PostGIS con datos procedente de un planet.osm. Con ello,  conseguiremos tener un repositorio de datos cuyo único fin es ser utilizado por Mapnik para renderizar tiles, y no para otras funciones como la de routing.  Por esta razón utilizamos &#8220;osm2pgsql&#8221;. Esta herramienta suele formar parte de los repositorios de las distros, por lo que sólo necesitas:</p>
<pre>Ubuntu: sudo apt-get install osm2pgsql
Centos: yum install osm2pgsql</pre>
<p><span style="font-weight: bold">Compilación de osm2pgsql</span>:</p>
<pre class="brush: bash; title: ; notranslate">&lt;br&gt;svn co http://svn.openstreetmap.org/applications/utils/export/osm2pgsql/&lt;br&gt;cd osm2pgsql&lt;br&gt;make&lt;br&gt;</pre>
<p><span style="background-color: #ffffff">Crear una geodatabase con datos de OSM es una labor sencilla, pero ten en cuenta que OSM cambia constantemente, por lo que la validez de tus datos tienen una caducidad. Por esta razón es importante que articulemos algún sistema para que cada cierto tiempo la base de datos de OSM se actualice sólo. Por ello te recomiendo que personalices el siguiente script en bash que te facilitará la labor:</span><span style="background-color: #ffffff"> </span></p>
<pre class="brush: bash; title: ; notranslate">
#!/bin/sh
echo &quot;Script en Bash para actualizar un planet.osm en una base de datos OSM con Postgis v. 1.4.&quot;
echo &quot;Borrando spain.osm.bz2&quot;
rm spain.osm.bz2
echo &quot;Descargando fichero osm de Spain desde Cloudmade.com&quot;
wget http://downloads.cloudmade.com/europe/spain/spain.osm.bz2
echo &quot;Descomprimiendo&quot;
bzip2 -d spain.osm.bz2
echo &quot;Borrando bbdd osm&quot;
dropdb -h localhost -U postgres osm
echo &quot;Creando bbdd osm&quot;
createdb -U postgres -E UTF8 osm
echo &quot;Instalar lenguaje pl/pgsql y Postgis en bbdd osm&quot;
createlang -U postgres plpgsql osm
psql -U postgres -f /usr/share/pgsql/contrib/postgis.sql osm
psql -U postgres -f /usr/share/pgsql/contrib/spatial_ref_sys.sql osm
echo &quot;Añadir datos de spain.osm a la bbdd osm&quot;
echo &quot;Tiempo aproximado: 11 minutos&quot;
osm2pgsql --slim -H localhost -U postgres -d osm ./spain.osm
echo &quot;fin&quot;
</pre>
<p>NOTA: Es muy importante utilizar el parámetro &#8220;slim&#8221; en osm2pgsql, puesto que permite introducir datos directamente a la base de datos, lo que resulta muy útil con planet.osm de mucho peso. Si no se usa, cargará en memoria todo el osm hasta que ocupe toda la memoria de tu ordenador.  Contenido de la geodatabase</p>
<pre>psql -h localhost -U postgres osm
Welcome to psql 8.3.8 (server 8.3.7), the PostgreSQL interactive terminal.

Type:  \copyright for distribution terms
       \h for help with SQL commands
       \? for help with psql commands
       \g or terminate with semicolon to execute query
       \q to quit

osm=# \d
                     List of relations
 Schema |            Name            |   Type   |  Owner
--------+----------------------------+----------+----------
 public | geometry_columns           | table    | postgres
 public | planet_osm_line            | table    | postgres
 public | planet_osm_line_pid_seq    | sequence | postgres
 public | planet_osm_nodes           | table    | postgres
 public | planet_osm_point           | table    | postgres
 public | planet_osm_point_pid_seq   | sequence | postgres
 public | planet_osm_polygon         | table    | postgres
 public | planet_osm_polygon_pid_seq | sequence | postgres
 public | planet_osm_rels            | table    | postgres
 public | planet_osm_roads           | table    | postgres
 public | planet_osm_roads_pid_seq   | sequence | postgres
 public | planet_osm_ways            | table    | postgres
 public | spatial_ref_sys            | table    | postgres
(13 rows)</pre>
<p><span style="font-weight: bold">INTRODUCCIÓN A MAPNIK CON PYTHON</span><br />
Como ya he dicho, no soy un especialista en este lenguaje, aunque su sintaxis me parece muy clara para entenderla sin problemas.<br />
Lo primero que debemos de tener en cuenta es que el diagrama de flujo de Mapnik exige para su uso de un script en Python (o desde la misma consola de Python) donde se aprecien las siguientes fases:</p>
<p>1. Cargar los paquetes (bindings) de Mapnik para Python<br />
2. Indicar la fuente de datos (datasource) de cada layer a representar<br />
3. Definir la simbología de cada layer<br />
4. Indicar los parámetros de la imagen a crear: tamaño, nombre del fichero</p>
<p>Veamos el ejemplo más sencillo: Crear una imagen a partir de un shapefile</p>
<pre class="brush: python; title: ; notranslate">
#!/usr/bin/env python
# Fichero: hola_mundo.py
# Cargamos los bindings de mapnik
import mapnik
# Definimos un objeto mapa, con un tamaño (en píxeles) y una proyección según PROJ4
m = mapnik.Map(600,300,'+proj=latlong +datum=WGS84')
# Indicamos el color de fondo
m.background = mapnik.Color('steelblue')
# Creamos un estilo. Un estilo es un tipo de objeto que puede almacenar una o varias reglas (rules)
# para la representación cartográfica
s = mapnik.Style()
# Creamos un objeto rule
r=mapnik.Rule()
# Aplicamos parámetros a esa regla. En este caso:
# 1. relleno del polígono
# 2. simbología de la línea (outline)
r.symbols.append(mapnik.PolygonSymbolizer(mapnik.Color('#f2eff9')))
r.symbols.append(mapnik.LineSymbolizer(mapnik.Color('rgb(50%,50%,50%)'),0.1))
# asociamos la &quot;rule&quot; (r) al estilo (s)
s.rules.append(r)
# Asignamos un nombre al estilo
m.append_style('My Style',s)
# Creamos un objeto layer de mapnik: un nombre y una projección. En este caso la geodésica mundial EPSG:4326
lyr = mapnik.Layer('world','+proj=latlong +datum=WGS84')
# Definimos el origen de datos
lyr.datasource = mapnik.Shapefile(file='/Users/path/to/your/data/world_borders')
# Asociamos a la layer el estilo que hemos creado
lyr.styles.append('My Style')
# Asociamos la layer al objeto mapa (m)
m.layers.append(lyr)
# Indicamos que se renderizará el total de la extensión del shapefile
m.zoom_to_box(lyr.envelope())
# Renderizamos la imagen: indicando el objeto mapa (m) y el nombre del fichero de imagen a crear
mapnik.render_to_file(m,'world.png', 'png')
# Si queremos que los parámetros indicados en el objeto mapa sean serializados a un fichero
# mapfile (XML) sólo tenemos que añadir esta linea
mapnik.save_map(m,'mi_mapfile.xml')
</pre>
<p>Fíjate en la línea que hace referencia a la proyección a utilizar. ¿De donde saco estos parámetros? En esta página puedes encontrar los parámetros necesarios: <a href="http://spatialreference.org/ref/epsg/">http://spatialreference.org/ref/epsg/</a>. Veamos algunos ejemplos:</p>
<ul>
<li><span style="background-color: #ffffff">EPSG 4326: +proj=latlong +datum=WGS84 (<a href="http://spatialreference.org/ref/epsg/4326/proj4/">http://spatialreference.org/ref/epsg/4326/proj4/</a>)</span></li>
<li><span style="background-color: #ffffff">EPSG 900913: <span>+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs (<a href="http://spatialreference.org/ref/sr-org/6627/proj4/">http://spatialreference.org/ref/sr-org/6627/proj4/</a>)</span></span></li>
<li><span style="background-color: #ffffff">EPSG 23030: <span>+proj=utm +zone=30 +ellps=intl +units=m +no_defs (<a href="http://spatialreference.org/ref/epsg/23030/proj4/">http://spatialreference.org/ref/epsg/23030/proj4/</a>)</span></span></li>
</ul>
<p>Para ejecutar este script sólo tienes que indicarle que es un ejecutable:</p>
<pre class="brush: bash; title: ; notranslate">&lt;br&gt;chmod +x hola_mundo.py&lt;br&gt;</pre>
<p>Y ahora lo ejecutamos:</p>
<pre class="brush: bash; title: ; notranslate">&lt;br&gt;python hola_mundo.py&lt;br&gt;</pre>
<p>Resultado:</p>
<p style="text-align: center">
<div class="mceTemp mceIEcenter">
<dl>
<dt><img class="size-medium wp-image-704" src="http://www.gisandchips.org/wp-content/hola_mundo-300x150.png" alt="hola munco con Mapnik" width="300" height="150" /></dt>
<dd>hola mundo con Mapnik</dd>
</dl>
</div>
<p><span style="font-weight: bold">Mapfile de Mapnik</span><br />
Como ves, con muy pocas líneas tenemos una imagen creada con Mapnik, pero, ¿porque no separamos la lógica de representación y los orígenes de datos en un fichero de mapa (mapfile)? Posiblemente este nos recuerda a más de uno los famosos &#8220;mapfile&#8221; de MapServer, y realmente bastante se parecen a ellos, pero esta vez la lógica está en un fichero XML.  Manos a la obra. Primero generamos el script de Python sin referencia alguna a la simbología y los datasources:</p>
<pre class="brush: python; title: ; notranslate">
#!/usr/bin/env python
import mapnik
mapfile = 'hola_mundo.xml'
map_output = 'hola_mundo.png'
m = mapnik.Map(600, 300)
mapnik.load_map(m, mapfile)
bbox = mapnik.Envelope(mapnik.Coord(-180.0, -90.0), mapnik.Coord(180.0, 90.0))
m.zoom_to_box(bbox)
mapnik.render_to_file(m, map_output)
</pre>
<p>Fíjate que en la tercera línea llamamos a un fichero de mapa. Se trata de un fichero XML con una estructura definida, donde se indican los datasources (layers) y su representación (styles y rules dentro de ella)</p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;!DOCTYPE Map&gt;
&lt;Map bgcolor=&quot;steelblue&quot; srs=&quot;+proj=latlong +datum=WGS84&quot;&gt;
  &lt;Style name=&quot;My Style&quot;&gt;
    &lt;Rule&gt;
      &lt;PolygonSymbolizer&gt;
        &lt;CssParameter name=&quot;fill&quot;&gt;#f2eff9&lt;/CssParameter&gt;
      &lt;/PolygonSymbolizer&gt;
      &lt;LineSymbolizer&gt;
        &lt;CssParameter name=&quot;stroke&quot;&gt;rgb(50%,50%,50%)&lt;/CssParameter&gt;
        &lt;CssParameter name=&quot;stroke-width&quot;&gt;0.1&lt;/CssParameter&gt;
      &lt;/LineSymbolizer&gt;
    &lt;/Rule&gt;
  &lt;/Style&gt;&lt;
  &lt;Layer name=&quot;world&quot; srs=&quot;+proj=latlong +datum=WGS84&quot;&gt;
    &lt;StyleName&gt;My Style&lt;/StyleName&gt;
    &lt;Datasource&gt;
      &lt;Parameter name=&quot;type&quot;&gt;shape&lt;/Parameter&gt;
      &lt;Parameter name=&quot;file&quot;&gt;&lt;ruta al shapefile, pero sin extensión .shp&gt;&lt;/Parameter&gt;
    &lt;/Datasource&gt;
  &lt;/Layer&gt;
&lt;/Map&gt;
</pre>
<p>En resumen tenemos:<br />
- Fondo de mapa<br />
- Style &#8211;&gt; Rule &#8211;&gt; Simbología<br />
- Layer &#8211;&gt; Proyección &#8211;&gt; DataSource &#8211;&gt; Style</p>
<p>Estos dos ejemplos utilizan shapefile. Para indicar un datasource PostGIS hay que especificar estas líneas</p>
<p>a) En el caso del fichero Python sin XML</p>
<pre class="brush: python; title: ; notranslate">
# proyección UTM ED-50 (España) --&gt; EPSG 23030
lyr = mapnik.Layer('spain','+proj=utm +zone=30 +ellps=intl +units=m +no_defs')
lyr = mapnik.Layer('Layer de PostGIS')
lyr.datasource = mapnik.PostGIS(host='localhost',user='postgres',password='',dbname='hispania',table='provincias')
lyr.styles.append('Mi estilo')
</pre>
<p>b) En el caso de un XML este sería el contenido de la parte referente a la layer</p>
<pre class="brush: xml; title: ; notranslate">
&lt;Layer name=&quot;provincias&quot; status=&quot;on&quot; srs=&quot;+proj=utm +zone=30 +ellps=intl +units=m +no_defs&quot;&gt;
    &lt;StyleName&gt;mi estilo&lt;/StyleName&gt;
    &lt;Datasource&gt;
      &lt;Parameter name=&quot;type&quot;&gt;postgis&lt;/Parameter&gt;
      &lt;Parameter name=&quot;host&quot;&gt;localhost&lt;/Parameter&gt;
      &lt;Parameter name=&quot;user&quot;&gt;postgres&lt;/Parameter&gt;
      &lt;Parameter name=&quot;password&quot;&gt;&lt;/Parameter&gt;
      &lt;Parameter name=&quot;dbname&quot;&gt;hispania&lt;/Parameter&gt;
      &lt;Parameter name=&quot;table&quot;&gt;provincias&lt;/Parameter&gt;
      &lt;Parameter name=&quot;estimate_extent&quot;&gt;false&lt;/Parameter&gt;
      &lt;Parameter name=&quot;extent&quot;&gt;-510000.0,3800000.0,1200000.0,4900000.0&lt;/Parameter&gt;
    &lt;/Datasource&gt;
&lt;/Layer&gt;
</pre>
<p>El parámetro table (provincias) podemos sustituir el nombre de una tabla geométrica por una consulta SQL encerrada entre paréntesis:<br />
(select * from provincias)</p>
<p>Resultado:<br />
<img src="http://www.gisandchips.org/wp-content/hola_spain_xml-300x300.png" alt="hola spain" width="300" height="300" /></p>
<p>Enlazando con lo último, en el momento en que definimos en una &#8220;layer&#8221; un &#8220;datasource&#8221;, sea un shapefile o PostGIS, con su correspondiente &#8220;style&#8221;, tenemos a disposición de las &#8220;rules&#8221; todas las columnas o campos del datasource, que pueden ser utilizados como filtros, lo que resulta muy util para la edición de cartografía temática.<br />
Fichero: mapa_spain_xml2.py</p>
<pre class="brush: python; title: ; notranslate">
#!/usr/bin/env python
import mapnik
mapfile = 'provincias2.xml'
map_output = 'hola_spain_xml2.png'
m = mapnik.Map(600, 600)
mapnik.load_map(m, mapfile)
bbox = mapnik.Envelope(mapnik.Coord(-510000.0, 3800000.0), mapnik.Coord(1200000.0, 4900000.0))
m.zoom_to_box(bbox)
mapnik.render_to_file(m, map_output)
</pre>
<p>Ahora el fichero<br />
Fichero provincias2.xml</p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;!DOCTYPE Map&gt;
&lt;Map bgcolor=&quot;steelblue&quot; srs=&quot;+proj=utm +zone=30 +ellps=intl +units=m +no_defs&quot;&gt;
  &lt;Style name=&quot;mi estilo provincias&quot;&gt;
    &lt;Rule&gt;
      &lt;PolygonSymbolizer&gt;
        &lt;CssParameter name=&quot;fill&quot;&gt;#f2eff9&lt;/CssParameter&gt;
      &lt;/PolygonSymbolizer&gt;
      &lt;LineSymbolizer&gt;
        &lt;CssParameter name=&quot;stroke&quot;&gt;rgb(50%,50%,50%)&lt;/CssParameter&gt;
        &lt;CssParameter name=&quot;stroke-width&quot;&gt;0.1&lt;/CssParameter&gt;
      &lt;/LineSymbolizer&gt;
    &lt;/Rule&gt;
  &lt;/Style&gt;
  &lt;Style name=&quot;mi estilo vias&quot;&gt;
&lt;!-- autopistas --&gt;
    &lt;Rule&gt;
      &lt;Filter&gt;[level] = 41&lt;/Filter&gt;
      &lt;LineSymbolizer&gt;
        &lt;CssParameter name=&quot;stroke&quot;&gt;#506077&lt;/CssParameter&gt;
        &lt;CssParameter name=&quot;stroke-width&quot;&gt;4&lt;/CssParameter&gt;
      &lt;/LineSymbolizer&gt;
      &lt;TextSymbolizer name=&quot;codigo&quot; face_name=&quot;DejaVu Sans Book&quot; size=&quot;11&quot; fill=&quot;#000&quot; halo_radius=&quot;1&quot; spacing=&quot;400&quot; placement=&quot;line&quot; /&gt;
    &lt;/Rule&gt;
&lt;!-- nacionales --&gt;
    &lt;Rule&gt;
      &lt;Filter&gt;[level] = 40&lt;/Filter&gt;
      &lt;LineSymbolizer&gt;
        &lt;CssParameter name=&quot;stroke&quot;&gt;#a37b48&lt;/CssParameter&gt;
        &lt;CssParameter name=&quot;stroke-width&quot;&gt;2&lt;/CssParameter&gt;
       &lt;/LineSymbolizer&gt;
    &lt;/Rule&gt;
  &lt;/Style&gt;
&lt;Layer name=&quot;provincias&quot; status=&quot;on&quot; srs=&quot;+proj=utm +zone=30 +ellps=intl +units=m +no_defs&quot;&gt;
    &lt;StyleName&gt;mi estilo provincias&lt;/StyleName&gt;
    &lt;Datasource&gt;
      &lt;Parameter name=&quot;type&quot;&gt;postgis&lt;/Parameter&gt;
      &lt;Parameter name=&quot;host&quot;&gt;localhost&lt;/Parameter&gt;
      &lt;Parameter name=&quot;user&quot;&gt;postgres&lt;/Parameter&gt;
      &lt;Parameter name=&quot;password&quot;&gt;&lt;/Parameter&gt;
      &lt;Parameter name=&quot;dbname&quot;&gt;hispania&lt;/Parameter&gt;
      &lt;Parameter name=&quot;table&quot;&gt;provincias&lt;/Parameter&gt;
      &lt;Parameter name=&quot;estimate_extent&quot;&gt;false&lt;/Parameter&gt;
      &lt;Parameter name=&quot;extent&quot;&gt;-510000.0,3800000.0,1200000.0,4900000.0&lt;/Parameter&gt;
    &lt;/Datasource&gt;&lt;br&gt;&lt;/Layer&gt;
&lt;Layer name=&quot;vias&quot; status=&quot;on&quot; srs=&quot;+proj=utm +zone=30 +ellps=intl +units=m +no_defs&quot;&gt;
    &lt;StyleName&gt;mi estilo vias&lt;/StyleName&gt;
    &lt;Datasource&gt;
      &lt;Parameter name=&quot;type&quot;&gt;postgis&lt;/Parameter&gt;
      &lt;Parameter name=&quot;host&quot;&gt;localhost&lt;/Parameter&gt;
      &lt;Parameter name=&quot;user&quot;&gt;postgres&lt;/Parameter&gt;
      &lt;Parameter name=&quot;password&quot;&gt;&lt;/Parameter&gt;
      &lt;Parameter name=&quot;dbname&quot;&gt;hispania&lt;/Parameter&gt;
      &lt;Parameter name=&quot;table&quot;&gt;vias&lt;/Parameter&gt;
      &lt;Parameter name=&quot;estimate_extent&quot;&gt;false&lt;/Parameter&gt;
      &lt;Parameter name=&quot;extent&quot;&gt;-510000.0,3800000.0,1200000.0,4900000.0&lt;/Parameter&gt;
    &lt;/Datasource&gt;
&lt;/Layer&gt;
&lt;/Map&gt;
</pre>
<p>Este mapfile consta de:<br />
- 2 datasources<br />
- provincias &#8211;&gt; asociado al style &#8220;mi estilo provincias&#8221;<br />
- vias &#8211;&gt; asociado al style &#8220;mi estilo vias&#8221;<br />
- 2 styles<br />
- &#8220;mi estilo provincias&#8221;  &#8211;&gt; con una sola &#8220;rule&#8221;<br />
- &#8220;mi estilo vias&#8221; &#8211;&gt; con 2 &#8220;rules&#8221;<br />
&#8211;&gt; Autopistas: Filtro: &#8220;[level] = 40&#8243;, Texto: ver TextSymbolizer<br />
&#8211;&gt; Nacionales: [level] = 40</p>
<p>Resultado:</p>
<p><img src="http://www.gisandchips.org/wp-content/hola_spain_xml2-300x300.png" alt="hola spain 2" width="300" height="300" /></p>
<p>Este artículo es el primero de una serie formada por:</p>
<ul>
<li>Renderizado con Mapnik (este post)</li>
<li>Mapnik y OpenStreetMap</li>
<li>Thumbnails con Mapnik y OpenStreetMap</li>
<li>Uso de TMS de OpenStreetMap con OpenLayers</li>
<li>TMS de OpenStreetMap con Mod_tile</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.gisandchips.org/2009/10/22/renderizado-de-osmcon-mapnik-para-usar-en-openlayers/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Análisis de redes con OpenStreetMap y PgRouting en un ambiente web</title>
		<link>http://www.gisandchips.org/2009/10/03/analisis-de-redes-con-openstreetmap-y-pgrouting-en-un-ambiente-web/</link>
		<comments>http://www.gisandchips.org/2009/10/03/analisis-de-redes-con-openstreetmap-y-pgrouting-en-un-ambiente-web/#comments</comments>
		<pubDate>Fri, 02 Oct 2009 23:50:27 +0000</pubDate>
		<dc:creator>jose</dc:creator>
				<category><![CDATA[Análisis]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[OpenStreetMap]]></category>
		<category><![CDATA[pgRouting]]></category>

		<guid isPermaLink="false">http://www.gisandchips.org/?p=290</guid>
		<description><![CDATA[OBJETIVO: El objetivo de esta serie de artículos es proporcionar una metodología para crear servicios de enrutamiento (routing) en portales web utilizando datos de OpenStreetMap (http://www.openstreetmap.org) que serán añadidos a una geodatabase de PostgreSQL, con la extensión PostGIS y PgRouting. Para llevar a cabo este servicio son necesarias las siguientes fases: Instalación de las extensiones [...]]]></description>
			<content:encoded><![CDATA[<p><strong>OBJETIVO</strong>:</p>
<p>El objetivo de esta serie de artículos es proporcionar una metodología para crear servicios de enrutamiento (routing) en portales web utilizando datos de OpenStreetMap (<a href="http://www.openstreetmap.org/">http://www.openstreetmap.org</a>) que serán añadidos a una geodatabase de PostgreSQL, con la extensión PostGIS y PgRouting.</p>
<p>Para llevar a cabo este servicio son necesarias las siguientes fases:</p>
<ol>
<li>Instalación de las extensiones PostGIS y PgRouting</li>
<li>Importación de los datos de OSM</li>
<li>Conversión a una geodatabase de PostGIS en PostgreSQL</li>
<li>Explotación de una geodatabase con PgRouting.</li>
<li>Diseño de la interfaz de usuario. Contenedor HTML y Javascript (Openlayers)</li>
<li>Creación de lógica de enrutamiento en PHP</li>
</ol>
<p>En la medida en que vaya finalizando los capítulos serán publicados en esta web. En este primer artículo se abordarán las tres primeras fases.</p>
<p><span id="more-290"></span></p>
<p><strong>OBSERVACIONES</strong>:</p>
<p>Para la realización del presente documento me he basado en las referencias web de los portales de PgRouting (<a href="http://pgrouting.postlbs.org/">http://pgrouting.postlbs.org/</a>) y OpenLayers (<a href="http://openlayers.org/">http://openlayers.org</a>), así como en diferentes páginas encontradas en Internet. En la medida en que se utilice información de otra fuente será convenientemente citada, salvo olvido.</p>
<p><strong>ÁMBITO DE APLICACIÓN</strong></p>
<p>Todas las fases se han ensayado satisfactoriamente en los siguientes entornos: Ubuntu 9.04 y Centos 5.3. (clon de Red Hat Enterpise Linux). No obstante, las indicaciones y comandos utilizados en este artículo hacen referencia a este último sistema operativo.</p>
<p>Partimos de la base que tenemos un servidor Linux con el sistema operativo Centos 5.3 de 64 bits, con la base de datos PostgreSQL 8.3 ya instaladas. Por defecto, Centos dispone de PostgreSQL 8.1, si bien hemos preferido añadir los repositorios para la 8.3.7. Para información sobre la instalación ver este enlace: <a href="https://projects.commandprompt.com/public/pgcore">https://projects.commandprompt.com/public/pgcore</a></p>
<p><strong><em>NOTA</em></strong><em>: La instalación de PostgreSQL desde este repositorio “oficial” de PostgreSQL no ha sido todo lo gratificante que esperaba. Hay problemas de dependencias con respecto a otras librerías del sistema Centos. Esto en cambio no ocurre con Ubuntu, que por defecto tiene en su paquetería las últimas versión de esta base de datos.</em></p>
<p>APLICACIONES UTILIZADAS</p>
<ul>
<li>PostgreSQL 8.3</li>
<li>PostGIS 1.4</li>
<li>OpenLayers 2.8</li>
<li>PgRouting 1.03</li>
<li>Osm2pgRouting</li>
<li>Osmosis</li>
</ul>
<p><strong>INSTRUCCIONES Y RECOMENDACIONES GENERALES:</strong></p>
<p>Las instrucciones para la instalación o compilación de programas se especifican mediante comandos de la consola Linux, sin tener que recurrir a ninguna herramienta gráfica. Esta forma de actuar puede ser útil para aquellos usuarios que disponen de acceso a un servidor remoto, donde sólo se tiene acceso mediante la consola.</p>
<p>Con el fin de facilitar las tareas conviene crear un directorio donde descargar, descomprimir y compilar las fuentes de los programas. Para ello, crea un directorio de compilación dentro del home del usuario<br />
<code><br />
$ mkdir /home/&lt;usuario&gt;/compilar</code></p>
<p><code> </code></p>
<p><code>$ cd /home/&lt;usuario&gt;/compilar<br />
</code></p>
<p><strong><em>NOTA</em></strong><em>: El símbolo “$” es sólo el indicativo del prompt del sistema, no es necesario escribirlo.</em></p>
<p>Otra opción es crear un directorio de fuentes (por ejemplo “src” dentro de la carpeta “usr/local/”, y trabajar siempre como root. Yo particularmente prefiero la primera opción.</p>
<p><strong>INSTALACIÓN DE POSTGIS</strong></p>
<p>Aunque es posible utilizar la paquetería de PostGIS que aparece en el repositorio (yum info Postgis) he preferido compilar el paquete con el fin de tener la última versión estable de PostGIS</p>
<p>Sí no estás interesado en compilar PostGIS, puedes instalar el paquete oficial que viene con el repositorio de Postgres para Centos. Utiliza estos comandos</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top"># yum info postgis</td>
</tr>
</tbody>
</table>
<p>Y obtendrás esta información:</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">Available PackagesName : postgisArch : x86_64Version : 1.3.6Release : 1.rhel5Size : 1.0 MRepo : pgdg83Summary : Geographic Information Systems Extensions to PostgreSQLURL : <a href="http://postgis.refractions.net/">http://postgis.refractions.net/</a></p>
<p>License : GPL</p>
<p>Description: PostGIS adds support for geographic objects to the PostgreSQL</p>
<p>: object-relational database. In effect, PostGIS “spatially enables”</p>
<p>: the PostgreSQL server, allowing it to be used as a backend spatial</p>
<p>: database for geographic information systems (GIS), much like ESRI’s</p>
<p>: SDE or Oracle’s Spatial extension. PostGIS follows the OpenGIS</p>
<p>: “Simple Features Specification for SQL” and has been certified as</p>
<p>: compliant with the “Types and Functions” profile.</td>
</tr>
</tbody>
</table>
<p>Luego, como superusuario:</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top"># yum install postgis</td>
</tr>
</tbody>
</table>
<p><strong>Compilación de PostGIS 1.4</strong></p>
<p>Por suerte, PostGIS es una extensión bastante utilizada, y por tanto su compilación está muy bien detallada en el portal de Reflaction Research (<a href="http://postgis.refractions.net/">http://postgis.refractions.net/</a>)</p>
<p>Esta extensión requiera para su compilación de dos librerías de las que depende (dependencias), por una parte PROJ y por otra GEOS. Ambas son bien conocidas y utilizadas en multitud de programas de SIG de código abierto. Ambas son opcionales, y no son necesarias para la compilación, pero sin ellas dejariamos a PostGIS huerfano de utilidades de reproyección y geoprocesamiento.</p>
<p><strong>Instalación de la librería PROJ.4</strong></p>
<p>PROJ.4 (<a href="http://trac.osgeo.org/proj/">http://trac.osgeo.org/proj/</a>) es la librería más utilizada para las proyecciones cartográficas y permite, como no podía ser de otra manera realizar conversiones entre diferentes proyecciones cartográficas.</p>
<p><strong><em>NOTA</em></strong><em>: En el blog de Geomaticblog.net hay un excelente post de como utlizar esta librería desde aplicaciones como GDAL/OGR o GvSIG. Particularmente, me ha resultado útil para transformar datos desde ED50 a WGS84. Enlace: <a href="http://geomaticblog.net/2009/01/23/2009-01-23-ogrs_y_rejillas_ntv2/">http://geomaticblog.net/2009/01/23/2009-01-23-ogrs_y_rejillas_ntv2/</a></em></p>
<p>Pasos para la compilación:</p>
<p>1. Descargar la fuente</p>
<p>$ wget <a href="http://download.osgeo.org/proj/proj-4.7.0.tar.gz">http://download.osgeo.org/proj/proj-4.7.0.tar.gz</a></p>
<p>2. Descomprimirla</p>
<p>$ tar xzvf proj-4.7.0.tar.gz</p>
<p>3. Cambio de directorio</p>
<p>$ cd proj-4.7.0</p>
<p>4. Configuración y puesta a punto. El siguiente comando prepara la compilación advirtiéndonos si falta algún que otro paquete</p>
<p>$ ./configure</p>
<p><em>NOTA: Es posible que en este paso aparezcan errores porque falta algún paquete. Tan sólo fíjate en que es lo que falta e instálalo (con “yum” o “apt” en el caso de Ubuntu). Habitualmente serán necesarias algunas herramienta propias de la compilación, como flex, bison, make, etc. En Ubuntu tienes un paquete específico para ello: “sudo apt-get install build-essential”.</em></p>
<p>5. Compilación</p>
<p>$ make</p>
<p>6. Colocar las librerias en su sitio como “root”</p>
<p>$ make install</p>
<p>Para “impersonarse” como superusuario sólo tienes que escribir: “<strong>su -</strong>”. En Ubuntu sólo tienes que anteceder la palabra “<strong>sudo</strong>” a cualquier comando que quieras ejecutar como root. En ambos casos te pedirá la clave de acceso del administrador del sistema.</p>
<p>7. Comprobación</p>
<p>$ proj</p>
<p>Obtendremos lo siguiente:</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">Rel. 4.7.1, 23 September 2009usage: proj [ -beEfiIlormsStTvVwW [args] ] [ +opts[=arg] ] [ files ]</td>
</tr>
</tbody>
</table>
<p><strong>Instalación de GEOS (Geometry Engine Open Source)</strong></p>
<p>GEOS (<a href="http://trac.osgeo.org/geos/">http://trac.osgeo.org/geos/</a>) es una librería que proporciona a PostGIS la capacidad de realizar geoprocesamiento. Sin ella no podriamos realizar las típicas operaciones de unión, intersección, diferencia, buffer, etc, todas ellas correspondiente a la especificación SFA (Simple Feature Access) del OGC. GEOS es un porting de la librería JTS (Java Topology Suite) en el lenguaje C++, lo que le convierte en una máquina de geoprocesos bastante eficiente.</p>
<p>Pasos para la compilación</p>
<p>1. Descarga de la fuente</p>
<p>$ wget <a href="http://download.osgeo.org/geos/geos-3.1.1.tar.bz2">http://download.osgeo.org/geos/geos-3.1.1.tar.bz2</a></p>
<p>2.Descomprimir:</p>
<p>$ tar -xjvf geos-3.1.1.tar.bz2</p>
<p>3.Cambio de directorio</p>
<p>$ cd geos-3.1.1</p>
<p>4.Configuración</p>
<p>$ LDFLAGS=-lstdc++ ./configure</p>
<p>La directiva LDFLAGS tiene como objetivo enlazar expliciatamente PostgreSQL con el estándar C++</p>
<p>5. Compilar</p>
<p>$ make</p>
<p>El tiempo de compilación suele ser bastante largo (en torno a los 7-10 minutos)</p>
<p>6. Colocar librerías en su sitio (Como usuario root):</p>
<p>$ make install</p>
<p>7. Comprobación</p>
<p>$ geos-config –version</p>
<p>Devolverá:</p>
<p>3.1.1</p>
<p><strong>Instalar PostGIS</strong></p>
<p>Sí las dos librerías anteriores han sido instaladas correctamente deberás de tener los siguientes archivos en el directorio “/usr/local/lib”. Compruébalo:</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">$ ls /usr/local/lib/libproj*/usr/local/lib/libproj.a /usr/local/lib/libproj.so.0/usr/local/lib/<a href="http://libproj.la/">libproj.la</a> /usr/local/lib/libproj.so.0.5.5/usr/local/lib/libproj.so /usr/local/lib/libproj.so.0.6.6$ ls /usr/local/lib/libgeos*/usr/local/lib/<a href="http://libgeos-3.1.0.so/">libgeos-3.1.0.so</a> /usr/local/lib/<a href="http://libgeos_c.la/">libgeos_c.la</a>/usr/local/lib/<a href="http://libgeos-3.1.1.so/">libgeos-3.1.1.so</a> /usr/local/lib/libgeos_c.so/usr/local/lib/libgeos.a /usr/local/lib/libgeos_c.so.1/usr/local/lib/<a href="http://libgeos.la/">libgeos.la</a> /usr/local/lib/libgeos_c.so.1.5.0</p>
<p>/usr/local/lib/libgeos.so /usr/local/lib/libgeos_c.so.1.6.0</td>
</tr>
</tbody>
</table>
<p><strong>Proceso de instalación:</strong></p>
<p>1. Descarga de las fuentes:</p>
<p>$ wget <a href="http://postgis.refractions.net/download/postgis-1.4.0.tar.gz">http://postgis.refractions.net/download/postgis-1.4.0.tar.gz</a></p>
<p>2. Descomprimir:</p>
<p>$ tar xzvf postgis-1.4.0</p>
<p>3. Cambiar de directorio</p>
<p>$ cd postgis-1.4.0</p>
<p>4. Configurar</p>
<p>Es el paso que más atención debemos de prestar, puesto que al final del comando emitirá un informe con las dependencias satisfechas</p>
<p>$ ./configure</p>
<p>Sí todo va bien obtendremos un mensaje similar a este:</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">PostGIS is now configured for x86_64-unknown-linux-gnu&#8212;&#8212;&#8212;&#8212;&#8211; Compiler Info &#8212;&#8212;&#8212;&#8212;-C compiler: gcc -g -O2C++ compiler: g++ -g -O2&#8212;&#8212;&#8212;&#8212;&#8211; Dependencies &#8212;&#8212;&#8212;&#8212;&#8211;GEOS config: /usr/local/bin/geos-configGEOS version: 3.1PostgreSQL config: /usr/bin/pg_configPostgreSQL version: 8.3</p>
<p>PROJ4 version: 47</p>
<p>PostGIS debug level: 0</p>
<p>&#8212;&#8212;&#8211; Documentation Generation &#8212;&#8212;&#8211;</p>
<p>xsltproc: /usr/bin/xsltproc</p>
<p>xsl style sheets:</p>
<p>dblatex:</p>
<p>convert:</td>
</tr>
</tbody>
</table>
<p>5.Compilación y colocación de las librerías</p>
<p>$ make</p>
<p>$ make install (como root)</p>
<p><strong>Post-instalación de PostGIS</strong></p>
<p>El siguiente paso es de vital importancia y nos puede ahorrar muchos problemas en el futuro. Por defecto las librerías se instalan en el directorio ya citado (”/usr/local/lib”), sin embargo, éste directorio no es reconocido por los programas de compilación como un directorio válido para instalar librerías, por lo que es necesario indicárselo de forma expresa. Para ello como root:</p>
<p>1. Edita el fichero “/etc/ld.so.conf” con tu editor preferido (vi, pico, nano, etc):</p>
<p>$ nano /etc/ld.so.conf</p>
<p>Y añade las siguientes líneas al final del documento</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">/usr/local/lib/usr/local/lib64</td>
</tr>
</tbody>
</table>
<p>2. Finalmente ejecuta este comando</p>
<p>$ /sbin/ldconfig</p>
<p><strong>INSTALACIÓN DE LA LIBRERÍA PgRouting</strong></p>
<p>PgRouting (<a href="http://pgrouting.postlbs.org/">http://pgrouting.postlbs.org</a>), al igual que PostGIS, es una extensión de PostgreSQL que permite trabajar con redes</p>
<p>A diferencia de PostGIS, aun no tenemos un paquete que instale PgRouting que satisfaga todas sus dependencias, por ello es necesario optar por la compilación de todas y cada una de las librerías utilizadas.</p>
<p>Librerías de las que depende:</p>
<ul>
<li>BOOST (<a href="http://www.boost.org/">http://www.boost.org</a> ): Es la librería principal, y la única que es obligatoria. Se trata de un basto conjunto de utilidades, entre las que destacan las referidas al análisis y explotación de grafos.</li>
<li>GAUL (Genetic Algorithm Utility Library): Esta librería sólo es necesario si deseamos dotar a PostgreSQL de la utilidad de cálculo del llamado “Problema del viajante”, conocida por sus signas en inglés, TSP (Traveling Salesman Problem), es decir, proporcionar la ruta óptima pasando por varios sitios. Enlace: <a href="http://gaul.sourceforge.net/">http://gaul.sourceforge.net/</a></li>
<li>CGAL (Computacional Geometric Algorithm Library): Librería especializada en el cálculo y procesamiento de estructuras geométricas en 2D y 3D. En el caso de PgRouting proporciona el algoritmo de cálculo conocido como Driving Distance.</li>
</ul>
<p><em>NOTA: Adicionalmente compilaremos el compilador CMAKE</em></p>
<p><strong>BOOST</strong></p>
<p>Como ya se ha comentado se trata de una librería muy ambiciosa por la gran cantidad de utilidades que engloba, de ahí que se compilación sea muy pesada (en tiempo de compilación). En los repositorios, Centos dispone actualmente de la versión 1.33.1, bastante antigua, si pensamos que la última estable es la 1.4.0. No obstante, para este artículo utilizaré la 1.39.0 que es la que tenemos más ensayada.</p>
<p>Con todo, de esta líbrería sólo necesitamos las utilidades de grafos (graphs)</p>
<p>Para ver la versión de las fuentes boost del repositorio:</p>
<p>$ yum info boost-devel</p>
<p>Sí no queremos complicarnos la vida con la compilación añade el paquete con “yum”:</p>
<p>$ yum install boost-devel</p>
<p>En Ubuntu 9.04, la versión disponible es la 1.34, sería:</p>
<p>$ sudo apt-get install libboost-graph-dev</p>
<p><strong>Compilación de las fuentes de boost 1.39</strong></p>
<p>1. Descarga las fuentes:</p>
<p>wget <a href="http://sourceforge.net/projects/boost/files/boost/1.39.0/boost_1_39_0.tar.bz2/download">http://sourceforge.net/projects/boost/files/boost/1.39.0/boost_1_39_0.tar.bz2/download</a></p>
<p>2. Descomprimir</p>
<p>$ tar –xjvf boost_1_39_0.tar.bz2</p>
<p>3. Cambiamos al usuario administrador</p>
<p>$ su -</p>
<p>4. Instalar dependencias:</p>
<p>$ yum install expat-devel</p>
<p>$ yum install python-devel</p>
<p>$ yum install freetype-devel</p>
<p>5. Cambio de directorio</p>
<p>$ cd /home/&lt;usuario&gt;/compilar/boost_1_39_0</p>
<p>6. Configuración</p>
<p>Para obtener ayuda sobre la configuración</p>
<p>$ ./bootstrap.sh –help</p>
<p>Configuración genérica: Instala todas las librerías en este directorio</p>
<p>$ ./bootstrap.sh –prefix=/usr/local/</p>
<p>Sí sólo necesitamos compilar la librería de cabezera (header library)</p>
<p>$ ./bootstrap.sh –prefix=/usr/local/ &#8211;with-libraries=graph</p>
<p>5. Compilar</p>
<p>$ ./bjam install</p>
<p>Es posible que tengas al inicio de la compilación algún mensaje relacionado con la librería EXPAT ques es utilizada por libGraph.  Para evitar este mensaje define estas variables:</p>
<p>$ export EXPAT_INCLUDE=/usr/include</p>
<p>$ export EXPAT_LIBPATH=/usr/lib</p>
<p>Y de nuevo ejecuta</p>
<p><span style="background-color: #ffffff">$ ./bjam install</span></p>
<p><em>NOTA</em><em>: Sí deseas compilar versiones anteriores, como la 1.36 y 1.37, la compilación es más sencilla. Usa los clásicos: ./configure, make, make install</em></p>
<p><strong>GAUL</strong></p>
<p>Instalación de Gaul (librería necesaria para el algoritmo TSP -Traveling Salesman Problem)</p>
<p>$ wget <a href="http://nfsi.dl.sourceforge.net/sourceforge/gaul/gaul-devel-0.1849-0.tar.gz">http://nfsi.dl.sourceforge.net/sourceforge/gaul/gaul-devel-0.1849-0.tar.gz</a></p>
<p>$ tar xzvf gaul-devel-0.1849-0.tar.gz</p>
<p>$ ./configure &#8211;disable-slang</p>
<p><strong>Instalar CMAKE</strong></p>
<p>Este compilador sólo se instalará para el usuario, y no estará disponible para todos (no hacer nunca “make install” como root)</p>
<p>$ wget <a href="http://www.cmake.org/files/v2.4/cmake-2.4.8.tar.gz">http://www.cmake.org/files/v2.4/cmake-2.4.8.tar.gz</a></p>
<p>$ tar -zxvf cmake-2.4.8.tar.gz</p>
<p>$ cd cmake-2.4.8</p>
<p>$ ./configure</p>
<p>Como usuario (¡no como administrador¡)</p>
<p>$ gmake</p>
<p><strong>CGAL</strong></p>
<p>Librería necesaria para utilizar el algoritmo Driving Distance). La versión que vamos a compilar es la 3.3.1, aunque existe otra más moderna (3.4). Sin lugar a dudas es la libraría que más problemas ofrece.</p>
<p>$ wget <a href="ftp://ftp.mpi-sb.mpg.de/pub/outgoing/CGAL/CGAL-3.3.1.tar.gz">ftp://ftp.mpi-sb.mpg.de/pub/outgoing/CGAL/CGAL-3.3.1.tar.gz</a></p>
<p>$ tar xzvf CGAL-3.3.1.tar.gz</p>
<p>$ cd CGAL-3.3.1</p>
<p>Compilar como root:</p>
<p>$ ./install_cgal &#8211;prefix=/usr/local &#8211;with-boost=n &#8211;without-autofind -ni /usr/bin/g++</p>
<p>Las librerías y los include se colocan en estos directorios:</p>
<p>/usr/local/lib/</p>
<p>/usr/local/include/</p>
<p><strong>INSTALACIÓN DE LA LIBRERÍA PGROUTING</strong></p>
<p>Una vez satisfechas todas las dependencias de librerías vamos a compilar PgRouting<span> </span></p>
<p>1. Primero nos descargamos las fuentes desde el repositorio subversión. Recuerda que debes de tener instalada dicha utilidad (“yum install subversión”)</p>
<p>$ svn checkout <a href="http://pgrouting.postlbs.org/svn/pgrouting/trunk">http://pgrouting.postlbs.org/svn/pgrouting/trunk</a> pgrouting</p>
<p>2. Nos cambiamos al directorio</p>
<p>$ cd pgrouting</p>
<p>3. Configuramos</p>
<p>Para ello utilizamos el compilador CMAKE que ya tenemos disponibles.</p>
<p>Compilar (no olvides el punto al final del comando)</p>
<p>La sintaxis de compilación es:</p>
<p>&lt;ruta al ejecutable CMAKE) .</p>
<p>NOTA: No olvides el punto al final. Es para indicarle que compile el directorio actual</p>
<p>Con ello instalaremos la librería, pero apenas nos servirá de nada porque no hemos hecho referencia a que queremos los algoritmos “Driving Distance (DD)” y TSP.</p>
<p>Por ello cambia lo anterior por esto</p>
<p># ../cmake-2.4.8/bin/cmake -DWITH_TSP=ON -DWITH_DD=ON .</p>
<p>Resultado del configure</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">Output directory for libraries is set to /usr/lib64/pgsqlInstallation directory for libraries is set to /usr/lib64/pgsql and for SQL files is set to /usr/share/postlbsInstallation directory for libraries is set to /usr/lib64/pgsql&#8211; Configuring done&#8211; Generating done&#8211; Build files have been written to: /home/jose/compilar/pgrouting../cmake-2.4.8/bin/cmake -DWITH_TSP=ON -DWITH_DD=ON .</p>
<p>&#8211; Check for working C compiler: /usr/bin/gcc</p>
<p>&#8211; Check for working C compiler: /usr/bin/gcc &#8212; works</p>
<p>&#8211; Check size of void*</p>
<p>&#8211; Check size of void* &#8211; done</p>
<p>&#8211; Check for working CXX compiler: /usr/bin/c++</p>
<p>&#8211; Check for working CXX compiler: /usr/bin/c++ &#8212; works</p>
<p>&#8211; Found PostgreSQL: /usr/include/pgsql/server, /usr/lib64/libpq.so</p>
<p>Boost headers were found here: /usr/local/include</p>
<p>Output directory for libraries is set to /usr/lib64/pgsql</p>
<p>&#8211; Found PGROUTING_CORE core: /home/jose/compilar/pgrouting/core/src</p>
<p>Installation directory for libraries is set to /usr/lib64/pgsql and for SQL files is set to /usr/share/postlbs</p>
<p>&#8211; Found GAUL: /usr/local/lib/libgaul.so, /usr/local/lib/libgaul_util.so</p>
<p>Installation directory for libraries is set to /usr/lib64/pgsql</p>
<p>&#8211; Found CGAL: /usr/local/include, /usr/local/lib/libCGAL.so</p>
<p>&#8211; Configuring done</p>
<p>&#8211; Generating done</p>
<p>&#8211; Build files have been written to: /home/jose/compilar/pgrouting</td>
</tr>
</tbody>
</table>
<p>4. Finalmente generamos la librería</p>
<p># make</p>
<p>Resultado de la compilación</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">Scanning dependencies of target routing_tsp[ 8%] Building C object extra/tsp/src/CMakeFiles/routing_tsp.dir/tsp.o[ 16%] Building CXX object extra/tsp/src/CMakeFiles/routing_tsp.dir/tsp_solver.oLinking CXX shared library ../../../lib/librouting_tsp.so[ 16%] Built target routing_tspScanning dependencies of target routing_dd[ 25%] Building C object extra/driving_distance/src/CMakeFiles/routing_dd.dir/alpha.o</p>
<p>[ 33%] Building CXX object extra/driving_distance/src/CMakeFiles/routing_dd.dir/alpha_drivedist.o</p>
<p>[ 41%] Building CXX object extra/driving_distance/src/CMakeFiles/routing_dd.dir/boost_drivedist.o</p>
<p>[ 50%] Building C object extra/driving_distance/src/CMakeFiles/routing_dd.dir/drivedist.o</p>
<p>Linking CXX shared library ../../../lib/librouting_dd.so</p>
<p>[ 50%] Built target routing_dd</p>
<p>Scanning dependencies of target routing</p>
<p>[ 58%] Building C object core/src/CMakeFiles/routing.dir/dijkstra.o</p>
<p>[ 66%] Building C object core/src/CMakeFiles/routing.dir/astar.o</p>
<p>[ 75%] Building C object core/src/CMakeFiles/routing.dir/shooting_star.o</p>
<p>[ 83%] Building CXX object core/src/CMakeFiles/routing.dir/boost_wrapper.o</p>
<p>[ 91%] Building CXX object core/src/CMakeFiles/routing.dir/astar_boost_wrapper.o</p>
<p>[100%] Building CXX object core/src/CMakeFiles/routing.dir/shooting_star_boost_wrapper.o</p>
<p>Linking CXX shared library ../../lib/librouting.so</p>
<p>[100%] Built target routing</td>
</tr>
</tbody>
</table>
<p>5. Por último colocamos los archivos en su sitio (como root):</p>
<p>$ make install</p>
<p>Resultados</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">[ 16%] Built target routing_tsp[ 50%] Built target routing_dd[100%] Built target routingLinking CXX shared library CMakeFiles/CMakeRelink.dir/librouting_tsp.soLinking CXX shared library CMakeFiles/CMakeRelink.dir/librouting_dd.soInstall the project&#8230;&#8211; Install configuration: &#8220;&#8221;</p>
<p>&#8211; Install configuration: &#8220;&#8221;</p>
<p>&#8211; Install configuration: &#8220;&#8221;</p>
<p>&#8211; Install configuration: &#8220;&#8221;</p>
<p>&#8211; Installing /usr/lib64/pgsql/librouting.so</p>
<p>&#8211; Install configuration: &#8220;&#8221;</p>
<p>&#8211; Installing /usr/share/postlbs/routing_core.sql</p>
<p>&#8211; Installing /usr/share/postlbs/routing_core_wrappers.sql</p>
<p>&#8211; Installing /usr/share/postlbs/routing_topology.sql</p>
<p>&#8211; Installing /usr/share/postlbs/matching.sql SELECT assign_vertex_id(&#8216;ways&#8217;, 0.00001, &#8216;the_geom&#8217;, &#8216;gid&#8217;);</p>
<p>&#8211; Install configuration: &#8220;&#8221;</p>
<p>&#8211; Install configuration: &#8220;&#8221;</p>
<p>&#8211; Installing /usr/lib64/pgsql/librouting_tsp.so</p>
<p>&#8211; Install configuration: &#8220;&#8221;</p>
<p>&#8211; Installing /usr/share/postlbs/routing_tsp.sql</p>
<p>&#8211; Installing /usr/share/postlbs/routing_tsp_wrappers.sql</p>
<p>&#8211; Install configuration: &#8220;&#8221;</p>
<p>&#8211; Install configuration: &#8220;&#8221;</p>
<p>&#8211; Installing /usr/lib64/pgsql/librouting_dd.so</p>
<p>&#8211; Install configuration: &#8220;&#8221;</p>
<p>&#8211; Installing /usr/share/postlbs/routing_dd.sql</p>
<p>&#8211; Installing /usr/share/postlbs/routing_dd_wrappers.sql</td>
</tr>
</tbody>
</table>
<p>Ya tenemos PgRouting disponible</p>
<p><strong>OSM2PGROUTING</strong></p>
<p>Descripción:</p>
<p>Se trata de una utilidad que permite convertir un fichero de OpenStreetMap, en formato OSM o XML, en una base de datos PostgreSQL que tenga instaladas las extensiones PostGIS y PgRouting.</p>
<p>Esta utilidad está específicamente diseñada para OSM, y realiza un especial tratamiento de los datos teniendo en cuenta los tags OSM, de forma tal que es capaz de generar un grafo adecuado para el ruteo. Bien es cierto que tenemos otras posibilidades, como descargar un shapefile de datos OSM de cualquiera de los servidores existentes (Geofabrik, CloudMade) y convertirlos a PostgreSQL con la utilidad “shp2pgsql” provista por PostGIS, sin embargo el resultado no será el mismo. Veamos un ejemplo</p>
<p>Fíjate en esta zona donde tenemos vías que aparecen a distinto nivel: <a href="http://www.openstreetmap.org/?lat=38.379259&amp;lon=-0.452301&amp;zoom=18&amp;layers=B000FTF">http://www.openstreetmap.org/?lat=38.379259&amp;lon=-0.452301&amp;zoom=18&amp;layers=B000FTF</a></p>
<p><img class="alignnone size-full wp-image-288" src="http://www.gisandchips.org/wp-content/osm_cruce.png" alt="osm_cruce" width="470" height="509" /></p>
<p>Sí no utilizamos esta utilidad se generará un nodo por cada cruce de vías, independientemente de que si están o no a diferentes niveles. Osm2pgrouting entiende los tags de OSM y evita colocar nodos donde no le corresponde.</p>
<p>Nodos en OpenStreetMap. Las vías es azul contienen el tag bridge=yes, y por tanto están a diferente nivel</p>
<p><strong><img class="alignnone size-full wp-image-289" src="http://www.gisandchips.org/wp-content/osm_cruce_josm.png" alt="osm_cruce_josm" width="508" height="466" /></strong></p>
<p><strong>Compilar osm2pgrouting</strong></p>
<p>$ svn checkout <a href="http://pgrouting.postlbs.org/svn/pgrouting/tools/osm2pgrouting/trunk">http://pgrouting.postlbs.org/svn/pgrouting/tools/osm2pgrouting/trunk</a> osm2pgrouting</p>
<p>$ cd osm2pgrouting</p>
<p>$ make</p>
<p>Como administrador:</p>
<p>$ make install</p>
<p><strong>CREAR UNA PLANTILLA DE GEODATABASE CON POSTGIS Y PGROUTING</strong></p>
<p>Este es quizás el momento más excitante de todos, puesto que vamos a crear una base de datos con datos ya normalizados para ser explotado con las utilidades de PgRouting.</p>
<p>El proceso siempre es el mismo:</p>
<ol>
<li>Crear la base de datos</li>
<li>Añadirle la funcionalidad de PostGIS</li>
<li>Añadirle la funcionalidad de PgRouting</li>
<li>Cargar datos desde OSM a la base de datos (osm2pgsql)</li>
<li>Crear topología</li>
<li>Explotar la base de datos</li>
</ol>
<p><strong>Creación de una plantilla de base de datos</strong></p>
<p>Es una buena práctica tener creada una plantilla de base de datos con todas las extensiones ya incluidas para que podamos utilizarla en cualquier geodatabase que creemos.</p>
<p>Puesto que estamos intentando cargar datos desde OSM es conveniente utilizar una codificación de caracteres que albergue todas las posibilidades multilenguaje que se pueden dar (alfabetización árabe, cirílica, etc)</p>
<ol>
<li>Sín más preámbulos creamos la base de datos</li>
</ol>
<p>Sintaxis:</p>
<p>$ createdb –h &lt;host&gt; –U &lt;user&gt; -p &lt;puerto&gt; -E &lt;código codificación &lt;nombre base de datos&gt;</p>
<p>En nuestro caso este es el resultado</p>
<p>$ createdb -U postgres -E UTF8 routing</p>
<ol>
<li>PostGIS exige que la base de datos tenga cargado el lenguaje procedural Pl/PgSql. De hecho la gran mayoría de las funciones de PostGIS y PgRouting están en este lenguaje:</li>
</ol>
<p>$ createlang -U postgres plpgsql &lt;nombre base de datos&gt;</p>
<p>$ createlang -U postgres plpgsql routing</p>
<ol>
<li>Añadir la funcionalidad PostGIS</li>
</ol>
<p>Ahora necesitamos saber donde ha instalado Postgis los ficheros SQL necesarios para crear la geodatabase. Recomendamos utilizar el comando find para encontrarlos</p>
<p>Para las versiones &lt; 1.4</p>
<p>$ find /usr/ -name lwpostgis.sql</p>
<p>Para versiones &gt;= 1.4</p>
<p>$ find /usr/ -name postgis.sql</p>
<p>/usr/share/lwpostgis.sql</p>
<p>Lo mismo para las referencias espacials</p>
<p>$ find /usr/ -name spatial_ref_sys.sql</p>
<p>/usr/share/spatial_ref_sys.sql</p>
<p>Ahora sólo queda cargar el fichero de funcionalidades en la base de datos:</p>
<p>Para la versión 1.3.x</p>
<p>$ psql -U postgres -f /usr/share/lwpostgis.sql routing</p>
<p>$ psql -U postgres -f /usr/share/spatial_ref_sys.sql routing</p>
<p>Para la version 1.4</p>
<p>$ psql -U postgres -f /usr/share/pgsql/contrib/postgis.sql routing</p>
<p>$ psql -U postgres -f /usr/share/pgsql/contrib/spatial_ref_sys.sql routing</p>
<ol>
<li>Añadir la funcionalidad PgRouting</li>
</ol>
<p>Más de lo mismo. Primero añadimos la funcionalidad básica:</p>
<p>$ psql -U postgres -f /usr/share/postlbs/routing_core.sql routing</p>
<p>$ psql -U postgres -f /usr/share/postlbs/routing_core_wrappers.sql routing</p>
<p>$ psql -U postgres -f /usr/share/postlbs/routing_topology.sql routing</p>
<ol>
<li>Luego la funcionalidad para el algoritmo TSP (opcional)</li>
</ol>
<p>$ psql -U postgres -f /usr/share/postlbs/routing_tsp.sql routing</p>
<p>$ psql -U postgres -f /usr/share/postlbs/routing_tsp_wrappers.sql routing</p>
<ol>
<li>Por ultimo la funcionalidad para el algoritmo “Driving Distance”</li>
</ol>
<p>$ psql -U postgres -f /usr/share/postlbs/routing_dd.sql routing</p>
<p>$ psql -U postgres -f /usr/share/postlbs/routing_dd_wrappers.sql routing</p>
<ol>
<li>Vamos a comprobar que todo está bien.</li>
</ol>
<p>Accedemos a la base de datos creada:</p>
<p>$ psql -U postgres routing</p>
<p>routing=# <strong>select postgis_full_version();</strong></p>
<p>Devolverá:</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">postgis_full_version&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-POSTGIS=&#8221;1.4.0&#8243; GEOS=&#8221;3.1.1-CAPI-1.6.0&#8243; PROJ=&#8221;Rel. 4.7.1, 23 September 2009&#8243; USE_STATS(1 row)</td>
</tr>
</tbody>
</table>
<ol>
<li>Últimos retoques:</li>
</ol>
<p>Con el objetivo de utilizar en el futuro OpenLayers, y con el fin de arbitrar un sistema de proyección que sea común para el uso de varios proveedores de datos (Google Maps, Yahoo Maps, Microsoft Live Maps, etc), vamos a incluir un nuevo registro en la tabla spatial_ref_sys, para añadir la denominada proyección esférica de Mercator, a veces conocida como “proyección Google”, por ser ésta la que se utiliza en en Google Maps. Lo interesante de esta proyección, a diferencia del sistema geodésico mundial con datum WGS84 es que las unidades están en metros, lo que facilita la compresión de los cálculos de distancias, en contraposición a la medición en grados decimales de arco de circunferencia. Más información aquí:</p>
<p><a href="http://trac.openlayers.org/wiki/SphericalMercator">http://trac.openlayers.org/wiki/SphericalMercator</a></p>
<p>En este enlace tienes una imagen de gran formato de la tierra con esta proyección: <a href="http://designintelligences.files.wordpress.com/2009/03/mercator-projection1.jpg">http://designintelligences.files.wordpress.com/2009/03/mercator-projection1.jpg</a></p>
<p>En definitiva, sólo tenemos que añadir este registro:</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">routing=#  INSERT into spatial_ref_sys (srid, auth_name, auth_srid, srtext, proj4text) values (900913 ,&#8217;EPSG&#8217;,900913,&#8217;GEOGCS["WGS 84", DATUM["World Geodetic System1984", SPHEROID["WGS 84", 6378137.0, 298.257223563,AUTHORITY["EPSG","7030"]], AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], NIT["degree",0.017453292519943295], AXIS["Longitude", EAST], AXIS["Latitude", NORTH],AUTHORITY["EPSG","4326"]], PROJECTION["Mercator_1SP"],PARAMETER["semi_minor", 6378137.0],PARAMETER["latitude_of_origin",0.0], PARAMETER["central_meridian", 0.0], PARAMETER["scale_factor",1.0], PARAMETER["false_easting", 0.0], PARAMETER["false_northing", 0.0],UNIT["m", 1.0], AXIS["x", EAST], AXIS["y", NORTH],AUTHORITY["EPSG","900913"]] |&#8217;,'+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs&#8217;);</td>
</tr>
</tbody>
</table>
<p>La proyección esférica de Mercator tiene el código 900913, según el European Petroleum Survey Group (EPSG) mientras que OpenStreetMap utiliza como proyección global  predeterminada el geodésico mundial con datum WGS84, cuyo código es 4326.</p>
<p>Ya nos podemos salir de la consola de Postgresql</p>
<p>routing=# \q</p>
<ol>
<li>Crear una geodatabase a partir de la plantilla creada</li>
</ol>
<p>Bueno ya está hecho todo el trabajo de la plantilla, ahora el proceso de crear una nueva base de datos con todo ya preparado es sólo una línea;</p>
<p>createdb -U &lt;usuario&gt; -T &lt;plantilla&gt; &lt;nueva bd&gt;</p>
<p>En nuestro ejemplo sería</p>
<p>createdb -U postgres -T routing osmtest</p>
<p><strong>Truco del día:</strong></p>
<p>Sí tu servidor no tienes un sistema gráfico funcionando y necesitas llevar un fichero desde tu ordenador al servidor utiliza el comando “scp”. Es muy práctico</p>
<p>Sintaxis:</p>
<p>$ scp &lt;fichero de tu ordenador&gt;@&lt;hostname o IP&gt;:&lt;directorio del servidor donde copiar el fichero&gt;</p>
<p>Ejemplo:</p>
<p>$ scp OSM/alicante_30_09_2009.osm jose@www.gisandchips.org:/home/jose/compilar/osm2pgrouting</p>
<p><strong>DESCARGAR DATOS DE OPENSTREETMAP</strong></p>
<p>Esto ya va tomando color. Queda lo más bonito. ¡Paciencia!</p>
<p>Tenemos 2 opciones:</p>
<p>a)     Descargar un fichero OSM ya creado en cualquiera de los portales web que hay a tal efecto (Geofabrik (<a href="http://download.geofabrik.de/osm/">http://download.geofabrik.de/osm/</a>) , CloudMade (<a href="http://downloads.cloudmade.com/">http://downloads.cloudmade.com/</a>) , etc)</p>
<p>Ejemplo:</p>
<p>Descargar OSM de España (69 Mb)<span> </span></p>
<p>$ wget <a href="http://download.geofabrik.de/osm/europe/spain.osm.bz2">http://download.geofabrik.de/osm/europe/spain.osm.bz2</a></p>
<p>Descomprimir</p>
<p>$ tar -xjvf spain.osm.bz2</p>
<p>a)     Recortar una zona del planet.osm con Osmosis (<a href="http://wiki.openstreetmap.org/wiki/Osmosis">http://wiki.openstreetmap.org/wiki/Osmosis</a>)</p>
<p>$ wget <a href="http://dev.openstreetmap.org/~bretth/osmosis-build/osmosis-latest-bin.tar.gz">http://dev.openstreetmap.org/~bretth/osmosis-build/osmosis-latest-bin.tar.gz</a></p>
<p>$ tar xzvf osmosis-latest-bin.tar.gz</p>
<p>$ cd osmosis-0.31/ bin/</p>
<p>La sintaxis básica es la siguiente:</p>
<p>$./osmosis &#8211;read-xml &lt;fichero osm matriz&gt; &#8211;bb left=&lt;coord oeste&gt; right&lt;coord este&gt; top=&lt;coord norte&gt; bottom=&lt;coord sur&gt; &#8211;write-xml &lt;nombre fichero a crear extraido de matriz&gt;</p>
<p>Por supuesto las coordenadas deben de estar en grados decimales del WGS84. Pero, ¿de dónde las saco?</p>
<p>Esta vez viene en nuestra ayuda el portal <a href="http://www.openstreetmap.org/">www.openstreetmap.org</a> que viene con una utilidad (pestaña “Exportar”) para proporcionarnos información sobre la caja que estamos visualizando  en el mapa</p>
<p><img class="alignnone size-full wp-image-287" src="http://www.gisandchips.org/wp-content/osm_box.png" alt="osm_box" width="483" height="343" /></p>
<p>Ejemplo práctico a un barrio de Alicante</p>
<p>$./osmosis &#8211;read-xml /home/jose/OSM/alicante_30_09_2009.osm &#8211;bb left=-0.4359 right=-0.42006 top=38.37225 bottom=38.36192 &#8211;write-xml mibarrio.osm</p>
<p><strong>IMPORTAR UN OSM EN LA GEODATABASE</strong></p>
<p>Después de este tortuoso camino, ya queda lo más fácil importar el OSM, utilizando “osm2pgrouting”. La sintaxis es la siguiente:</p>
<p>$ ./osm2pgrouting -file &lt;fichero osm&gt; -conf &lt;fichero configuración&gt;  -dbname &lt;base de datos&gt; -user &lt;usuario&gt;</p>
<p>Ló único extraño aquí es el fichero de configuración, que es un XML donde se incluyen las tipologías de la vías  que queremos cargar a la base de datos (según OSM: motorway, trunk, primary, secondary, tertiary, residential, etc.). En las fuentes aparece un XML de ejemplo que nos puede servir. Debemos de tener en cuenta que en función de nuestros objetivos utilizaremos unas vías u otras. Por ejemplo, sí sólo queremos las grandes vías nos quedaremos con motorway y trunks. Sí sólo nos interesa el tema caminos rurales y rutas ciclistas nos toca trabajar el XML.</p>
<p>Ejemplo:</p>
<p>./osm2pgrouting -file ./alicante_30_09_2009.osm -conf ./mapconfig.xml -dbname osmtest -user postgres</p>
<p>NOTA: Este proceso suele ser bastante largo, y estará en función del tamaño del OSM. Como ejemplo, el área metropolitana de Alicante tardó unos 5 minutos, en un ordenador bien dotado. Toda España nos llevaría unas cuantas horas.</p>
<p>Tras un periodo de tiempo verás este mensaje</p>
<table border="1" cellspacing="0" cellpadding="0" width="100%">
<col span="1" width="256"></col>
<tbody>
<tr>
<td width="100%" valign="top">Ways table created<br />
<span style="background-color: #ffffff">Types table created<br />
Classes table created<br />
http://wiki.openstreetmap.org/wiki/Osmosis</span>create topology</p>
<p>#########################</p>
<p>size of streets: 4742</p>
<p>size of splitted ways : 12780</p>
<p>finished</td>
</tr>
</tbody>
</table>
<p>Para comprobar que tablas se han creado:</p>
<p>Entramos en la base de datos</p>
<p>psql -U postgres osmtest</p>
<p>Listamos las tablas:</p>
<p>osmtest=# \d</p>
<table border="1" cellspacing="0" cellpadding="0" width="1153">
<tbody>
<tr>
<td width="576" valign="top">List of relationsSchema | Name | Type | Owner</p>
<p>&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;-+&#8212;&#8212;&#8212;-</p>
<p>public | classes | table | postgres</p>
<p>public | geometry_columns | table | postgres</p>
<p>public | nodes | table | postgres</p>
<p>public | spatial_ref_sys | table | postgres</p>
<p>public | types | table | postgres</p>
<p>public | ways | table | postgres</p>
<p>(6 rows)</td>
<td width="576" valign="top"></td>
</tr>
</tbody>
</table>
<p>La tabla que contiene la geometría es ways;</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">osmtest=# select * from geometry_columns ;f_table_catalog | f_table_schema | f_table_name | f_geometry_column | coord_dimension | srid | type</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;+&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p>| public | ways | the_geom | 2 | 4326 | MULTILINESTRING</p>
<p>(1 row)</p>
<p>osmtest=# select srid, f_geometry_column as geometria from geometry_columns;</p>
<p>srid | geometria</p>
<p>&#8212;&#8212;+&#8212;&#8212;&#8212;&#8211;</p>
<p>4326 | the_geom</p>
<p>(1 row)</td>
</tr>
</tbody>
</table>
<h2>CREACIÓN DE TOPOLOGÍA DE RED</h2>
<p>Si nos fijamos la tabla ways tiene vacíos los campos <em>source</em> y <em>target</em>, que son los identificadores de nodo. Es necesario crear topología. Para ello recurrimos a una función topológica de pgrouting: <em>“assign_vertex_id”</em></p>
<p>Lo único que tenemos es un identificador de arco (gid)</p>
<p>Para el caso de datos procedentes de OSM, al estar los datos en grados decimales y la proyección en geodésica (4326) la tolerancia a aplicar es muy pequeña. 0.00001</p>
<p>SELECT assign_vertex_id(&#8216;ways&#8217;, 0.00001, &#8216;the_geom&#8217;, &#8216;gid&#8217;);</p>
<p>Listado de tablas</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">osmtest=# \dList of relationsSchema | Name | Type | Owner</p>
<p>&#8212;&#8212;&#8211;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;-+&#8212;&#8212;&#8212;-</p>
<p>public | classes | table | postgres</p>
<p>public | geometry_columns | table | postgres</p>
<p>public | nodes | table | postgres</p>
<p>public | spatial_ref_sys | table | postgres</p>
<p>public | types | table | postgres</p>
<p>public | vertices_tmp | table | postgres</p>
<p>public | vertices_tmp_id_seq | sequence | postgres</p>
<p>public | ways | table | postgres</p>
<p>(8 rows)</td>
</tr>
</tbody>
</table>
<p>Ahora la tabla ways está preparada para topología.</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="576" valign="top">osmtest=# select gid,source,target from ways;gid | source | target</p>
<p>&#8212;&#8212;-+&#8212;&#8212;&#8211;+&#8212;&#8212;&#8211;</p>
<p>46 | 46 | 47</p>
<p>187 | 190 | 191</p>
<p>213 | 215 | 216</p>
<p>309 | 308 | 309</p>
<p>327 | 325 | 240</p>
<p>447 | 437 | 438</p>
<p>564 | 554 | 555</p>
<p>649 | 635 | 636</p>
<p>711 | 700 | 701</p>
<p>832 | 814 | 815</p>
<p>882 | 863 | 864</p>
<p>927 | 906 | 907</td>
</tr>
</tbody>
</table>
<p>Con este extenso post queda por finalizada la primera parte sobre PgRouting con OpenStreetMap. Nos esperan dos nuevos artículos que espero sean de vuestro interés:</p>
<ol>
<li>Explotación de una geodatabase con OpenStreetMap y PgRouting</li>
<li>Diseño de una interfaz web con OpenLayers para el análisis de redes.</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.gisandchips.org/2009/10/03/analisis-de-redes-con-openstreetmap-y-pgrouting-en-un-ambiente-web/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>

