archivo

Archivo de la etiqueta: Python

linexyEl otro día un compañero en las listas de usuarios de gvsig de nos planteó una necesidad que tenía: añadir a la tabla de datos 4 campos nuevos que contuvieran las coordenadas X, Y de inicio y fin de las lineas que tenía su shape.

La solución sin usar programación la podéis seguir en dos emails[1] [2], explicada gracias a Gustavo Agüero Córdoba . 

Me parecía un buen ejemplo sencillo para exponer y aquí lo tenéis, simplemente tenéis que tener la capa activa donde queréis realizar los cambios,  y ejecutarlo. Sencillo pero que te puede ser muy útil, o servirte de base para algo que utilices.

import gvsig
from geom import *

def main():
    """Obtiene los vertices finales de 
        lineas y los agrega a tabla"""
    #Nuevo nombre para los campos X,Y
    campoX = "X_final"
    campoY = "Y_final"
    campoXi = "X_inicial"
    campoYi = "Y_inicial"
    layer = gvsig.currentLayer()
    
    schema = layer.getSchema()
    schema.modify()
    schema.append(campoXi,"DOUBLE", 30)
    schema.append(campoYi,"DOUBLE", 30)
    schema.append(campoX, "DOUBLE", 30)
    schema.append(campoY, "DOUBLE", 30)
    layer.edit()
    layer.updateSchema(schema)
    layer.commit()
    features = layer.features()
    for line in features:
        geom = line.geometry()
        vertex = geom.getVertex((geom.numVertices-1))
        vertexi = geom.getVertex(0)
        x,y = vertex.getX(), vertex.getY()
        xi,yi = vertexi.getX(), vertexi.getY()
        line.edit()
        line.set(campoX, x)
        line.set(campoY, y)
        line.set(campoXi, xi)
        line.set(campoYi, yi)
        layer.update(line)
        
    layer.commit()

 

leafgvsigEste post es un pequeño ejemplo de algunas funcionalidades extra que le podemos dar a nuestro módulo de Scripting en gvSIG 2, en este caso crear un visor web con solo un click. Pero primero de todo decir que el script ni esta perfecto ni pretende estarlo, solo quiero demostrar posibles nuevos usos de este módulo y animaros a probar cosas nuevas.

gvsig2leaflet viene de la idea del post que salió recientemente sobre qgis2leaflet un plugin (mucho más completo) para QGIS, pero en un rato que tuve libre quería demostrar que esto también es posible desde gvSIG 2 y directamente con el módulo de Scripting.

leafgvsig3

Carpeta visor Leaflet

Actualmente lo que hace es, desde una vista donde las capas estén en WGS84 y una capa de polígonos(polígonos simples, el problema es que aún no dispongo de un exportador a geojson completo desde scripting) que tengamos activa (con o sin selección), nos genera en una carpeta (que configuramos en las primeras lineas de código) con un archivo index.html (visor de Leaflet) y una carpeta dentro con el archivo js/geojson.js que contendrá los datos vectoriales de nuestros polígonos en formato geojson, pero preparados para ser usado en Leaflet. Hay alguna funcionalidad básica añadida, si modificáis la linea 55 podéis cambiar las propiedades de vuestros polígonos que muestran los popup al hacer click.

En un click

En un click

El resultado es una carpeta con un visor que podéis abrir o subir a vuestro host y visualizar vuestros datos. La podéis abrir directamente en cualquier navegador.

Ejemplo de uso

Ejemplo de uso

Añadir más funcionalidades, o exportar diferentes capas a la vez, añadir menus o estilos, sería cuestión de ponerse y programar, pero la posibilidad de hacerlo esta ahí, solo quería demostrar que hay más cosas posibles desde este módulo.

from gvsig import *
import os

