Creación de un mapa sensible utilizando Pl/PgSQL y PHP_Mapscript

El objetivo no es otro que convertir nuestro mapa en algo más interactivo para el usuario, dando más información que la meramente visual. El siguiente artículo, muestra el camino para alcanzar nuestra meta,  seguramente no sea la única ni la mejor, pero nos será útil para nuestro fín.

Obviaremos la construcción del mapfile, ya que fue abordado en otro artículo, pero trabajaremos a partir de ese código.

Lo primero que necesitamos hacer es preparar la función en lenguaje PL/pgSQL que nos devolverá las coordenadas de los vértices que componen cada uno de los polígonos a partir de un parámetro, que en nuestro caso será el código municipal, y posteriormente se hará la conversión de esos vértices de metros a pixel con PHP.

CREATE OR REPLACE FUNCTION gc_listvertices(cod_municipal character varying)
RETURNS SETOF record AS
$BODY$
DECLARE
countpol bigint;
polygon geometry;
perimeter geometry;
countpoints integer;
i integer;
j integer;
point record;
BEGIN
--Teniendo en cuenta que cada municipio puede estar compuesto de varios polígonos, primero hacemos un conteo de los polígonos de cada municipio
select into countpol count(*) from (select st_dump(geometria) from municipios where codmun= cod_municipal) foo;
for j in 1..countpol loop
--Por cada municipio, nos devolverá los polígonos descompuestos. La función st_dump, lo que nos devuelve es tantas filas como polígonos contiene el municipio en cuestión, es decir nos descompone el MULTIPOLYGON en un array que contiene un único POLYGON
select into polygon geom from (select (st_dump(geometria)).path, (st_dump(geometria)).geom from municipios where codmun = cod_municipal) foo where path[1] = j;
--La función st_exteriorring nos devuelve un LINESTRING, que representa el anillo exterior del polígono dado.
--ATENCIÓN: esta función no trabaja con MULTIPOLYGON, de ahí la descomposición anterior
select into perimeter st_exteriorring(polygon);
--Contamos el número de vértices que componen el POLYGON
select into countpoints st_npoints(polygon);
for i in 1..countpoints loop
--Por último, nos guardamos en una variable, la coordenada X y la coordenada Y de cada vértice.
--ATENCIÓN: puesto que nos devuelve indiscriminadamente las coordenadas de todos los vértices de todos los polígonos que componen el municipio, se ha añadido un campo booleano que nos devuelve true en caso de ser un vértice final.
select into point st_x(st_pointn(perimeter, i)), st_y(st_pointn(perimeter, i)), (i=countpoints) as tend;
return next point;
end loop;
end loop;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION gc_listvertices(character varying) OWNER TO postgres;

Ahora sólo queda hacer la conversión con PHP:

<?php header('Content-Type: text/html; charset=UTF-8'); ?>
<html>
<head>
<title>Ejemplo de mapa sensible</title>
<style type="text/css">
.munInfo{
border: 1px solid #C0C0C0;
background-color: white;
border-bottom-width: 0;
width: 170px;
height: 70px;
position: absolute;
}
</style>

<script>
function info(nombre,area)
{
        document.getElementById('munInfo').innerHTML = "Municipio="  + nombre +" <br> Area =" + area +" km2";
}
</script>
</head>
<body>
<?php
//Este código es el usado en un artículo anterior, pero para hacer más fluido el ejemplo, sólo se han seleccionado los municipios de la Comunidad Valenciana. Lo ideal es automatizar tanto la extensión máxima de la caja de la geometría como su equivalencia en el tamaño en pixeles del mapa. Para hacer el ejemplo más fluido se añaden estos parámetros a mano.
dl('php_mapscript.so');
$Map=ms_newMapObj("");
$Map->set("name","Mapa");
//Equivalencia en pixeles del tamaño del mapa
$Map->setSize(432,750);
//Extensión de la caja para los municipios de la Comunidad Valenciana
$Map->setExtent(626680,4191059,815673.125,4519371);
$Map->web->set("imagepath","/tmp");
$Map->web->set("imageurl","/ms_tmp");
$Layer2=ms_newLayerObj($Map);
$Layer2->set("name","Municipios");
$Layer2->set("type",MS_LAYER_POLYGON);
$status = $Layer2->set("status",MS_ON);
$Layer2->setConnectionType(MS_POSTGIS);
$Layer2->set("connection","user=****** dbname=demos host=localhost");
//La consulta ha sido modificada con respecto a la original, para mostrar sólo los municipios de la Comunidad Valenciana
$Layer2->set("data","geometria from (select geometria,gid from  ine.municipios where com = '10') as foo  using unique gid using srid=23030");
$clase = ms_newClassObj($Layer2);
$estilo = ms_newStyleObj($clase);
$estilo->color->setRGB(217,233,107);
$estilo->outlinecolor->setRGB(0,0,0);
$Image=$Map->Draw();
$url_imagen=$Image->saveWebImage();
//Añadimos que la imagen será usada como un mapa de html
echo "<img src=".$url_imagen." usemap='#sensible'>";
echo "<map name='sensible'>";

