<?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; Programación</title>
	<atom:link href="http://www.gisandchips.org/category/code/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>GeoFOV: Incorporando el campo de visión a una geodatabase multimedia</title>
		<link>http://www.gisandchips.org/2011/11/21/geofov-incorporando-el-campo-de-vision-a-una-geodatabase-multimedia/</link>
		<comments>http://www.gisandchips.org/2011/11/21/geofov-incorporando-el-campo-de-vision-a-una-geodatabase-multimedia/#comments</comments>
		<pubDate>Sun, 20 Nov 2011 22:56:39 +0000</pubDate>
		<dc:creator>benizar</dc:creator>
				<category><![CDATA[Análisis]]></category>
		<category><![CDATA[Presentación]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[campo de visión]]></category>
		<category><![CDATA[EXIF]]></category>
		<category><![CDATA[field of view]]></category>
		<category><![CDATA[GIS & Chips]]></category>
		<category><![CDATA[NTS]]></category>

		<guid isPermaLink="false">http://www.gisandchips.org/?p=1937</guid>
		<description><![CDATA[Hola a todos, quiero compartir aquí el enlace de mi segunda presentación en slideshare. Es de un taller que no se llegó a realizar pero espero que os resulte interesante. En esta presentación comento un poco cómo se utilizan los metadatos de las imágenes para generar información espacial de interés o incluso cómo puede evolucionar [...]]]></description>
			<content:encoded><![CDATA[<p>Hola a todos, quiero compartir aquí el enlace de mi segunda presentación en slideshare. Es de un taller que no se llegó a realizar pero espero que os resulte interesante. En esta presentación comento un poco cómo se utilizan los metadatos de las imágenes para generar información espacial de interés o incluso cómo puede evolucionar el panorama de los geotags a corto plazo.</p>
<p>Más adelante, cuando tenga tiempo, colgaré una librería que he desarrollado en mi tesis y explicaré en detalle el código.</p>
<p>Hasta pronto.</p>
<div style="width:425px" id="__ss_10245372"> <strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/BeniZaragoz/geofov-incorporando-el-campo-de-visin-en-una-multimedia-geodatabase" title="GeoFOV: Incorporando el campo de visión en una multimedia geodatabase" target="_blank">GeoFOV: Incorporando el campo de visión en una multimedia geodatabase</a></strong> <object id="__sse10245372" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=geofov-111120161539-phpapp01&#038;stripped_title=geofov-incorporando-el-campo-de-visin-en-una-multimedia-geodatabase&#038;userName=BeniZaragoz" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><param name="wmode" value="transparent"/><embed name="__sse10245372" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=geofov-111120161539-phpapp01&#038;stripped_title=geofov-incorporando-el-campo-de-visin-en-una-multimedia-geodatabase&#038;userName=BeniZaragoz" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" wmode="transparent" width="425" height="355"></embed></object>
<div style="padding:5px 0 12px"> View more <a href="http://www.slideshare.net/" target="_blank">presentations</a> from <a href="http://www.slideshare.net/BeniZaragoz" target="_blank">Beni Zaragozí</a> </div>
</p></div>
]]></content:encoded>
			<wfw:commentRss>http://www.gisandchips.org/2011/11/21/geofov-incorporando-el-campo-de-vision-a-una-geodatabase-multimedia/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Proveedor de datos SQLite</title>
		<link>http://www.gisandchips.org/2011/11/13/proveedor-de-datos-spatialite/</link>
		<comments>http://www.gisandchips.org/2011/11/13/proveedor-de-datos-spatialite/#comments</comments>
		<pubDate>Sat, 12 Nov 2011 22:25:15 +0000</pubDate>
		<dc:creator>josetomas</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Mono]]></category>
		<category><![CDATA[SpatiaLite]]></category>
		<category><![CDATA[SQLite]]></category>

		<guid isPermaLink="false">http://www.gisandchips.org/?p=1886</guid>
		<description><![CDATA[Si habéis intentado alguna vez acceder con C# a una base de datos SQLite, ya sabréis que existe un proveedor ADO.NET de código abierto, System.Data.SQLite. Sin embargo, a los que trabajamos en Linux, Mono ya nos ofrece un proveedor integrado en la propia plataforma (Mono.Data.Sqlite), que de hecho deriva del anterior. Mejor aún, las distros [...]]]></description>
			<content:encoded><![CDATA[<p align="justify">Si habéis intentado alguna vez acceder con C# a una base de datos <a href="http://www.sqlite.org/">SQLite</a>, ya sabréis que existe un proveedor ADO.NET de código abierto, <a href="http://system.data.sqlite.org">System.Data.SQLite</a>. Sin embargo, a los que trabajamos en Linux, Mono ya nos ofrece un proveedor integrado en la propia plataforma (<a href="http://www.mono-project.com/SQLite">Mono.Data.Sqlite</a>), que de hecho deriva del anterior. Mejor aún, las distros más populares incorporan SQLite por defecto. Y si, además, hablamos de una geodatabase, no hay ningún problema: hay paquetes de <a href="http://www.gaia-gis.it/spatialite/">SpatiaLite</a> a nuestra disposición. Todo parece un camino de rosas hasta que, a pesar de tenerlo todo felizmente instalado, uno se encuentra con que no es posible cargar la librería de SpatiaLite: al ejecutar el comando &#8220;SELECT load_extension(&#8216;libspatialite.so.2&#8242;)&#8221; obtenemos un lacónico &#8220;Not authorized&#8221;. Afortunadamente hay soluciones. La que aquí os propongo consiste simplemente en que uséis el proveedor Org.Gisandchips.Sqlite.</p>
<p><span id="more-1886"></span></p>
<h2>El problema de las extensiones de SQLite</h2>
<p align="justify">SpatiaLite es fundamentalmente una librería (libspatialite) que actúa como extensión de SQLite: explota el índice R-Tree de SQLite y añade el tipo Geometry y una serie de funciones que a su vez invocan a las de GEOS y PROJ. El resultado, al igual que el tándem PostgreSQL / PostGIS, es una implementación de la especificación &#8220;Simple Features for SQL&#8221; del OGC pero sin necesidad de una infraestructura cliente-servidor. Este tipo de librerías, que contienen funciones que extienden las propias de SQLite, se cargan dinámicamente, es decir, es el desarrollador el encargado de enlazarlas en tiempo de ejecución en el ámbito de una conexión de base de datos. El problema es que este mecanismo de extensión constituye a su vez un agujero de seguridad, un &#8220;coladero&#8221; de software malicioso. Así que, por defecto, las distros de Linux proporcionan el binario de SQLite compilado de forma que no se autoriza la carga dinámica de extensiones. Supongo que al final, quienes lo necesitan, optan por compilar su propio binario de SQLite o utilizar el que proporciona SpatiaLite, donde la extensión está estáticamente enlazada. Los que trabajáis con Mono tenéis ahora otra opción más.</p>
<h2>Org.Gisandchips.Sqlite</h2>
<p align="justify">Podéis descargar el código fuente de este proveedor ADO.NET para SQLite desde el repositorio SVN de Gis&amp;Chips:</p>
<p><code>svn co http://www.gisandchips.org/svn/sqliteprovider</code></p>
<p align="justify">Para vuestra comodidad, os proporciono el fichero de proyecto MonoDevelop. Yo compilo sin problemas en una máquina Ubuntu 11.04, con Mono 2.10, MonoDevelop 2.6 y bajo el framework .NET 4.</p>
<p align="justify">Si echáis un vistazo al código comprobaréis que se trata del mismo proveedor Mono.Data.Sqlite (obtenido de la master branch del repositorio público de Mono en github) con los siguientes añadidos:</p>
<ul>
<li>Wrappers para las funciones enable_load_extension() and load_extension() del API de sqlite3.</li>
<li>Método público SqliteConnection.LoadExtension() que permite cargar dinámicamente extensiones de sqlite3.</li>
</ul>
<p align="justify">El siguiente código de ejemplo es suficientemente explicativo:</p>
<pre class="brush: csharp; title: ; notranslate">
using (var cn = new SqliteConnection (&quot;Data Source=path/file.sqlite&quot;)) {
  cn.Open ();
  cn.LoadExtension (&quot;libspatialite.so.2&quot;);
  //your sql commands follow
}
</pre>
<p align="justify">Respecto a la licencia, me remito a la de Mono (MIT), que es la más permisiva y entiendo que compatible con las instrucciones del autor original de System.Data.SQLite, Robert Simpson, quien liberó este proveedor como de dominio público. Que lo disfrutéis.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gisandchips.org/2011/11/13/proveedor-de-datos-spatialite/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Paralelización con SIMD</title>
		<link>http://www.gisandchips.org/2011/10/20/paralelizacion-con-simd/</link>
		<comments>http://www.gisandchips.org/2011/10/20/paralelizacion-con-simd/#comments</comments>
		<pubDate>Thu, 20 Oct 2011 16:31:37 +0000</pubDate>
		<dc:creator>josetomas</dc:creator>
				<category><![CDATA[Análisis]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[Mono]]></category>
		<category><![CDATA[Mono.Simd]]></category>

		<guid isPermaLink="false">http://www.gisandchips.org/?p=1744</guid>
		<description><![CDATA[En este artículo os presento un test en el que medimos los tiempos que un programa escrito en C# emplea para procesar una serie de modelos digitales del terreno. El objetivo final es comparar el rendimiento de un código que hace un cálculo secuencial frente a otro que hace uso de una tecnología de paralelización [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify">En este artículo os presento un test en el que medimos los tiempos que un programa escrito en C# emplea para procesar una serie de modelos digitales del terreno. El objetivo final es comparar el rendimiento de un código que hace un cálculo secuencial frente a otro que hace uso de una tecnología de paralelización de datos por hardware denominada SIMD, disponible en la mayoría de microprocesadores que usamos hoy en día. Y el resultado es muy interesante.<div id="attachment_1801" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.gisandchips.org/wp-content//SIMD_performance.png"><img src="http://www.gisandchips.org/wp-content//SIMD_performance-300x131.png" alt="SIMD performance test" width="300" height="131" class="size-medium wp-image-1801" /></a><p class="wp-caption-text">Test de rendimiento SIMD con Mono.Simd.Vector8s</p></div><span id="more-1744"></span></p>
<p style="text-align: justify">Las técnicas de computación paralela encuentran en los sistemas de información geográfica una de sus aplicaciones más elocuentes dada la naturaleza masiva de los datos geográficos, en particular si hablamos de estructuras de datos raster. No en vano, allá por el año 1997, Richard Healey, junto con otros compañeros del Dpto. de Geografía de la Universidad de Edimburgo, editó &#8220;Parallel Processing Algorithms For GIS&#8221;. El profesor Healey es un geógrafo que ya a principios de los 90 planteaba a sus alumnos problemas de geoprocesamiento paralelo que éstos habían de resolver con ADA, el único lenguaje accesible que por aquel entonces prefiguraba el paradigma OOP y permitía definir tareas concurrentes. Desde entonces se han producido muchos avances en paralelización de procesos, tanto a nivel de software como de hardware. Sin embargo, existe un tipo de paralelización que no parece haber alcanzado demasiada notoriedad en el campo de los GIS: se trata del conjunto de instrucciones <a href="http://en.wikipedia.org/wiki/SIMD">SIMD</a> (Single Instruction, Multiple Data) que permite realizar operaciones aritméticas entre pares de vectores mediante una sola instrucción. No se trata por tanto de concurrencia de procesos, sino de paralelismo a nivel de datos. Lo más curioso es que, desde 1996, los fabricantes de microprocesadores han ido incorporando extensiones que soportan en mayor o menor medida este juego de instrucciones.</p>
<p style="text-align: justify">Cualquier PC o videoconsola actual permite ejecutar instrucciones SIMD. Evidentemente los desarrolladores de juegos hace tiempo que explotan este tipo de paralelización. Sin embargo, encontrar referencias recientes a SIMD en el ámbito de la Geomática resulta más complicado: <a href="http://gisws.media.osaka-cu.ac.jp/grass04/viewpaper.php?id=15">aquí tenéis una interesante ponencia</a> de la conferencia de usuarios de GRASS del 2004. Seguramente habrá más trabajos publicados, así que hacednos saber lo que encontréis.</p>
<p style="text-align: justify">Yo sinceramente no sabía nada de esto hasta que leí <a href="http://tirania.org/blog/archive/2008/Nov-03.html">este post de Miguel de Icaza</a>, anunciando la publicación de <a href="http://docs.go-mono.com/monodoc.ashx?link=N%3aMono.Simd">Mono.Simd</a>. Este API es un paso en la estrategia de Mono por entrar de lleno en la industria del &#8220;gaming&#8221;. ¿Y para las desarrolladores de GIS? En mi opinión significa un abanico de posibilidades a la hora de paralelizar geoprocesos que queda pendiente de explorar.</p>
<p style="text-align: justify">En este artículo sólo quiero dejar constancia de un test para un caso extremo de optimización con SIMD. En la práctica carece de utilidad, mi única intención es medir diferencias de tiempo en un escenario absolutamente favorable a la paralelización mediante vectores. Dado un grid de elevaciones, donde cada valor de altitud puede representarse como un entero de 16 bits, se trata simplemente de doblar el valor de cada pixel para &#8220;exagerar&#8221; el modelo del terreno. Estas son las especificaciones de la máquina en que he realizado el test:</p>
<ul>
<li>Intel Q8300 @ 2.50GHz</li>
<li>3.9 GiB RAM</li>
<li>Ubuntu 11.04 i686</li>
<li>Mono 2.10.5</li>
</ul>
<p style="text-align: justify">El test se ha realizado sobre 4 ficheros <a href="http://en.wikipedia.org/wiki/Esri_grid">ARC/INFO ASCII GRID</a> de distintos tamaños. Para cada fichero se han tomado 5 muestras del tiempo transcurrido en el cálculo aritmético sin aceleración de hardware, pixel a pixel, tomando finalmente la mediana. La misma metodología de muestreo se ha empleado para obtener los tiempos con aceleración de hardware, es decir, empleando Mono.Simd para multiplicar los pixels de 8 en 8 mediante una única instrucción. Para ello es necesario agrupar previamente los valores de altitud y almacenar cada vector en una lista de tipo Mono.Simd.Vector8s. Estos son los resultados:</p>
<table style="height: 5px;width: 80%" border="1" cellspacing="1" cellpadding="1" align="center">
<thead>
<tr>
<th scope="col"><span class="Apple-style-span" style="font-weight: normal">File size (MiB)</span></th>
<th scope="col"><span class="Apple-style-span" style="font-weight: normal">Elapsed time (miliseconds)</span></th>
<th scope="col"><span class="Apple-style-span" style="font-weight: normal">Elapsed time with SIMD (miliseconds)</span></th>
<th scope="col"><span class="Apple-style-span" style="font-weight: normal">Time reduction</span></th>
<th scope="col"><span class="Apple-style-span" style="font-weight: normal">Performance gain</span></th>
</tr>
</thead>
<tbody>
<tr>
<td>12.0</td>
<td>49.7447</td>
<td>14.2542</td>
<td>71.3</td>
<td><span style="background-color: #ffd700"><strong>3.5x</strong></span></td>
</tr>
<tr>
<td>48.2</td>
<td>197.5251</td>
<td>56.149</td>
<td>71.6</td>
<td><span style="background-color: #ffd700"><strong>3.5x</strong></span></td>
</tr>
<tr>
<td>192.7</td>
<td>783.0778</td>
<td>216.305</td>
<td>72.4</td>
<td><span style="background-color: #ffd700"><strong>3.6x</strong></span></td>
</tr>
<tr>
<td>535.4</td>
<td>2180.8665</td>
<td>618.2225</td>
<td>71.7</td>
<td><span style="background-color: #ffd700"><strong>3.5x</strong></span></td>
</tr>
</tbody>
</table>
<p style="text-align: justify">Como veis, en todos los casos, con SIMD el proceso de cálculo se completa en torno a 3,5 veces más rápido. Esto es una buena noticia, pero no significa que este tipo de paralelismo sea la panacea. En mi opinión la conclusión es que, si con un escenario favorable se puede reducir el tiempo de proceso en más de un 70%, merece la pena explorar otros escenarios con verdadera utilidad práctica.</p>
<p style="text-align: justify">Si alguien se pregunta por la incidencia que puede tener esta mejora en el rendimiento si contabilizamos el proceso de lectura y carga en memoria de los datos, me atrevería a decir que escasa. En cualquier caso, los procesos de lectura y manejo de grandes volúmenes de información raster constituyen un problema distinto y existen diversas técnicas para su optimización.</p>
<p style="text-align: justify">Si os pica la curiosidad, podeis averiguar el conjunto de instrucciones SIMD que soporta vuestro procesador ejecutando el código de ejemplo que aparece en la documentación de la clase <a href="http://docs.go-mono.com/monodoc.ashx?link=T%3aMono.Simd.SimdRuntime">Mono.Simd.SimdRuntime</a> (es posible que tengáis que hacer algunas modificaciones). Para aquellos que quieran reproducir un test de rendimiento con sus propios ficheros ASCII GRID, aquí os dejo el código C# que he usado (por cierto, ni es óptimo ni puedo garantizar que esté libre de bugs):</p>
<pre class="brush: csharp; title: ; notranslate">
using System;
using System.IO;
using Mono.Simd;
using System.Collections.Generic;
using System.Globalization;
using System.Diagnostics;
using System.Linq;

namespace SIMDTest
{
 class MainClass
 {
  public static void Main (string[] args)
  {
   bool useSIMD = true;
   string path = string.Empty;
   CultureInfo formatProvider = null;
   try {
    if (args.Length == 0) {
     Console.WriteLine (&quot;Usage: SIMDTest [ASC GRID file name] [specific culture name]&quot;);
     return;
    }
    if (args.Length &gt; 0)
     path = args[0];
    if (args.Length &gt; 1)
     formatProvider = CultureInfo.GetCultureInfo (args[1]);
    Console.WriteLine (&quot;Use hardware acceleration? [y / n]? : &quot;);
    ConsoleKeyInfo key = Console.ReadKey ();
    while (key.Key != ConsoleKey.Y &amp;&amp; key.Key != ConsoleKey.N) {
     Console.WriteLine (&quot;Press 'y' to use hardware acceleration, 'n' otherwise: &quot;);
     key = Console.ReadKey ();
    }
    Console.WriteLine ();
    Console.WriteLine (&quot;Processing, please wait ...&quot;);
    useSIMD = (key.Key == ConsoleKey.Y);
    ElevationModel dem = new ElevationModel(path, formatProvider);
    Stopwatch stopwatch = new Stopwatch ();
    if (useSIMD) {
     Vector8s scale = new Vector8s (2);
     foreach (IList vPage in dem.EnumerateVectors ()) {
      stopwatch.Start ();
      for (int i = 0; i &lt; vPage.Count; i ++)
       vPage[i] = vPage[i] * scale;
      stopwatch.Stop ();
     }
    } else {
     foreach (IList page in dem.EnumerateData ()) {
      stopwatch.Start ();
      for (int i = 0; i &lt; page.Count; i ++)
       page[i] = (short) (page[i] * 2);
      stopwatch.Stop ();
     }
    }
    Console.WriteLine (&quot;Time elapsed: {0}&quot;, stopwatch.Elapsed);
   } catch (Exception ex) {
    Console.WriteLine (ex.Message);
   }
  }
 }

 class ElevationModel
 {
  string _path;
  IFormatProvider _formatProvider = CultureInfo.CurrentCulture;
  short _noData = -9999;
  int BOD = 0;

  public ElevationModel (string path, IFormatProvider formatProvider)
  {
   if (string.IsNullOrEmpty (path))
    throw new ArgumentNullException ();
   else if (!File.Exists (path))
    throw new FileNotFoundException ();
   else {
    _path = path;
    if (formatProvider != null)
     _formatProvider = formatProvider;
    this.SetMetadata ();
   }
  }

  public ElevationModel (string path) : this(path, null)
  {
  }

  void SetMetadata ()
  {
   using (StreamReader reader = new StreamReader (_path)) {
    //skip GRID header
    for (int i = 0; i &lt; 5; i++)
     reader.ReadLine ();
    _noData = short.Parse (reader.ReadLine ().Split (' ')[1], _formatProvider);
    reader.BaseStream.Position = 0;
    reader.DiscardBufferedData ();
    char[] buffer = new char[512];
    reader.Read(buffer, 0, buffer.Length);
    BOD = this.GetBeginOfData (buffer);
   }
  }

  int GetBeginOfData (char[] source)
  {
   int i = 0;
   bool isNewHeaderLine = false;
   bool isNewLine = true;
   foreach (char c in source) {
    if (char.IsLetter (c) &amp;&amp; isNewLine) {
     isNewHeaderLine = true;
     isNewLine = false;
    } else if ((char.IsDigit (c) || c == '-') &amp;&amp; isNewLine &amp;&amp; !isNewHeaderLine) {
     break;
    } else if (char.IsControl (c)) {
     isNewLine = true;
     isNewHeaderLine = false;
    }
    i++;
   }
   return i;
  }

  IList&lt;short&gt; GetPage (char[] source, int sourceIndex, int sourceLength, IList pendingDigits)
  {
   IList&lt;short&gt; page = new List&lt;short&gt; ();
   for (int i = sourceIndex; i &lt; sourceLength; i++) {
    if (char.IsDigit (source[i]) || source[i] == '-')
     pendingDigits.Add (source[i]);
    else if (pendingDigits.Count &gt; 0 &amp;&amp; (char.IsWhiteSpace (source[i]) || char.IsControl (source[i]))) {
     short n = _noData;
     string s = new string(pendingDigits.ToArray ());
     if (!short.TryParse(s, NumberStyles.Integer, _formatProvider, out n))
      Console.WriteLine (&quot;Error parsing {0}&quot;, s);
     page.Add (n);
     pendingDigits.Clear ();
    }
   }
   return page;
  }

  public IEnumerable&lt;IList&lt;short&gt;&gt; EnumerateData ()
  {
   using (StreamReader reader = new StreamReader (_path)) {
    char[] buffer = new char[25000 * 1024];
    int beginOfData = 0;
    IList pendingDigits = new List ();
    int charCount = 0;
    while ((charCount = reader.Read (buffer, 0, buffer.Length)) &gt; 0) {
     if (beginOfData == 0) {
      beginOfData = this.BOD;
      yield return this.GetPage (buffer, beginOfData, charCount, pendingDigits);
     } else
      yield return this.GetPage (buffer, 0, charCount, pendingDigits);
    }
   }
  }

  IList&lt;Vector8s&gt; GetVectorPage(IList page, IList pendingValues)
  {
   List vPage = new List ();
   foreach (short n in page) {
    pendingValues.Add (n);
    if (pendingValues.Count == 8 ) {
     vPage.Add (new Vector8s (pendingValues[0], pendingValues[1],
      pendingValues[2], pendingValues[3], pendingValues[4],
      pendingValues[5], pendingValues[6], pendingValues[7]));
     pendingValues.Clear ();
    }
   }
   return vPage;
  }

  public IEnumerable&lt;IList&lt;Vector8s&gt;&gt; EnumerateVectors ()
  {
   int lastPageSize = -1;
   IList pendingValues = new List ();
   foreach (IList page in this.EnumerateData ()) {
    IList vPage = this.GetVectorPage (page, pendingValues);
    // complete last vector in last page
    if (lastPageSize != -1 &amp;&amp; vPage.Count &lt; lastPageSize) {
     if (pendingValues.Count &gt; 0 &amp;&amp; pendingValues.Count &lt; 8 ) {
      for (int i = pendingValues.Count; i &lt; 8; i++)
	pendingValues.Add(_noData);
      vPage.Add (new Vector8s (pendingValues[0], pendingValues[1],
       pendingValues[2], pendingValues[3], pendingValues[4],
       pendingValues[5], pendingValues[6], pendingValues[7]));
     }
    }
    lastPageSize = vPage.Count;
    yield return vPage;
   }
  }
 }
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.gisandchips.org/2011/10/20/paralelizacion-con-simd/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Usando RSAGA para procesar un raster &#8220;grande&#8221; por partes</title>
		<link>http://www.gisandchips.org/2011/01/13/usando-rsaga-para-procesar-un-raster-grande-por-partes/</link>
		<comments>http://www.gisandchips.org/2011/01/13/usando-rsaga-para-procesar-un-raster-grande-por-partes/#comments</comments>
		<pubDate>Thu, 13 Jan 2011 17:20:21 +0000</pubDate>
		<dc:creator>benizar</dc:creator>
				<category><![CDATA[Análisis]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[GIS libre]]></category>
		<category><![CDATA[R]]></category>

		<guid isPermaLink="false">http://www.gisandchips.org/?p=1692</guid>
		<description><![CDATA[Hola a todos, hoy propongo una de las posibles soluciones a un problema que suele aparecer trabajando con GIS: ¿qué hacer cuando queremos procesar un raster “relativamente grande” y los GIS de escritorio más populares tienen problemas de memoria o no acaban el proceso? En algunas ocasiones la solución a estos problemas sería “trocear” el [...]]]></description>
			<content:encoded><![CDATA[<p><span style="font-weight: normal;font-size: 13px">Hola a todos, hoy propongo una de las posibles soluciones a un problema que suele aparecer trabajando con GIS: ¿qué hacer cuando queremos procesar un raster “relativamente grande” y los GIS de escritorio más populares tienen problemas de memoria o no acaban el proceso?</span></p>
<p>En algunas ocasiones la solución a estos problemas sería “trocear” el raster y procesarlo por partes. Esto lo podríamos hacer con varios software y en todos ellos sería interesante poder automatizar la tarea al máximo.</p>
<div id="attachment_1695" class="wp-caption aligncenter" style="width: 250px"><a href="http://www.gisandchips.org/wp-content/mdt1.jpg"><img class="size-medium wp-image-1695 " src="http://www.gisandchips.org/wp-content/mdt1-300x300.jpg" alt="" width="240" height="240" /></a><p class="wp-caption-text">1. Modelo digital raster</p></div>
<p>En este post propongo realizar una prueba con RSAGA, que es un modulo de R que permite acceder a las funciones disponibles en la consola de SAGA GIS. Para este post he trabajado en Windows, con R 2.10.1 y SAGA 2.0.4, aunque supongo que no habrá problemas con usar otras versiones más recientes. Además, deberéis instalar en R el paquete RSAGA.<span id="more-1692"></span></p>
<div id="attachment_1696" class="wp-caption aligncenter" style="width: 248px"><a href="http://www.gisandchips.org/wp-content/municipios1.jpg"><img class="size-medium wp-image-1696 " src="http://www.gisandchips.org/wp-content/municipios1-298x300.jpg" alt="" width="238" height="240" /></a><p class="wp-caption-text">2. Shapefile de polígonos con que trocearemos el raster</p></div>
<p>Para esta práctica podéis <a title="descargar ficheros de prueba" href="http://dl.dropbox.com/u/17558342/rsaga_tests.rar" target="_blank">descargar dos ficheros</a> (Imagenes 1 y 2), uno raster y otro vectorial (polígonos). A efectos didácticos se utilizan ficheros pequeños, pero esto resultaría más útil en caso de necesitar fragmentar más el raster. Por ejemplo, en artículos anteriores he presentado código para trabajar imágenes a nivel de parcelas agrícolas. En aquellos artículos trabajábamos las imágenes de una en una y ya estaban recortadas, pero las parcelas podrían ser miles y no queremos hacer eso a mano.</p>
<h3>Conociendo SAGA GIS</h3>
<p>Lo principal, como en tantos otros casos, sería conocer como se trocea un fichero raster con SAGA GIS. Para ello abrimos la GUI de SAGA y vamos a la pestaña “modules” que tiene forma de TOC, generalmente se encuentra a la izquierda de la ventana principal. En esta pestaña podemos encontrar las distintas librerías que agrupan módulos que se relacionan por algún motivo. Explorar estas librerías y practicar con ellas es el mejor modo de estar seguro de lo que se quiere hacer.</p>
<div id="attachment_1697" class="wp-caption aligncenter" style="width: 148px"><a href="http://www.gisandchips.org/wp-content/toc.jpg"><img class="size-medium wp-image-1697" src="http://www.gisandchips.org/wp-content/toc-172x300.jpg" alt="" width="138" height="240" /></a><p class="wp-caption-text">3. TOC de SAGA GIS donde podemos explorar las librerías y los módulos disponibles. </p></div>
<p>Finalmente, he decidido que lo mejor es empezar por separar el shapefile original por polígonos (shapes-tools/separate shapes) y después realizar un recorte por cada uno de los nuevos shapefiles (shapes-grid/clip grid with polygon). Practicar con el interfaz gráfico es un bueno modo de estar seguros de los parámetros que nos pide cada módulo.</p>
<p>Antes de empezar a usar RSAGA he tenido que averiguar los nombres exactos de los módulos y los parámetros que aceptan. Esto lo he hecho con los métodos rsaga.get.modules() y rsaga.get.usage(). Aunque generalmente se puede averiguar también viendo los nombres en el SAGA GUI o mirando en la carpeta donde se contienen las *.dll.</p>
<h3>Trabajando con RSAGA</h3>
<pre class="brush: bash; title: ; notranslate">
## Abrimos la consola de R y cargamos la librería
library(RSAGA)

## Seleccionamos el fichero raster que queremos trocear y una capa vectorial que queramos usar como límites. También especificamos el directorio donde van los outputs. Para trabajar con Windows recomiendo rutas sin espacios.
raster&lt;- file.choose()
poligonos&lt;- file.choose()
directorio &lt;-choose.dir()

## Consultamos hasta encontrar la herramienta que nos separa un shapefile en varios, obteniendo un shapefile por cada polígono, o lo que quisiéramos. Por ejemplo:
## rsaga.get.modules(&quot;shapes_grid&quot;)
## rsaga.get.usage(&quot;shapes_tools&quot;, 7)
## Se ejecuta el método con los parámetros necesarios.
rsaga.geoprocessor(lib=&quot;shapes_tools&quot;, module=7, param=list(SHAPES=poligonos, PATH=directorio, NAMING=0, FIELD=6))
</pre>
<p>Una vez ejecutado este proceso ya disponemos de un shapefile por cada polígono, esto significa que aunque el shapefile original tuviera miles de registros solamente cargaremos en memoria uno a la vez. Esto ya supone un ahorro importante de recursos.</p>
<pre class="brush: bash; title: ; notranslate">
# Listamos los nuevos shapefiles
shapefiles &lt;- list.files(directorio, full.names=T, pattern=&quot;\\.shp&quot;)
## Por último utilizamos RSAGA para realizar tantos recortes del raster como polígonos habíamos extraído
for(i in 1:length(shapefiles)){
select &lt;- shapefiles[i]&lt;/code&gt;
rsaga.geoprocessor(lib=&quot;shapes_grid&quot;, module=7, param=list(OUTPUT=select, INPUT=raster, POLYGONS=select))
}#fin bucle
</pre>
<h3>Conclusiones</h3>
<p>Tras ver este ejemplo nos queda más claro el uso de RSAGA y se visualiza bien como se podrían automatizar gran número de tareas habituales en los GIS. Intentar realizar estas tareas herramientas básicas y generales podría ser algo trabajoso.</p>
<p>En nuestro fichero de prácticas había 18 polígonos por lo que nuestro resultado es una carpeta donde encontramos guardados 18 shapefiles y 18 ficheros raster. Los recortes quedan como se puede ver en la siguiente imagen:</p>
<div id="attachment_1702" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.gisandchips.org/wp-content/recorte.jpg"><img class="size-medium wp-image-1702" src="http://www.gisandchips.org/wp-content/recorte-300x289.jpg" alt="" width="300" height="289" /></a><p class="wp-caption-text">4. Uno de los 18 raster resultantes de los recortes y al fondo el shapefile original</p></div>
<p>En caso de ser necesario, se podría realizar más tareas sobre todos estos ficheros, con este software o con otros programas. Incluso se plantea la posibilidad de usar multithreading en R para ejecutar tareas con RSAGA. Proximamente haremos alguna demostración de esto.</p>
<p>Antes de acabar, solo quiero recordar quando trabajamos con RSAGA en Windows es posible que nos afecten algunas particularidades sobre el modo en que se forman las rutas (espacios, barras de directorios…) y los mensajes de error que aparecen no son de gran ayuda.</p>
<p>——————————————————————</p>
<p>Si queréis contactar podéis enviarme un email (asunto: gisandchips):</p>
<p>Benito M. Zaragozí</p>
<p>benito.zaragozi@ua.es</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gisandchips.org/2011/01/13/usando-rsaga-para-procesar-un-raster-grande-por-partes/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>La ciencia de los suelos (I). Creación de triángulos texturales directamente desde una Geodatabase.</title>
		<link>http://www.gisandchips.org/2010/09/08/la-ciencia-de-los-suelos-i-creacion-de-triangulos-texturales-directamente-desde-una-geodatabase/</link>
		<comments>http://www.gisandchips.org/2010/09/08/la-ciencia-de-los-suelos-i-creacion-de-triangulos-texturales-directamente-desde-una-geodatabase/#comments</comments>
		<pubDate>Wed, 08 Sep 2010 04:44:54 +0000</pubDate>
		<dc:creator>benizar</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[gráficas]]></category>
		<category><![CDATA[pl/R]]></category>
		<category><![CDATA[PostGIS]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[R]]></category>

		<guid isPermaLink="false">http://www.gisandchips.org/?p=1629</guid>
		<description><![CDATA[Llevo un tiempo ocupado pero ya es hora de enseñar en GIS&#38;Chips algunas de las demos que realicé para mi Trabajo Fin de Master (TFM) sobre SIG. En este artículo os muestro la demo nº2 que consiste en la creación de triángulos texturales directamente desde una Geodatabase que diseño y creo previamente dentro del TFM. [...]]]></description>
			<content:encoded><![CDATA[<p>Llevo un tiempo ocupado pero ya es hora de enseñar en GIS&amp;Chips algunas de las demos que realicé para mi Trabajo Fin de Master (TFM) sobre SIG.</p>
<p>En este artículo os muestro la demo nº2 que consiste en la creación de triángulos texturales directamente desde una <strong>Geodatabase </strong>que diseño y creo previamente dentro del TFM. Pero más adelante añadiré algunos comentarios de las otras demos.</p>
<div id="attachment_1630" class="wp-caption aligncenter" style="width: 277px"><a href="http://www.gisandchips.org/wp-content/clasificacion_textural.png"><img class="size-medium wp-image-1630" src="http://www.gisandchips.org/wp-content/clasificacion_textural-267x300.png" alt="" width="267" height="300" /></a><p class="wp-caption-text">Triángulo textural</p></div>
<h3><span id="more-1629"></span>El TFM</h3>
<p>Es interesante comentar que el TFM es una pequeña parte de otra investigación que estoy realizando en el contexto de la comarca de la Marina Baja (Alicante-España) y los datos que se muestra en las distintas demos son parte de un muestreo para entender mejor el potencial nutritivo de los suelos de la comarca.</p>
<p>El título del TFM es “Diseño de una base de datos geográfica orientada al estudio de los suelos”. Evidentemente, en el espacio de un TFM no cabía desarrollar toda la idea, que podría ser muy trabajosa para una sola persona, del mismo modo que aquí no cabe desarrollar todo el TFM ya que hay algunas partes que no tienen que ver con lo que queremos contar en G&amp;C.</p>
<p>Si entráis en la página principal del la Web de demostraciones podéis leer un poco más sobre la intención y contenidos del TFM ( <a href="http://www.gisandchips.org/demos/suelosTFM/">http://www.gisandchips.org/demos/suelosTFM/</a> ). Además en esta página se explican los detalles de la tecnología utilizada y del método de trabajo (<strong>PostgreSQL</strong>, <strong>PostGIS</strong>, R, <strong>pl/R</strong>&#8230;).</p>
<p>Considero que las demos 2 y 3 son más interesantes, puesto que la primera es solo una función de clasificación que cada uno podría hacer a su gusto, pero si a alguien le interesa la explicaré más adelante.</p>
<div id="attachment_1645" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.gisandchips.org/wp-content/tritex_1.jpg"><img class="size-medium wp-image-1645" src="http://www.gisandchips.org/wp-content/tritex_1-300x232.jpg" alt="" width="300" height="232" /></a><p class="wp-caption-text">Página de la Demo</p></div>
<h3>Los triángulos texturales</h3>
<p>El estudio de los suelos es una disciplina tremendamente amplia, por lo que aquí nos limitaremos a dar, de un modo más breve, las explicaciones necesarias para entender los gráficos resultantes.</p>
<p>Los <strong>triángulos texturales</strong> son gráficos que facilitan la interpretación de la granulometría del suelo. Así pues, habitualmente la textura del suelo se puede representar en un diagrama ternario. En dicho diagrama ternario se emplean reglas trigonométricas para representar en el plano coordenadas tridimensionales que se refieren a distintas combinaciones de clases granulométricas (clases texturales) cuya suma es constante (100). Por último, las clases texturales son convenientes para representar a la textura del suelo en mapas ya que resulta más sencillo representar una variable que no tres, y su uso es bastante amplio en descripciones y clasificaciones del suelo.</p>
<p>A pesar de todas las ventajas de estos gráficos, aparece la dificultad de que existen gran número de triángulos texturales que permiten la clasificación de los suelos, lo que implica que en caso de querer incluir herramientas relacionadas en una BDGS (Geodatabase de suelos) se deba considerar tal variedad de criterios. En este caso se ha creado un gráfico de texturas según las especificaciones del USDA (1951) en castellano.</p>
<p>Pese a que existe cierta variedad de programas pensados para la elaboración de los triángulos de texturas parece interesante que una BDGS que almacene los datos de textura de suelos sea capaz de generar este tipo de gráficos. Además, los otros software suelen ser difíciles de encontrar y poco flexibles. Aquí podemos modificar nuestro gráfico a placer.</p>
<div id="attachment_1643" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.gisandchips.org/wp-content/tritex_results.jpg"><img class="size-medium wp-image-1643" src="http://www.gisandchips.org/wp-content/tritex_results-300x188.jpg" alt="" width="300" height="188" /></a><p class="wp-caption-text">Página de resultados de la demo</p></div>
<h3>Demostración  Triángulo Textural</h3>
<p>En la propia página Web (<a href="http://www.gisandchips.org/demos/suelosTFM/paginas/demo_tritex1.php">http://www.gisandchips.org/demos/suelosTFM/paginas/demo_tritex1.php</a> ) se explica como interactuar con la demo, seleccionando las muestras que se desee, si se desea etiquetar las muestras y con que símbolo se van a representar. Como se puede ver, los datos han sido previamente ordenados según una consulta espacial usando funciones de <strong>PostGIS</strong>, por lo que la gráfica que vamos a generar se ve enriquecida por esta posibilidad. A partir de los datos seleccionados se ejecuta una función escrita en pl/R que usa la librería &#8220;Plotrix&#8221; de R para generar un triángulo textural personalizado y guardarlo como un fichero PNG en el servidor. La función que se usa en este ejemplo puede ser personalizada en muchos sentidos para obtener otros triángulos texturales (hay decenas), en otros idiomas, clasificando los símbolos por algún atributo de la base de datos&#8230;</p>
<p>El uso de la función resulta muy sencillo,</p>
<pre class="brush: sql; title: ; notranslate">
SELECT  _plr_clasif_textural ( 'arenas' , 'limos' , 'arcilla' , 'submuestra' , 'simbolo' , 'etiquetas') ;
</pre>
<p>y a continuación podemos ver el código que hemos usado para crearla, y algunos comentarios:</p>
<pre class="brush: sql; title: ; notranslate">
-- Function: _plr_clasif_textural(text, text, text, text, text, text)
-- DROP FUNCTION _plr_clasif_textural(text, text, text, text, text, text);
CREATE OR REPLACE FUNCTION _plr_clasif_textural(text, text, text, text, text, text)
RETURNS text AS
$BODY$
</pre>
<pre class="brush: bash; title: ; notranslate">
# Cargamos las librerias necesarias
library(Cairo)
library(plotrix)
# Definimos la funcion
clasif.textural = function (soiltexture = NULL, at = seq(0.1, 0.9, by = 0.1),
axis.labels = c(&quot;% arena (entre 0,05 y 2 mm)&quot;, &quot;% limo (entre 0,05 y 0,002 mm)&quot;, &quot;% arcilla (menor de 0,002  mm)&quot;),
tick.labels = list(l = seq(10, 90, by = 10), r = seq(10,
90, by = 10), b = seq(10, 90, by = 10)), show.names = TRUE,
show.lines = TRUE, col.names = &quot;black&quot;, bg.names = par(&quot;bg&quot;),
show.grid = TRUE, col.axis = &quot;black&quot;, col.lines = &quot;black&quot;,
col.grid = &quot;gray&quot;, lty.grid = 3, show.legend = FALSE, label.points = FALSE,
point.labels = '', col.symbols = &quot;blue&quot;, pch = par(&quot;pch&quot;),
...)
{
par(xpd = TRUE)
plot(0.5, type = &quot;n&quot;, axes = FALSE, xlim = c(0, 1), ylim = c(0,
1), main = NA, xlab = NA, ylab = NA)
triax.frame(at = at, axis.labels = axis.labels,
tick.labels = tick.labels, col.axis = col.axis, show.grid = show.grid,
col.grid = col.grid, lty.grid = lty.grid)
arrows(0.12, 0.41, 0.22, 0.57, length = 0.15)
arrows(0.78, 0.57, 0.88, 0.41, length = 0.15)
arrows(0.6, -0.1, 0.38, -0.1, length = 0.15)
if (show.lines) {
triax.segments &lt;- function(h1, h3, t1, t3, col) {
segments(1 - h1 - h3/2, h3 * sin(pi/3), 1 - t1 -
t3/2, t3 * sin(pi/3), col = col)
}
h1 &lt;- c(85, 70, 80, 52, 52, 50, 20, 8, 52, 45, 45, 65,
45, 20, 20)/100
h3 &lt;- c(0, 0, 20, 20, 7, 0, 0, 12, 20, 27, 27, 35, 40,
27, 40)/100
t1 &lt;- c(90, 85, 52, 52, 43, 23, 8, 0, 45, 0, 45, 45,
0, 20, 0)/100
t3 &lt;- c(10, 15, 20, 7, 7, 27, 12, 12, 27, 27, 55, 35,
40, 40, 60)/100
triax.segments(h1, h3, t1, t3, col.lines)
}
if (show.names) {
xpos &lt;- c(0.5, 0.7, 0.7, 0.73, 0.73, 0.5, 0.275, 0.275,
0.27, 0.27, 0.25, 0.135, 0.18, 0.07, 0.49, 0.72,
0.9)
ypos &lt;- c(0.66, 0.49, 0.44, 0.36, 0.32, 0.35, 0.43, 0.39,
0.3, 0.26, 0.13, 0.072, 0.032, 0.024, 0.18, 0.15,
0.06) * sin(pi/3)
snames &lt;- c(&quot;arcillosa&quot;, &quot;arcillosa&quot;, &quot;limosa&quot;, &quot;franco arcillosa&quot;, &quot;limosa&quot;,
&quot;franco arcillosa&quot;, &quot;arcillosa&quot;, &quot;arenosa&quot;, &quot;franco arcillosa&quot;, &quot;arenosa&quot;,
&quot;franco arenosa&quot;, &quot;arenosa&quot;, &quot;franca&quot;, &quot;arenosa&quot;, &quot;franca&quot;, &quot;franco limosa&quot;,
&quot;limosa&quot;)
boxed.labels(xpos, ypos, snames, border = FALSE, col = col.names,
cex=0.8, xpad = 0.5)
}
par(xpd = FALSE)
if (is.null(soiltexture))
return(NULL)
soilpoints &lt;- triax.points(soiltexture, show.legend = show.legend,
label.points = label.points, point.labels = point.labels,
col.symbols = col.symbols, pch = pch, ...)
invisible(soilpoints)
}
# Definimos la imagen
CairoPNG(&quot;…/images/clasificacion_textural.png&quot;, width=500, height=560)
# Construimos las consultas
select = 'select '
arena = paste(arg1, ',', sep=' ');
limo = paste(arg2, ',', sep=' ');
arcilla = paste(arg3, ' ');
from = ' from '
tabla = arg4
selection = paste(select, arena, limo, arcilla, from, tabla, sep='');
selectgids = paste('select gid', from, tabla, sep='');
# Ejecutamos las consultas
texturas &lt;- pg.spi.exec(selection)
gids&lt;- pg.spi.exec(selectgids)
# Ejecutamos la funcion creada mas arriba
soiltex.return&lt;-clasif.textural(texturas,
pch=arg5, point.labels = gids[,], label.points=arg6)
dev.off()
# Damos permisos de lectura
system('chmod go+r …/images/clasificacion_textural.png');
# Una comprobacion para asegurarnos de que se ha creado la grafica, pero no afecta para nada.
if (file.exists('…/images/clasificacion_textural.png')) {
print ('Grafico realizado. Se llama …/images/clasificacion_textural.png')};
</pre>
<pre class="brush: sql; title: ; notranslate">
$BODY$
LANGUAGE 'plr' VOLATILE
COST 100;
ALTER FUNCTION _plr_clasif_textural(text, text, text, text, text, text) OWNER TO postgres;
</pre>
<h3>Algunas notas sobre todo esto:</h3>
<p>(1) En el TFM se apunta que en breve aparecerá una librería creada por Julien Moeys ( <a href="https://r-forge.r-project.org/projects/soiltexture/">https://r-forge.r-project.org/projects/soiltexture/</a> ) que hará innecesario tener que crear la función por nosotros mismos como hemos hecho en esta demostración. Será interesante comentar con los miembros del proyecto la posibilidad de compartir las funciones de su proyecto en una Web completamente dedicada al cálculo de triángulos texturales, donde los usuarios puedan añadir sus propios datos.</p>
<p>(2) Los datos de ejemplo están un poco modificados pero se aproximan a la realidad.</p>
<p>(3) La función tiene muchos aspectos mejorables, por lo menos en cuanto a la legibilidad. Otra posibilidad sería la de que no todos los argumentos de entrada fuesen de tipo texto. Esto lo veremos plasmado en el código de la demo 3.</p>
<p>(4) Si en la página  inicial de la demo no se ve una lista con las muestras disponibles (como en la segunda imagen) será porqué el servidor de la base de datos ha caído y lo deberemos reiniciar en breve. Solo ha pasado una vez, pero quien sabe…</p>
<p>(5) Este artículo destaca la potencia de pl/R a la hora de crear gráficos de lo más complejos.</p>
<h3>Agradecimientos:</h3>
<p>El TFM de que he estado hablando ha sido dirigido por Toni Hernández, en el Máster en Sistemas de Información Geográfica y Teledetección de la Universitat de Girona (UNIGIS), y fue presentado en Mayo de 2010. Les estoy muy agradecido a todos los miembros, tanto alumnos como profesores, por estos últimos años disfrutando y aprendiendo SIG.</p>
<p>Especialmente, quiero agradecer a los compañeros de G&amp;C por la ayuda con instalaciones, creación de la Web y otros dolores de cabeza.</p>
<p>——————————————————————</p>
<p>Si queréis contactar podéis enviarme un email (asunto: gisandchips):</p>
<p>Benito M. Zaragozí</p>
<p>benito.zaragozi@ua.es</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gisandchips.org/2010/09/08/la-ciencia-de-los-suelos-i-creacion-de-triangulos-texturales-directamente-desde-una-geodatabase/feed/</wfw:commentRss>
		<slash:comments>0</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>API de Google + OpenStreetMap y otros servicios georreferenciados</title>
		<link>http://www.gisandchips.org/2010/03/24/api-de-google-openstreetmap-y-otros-servicios-georreferenciados/</link>
		<comments>http://www.gisandchips.org/2010/03/24/api-de-google-openstreetmap-y-otros-servicios-georreferenciados/#comments</comments>
		<pubDate>Wed, 24 Mar 2010 11:22:00 +0000</pubDate>
		<dc:creator>pepe</dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[Google Maps]]></category>
		<category><![CDATA[mashup]]></category>
		<category><![CDATA[OpenStreetMap]]></category>

		<guid isPermaLink="false">http://www.gisandchips.org/?p=1551</guid>
		<description><![CDATA[Voy a tratar de diseñar un visor de cartografía utilizando el API de Google Maps ( si quereis saber más sobre este tema, podeis leer artículos en esta misma web que hacen referencia:  Google Web Toolkit &#38; Google Maps, Integración de un servicio WMS en GoogleMaps usando GWT )  con la cartografía de OpenStreetMap y [...]]]></description>
			<content:encoded><![CDATA[<p>Voy a tratar de diseñar un visor de cartografía utilizando el <a href="http://code.google.com/intl/es/apis/maps/" target="_blank"><strong>API </strong>de <strong>Google Maps</strong></a> ( si quereis saber más sobre este tema, podeis leer artículos en esta misma web que hacen referencia:  <a title="Google Web Toolkit &amp; Google Maps" href="../2009/11/20/google-web-toolkit-google-maps/"><a href="http://www.gisandchips.org/2009/11/20/google-web-toolkit-google-maps/" target="_blank">Google Web Toolkit &amp;  Google Maps</a>,</a><a title="Integración de un servicio WMS en GoogleMaps usando GWT" href="http://www.gisandchips.org/2010/01/18/integracion-de-un-servicio-wms-en-googlemaps-usando-gwt/" target="_self"> Integración  de un servicio WMS en GoogleMaps usando GWT </a>)  con  la cartografía de <a href="http://www.openstreetmap.org" target="_blank"><strong>OpenStreetMap</strong></a> y además voy a incluir datos de servicios de tipo  georeferenciado (<a href="http://www.panoramio.com" target="_blank"><strong>panoramio</strong></a>, <a href="http://www.youtube.com" target="_blank"><strong>youtube</strong></a>, <a href="http://www.flickr.com" target="_blank"><strong>flickr</strong></a>, <strong>kml</strong>, <a href="http://es.wikipedia.org" target="_blank"><strong>wikipedia</strong></a>,&#8230;) en dicho mapa.</p>
<p><a href="http://www.gisandchips.org/wp-content/mapa.png"><img class="alignleft size-medium wp-image-1567" style="margin-left: 5px;margin-right: 5px;border: 0pt none" src="http://www.gisandchips.org/wp-content/mapa-300x170.png" alt="Muestra un curioso mapa mundial lleno de etiquetas de los servicios georreferenciados" width="300" height="170" /></a>Ya aviso que, una vez que muestro todos los servicios en el mapa,  queda  un mapa demasiado lleno de elementos, pero eso  es justo lo que  pretendo con este visor, tener todos los elementos en el mismo mapa y  que se visualicen tanto fotos, videos, artículos de la wikipedia,kml,&#8230;  a la vez, permitiéndo incluso añadir muchos más elementos.</p>
<p>En primer lugar, para mostrar el mapa de <strong>Google Maps</strong>, necesito registrar la clave previamente para el sitio web en cuestion que voy a tratar.</p>
<p><span id="more-1551"></span></p>
<p>Y una vez registrada la clave ya se puede realizar el visor,  la programación del visor sería la siguiente:</p>
<pre class="brush: php; title: ; notranslate">
//Hojas de estilo de google maps
 &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;http://ajax.googleapis.com/ajax/libs/yui/2.7.0/build/reset-fonts-grids/reset-fonts-grids.css&quot;&gt;
 &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;http://ajax.googleapis.com/ajax/libs/yui/2.7.0/build/base/base-min.css&quot;&gt;
 &lt;script type=&quot;text/javascript&quot; src=&quot;http://maps.google.com/maps?file=api&amp;amp;v=2&amp;amp;key=clave_registrada_en_google&quot;&gt;&lt;/script&gt;

&lt;script
type=&quot;text/javascript&quot; charset=&quot;utf-8&quot;&gt;
 google.load(&quot;maps&quot;, &quot;2.x&quot;);

 function load()
 {
 if (!GBrowserIsCompatible())
 return;

 //definicion de variable de copyright del mapa
 var copyOSM = new GCopyrightCollection(&quot;GIS&amp;Chips 2010&quot;);
 copyOSM.addCopyright(new GCopyright(1, new GLatLngBounds(new GLatLng(-90,-180), new GLatLng(90,180)), 0, &quot; &quot;));

 //definicion de las tiles de OpenStreetMap que iran por bajo del mapa
 var tilesMapnik     = new GTileLayer(copyOSM, 1, 17, {tileUrlTemplate: 'http://tile.openstreetmap.org/{Z}/{X}/{Y}.png'});
 var tilesOsmarender = new GTileLayer(copyOSM, 1, 17, {tileUrlTemplate: 'http://tah.openstreetmap.org/Tiles/tile/{Z}/{X}/{Y}.png'});
 var tilesCycle = new GTileLayer(copyOSM, 1, 17, {tileUrlTemplate: 'http://a.andy.sandbox.cloudmade.com/tiles/cycle/{Z}/{X}/{Y}.png'});

 var mapMapnik     = new GMapType([tilesMapnik],     G_NORMAL_MAP.getProjection(), &quot;Mapnik&quot;);
 var mapOsmarender = new GMapType([tilesOsmarender], G_NORMAL_MAP.getProjection(), &quot;Osmarend&quot;);
 var mapCycleMap = new GMapType([tilesCycle], G_NORMAL_MAP.getProjection(), &quot;CycleMap&quot;);

 //definicion de mapa incluyendo los tres tiles de OpenStreetMap

 var map           = new GMap2(document.getElementById(&quot;map&quot;), { mapTypes: [mapMapnik, mapOsmarender,mapCycleMap] });

 //se añaden los controles de mapa del API de Google Maps
 map.addControl(new GLargeMapControl()); //control de desplazamiento y acercamiento de gran tamaño empleado en  Google Maps.
 map.addControl(new GMapTypeControl());  //Permite alternar entre los diferentes tipos de mapas (en este caso las tres tiles)
 map.addControl(new GOverviewMapControl()); //Este control nos permite mostrar una ventana de vista rápida contraible

//Situado el centro, las coordenadas de la Universidad de Alicante
 map.setCenter( new GLatLng(38.38575, -0.51486), 16);

 map.enableScrollWheelZoom();
 map.enableContinuousZoom();

 //Se definen como GLayers los servicios georeferenciados que quiero incluir

 var wikipedia = new GLayer(&quot;org.wikipedia.es&quot;);
 var panoramio = new GLayer(&quot;com.panoramio.all&quot;);
 var youtube = new GLayer(&quot;com.youtube.all&quot;);
 var webcam = new GLayer(&quot;com.google.webcams&quot;);

 map.addOverlay(wikipedia);
 map.addOverlay(panoramio);
 map.addOverlay(youtube);
 map.addOverlay(webcam);

 //En este caso como los servicios son un xml se añaden como GGeoXML(tanto el api de flickr como cualquier KML que quiera incluir lo debo definir as�)
 var geoXmlFlickr;
 geoXmlFlickr = new GGeoXml(&quot;http://api.flickr.com/services/feeds/geo/?tags=universidad+alicante&quot;);
 map.addOverlay(geoXmlFlickr);

//Ejemplo de introducción de un KML
 var tram = new GGeoXml(&quot;http://www.sigua.ua.es/web/utils/acceso/kml/TRAM4.kml&quot;);
 map.addOverlay(tram);
 }
</pre>
<p style="text-align: center">Aqui se puede ver como se muestra en el mapa el KML importado<br />
<a href="http://www.gisandchips.org/wp-content/kml.png"><img class="size-medium wp-image-1560 aligncenter" src="http://www.gisandchips.org/wp-content/kml-300x246.png" alt="Se muestra como queda la integracion de un archivo kml con información sobre una linea de tranvia en Alicante en el mapa anteriormente desarrollado" width="300" height="246" /></a></p>
<pre class="brush: xml; title: ; notranslate">

 &lt;/script&gt;

 &lt;/head&gt;

 &lt;body onload=&quot;load()&quot; onunload=&quot;GUnload()&quot;&gt;
 &lt;div&gt;
 &lt;div role=&quot;main&quot;&gt;
 &lt;div&gt;
 &lt;div id=&quot;map&quot; style=&quot;width: 100%; height: 600px;&quot;&gt;&lt;/div&gt;

 &lt;/div&gt;
 &lt;/div&gt;
 &lt;/div&gt;
</pre>
<p>El visor completo con todos los datos estaría accesible desde la siguiente URL:<br />
<a class="aligncenter" title="Mapa " href="http://www.gisandchips.org/demos/pepe/osm/test.html" target="_blank">http://www.gisandchips.org/demos/pepe/osm/test.html</a></p>
<p>Como digo, este mapa incluye tantos elementos que a veces es un poco  extraño, pero es justo lo que pretendía, incluir muchos servicios en un  mismo mapa.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gisandchips.org/2010/03/24/api-de-google-openstreetmap-y-otros-servicios-georreferenciados/feed/</wfw:commentRss>
		<slash:comments>4</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>
	</channel>
</rss>

