Automatizar la generación de informes en gvSIG mediante Scripting

La realización de informes puede ser una tarea tediosa y repetitiva, la cual puede ser automatizada. Después de aparecer varias preguntas relacionadas con este tema en la Lista de Usuarios y visto que podía ser de utilidad para la comunidad, hemos desarrollado un script en el que mostraremos un camino para realizar esta automatización.

En este caso vamos a utilizar la librería jOpenDocument, una librería que nos permitirá editar ficheros de texto (odt)  y hojas de cálculo (ods) de LibreOffice desde nuestro código.

Vamos a utilizar una plantilla y un script ya preparados para mostrar lo que sería el objetivo final de esta automatización. En este ejemplo tenemos un script que saca información de las parcelas que tengamos seleccionadas en una capa determinada. Para ello, accede a la información de varios campos de la entidad, y saca dos imágenes de nuestra Vista, una de la capa completa y otra sobre la entidad seleccionada. La escala que se aplicará a la imagen centrada sobre la entidad será la escala que tengamos en ese momento sobre la Vista.

El resultado que buscamos es este:

2015-09-28 12_05_27-Informess

Para realizar la prueba, he preparado un fichero .zip que contiene todo lo necesario. Podéis descomprimir este fichero quedándose así en C:\gvsig_informes\. El script está preparado para esta ruta, si es otra, deberéis modificarlas al inicio del script.

Lo primero será instalar la librería jOpenDocument. En su web podéis encontrar documentación de ejemplos y su API. Para este ejemplo he utilizado jOpenDocument 1.3 que es la última versión estable. Lo podemos descargar, o copiar desde el zip que os habéis descargado. Es un fichero .jar (el facilitado en el .zip sería jOpenDocument-1.3.jar), el cual deberemos copiar a la carpeta \gvSIG\extensiones\org.gvsig.scripting.app.extension\lib de la carpeta de instalación de gvSIG. Normalmente será:

  • Win: C:\Program Files (x86)\gvSIG desktop 2.2.0\gvSIG\extensiones\org.gvsig.scripting.app.extension\lib
  • Linux: \home\[usuario]\gvsig-desktop\gvSIG_2.2.0_final\gvSIG\extensiones\org.gvsig.scripting.app.extension\lib

Para el ejemplo, ya en gvSIG, debemos de tener abierta en una Vista la capa de manzanas_valencia.shp que adjunto en el zip, y tener esta capa seleccionada en la Tabla de Contenidos (ToC) de la Vista (debe de aparecer en negrita). Además tenemos que seleccionar las entidades de las cuales queremos generar el informe.

Una vez esto, solo quedaría cargar nuestro script en el Módulo de Scripting, modificar las cuatro rutas que aparecen en él en caso de que fuera necesario, y ejecutarlo:


from gvsig import *
import sys
from geom import *
from java.io import File
from org.jopendocument.dom.template import JavaScriptFileTemplate
from org.jdom import Namespace
from com.sun.jimi.core import Jimi
from org.gvsig.app import ApplicationLocator
import time