def main(*args):

    global pathGeojson
    global pathHtml
    global path

    path = "C:/gvsig/geojson/leaflet/"
    pathGeojson = path + "js/geojson.js"
    pathHtml = path + "index.html"

    directorio = os.path.join(path, 'js')
    if not os.path.isdir(directorio): os.mkdir(directorio)

    f1 = open(pathHtml,"wb")
    layer = currentLayer()
    exportLayer(layer)

    f1.write("""
    <!DOCTYPE html>

    <html>
    <head>
        <title>Prueba</title>
        <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.css" />
        <script src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js"></script>
        <script type="text/javascript" src="js/geojson.js"></script>
    </head>

    <body>
    <div id="map"></div>

    <div id="map-div"></div>

    <style>
        #map { height: 600px; }
    </style>

    """)

    f1.write("""
    <script>

    var map = L.map('map').setView([%s, %s], 5);
        L.tileLayer('http://a.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: 'Map data &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="http://cloudmade.com">CloudMade</a>',
        minZoom: 3,
        maxZoom: 15,
        tms: false
        }).addTo(map);

    function onEachFeature(feature, layer) {
        layer.bindPopup(feature.properties.NAME);
    }

    L.geoJson(geojsonFeature, {
    onEachFeature: onEachFeature
    }).addTo(map);

    </script>
    </body>
    </html>
    """ % (40,3))
    f1.close()

def exportLayer(layer):
    #features o seleccion
    if layer.getSelection().getCount() == 0:
        selection = layer.features()
    else:
        selection = layer.getSelection()
    exportFeatureCollection(selection)

def exportFeatureCollection(selection):
    print "Export Feature Collection"
    f = open(pathGeojson,"wb")
    f.write(""" var geojsonFeature = {
    "type": "FeatureCollection",
    "features": [
    """
    )
    listVert = []
    typeGeom = ""
    count=0
    end= selection.getCount()

    for sel in selection:
      geom = sel.geometry()
      listVert, typeGeom = exportGeom(geom)
      #todos los atributos a una lista de properties:
      dic =  sel.getValues()
      properties = []
      for di in dic:
        try:
            value = str(di)
        except:
            value = "field1"
        try:
            value2 = str(dic[di])
        except:
            value2 = "row2"

        properties.append([value,value2])
      writeFeatureCollection(listVert, typeGeom, properties, f)
      count +=1
      if not(count == end): f.write(",")

    f.write("""
    ]
    };
    """
    )
    f.close()

def writeFeatureCollection(listVert, typeGeom, properties, f):
    tipo = '"'+str(typeGeom)+'"'

    f.write( """
    {
    "type": "Feature",
      "properties": {
      """)
    end = len(properties)
    start = 0
    for prop in properties:
            prop1 = '"'+str(prop[0])+'"'
            prop2 = '"'+str(prop[1])+'"'
            stri = str(prop1) + ":" + str(prop2)
            f.write(stri)
            start += 1
            if not(start == end): f.write(",")

    f.write( """
      },
      "geometry": {
        "type": """+ tipo +""" ,
        "coordinates": [ """ +
    str(listVert) +
    """
    ]
    }
    }
    """ )

def exportGeom(geom):
    print "geometria: ", geom
    if str(geom) == "Surface2D":
        typeGeom = "Polygon"
        vert = geom.getNumVertices()
        listVert = []
        for i in range(0, vert):
            x = geom.getVertex(i).getX()
            y = geom.getVertex(i).getY()
            listVert.append([x,y])

        x = geom.getVertex(0).getX()
        y = geom.getVertex(0).getY()
        listVert.append([x,y])
    return listVert, typeGeom

Espero animaros a que creéis vuestros propios scripts, y si veo la gente animada, me comprometo a acabar mejorando este script para que sea mucho más completo.

Un saludo

dialogPDF01

El otro día salió publicado en el Blog de gvSIG un post de Joaquin del Cerro sobre como abrir un PDF en un visor dentro de gvSIG desde el módulo de scripting. Así que aprovechando la nueva opción de Dialog de la nueva build de gvSIG he decidido hacer un pequeño ejemplo que lo junte todo. También gracias a la ayuda de unos ejemplos realizados por Victor Acevedo.

Los que os lo habéis perdido podéis echar un vistazo a mi anterior post donde explico todo este nuevo apartado paso a paso.