//Lo siguiente, es conectarse a la base de datos para consultar los municipios que vamos a mostrar en el mapa
$conn = pg_connect("host=localhost port=5432 dbname=demos user=****** password=******");
$sqlmun= "select nombre04 as nombre,codmun as codmunicipal, area(geometria) as area from ine.municipios where com = '10'";
$poligono = pg_query($conn,$sqlmun);
$rowspol=pg_num_rows($poligono);
for ($h=0; $h<$rowspol; $h++)
{
        $arraypol=pg_fetch_array($poligono,$h);
        $nombre=$arraypol['nombre'];
        $codmun=$arraypol['codmunicipal'];
        $area=round(($arraypol['area']/1000000),2);

        $pxmin = 626680;
        $pymin = 4191059;
        $pxmax = 815673.125;
        $pymax = 4519371;

        //distancia en metros de los ejes
        $eqx=$pxmax-$pxmin;
        $eqy=$pymax-$pymin;
//Llegados a este punto ejecutamos la función anterior que creamos en la base de datos, para que nos devuelva las coordenadas de los vértices de cada uno de los polígonos
        $sqlpolcoords="select * from ine.gc_listvertices('$codmun') as foo(x float, y float,tend boolean)";
        $polcoords= pg_query($conn,$sqlpolcoords);
        $rowspolcoords=pg_num_rows($polcoords);
        for ($j=0; $j<$rowspolcoords; $j++)
        {
//Obtenemos los tres parámetros por separado que nos devuelve la función y hacemos la conversión de metros a pixel. Como he comentado anteriormente lo ideal es que esto se realice de manera automatizada, sin que introduzcamos nosotros la anchura y la altura del mapa
                $arraypolcoords=pg_fetch_array($polcoords,$j);
                $x=$arraypolcoords['x'];
                $y=$arraypolcoords['y'];
                $tend=$arraypolcoords['tend'];
                $xcal=$x-$pxmin;
                $ycal=$pymax-$y;

                $resx=round(((432*$xcal)/$eqx),0);
                $resy=round(((750*$ycal)/$eqy),0);
                $coordenadas.=$resx.",".$resy.",";
//Lo que va a hacer este parámetro es cortar cada polígono cada vez que devuelva true, de manera que en el caso de Alicante que son cuatro polígonos, los separa cada uno en un área distinta
                if($tend=='t')
                {
                        echo"<area shape=poly coords='$coordenadas' href=\"javascript:void(0);\" onClick=\"munInfo('$nombre','$area');\" alt='$nombre'>\n";
//Para el ejemplo he creado una simple función en javascript que nos muestre el nombre y el área en un div cuando pasemos el ratón por encima de cualquier municipio.
                        $coordenadas="";
                }
        }
}
echo"</map>";
?>
<div id="munInfo" class="munInfo"></div>
</body>
</html>