def main(*args):
    #Input
    pathTemplate = "C:\\gvsig_informes\\plantilla_informe_test2.odt"

    #Output
    # Utilizar este formato con una unica barra
    pathOutputFile = r"C:/gvsig_informes/resultado/test_parcela%02d"
    pathEnvelope = r"C:/gvsig_informes/resultado/envelope.png"
    pathImageOut = r"C:/gvsig_informes/resultado/img%02d.png"
    
    #Inicio
    print "Informe parcelario"
    application = ApplicationLocator.getManager()
    layer = currentLayer()
    docvista = currentView()
    docwin = application.getDocumentWindow(docvista())
    
    #Get scale
    gsv = currentView().getMapContext().getScaleView()
    
    #Envelope global
    envelope = currentView().getMap().getLayers().getMapContext().getFullEnvelope()
    currentView().getMapContext().getViewPort().setEnvelope(envelope)
    time.sleep(2)
    img = docwin.getMapControl().getImage()
    Jimi.putImage(img,pathEnvelope)



            
    #Create images and odt
    n=0
    for f in layer.getSelection():
        print f.MASA, f.geometry()
        #Center view and set same scale
        geomf = f.geometry()
        centerView(geomf)
        currentView().getMapContext().setScaleView(gsv)
        time.sleep(2)
        #Imagen
        img = docwin.getMapControl().getImage()
        pathImage = pathImageOut % n
        Jimi.putImage(img,pathImage)

        # Create odt
        pathOutFile = pathOutputFile % n
        templateFile = pathTemplate #File(pathTemplate)
        outFile = File(pathOutFile)
        bcFile = pathImage
        template = JavaScriptFileTemplate(templateFile)

        values = f.getValues()
        template.setField("hoja", str(values['HOJA']))
        template.setField("area", str(values['AREA']))
        template.setField("coorx", f.COORX)
        template.setField("coory", f.COORY)
        ddoc = template.createDocument()
        ddoc.getDescendantByName("draw:frame","Imagen2").setAttribute("href", "file:///" + bcFile,Namespace.getNamespace("xlink", "http://www.w3.org/1999/xlink"))
        ddoc.getDescendantByName("draw:frame","Imagen1").setAttribute("href", "file:///" + pathEnvelope,Namespace.getNamespace("xlink", "http://www.w3.org/1999/xlink"))
        ddoc.saveAs(outFile)
        #template.saveAs(outFile)
        n += 1
        
def centerView(geomi):
        envelope = geomi.getEnvelope()
        currentView().getMapContext().getViewPort().setEnvelope(envelope)
        time.sleep(2)

Resumen de los pasos a seguir:

  • Descomprimir fichero del zip quedando el contenido en C:/gvsig_informes (en caso de que sea otra ruta se debe indicar en el script).
  • Copiar el fichero jOpenDocument-1.3.jar a la carpeta \gvSIG\extensiones\org.gvsig.scripting.app.extension\lib de la carpeta de instalación de gvSIG
  • Cargar capa manzanas_valencia.shp en gvSIG
  • Teniendo la capa activa en negrita en la Tabla de Contenido de la Vista
  • Seleccionar varias entidades de la capa
  • Ejecutar el script

Sin embargo, este script aún tiene un par de limitaciones:

  • Las imágenes en la plantilla solo pueden ser remplazadas, esto es, la imagen que cogemos en gvSIG tiene la forma del recuadro de la Vista, si es de diferente tamaño se deformará en la plantilla. Tiene solución si editamos la plantilla con imágenes adaptadas al tamaño que tengamos adecuado, o mediante programación se podrían hacer recortes en la imagen creada.
  • Las imágenes aparecen como enlaces relativos a ficheros de imágenes, por tanto, la imagen no queda realmente dentro de la plantilla, sino es un enlace a ella.
  • Si la plantilla contiene caracteres especiales como tildes me aparece error a la hora de crear el documento. No se seguro por qué aparece este error, tal vez un problema en la codificación o de la plantilla o de la librería.

Si el post resulta de interés para vosotros, publicaremos otros nuevos explicando los pasos seguidos en este para que comprendáis cómo poder modificarlo, y ejemplos de otros tipos como la edición de hojas de cálculo ods o mirar su exportación a PDF.

La librería jOpenDocument abre muchas posibilidades. Cualquier ayuda o aportación será bienvenida.

Espero que les sea de su interés.

 

Actualización para la versión gvSIG 2.3:

He realizado una actualización del script para la nueva versión de gvSIG. Adjunto el código abajo, pero también podéis descargos directamente el paquete de aquí e instalarlo desde el Administrador de Complementos. Después de instalarlo os aparecerá dentro de la carpeta Addons desde el Scripting Composer. El único requisito será tener asignada una carpeta output (se puede modificar), en este script aparece como “C:/gvsig_informes/resultados”. El jar y la plantilla lo coge de forma relativa, haciendo referencia a la propia carpeta del plugin.