El script consiste en una ventana que al seleccionar una entidad en la capa y hacer click sobre el botón de “Info” nos sacará toda la información de esa entidad en la caja de texto. Podéis ver que el botón de PDF se encuentra desactivado por ahora.

dialogPDF02

Además, si existe un campo llamado “PDF” haremos que se active el botón de PDF, y al presionarlo, nos abrirá el visor que os comentaba al PDF enlazado. Está todo muy simplificado para hacerlo más entendible.

dialogPDF03

El código correspondiente al dialog es:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- generated by ThinG, the Thinlet GUI editor -->
<panel colspan="0" columns="1" gap="1" height="156" rowspan="3" scrollable="true" width="300">
    <panel columns="2">
        <label text="Info"/>
    </panel>
    <panel colspan="2" columns="2" rowspan="0" weightx="1" weighty="1">
        <textarea colspan="2" columns="36" editable="false" halign="center" height="1" name="txtArea" rows="6" valign="center"/>
    </panel>
    <panel>
        <button action="verInfo" name="btnInfo" text="Info"/>
        <button action="verPDF" enabled="false" name="btnPDF" rowspan="2" text="PDF"/>
    </panel>
</panel>

Y el código que ejecuta es:

from gvsig import *
from org.gvsig.tools import ToolsLocator
from org.gvsig.tools.swing.api import ToolsSwingLocator
from java.net import URI

def onload(*args):
    """Dialog info de entidad y abrir pdf"""

    global rutapdf
    rutapdf = "C://gvsig/testpuntos/"
    pass

def verInfo():
    #Accion del boton Info

    feature = iter(currentLayer().getSelection()).next()

    info = currentLayer().name
    schema = currentLayer().getSchema().getAttrNames()
    for sch in schema:
        info += "\n" + str(sch) + "    " + str(feature.get(sch))

    btnPDF = dialog.find("btnPDF")

    try:
         if feature.get("PDF"): dialog.setBoolean(btnPDF, "enabled", True)
    except:
         dialog.setBoolean(btnPDF, "enabled", False)

    txt = dialog.find("txtArea")
    dialog.setString(txt, "text", info)

def verPDF():
    #Accion del boton PDF
    feature = iter(currentLayer().getSelection()).next()
    name = feature.get("PDF")
    ruta = rutapdf + name
    visorPDF(ruta)

def visorPDF(rutaAbsoluta):
    formatManagers = ToolsLocator.getExtensionPointManager().get("HyperLinkAction")
    pdfManager = formatManagers.get("PDF_format").create()
    panel = pdfManager.createPanel(URI("file:/"+rutaAbsoluta))
    windowManager = ToolsSwingLocator.getWindowManager()
    windowManager.showWindow(panel,"Visor PDFs",windowManager.MODE.WINDOW)

Para cualquier duda me la podéis consultar en los comentarios o por email.

dialog00

Te permite crear ventanas a medida

Acaba de salir una nueva build para  gvSIG 2.1.0.2217 en fase de desarrollo en la que se han realizado grandes mejoras en el módulo de Scripting y tiene varias novedades que iremos anunciando próximamente. Al estar en fase de desarrollo se agradece que reportéis todos los bugs que podáis encontrar.

Una nueva característica (que ya estaba implementada pero no funcionaba correctamente) que podemos encontrar es el módulo de diálogos. Este módulo dentro del apartado de scripting nos permitirá crear fácilmente ventanas interactivas con etiquetas, cajas de texto, opciones, botones, etc. Las posibilidades son infinitas. Esto aumenta en gran medida la forma de interactuar con nuestro script, así como aumentar las capacidades del mismo.

·

Cómo crear un script de tipo Dialog

Para empezar a probar estos scripts lo que necesitamos es:

  • Nueva build en desarrollo de gvsig, minimo la versión 2.1.0.2217
  • Instalar el módulo de Scripting en el Administrador de complementos
Dentro de: Herramientas - Administrador de complementos

Dentro de: Herramientas – Administrador de complementos

dialog02

Con esto ya podemos ir a Herramientas – Scripting – Scripting Composer.