El resultado del ejemplo será este mapa sensible.

    • Rodrigo Abadia
    • 22 enero 2010 10:58pm

    Es posible descargar el codigo fuente? y conocer la estructura de la tabla”municipios”.

    que es ine.municipios?

    Es necesario que dentro código esté el evento “OnMouseOver”

    • Miguel
    • 26 enero 2010 8:16am

    Hola Rodrigo, el código fuente es el que está publicado. En definitiva es un simple ejemplo orientativo que puedes aplicar a tu propia base de datos.
    La tabla municipios consta del campo de geometría, el nombre del municipio, el cçodigo con que el INE (Instituto Nacional de Estadística) identifica a cada uno de los municipios, el código de la provincia a la cual pertenece y el guid (serial).
    “ine.municipios”, hace referencia a la tabla municipios dentro del esquema llamado ine.
    Y por último, no no hace falta que se use el evento OnMouseOver, puedes usar cualquier otro.

    Un saludo

    • Alejandro
    • 23 febrero 2010 10:24pm

    HOla, y saludos a todos en el foro, por cierto tendrás la estructura fisica de la tabla de municipios o el shape, ya que estoy tratando de seguir el ejemplo, pero no me funciona, quiero ver que campos tiene municipios, iniciando con el codmun ò cod_mun

    Otra cosa , para hacer las funciones pide el campo de geometrya, bueno en mi caso es (the_geom) asi tendre que declararlo ….

    • Alejandro
    • 23 febrero 2010 10:25pm

    Alejandro :
    Hola Miguel, y saludos a todos en el foro, por cierto tendrás la estructura fisica de la tabla de municipios o el shape, ya que estoy tratando de seguir el ejemplo, pero no me funciona, quiero ver que campos tiene municipios, iniciando con el codmun ò cod_mun
    Otra cosa , para hacer las funciones pide el campo de geometrya, bueno en mi caso es (the_geom) asi tendre que declararlo ….
    UN:F [1.7.7_1013]

    please wait…

    Rating: 0.0/5 (0 votes cast)

    • Alejandro
    • 1 marzo 2010 7:41pm

    HOla, a todos, continuando con el ejercicio de mapa sensible, hasta esten punto no me marca, error, ahora mi problema esque esta desfazado, y no se que es, ayuda gracias de antemano. el shape que estoy usando es el chiapas méxico.

      • Anónimo
      • 1 marzo 2010 8:34pm

      Hola Alejandro, entiende que con tan poca información me es imposible dar con una posible solución.
      ¿Hasta qué punto no te marca error?
      ¿A qué te refieres con “desfazado”?
      ¿Usas la shape directamente desde el archivo o previamente la insertaste en posgres?
      Un saludo

    • Alejandro
    • 2 marzo 2010 4:50am

    Gracias por contestar, mira te explico..
    el shape previamente lo converti a postgres y es el que estoy usando para visualizar mi mapa de chiapas (tabla=municipios en postgres),

    1. Puedo visualizar mi mapa del estado de chiapas con postgres.

    $layerPoligono->set( “name”, “Poligonos”);
    $layerPoligono->set( “type”, MS_LAYER_POLYGON);
    $layerPoligono->set( “status”, MS_ON);
    $layerPoligono->set(“connectiontype”,MS_POSTGIS);
    $layerPoligono->set(“connection”,”user=***** password=** dbname=template_postgis host=localhost”);
    $layerPoligono->set(“data”,”the_geom FROM municipios”);

    2. redibuja el mapa de chiapas, en ese momento aparece el puntero de la manita, pero no me muestra nada de información, pero en algunas zonas de mi mapa, la manita desaparece y si me voy afuera del mapa aparece la manita a eso me refiero con desfazado, pero no se cual es mi error, estoy usando mi tabla de municipios de postgres la misma que me pinta los poligonos de chiapas, me hara falta funciones o algún paso.

    asi lo mando a llamar
    $sqlpolcoords=”select * from gc_listvertices(‘”.$codmun.”‘) as foo(x float, y float,tend boolean)”;

    3. Te puedo enviar el shape de municipios, o mi archivo php, para ver cual es mi error, este es mi email: alejandronp417@hotmail.com

    nota:sigo utilizando la misma tabla municipios de postgres

    • Alejandro
    • 8 marzo 2010 9:15pm

    ok, funciona, gracias

    • Alejandro
    • 9 marzo 2010 7:26pm

    Gracias compañeros, me faltaba cambiar unos campos en la funcion gc_listvertices y con respecto al desface el recuadro del mapa lo hice más pequeño, también tuve que emigrar a postgres 8.4.2, porque tenia el 8.2 y no cargaba bien la función, pero gracias por el apoyo

    • Haner
    • 5 septiembre 2010 9:23pm

    Felicitaciones a este grupo y Gracias por los aportes q das para aquellos q nos desarrollamos en el campo de la geomatica
    tengo un a pregunta acerca como se define la conversion de medida geografica a medida pixel es “setSize(432,750)”

    He trabajado con mi propia capa se visualiza y funciona el evento pero en partes de esa capa me arrogan atributos
    que no corresponden a una zona especifica por ejemplo doy clic en un poligono me informa q es la ciudad X pero en relaidad no lo es mas bn es un poligono colindadnte.

  1. Aun no hay enlaces.