from gvsig import *
import sys
import os
path_script = os.path.dirname(__file__)
use_jar(os.path.join(path_script, "jar", "jOpenDocument-1.3.jar"))
from gvsig import geom
from java.io import File
from org.jopendocument.dom.template import JavaScriptFileTemplate
from org.jdom import Namespace
from com.sun.jimi.core import Jimi
from org.gvsig.app import ApplicationLocator
import time
def main(*args):
#Input
print "Script generacion de informes"
path_script = os.path.dirname(__file__)
pathTemplate = os.path.join(path_script, "plantilla", "plantilla_informe_test2.odt")
#Output
# Utilizar este formato con una unica barra
path_output = os.path.join("C:/","gvsig_informes", "resultado")
if os.path.exists(path_output) == False:
raise Exception(OSError, "Path not found: " + path_output)
pathOutputFile = os.path.join(path_output, "test_parcela%02d")
pathEnvelope = os.path.join(path_output, "envelope.png")
pathImageOut = os.path.join(path_output, "img%02d.png")
print pathImageOut
print pathEnvelope
print pathOutputFile
#Inicio
print "Informe parcelario"
application = ApplicationLocator.getManager()
layer = currentLayer()
docvista = currentView()
if layer == None or docvista == None:
raise Exception(ValueError, 'Not layer selected')
return
docwin = application.getDocumentWindow(docvista())
#Get scale
gsv = currentView().getMapContext().getScaleView()
#Envelope global
envelope = currentView().getMap().getLayers().getMapContext().getFullEnvelope()
currentView().getMapContext().getViewPort().setEnvelope(envelope)
time.sleep(2)
img = docwin.getMapControl().getImage()
Jimi.putImage(img,pathEnvelope)
#Create images and odt
n=0
for f in layer.getSelection():
print f.MASA, f.geometry()
#Center view and set same scale
geomf = f.geometry()
currentView().centerView(geomf.getEnvelope())
currentView().getMapContext().setScaleView(gsv)
time.sleep(4)
#Imagen
img = docwin.getMapControl().getImage()
pathImage = pathImageOut % n
Jimi.putImage(img,pathImage)
# Create odt
pathOutFile = pathOutputFile % n
templateFile = pathTemplate #File(pathTemplate)
outFile = File(pathOutFile)
bcFile = pathImage
template = JavaScriptFileTemplate(templateFile)
values = f.getValues()
template.setField("hoja", str(values['HOJA']))
template.setField("area", str(values['AREA']))
template.setField("coorx", f.COORX)
template.setField("coory", f.COORY)
ddoc = template.createDocument()
pathGeometry = ("file:///" + bcFile).replace('\\','/')
pathEnvelopeForHref = ("file:///" +pathEnvelope).replace('\\','/')
ddoc.getDescendantByName("draw:frame","Imagen2").setAttribute("href", pathGeometry,Namespace.getNamespace("xlink", "http://www.w3.org/1999/xlink"))
ddoc.getDescendantByName("draw:frame","Imagen1").setAttribute("href", pathEnvelopeForHref,Namespace.getNamespace("xlink", "http://www.w3.org/1999/xlink"))
ddoc.saveAs(outFile)
#template.saveAs(outFile)
n += 1
def use_jar(fname, root=__file__, isglobal=False):
from org.gvsig.scripting import ScriptingLocator
from java.io import File
import sys
import os
if isinstance(fname,File):
f = fname
fname = f.getPath()
else:
f = File(fname)
if not f.isAbsolute() :
rf = File(root)
if rf.isFile() :
rf = rf.getParentFile()
f = File( rf,fname)
fname = f.getCanonicalPath()
use_libs(fname,isglobal=isglobal)

view raw

informe_odt.py

hosted with ❤ by GitHub

This entry was posted in development, gvSIG Desktop, scripting, spanish and tagged . Bookmark the permalink.