Al crear un nuevo script, lo que debemos de cambiar ahora es el Type y seleccionar Dialog en vez de Script que es lo que estamos acostumbrados.

Nos aparecerá un nuevo script en la pantalla en blanco, parece todo similar aunque podemos ver que existen un par de diferencias. Si nos fijamos existen unas pestañas extras a los que no estamos acostumbrados. Estos scripts constan de dos partes (cuando se almacenan también constan de dos archivos), una parte se encarga de la creación de la ventana y de las características que tiene, y otra parte es el código que ejecuta este script.

dialog03

Podemos ver un Menu (#1) nuevo arriba que nos servirá para editar, cargar, etc, este tipo nuevo de scripts. Podemos ver la lista de objetos (#2) que contiene el formulario , aquí iremos añadiendo los nuevos botones y demás. En las pestañas (#3) podemos encontrar dos, una pestaña XML que nos sirve para ver nuestro diálogo en texto plano, también para pegar uno que nos pasen hecho, y la pestaña de Preview, que nos sirve para previsualizar directamente nuestro formulario. En las pestañas de código (#4) podemos cambiar entre Dialog (edición de la parte de diálogo) y Code (edición del código que ejecuta).

Nuestro primer Dialog

El siguiente paso es dar click sobre el Panel que nos aparece en la lista de objetos del formulario (#2). Al hacer click sobre este se nos cambia la barra de herramientas y nos aparecen un grupo nuevo con todas las opciones que tenemos, desde botones, combobox, cajas de text, listas…

dialog04

dialog05Para hacer un pequeño script de prueba, vamos a empezar por añadir un nuevo dialog a nuestro panel (es el segundo botón de la lista), esto va a crear una ventana (dialog) donde aparecerán todos los objetos que queramos. Para crear una etiqueta dentro de esta nueva ventana, hacemos click en el dialog que nos acaba de aparecer en la lista, y ahora damos click al boton que pone label. Nos aparecerá un nuevo objeto en la lista, y si hacemos click podemos acceder a sus propiedades como podemos ver en la imagen.

Podemos ir al campo text y cambiar el texto que contendrá. Al presionar Enter nos modificará el valor de la etiqueta, y la podremos ver ya en la pestaña de Preview. También en la propiedad name podemos escribir etiqueta1, esto nos permitirá más tarde poder encontrar y modificarla desde el botón.

Ahora siguiendo el mismo paso vamos a crear un botón que nos permitirá ejecutar acciones. Haciendo click primero en el dialog, y luego sobre “Create new button” a la derecha del botón de label. Editaremos sus propiedades y iremos a text = “Consola” y la propiedad de action o acción escribiremos “abrirConsola”, esto lo veremos ahora.

Escribir el código a ejecutar

Una diferencia con los scripts normales, es que el código de este tiene una estructura algo diferente, y es obligado que conste de una función onload(), al contrario que en los scripts normal que se denomina main().

Así que podemos borrar el código que nos aparece y sustituirlo por este:

from gvsig import *

def onload():
    pass

Este sería el código mínimo para lanzar un script de tipo dialog.

Esto que vamos a hacer es solo un ejemplo, pero las posibilidades son muy grandes, podemos crear botones que nos muestren información de las capas que tengamos, botones que nos modifiquen los valores de cajas de texto para mostrar información, que nos permitan lanzar filtros para hacer selecciones fácilmente ajustando barras que creemos, etc.

Para editar lo que hará nuestro botón tenemos que crear una función “abrirConsola”, esta se lanzará al presionar el botón tal y como indicamos en su propiedad de action.

Para que abra la consola desde el botón tenemos que importar una librería console. Así que el código podría quedar algo así:

from gvsig import *
from console import console

def onload():
    pass

def abrirConsola():
    #Abre la consola de Jython
    console.main()

    #Modifica el valor text de la etiqueta
    lblEtiqueta = dialog.find("etiqueta1")
    texto = "Consola abierta!"
    dialog.setString(lblEtiqueta,"text", texto)

También podéis pegar el XML (para la configuración del dialogo) directamente desde su pestaña. Si copias este de ejemplo y vais a la pestaña del XML, en el menú nuevo que nos aparece podemos hacer click en Edit – Paste from System Clipboard y ya tendremos el dialog que he creado aquí.

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- generated by ThinG, the Thinlet GUI editor -->
<panel>
    <dialog>
        <label name="etiqueta1" text="Script de prueba"/>
        <button action="abrirConsola" text="Consola"/>
    </dialog>
</panel>

El resultado como podéis ver al ejecutar este script es el siguiente, primero nos aparece una pantallita..

dialog06

…y al presionar el botón se nos cambia la etiqueta y se nos abre la consola.

dialog07

Próximamente realizaré algún ejemplo más elaborado y más aplicado a nuestro proyecto, pero lo básico está explicado, podéis crear un dialog y modificar sus propiedades, acepto sugerencias para scripts y no olvidéis que está en fase de desarrollo así que si veis algún bug podéis reportarlo a la página de gvsig.

gmailpythonsimple

Merodeando por Internet encontré una página con contenido muy interesante “Pythin, GIS and Stuff”, en ella vi un código que ya había pensado en buscar y es la opción de poder enviar un email con los resultados a nuestra cuenta desde el propio script. Y esto se hace de una forma muy sencilla. He simplificado un poco el de su web para que podáis pegar y copiar directamente sea cual sea la plataforma que vais a usar (en su web ejemplo de uso con ArcGIS).

Al ser una librería de Python, este código funciona desde cualquier sitio donde lo ejecutes, puedes agregarlo a scripts de OGR/GDAL desde editores externos,o a tus scripts en gvSIG, PyQGIS o ArcPy. En todos funciona perfectamente por lo que he podido probar.

Lo único que necesitamos introducir es la información una cuenta Gmail, la contraseña, y la cuenta destinatario. (Cuidado si le mandáis a alguien el script con eliminar la contraseña).

La función aceptará dos variables, el mensaje (una variable tipo string) y una variable booleana (True/False) por si queremos modificar el contenido de alguna forma, en este caso solo indica un supuesto de si se han procesado bien o mal los cálculos, cambiando el encabezado del email.

·

pythonemail

·

Además se ha añadido que la variable se cargue con los datos de un txt que hallamos podido crear. Esto se puede eliminar, solamente dejando que se asigne el valor de una variable string normal, asignar directamente a strResult un valor.

Copiando y pegando este bloque de código solamente deberéis de usarlo llamandolo de la forma:

sendResultEmail(strResult, success)

·

import smtplib

def sendResultEmail(msgContents, success_TF):
    '''sendResultEmail(msgContents, success_TF)'''

    #Info
    to = 'email@gmail.com'
    send_username = 'email@gmail.com'
    send_password = '*****'
    smtpserver = smtplib.SMTP("smtp.gmail.com",587)

    if success_TF:
        subject = 'Resultados de la prueba de email: script SUCCESS.'
    else:
        subject = 'Resultados de la prueba de email: script FAILURE.'
    smtpserver.ehlo()
    smtpserver.starttls()
    smtpserver.ehlo
    smtpserver.login(send_username, send_password)
    header = 'To:' + to + '\n' + 'From: ' + send_username + '\n' + 'Subject:' + subject + '\n'
    msg = header + '\nArcpy results: \n' + msgContents + '\n'
    smtpserver.sendmail(send_username, to, msg)
    smtpserver.close()

outTxt = "c:/gvsig/uni/dataInfo.txt"
txtFile = open(outTxt, "r")
strResult = txtFile.read()
txtFile.close()
success = True

sendResultEmail(strResult, success)

pyqgisCargarCapas01Hoy miramos el siguiente apartado sobre PyQGIS, programación Python en QGIS, sobre como cargar capas con PyQGIS. Este apartado es referente al segundo tema del PyQGIS Cookbook “Loading Layers”. Si te has perdido los dos post anteriores puedes buscarlos aquí.

Solo voy a poner los ejemplos básicos de como cargar un shape, un archivo csv y una imagen raster, y añadirlos a la vista. En la web podéis encontrar como acceder a más tipos de capas como WMS, WFS, SQL, etc. Por ahora vamos a lo sencillo.

Como cualquier script primero debemos de comenzar por importar la librería de QGIS con la linea:

from qgis.core import *

Ahora si queremos cargar una capa vectorial, indicaremos la capa que queremos y el formato de capa vectorial con el que lo queremos leer que es “ogr”, capaz de leer la mayoría de capas vectoriales (layer).

layer = QgsVectorLayer(“C:\gvsig\castilla-leon\embalses.shp”, “Embalses OGR” , “ogr”)

Para agregar un fichero del tipo csv como capa, debemos hacerlo de la siguiente manera: primero hay que indicar donde se encuentra el csv, como es un fichero tenemos que indicarlo primero con file:/// y luego la ruta, quedando así: file:///ruta/completa/hasta/fichero.csv sino dará error si no escribirmos el file. Además debemos indicar cual es el delimitador entre palabras, cual es la columna X y la columna Y, cargamos la capa (vlayer) con QgsVectorLayer(). En mi caso se queda así:

uri = “file:///C:/gvsig/tornados.csv?delimiter=%s&xField=%s&yField=%s” % (“,”, “X”, “Y”)
vlayer = QgsVectorLayer(uri, “Tornados CSV”, “delimitedtext”)

pyqgisCargarCapas02

Para cargar los raster, hay un problema en como lo explica en la página porque si escribes el código como indica da un error de que no encuentra el comando QFileInfo(). Esto sucede porque hay que importar primero las librerías que nos permiten esto. Si queremos seguir trabajando con el raster deberíamos de importar también la librería gdal

from PyQt4.QtCore import *
from PyQt4.QtGui import *

Así que queda de la siguiente manera, el raster de prueba me lo descargue de aquíCon esto lo que hacemos es indicar donde se encuentra nuestro raster (tif), accedemos a la info del archivo (fileInfo) que QGIS pueda leer, a su nombre (baseName), y cargamos por último la capa raster pasando la información (rlayer)

fileName = “C:\gvsig\donana.tif”
fileInfo = QFileInfo(fileName)
baseName = fileInfo.baseName()
rlayer = QgsRasterLayer(fileName, baseName)

Y por último agregamos las capas cargadas a nuestro mapa:

QgsMapLayerRegistry.instance().addMapLayer(layer)
QgsMapLayerRegistry.instance().addMapLayer(vlayer)
QgsMapLayerRegistry.instance().addMapLayer(rlayer)

El script final para agregar estás tres capa queda así:

from qgis.core import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *

#Vector
layer = QgsVectorLayer(&quot;C:\gvsig\castilla-leon\embalses.shp&quot;
  , &quot;Embalses OGR&quot;, &quot;ogr&quot;)
if not layer.isValid():
  print &quot;Layer failed to load!&quot;

#CSV
uri = &quot;file:///C:/gvsig/tornado.csv?delimiter=%s&amp;xField=%s&amp;yField=%s&quot;
   % (&quot;,&quot;, &quot;X&quot;, &quot;Y&quot;)
vlayer = QgsVectorLayer(uri, &quot;Tornados CSV&quot;, &quot;delimitedtext&quot;)

#Raster
fileName = &quot;C:\gvsig\donana.tif&quot;
fileInfo = QFileInfo(fileName)
baseName = fileInfo.baseName()
rlayer = QgsRasterLayer(fileName, baseName)
if not rlayer.isValid():
  print &quot;Layer failed to load!&quot;

#Add layer
QgsMapLayerRegistry.instance().addMapLayer(layer)
QgsMapLayerRegistry.instance().addMapLayer(vlayer)
QgsMapLayerRegistry.instance().addMapLayer(rlayer)

print &quot;End&quot;

Y esto es todo por ahora, con esto conseguimos cargar 3 capas en nuestro proyecto.

Manual basado en la guía de “PyQGIS Cookbook”, más información en su web.

Recordaros que ahora podéis seguir Másquesig también en la página de Facebook.