24 Responses to Automatizar la generación de informes en gvSIG mediante Scripting

  1. juampy says:

    Hola Oscar estuve probando el script me parece muy bueno pero solo he podido cargar las imagenes en el informe al seleccionar una entidad pero no asi no he podido hacer que me cargue los atributos en el mismo, nose si hay q definir nuevamente algun enlace o ruta.

  2. Buenas, me alegro que te guste. El script está preparado para funcionar con los atributos de la capa de manzanas_valencia.shp, si estás utilizando otra capa deberás modificar las líneas 63-66 donde utilizamos el setField, te lo explico a continuación. Si estás utilizando la capa de manzanas_valencia no sabría decirte el error.

    Los nombres de “hoja”, “area”, “coorx”, “coory”, hacen referencia al nombre de los campos que tienen en la plantilla, lo que sigue, es el atributo en sí de la capa. Si cambias por otra capa que por ejemplo tiene el campo “ID”, deberás modificar una de estas líneas por: template.setField(“hoja”, str(values[‘ID]))

    Esto es para hacer un apaño temporal y utilizar la misma plantilla. En otro post veremos como crear nuestra propia plantilla utilizando una extensión para LibreOffice, la puedes encontrar en la web de jOpenDocument.

    Un saludo, sigue probando y cualquier cosa nos comentas

  3. Pingback: Automatizar la generación de informes en gvSIG mediante Scripting | GeoNe.ws

  4. juampy says:

    Hola Oscar gracias por responder estuve probando con la capa manzanas_valencia en win xp y sigo todos los pasos como comentas pero ahora me sale el siguiente error en la linea 6: template = JavaScriptFileTemplate(templateFile), me parece que tienen q ver con la libreria pero descargue otra version y sigue lo mismo.

    Running script Informe_Parcelario.
    Informe parcelario
    53228 Surface2D
    java.lang.IllegalArgumentException: java.lang.IllegalArgumentException: Cannot change format version from org.jopendocument.dom.XMLFormatVersion@1919070 OD version null to org.jopendocument.dom.XMLFormatVersion@1a8895b OD version 1.2 in at line number 60S
    tript Informe_Parcelario aborted.
    org.gvsig.scripting.ExecuteErrorException: java.lang.IllegalArgumentException: java.lang.IllegalArgumentException: Cannot change format version from org.jopendocument.dom.XMLFormatVersion@1919070 OD version null to org.jopendocument.dom.XMLFormatVersion@1a8895b OD version 1.2 in at line number 60
    at org.gvsig.scripting.impl.DefaultScriptingScript.invokeFunction(DefaultScriptingScript.java:314)
    at org.gvsig.scripting.impl.DefaultScriptingScript.run(DefaultScriptingScript.java:301)
    at org.gvsig.scripting.impl.DefaultScriptingScript$ScriptTask.run(DefaultScriptingScript.java:372)
    Caused by: javax.script.ScriptException: java.lang.IllegalArgumentException: java.lang.IllegalArgumentException: Cannot change format version from org.jopendocument.dom.XMLFormatVersion@1919070 OD version null to org.jopendocument.dom.XMLFormatVersion@1a8895b OD version 1.2 in at line number 60
    at org.python.jsr223.PyScriptEngine.scriptException(PyScriptEngine.java:191)
    at org.python.jsr223.PyScriptEngine.invokeFunction(PyScriptEngine.java:126)
    at org.gvsig.scripting.impl.DefaultScriptingScript.invokeFunction(DefaultScriptingScript.java:312)
    … 2 more
    Caused by: Traceback (most recent call last):
    File “”, line 60, in main
    at org.jopendocument.dom.ODPackage.checkVersion(Unknown Source)

    at org.jopendocument.dom.ODPackage.setTypeAndVersion(Unknown Source)

    at org.jopendocument.dom.ODPackage.updateTypeAndVersion(Unknown Source)

    at org.jopendocument.dom.ODPackage.putFile(Unknown Source)

    at org.jopendocument.dom.ODPackage$1.processEntry(Unknown Source)

    at org.jopendocument.util.ZippedFilesProcessor.process(Unknown Source)

    at org.jopendocument.dom.ODPackage.(Unknown Source)

    at org.jopendocument.dom.ODPackage.(Unknown Source)

    at org.jopendocument.dom.template.Template.(Unknown Source)

    at org.jopendocument.dom.template.EngineTemplate.(Unknown Source)

    at org.jopendocument.dom.template.JavaScriptFileTemplate.(Unknown Source)

    at org.jopendocument.dom.template.JavaScriptFileTemplate.(Unknown Source)

    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)

    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)

    at java.lang.reflect.Constructor.newInstance(Unknown Source)

    at org.python.core.PyReflectedConstructor.constructProxy(PyReflectedConstructor.java:210)

    java.lang.IllegalArgumentException: java.lang.IllegalArgumentException: Cannot change format version from org.jopendocument.dom.XMLFormatVersion@1919070 OD version null to org.jopendocument.dom.XMLFormatVersion@1a8895b OD version 1.2

    at org.python.core.Py.JavaError(Py.java:481)
    at org.python.core.Py.JavaError(Py.java:474)
    at org.python.core.PyReflectedConstructor.constructProxy(PyReflectedConstructor.java:220)
    at org.python.core.PyReflectedConstructor.__call__(PyReflectedConstructor.java:179)
    at org.python.core.PyObject.__call__(PyObject.java:345)
    at org.python.core.PyMethod.instancemethod___call__(PyMethod.java:220)
    at org.python.core.PyMethod.__call__(PyMethod.java:211)
    at org.python.core.PyMethod.__call__(PyMethod.java:206)
    at org.python.core.Deriveds.dispatch__init__(Deriveds.java:19)
    at org.python.core.PyObjectDerived.dispatch__init__(PyObjectDerived.java:1057)
    at org.python.core.PyType.type___call__(PyType.java:1565)
    at org.python.core.PyType.__call__(PyType.java:1548)
    at org.python.core.PyObject.__call__(PyObject.java:387)
    at org.python.core.PyObject.__call__(PyObject.java:391)
    at org.python.pycode._pyx21.main$1(:43)
    at org.python.pycode._pyx21.call_function()
    at org.python.core.PyTableCode.call(PyTableCode.java:165)
    at org.python.core.PyBaseCode.call(PyBaseCode.java:301)
    at org.python.core.PyFunction.function___call__(PyFunction.java:376)
    at org.python.core.PyFunction.__call__(PyFunction.java:371)
    at org.python.core.PyFunction.__call__(PyFunction.java:361)
    at org.python.core.PyFunction.__call__(PyFunction.java:356)
    at org.python.jsr223.PyScriptEngine.invokeFunction(PyScriptEngine.java:124)
    … 3 more
    Caused by: java.lang.IllegalArgumentException: Cannot change format version from org.jopendocument.dom.XMLFormatVersion@1919070 OD version null to org.jopendocument.dom.XMLFormatVersion@1a8895b OD version 1.2
    at org.jopendocument.dom.ODPackage.checkVersion(Unknown Source)
    at org.jopendocument.dom.ODPackage.setTypeAndVersion(Unknown Source)
    at org.jopendocument.dom.ODPackage.updateTypeAndVersion(Unknown Source)
    at org.jopendocument.dom.ODPackage.putFile(Unknown Source)
    at org.jopendocument.dom.ODPackage$1.processEntry(Unknown Source)
    at org.jopendocument.util.ZippedFilesProcessor.process(Unknown Source)
    at org.jopendocument.dom.ODPackage.(Unknown Source)
    at org.jopendocument.dom.ODPackage.(Unknown Source)
    at org.jopendocument.dom.template.Template.(Unknown Source)
    at org.jopendocument.dom.template.EngineTemplate.(Unknown Source)
    at org.jopendocument.dom.template.JavaScriptFileTemplate.(Unknown Source)
    at org.jopendocument.dom.template.JavaScriptFileTemplate.(Unknown Source)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at org.python.core.PyReflectedConstructor.constructProxy(PyReflectedConstructor.java:210)
    … 23 more

    • Buenas,
      Pues no estoy seguro de lo que puede ser, tal vez la versión de Java instalada o que no tengas permisos de escritura en la carpeta que le indicas al crear la plantilla. ¿Has modificado la plantilla de alguna manera o utilizas directamente la que te descargaste?

      La librería use la versión anterior ya que es la estable, la más nueva me daba algunos problemas.

      A ver si conseguimos averiguar lo que es..

  5. Pingback: Editando Hojas de Cálculo desde gvSIG Scripting para generar informes | gvSIG blog

  6. Pingback: Automate reporting in gvSIG using Scripting | gvSIG blog

  7. Alberto says:

    Estoy probando el contenido del post para reutilizarlo en un TFG y me da error a la hora de cargar la libreria:
    ImportError: No module named jopendocument in at line number 6
    y en la linea 6 tengo lo siguiente:
    from org.jopendocument.dom.template import JavaScriptFileTemplate

    • Buenas,
      Parece que no te encuentra la librería. Faltaría dejar caer el fichero .jar tal y como dice en el punto:
      – Copiar el fichero jOpenDocument-1.3.jar a la carpeta gvSIG-carpeta-de-Instalacion\gvSIG\extensiones\org.gvsig.scripting.app.extension\lib de la carpeta de instalación de gvSIG

      Comprueba que tengas este fichero, si te pierdes en algo dime algun detalle más de versión de gvSIG y sistema operativo que utilizas y te comento mejor.

      En futuras versiones esta librería vendrá con gvSIG de serie.

      Por si te interesa también, se puede utilizar de forma muy similar, una librería que ya viene con gvSIG con la que permite hacer lo mismo pero trabajando con ficheros de excel. Si estás interesado podría sacar un post en la próxima semana.

      ¡Un saludo!

  8. Alberto says:

    Ya he conseguido que funcione todo perfecto. Gracias a este post y a algun video de unas jornadas de scripting, me está quedando un TFG bastante curioso jejejeje.
    Ahora quería preguntarle una ultima duda. He creado una interfaz en la que muestro información de la tabla de atributos de la capa parcela. Selecciono una entidad, ejecuto el script y muestra la interfaz con la información de la parcela seleccionada. Ahora quiero integrar un boton para exportar a archivo libreOffice. Tengo en un script distinto programado lo de generar el informe gracias a este post, pero quiero integrarlo bajo el boton, que de alguna manera al pulsar el boton ejecute el script de generacion de informe, pero no consigo que lo haga bien

    • Alvaro says:

      Alberto, ya que has usado gvSIG para tú TFG, anímate y preséntalo al concurso Cátedra gvSIG:

      II Concurso Cátedra gvSIG

    • Mmm te refieres a un botón en la barra de herramientas ¿no? Tendría que mirarlo para decirte exacto, a ver si estos días me puedo informar y junto al post de exportar a excel, puedo añadir la parte de asignarle la función a un botón.

      • Alberto says:

        No, yo he creado una interfaz en la que me muestra los diferentes campos que tiene una parcela en su tabla de atributos. Esta interfaz tiene dos botones, uno para cerrar la ventanita y otro para exportar informe, que no es mas que mi intencion de que al presionarlo exporte un informe de la parcela. Pero no consigo que funcione. El mismo codigo lo ejecuto en un script diferente y hace su funcion perfectamente exportando el archivo con sus datos y sus imagenes a diferentes escalas

  9. yelitza arias says:

    hola pregunto para la version 2.3 hay que realizar algun tipo de adaptación

  10. yelitza arias says:

    Hola Oscar de nuevo yo, pregunto porque en la linea 3 del scripts dice que hay un error de import
    “Error ImportError: No module named geom in at line number 3 prueba 3:0″… mis disculpa es que estoy aprendiendo python por mis propios medios…y me interesa poder crear documentos de información de catastro. y esto me dio LA IDEA para solucionarlo, solo debo adaptar a lo que necesito, y ayudar a una amiga para que lo utilice en su trabajo de tesis.

    • Buenas, te he hecho todas las modificaciones pertinentes y algunas mejoras menores. Te he puesto la solución abajo en el propio post, ya viene con un paquete que se puede instalar desde el Administrador de Complementos, y a partir de ahí ya puedes modificarlo, consultarlo o hacer lo que quieras.
      Cualquier cosa que no entiendas, me comentas. Saludos!

Leave a reply to Óscar Martínez Cancel reply