Luis Miguel Agudo Bravo
GIS Developer
PyQGIS
Python para QGIS
**WEB GIS DEVELOPER**
Cualquiera Puede Contribuir
Pros/Contras
Se accede desde:
Complementos > Administrar e instalar complementos...
No se pueden desinstalar los plugins Centrales
Los plugins centrales siempre se instalan en el directorio central de QGIS
Los complementarios son instalados en la carpeta del usuario:
QGIS lee datos vector, raster y muchos otros formatos que son proporcionados por las librerías reconocidas por la OGC (Open GIS Consortium)
OsGeo es una fundación sin ánimo de lucro que mantiene proyectos GIS de recursos abiertos (Open Source GIS Project), además de otros softwares abiertos como OpenLayers.
Es el principal desarrollador de QGIS y lo mantiene abierto al público usando estandares bajo el formato de la OGC
QGIS implementa las librerias:
OGR (Simple Features Library): Permite leer datos vectoriales
GDAL (Geospatial Data Abstraccion Library): Permite leer datos ráster
http://www.lfd.uci.edu/~gohlke/pythonlibs/#gdal
QGIS nos permite leer gran cantidad de datos implementados ahora por la OGC como son los servicios WMS, WFS, WCS, PostgreSQL/PostGIS,
Lenguaje de programacion 2 componentes
Un entorno de desarrollo integrado o entorno de desarrollo interactivo, es una aplicación informática que proporciona servicios integrales para facilitarle al desarrollador o programador el desarrollo de software
Prompt
Editor
La programación orientada a objetos es un paradigma de la programación que está basado en el concepto de los OBJETOS, los cuales son estructuras contenedoras de datos y código que realizan funciones (métodos).
Los principales componentes de la programación orientada a objetos son los objetos, las clases, los métodos y las propiedades
mi_variable = "perro"
mi_variable = 3
mi_variable = 3.2353456
mi_variable = {2,8,9}
mi_variable = False
mi_variable = (25, "Mayo", 1810)
mi_variable = False
mi_variable = {'x': 2, 'y': 1, 'z': 4}
#Para saber el tipo de mi variable usamos la función type
mi_variable = "perro"
type(mi_variable ) #me devuelve <tipe 'str'>
tupla = (1,2,3,4,5,6,7,8,9,10)
lista = [1,2,3,4,5,6,7,8,9,10]
#Las tuplas ocupan un menor tamaño en disco
print(tupla.__sizeof__()) # 52 bytes
print(lista.__sizeof__()) # 60 bytes
#no podemos alterar una tupla o dara error
tupla[0] = 20
print("%s %s" % ("Lista:",tupla))
#TypeError: 'tuple' object does not support item assignment
#encambio si podemos alterar una lista por su posicion
lista[0] = 20
print("%s %s" % ("Lista:",lista))
# Lista: [20, 2, 3, 4, 5, 6, 7, 8, 9, 10]
#Los strings se comportan como una tupla
mi_var = 'Perro'
print mi_var[2]
# r
materias = {}
materias["lunes"] = [6103, 7540]
materias["martes"] = [6201]
materias["miércoles"] = [6103, 7540]
materias["jueves"] = []
materias["viernes"] = [6201]
print materias["lunes"]
[6103, 7540]
#También podemos crear un diccionario mediante la función dict
materias = dict(lunes = [6103, 7540], martes = [6201])
#Muy utiles para determinar propiedades de objetos y sus valores correspondeintes
Simbologia_carretera = {'size':2, 'color':'255,0,220', 'name':'Carretera', 'posicionTOC':4}
#Con la función list() o el método keys() obtenemos las claves
list(Simbologia_carretera ) ó Simbologia_carretera.keys()
#['color', 'posicionTOC', 'name', 'size']
#Con la funcion values() obtenmos los valores de estas claves
Simbologia_carretera.values()
#['255,0,220', 4, 'Carretera', 2]
def nombre_funcion (argumento1, argumento2,...):
el cuerpo de la función
def mifuncion (arg1, arg2, arg3):
print "", arg1, " , ", arg2, " , ", arg3
mifuncion("mi","primera","funcion")
# mi , primera , funcion
def mioperacion(cifra1, cifra2, cifra3):
resultado = cifra1 * 4 + (cifra2 + cifra3) * 3-10
print resultado
mioperacion(1,4,5)
# 21
class Empleado:
#'Variable de toda la clase Empleados'
empCount = 0
def __init__(self, nombre, salario):
self.nombre= nombre #Esto es una propiedad
self.salario = salario
Employee.empCount += 1
def displayCount(self):
print "Total Empleados %d" % Empleado.empCount
def displayEmployee(self): #Esto es un método
print "Nombre : ", self.nombre, ", Salario: ", self.salario
"Esto crearía el primer objeto de la clase Empleado"
emp1 = Employee("Zara", 2000)
"Esto crearía el segundo objeto de la clase Empleado"
emp2 = Employee("Manni", 5000)
emp1.displayEmployee()
emp2.displayEmployee()
print "Total Empleados %d" % Empleado.empCount
#Nombre : Zara, Salario: 2000
#Nombre : Manni, Salario: 5000
#Total Empleados 2
A través de ella los diseñadores pueden crear nuevas clases partiendo de una clase o de una jerarquía de clases preexistente (ya comprobadas y verificadas) evitando con ello el rediseño, la modificación y verificación de la parte ya implementada.
La herencia facilita la creación de objetos a partir de otros ya existentes e implica que una subclase obtiene todo el comportamiento (métodos) y eventualmente los atributos (variables) de su superclase
BLOQUE 1:
BLOQUE 2:
BLOQUE 3:
num = 6
if num == 5:
print "La variable num es el número 5"
else:
print "La variable num no es el número 5"
edad = int(input("¿Cuántos años tiene? "))
if edad < 0:
print("No se puede tener una edad negativa")
elif edad < 18:
print("Es usted menor de edad")
else:
print("Es usted mayor de edad")
# Uno de estos dos programas no funcionará
edad = int(input("¿Cuántos años tiene? "))
if edad < 18:
print("Es usted menor de edad")
elif edad < 0:
print("No se puede tener una edad negativa")
else:
print("Es usted mayor de edad")
personas = ["Persona 1", "Persona 2", "Persona 3", "Persona 4"]
for persona in personas:
print "Hay alguna persona?"
print persona
print '\n'
print "No hay ninguna persona"
#Hay alguna persona?
#Persona 1
#Hay alguna persona?
#Persona 2
#Hay alguna persona?
#Persona 3
#Hay alguna persona?
#Persona 4
for un_numero in (1, 2, 3, 4, 5):
el_numero = str(un_numero)
print '---', un_numero, "es", el_numero + " es string"
#--- 1 es 1 es string
#--- 2 es 2 es string
#--- 3 es 3 es string
#--- 4 es 4 es string
#--- 5 es 5 es string
for numero in range(1,10,2)
print numero
#1
#3
#5
#7
#9
WHILE (condición):
comandos dentro de este bucle
Atención imprescindible: condición = condición +1
count = 0
while (count < 9):
print 'The count is:', count
count = count + 1
print "Good bye!"
# The count is: 0
# The count is: 1
# The count is: 2
# The count is: 3
# The count is: 4
# The count is: 5
# The count is: 6
# The count is: 7
# The count is: 8
# Good bye!
def f(x):
return {
'a': 1,
'b': 2,
}.get(x, 9) # 9 es el valor por defecto si no existe x
Integración de Python en QGIS
Ejercicio 9 (Función)
2 APIs para QGIS
Es un programa que nos permite a través de una llamada pasar unos datos de entrada y nos genera un resultado a través de unas funciones. Este resultado lo podemos incorporar al programa que estamos desarrollando.
Hoy en día hay una gran tendencia al desarrollo y uso de las API REST como protocolo de intercambio y manipulación de datos en los servicios de internet.
Existen infinidad de ejemplos en entornos SIG:
QGIS se ha desarrollado a partir de QT que es una librería para desarrollo de aplicaciones en C++
Parámetros de entrada
Parámetro de salida
Método
Clase
Parámetro de entrada opcional
No devuelve nada
Tipo parámetro
Valor por defecto
Instanciar un objeto en python:
Nombre_variable = Nombre_clase(parametros_entrada)
GitHub API pyQGIS
Puedes usarlo con cualquier clase o módulo
Este módulo define las clases fundamentales en QGIS. Gran parte de este módulo está enfocado a trabajar con todo tipo de formatos de datos geoespaciales, y mostrarlos dentro del mapa
Las capas siempre definidas por un sistéma de coordenadas
QgsCoordinateReferenceSystem()
from qgis.core import *
from qgis.gui import *
municipios= QgsVectorLayer()
municipios.setCrs( QgsCoordinateReferenceSystem(4258, QgsCoordinateReferenceSystem.EpsgCrsId) )
Se cargan desde una fuente de datos usando el constructor de QgsVectorLayer(Parámetros)
Si no usamos parámetros en el constructor se genera un objeto QgsVectorLayer donde le valor de las propiedades es indefinido
Cada entidad contiene los siguientes componentes (a los que se puede acceder con QgsFeature()):
- ID: Identificador único de cada entidad, dentro de la fuente de datos.
- Geometría: Esto es la forma por la que las entidades se representan en el mapa (punto, línea o polígono).
- Atributos: Estos son los valores y claves que proporcionan información adicional sobre las entidades.
En QGIS, un proveedor de datos permite a la CAPA vectorial acceder a las entidades en la fuente de datos. La clase correspondiente, QgsVectorDataProvider() incluye:
- Tipo geométrico de las entidades guardadas en la fuente de datos.
- Lista de campos que guardan la información de los atributos de cada entidad.
- La capacidad de buscar en las entidades en la fuente de datos, usando el método getFeatures() y la clase QgsFeatureRequest().
from qgis.core import *
from qgis.gui import *
municipios= QgsVectorLayer("Path_layer\MUNICIPIOS.shp","name","ogr")
entidades = municipios.getFeatures()
for entidad in entidades:
print "La entidad ", entidad.id(), " corresponde al municipio ", entidad["MUNICIPIO"]
La clase QgsRasterLayer() es equivalente a QgsVectorLayer(), proporcionando las funcionalidades fundamentales para gestionar un ráster
from qgis.core import QgsRasterLayer
from PyQt4.QtCore import QFileInfo
def StringToRaster(raster):
# Chequeo si el string que le paso tiene un raster valido
fileInfo = QFileInfo(raster)
path = fileInfo.filePath()
baseName = fileInfo.baseName()
layer = QgsRasterLayer(path, baseName)
QgsMapLayerRegistry.instance().addMapLayer(layer)
if layer.isValid() is True:
print "La capa se cargo correctamente!"
else:
print "Imposible leer basename y file path - Tu string probablemente es invalido"
raster = 'C:\MasterGIS\GeoSpatialTraining\Curso_PyQGIS\Presentacion_UD3\ot20160821i_s.tif'
StringToRaster(raster)
Este módulo define las herramientas (widgets) que pueden incluirse o ya están incluidos en QGIS. De este módulo veremos las clases que están referidas a la interfaz de QGIS, las cuales son imprescindibles para interactuar con el proyecto actual (el espacio de trabajo y la interfaz).
La clase QgisInterface() representa toda la interfaz de usuario de QGIS. Permite acceder al marco del mapa, a la barra del menú y más partes de la aplicación de escritorio. Esta clase es abstracta por lo que se ha programado una variable global que accede a la funcionalidad de esta clase, que es iface
raster = iface.addRasterLayer('Path_raster\ot20160821i_s.tif', 'raster')
capa_activa = iface.activeLayer()
iface.showAttributeTable(capa_activa)
iface.showLayerProperties(capa_activa)
Variable Global Iface
Con una instancia de QgisInteface (iface) se podría interactuar con la interfaz a través de los siguientes métodos:
- legendInterface(), con el que se accede a la tabla de contenidos y controlar cada capa, como por ejemplo, su visibilidad y posición
- mapCanvas(): este método es usado exclusivamente para la ventana del mapa, no el marco entero. En el próximo ejercicio usaremos este método.
- activeLayer(): escoge la capa en el proyecto (si hay varias, las escoge aleatoriamente). Para cambiar la capa activa, usa el método setActiveLayer()
from qgis.core import *
from qgis.gui import *
municipios= QgsVectorLayer("C:\MasterGIS\GeoSpatialTraining\Curso_PyQGIS\UD1\Datos\MUNICIPIOS.shp","name","ogr")
entidades = municipios.getFeatures()
expr = QgsExpression( "\"MUNICIPIO\"='Alcobendas'" )
it = municipios.getFeatures( QgsFeatureRequest( expr ) )
ids = [i.id() for i in it]
print ids
municipios.setSelectedFeatures( ids )
box = municipios.boundingBoxOfSelected()
iface.mapCanvas().setExtent(box)
iface.mapCanvas().refresh()
La librería de Qt es un denominado framework (marco de trabajo) con el que se consigue desarrollar interfaces de usuario, o UI (User Interfaces).
Esta librería es de desarrollo mutiplataforma, por lo que las aplicaciones de interfaces graficas de usuario que la utilizan se visualizan tanto en Windows, Mac Os x como en otras plataformas Unix.
Genera todas las vías de comunicación posibles para un programa: botones, cajas, ventanas, listas desplegables...
Qt está desarrollado en C++
pyQt enlaza el lenguaje Python con el toolkit Qt, a través de SIP
pyQT es un proyecto desarrollado por Sourceforge.
- Módulo QtCore: Contiene las clases que no se refieren a la interfaz de usuario. Estos son las interacciones entre botones, es decir, los mecanismos (Eventos) que producen señales y respuestas. También incluyen abstracciones para que el programa funcione de una determinada manera o de otra, tales como la codificación, regular expresiones y configuraciones de la aplicación del usuario.
- Módulo QtGui: Contiene todas las clases para desarrollar la interfaz del usuario.
pyQt nos permite desarrollar aplicaciones desde cero.
En QGIS tan solo la vamos a utilizar para desarrollar plugins.
Utilizaremos los módulos QtCore y QtGui
En la documentación de pyGIS Developer CookBook puedes encontrar más información de los plugins
Editores de texto muy avanzados, con gran cantidad de herramientas y funcionalidad que nos ayudan a la hora de desarrollar.
La clase QgsVectorLayer es la que va a proporcionar los datos vectoriales a una variable desde la fuente de datos. Por ello resulta más fácil acceder a través de una variable que invocar continuamente métodos y clases que accedan a esos datos.
mi_variable = QgsVectorLayer('ruta', 'nombre', 'proveedor')
Esta sentencia no carga la capa en el mapa, tan solo nos crea un objeto vector layer con el que podemos interactuar en el código
La clase abstracta QgsMapLayerRegistry nos permite trabajar con una capa en la interfaz de usuario:
QgsMapLayerRegistry.instance().addMapLayer(--capa--)
Para eliminar la capa de la visualización:
QgsMapLayerRegistry.instance().removeMapLayer(--capa--)
Estas capas que añadimos a la UI se guardan en memoria
Podemos cargar varias capas a la vez con el método addLayers():
capa1 = QgsVectorLayer("ruta_capa1","capa1","proveedor")
capa2 = QgsVectorLayer("ruta_capa1","capa2","proveedor")
QgsMapLayerRegistry.instance().addMapLayers([capa1, capa2])
Normalmente al desarrollar un script para la automatización de tareas no tendremos que cargar capas en la UI de QGIS, tan solo tendremos que registrarlas para poder seguir trabajando con ellas.
registro = QgsProviderRegistry.instance()
provider = registro.provider("ogr","paht_capas")
Un proyecto de QGIS se almacena en un fichero con extensión .qgs.
Es un XML que almacena en tags toda la información necesaria para que QGIS pueda volver a generar el proyecto con todas las propiedades salvadas
from PyQt4.QtCore import *
capa = QgsVectorLayer("path_shapefile", "nombre", "ogr")
registro_capa = QgsMapLayerRegistry.instance().addMapLayer(capa)
archivo = QFileInfo("Path_almacenProyecto/nombreProyecto.qgs")
proyecto = QgsProject.instance()
proyecto.write(archivo)
Al instanciar una clase QgsVectorLayer tenemos acceso a la tabla de esta capa.
from PyQt4.QtCore import *
capa = QgsVectorLayer("path_shapefile", "nombre", "ogr")
encabezado = municipios.pendingFields()
for column in encabezado.toList():
print "Nombre de la columna: ", column.name()
print "Tipo de dato: ", column.typeName()
print "Precisión: ", column.precision(), "\n"
El método pendingFields() nos devuelve la lista de campos (QgsFields) de la tabla.
from PyQt4.QtCore import *
capa = QgsVectorLayer("path_shapefile", "nombre", "ogr")
encabezado = municipios.pendingFields()
for column in encabezado.toList():
print "Nombre de la columna: ", column.name()
print "Tipo de dato: ", column.typeName()
print "Precisión: ", column.precision(), "\n"
print "Indexación de la columna 'Municipios': ", encabezado.indexFromName('Municipios')
from PyQt4.QtCore import *
municipios = QgsVectorLayer("path_shapefile", "nombre", "ogr")
encabezado = municipios.pendingFields()
for column in encabezado.toList():
print "Nombre de la columna: ", column.name()
print "Tipo de dato: ", column.typeName()
print "Precision: ", column.precision(), "\n"
expresion = QgsExpression( "\"MUNICIPIO\"='Alcobendas'" )
features = municipios.getFeatures( QgsFeatureRequest (expresion) )
feature = features.next()
print feature.attributes()
for numero_campo, valor in list(enumerate(feature.attributes())):
print "valor del campo" + str(numero_campo +1) + " = " + str(valor)
Iteradores - Listar Atributos
from PyQt4.QtCore import *
municipios = QgsVectorLayer("path_shapefile", "nombre", "ogr")
entidades = municipios.getFeatures()
for entidad in entidades:
geometria = entidad.geometry()
print "Area: ", geometria.area()
print "Perimeter: ", geometria.length()
Iteradores - Consultar geometría
from PyQt4.QtCore import *
municipios = QgsVectorLayer("path_capa", "name", "ogr")
municipiosLayer = QgsMapLayerRegistry.instance().addMapLayer(municipios)
expresion = QgsExpression( "\"MUNICIPIO\"='Alcobendas'" )
iteraResult = [i.id() for i in municipiosLayer.getFeatures(QgsFeatureRequest(expresion))]
print iteraResult
municipiosLayer.setSelectedFeatures(iteraResult)
iface.mapCanvas().zoomToSelected()
Iteradores - Crer variables locales
crs = QgsCoordinateReferenceSystem(25830, QgsCoordinateReferenceSystem.EpsgCsrid)
Por defecto usaremos la nomenclatura EPSG
from qgis.core import *
from qgis.gui import *
mi_capa = QgsVectorLayer("path_capa", "name", "ogr")
crs = QgsCoordinateReferenceSystem(mi_WKT)
mi_capa.setCrs(crs)
iface.activeLayer()
iface.showLayerProperties(mi_capa)
mi_WKT = 'PROJCS["ETRS89 / UTM zone 30N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-3],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25830"],AXIS["Easting",EAST],AXIS["Northing",NORTH]]'
fTools es un plugin. Proporciona herramientas para geoprocesamientos.
Al ser un plugin no hay posibilidad de mejoras por parte de los desarrolladores en en los nuevos plugins a no ser que modifiquen directamente fTools
Codificar siguiendo las normas de fTools puede ser muy tedioso.
Para solventar este problema se han creado dos clases que contienten métodos con las principales herramientas de geoprocesamiento (buffering, dissolving, etc...)
from qgis.analysis import QgsGeometryAnalyzer
QgsGeometryAnalyzer
Esta clase tiene unos constructores y métodos muy intuitivos y sencillos de entender.
QgsGeometryAnalyzer
Parámetro opcional
QgsGeometryAnalyzer
from qgis.analysis import QgsGeometryAnalyzer
municipios = QgsVectorLayer("path_Shape", "Municipios", "ogr")
QgsGeometryAnalyzer().centroids(municipios, "path_newShape")
centroides = QgsVectorLayer("path_newShape_centroides", "Centroides Municipios", "ogr")
QgsGeometryAnalyzer().buffer(centroides, "path_newShape", 1500)
buffers = QgsVectorLayer("path_newShape_buffer", "Buffer Centroides", "ogr")
QgsMapLayerRegistry.instance().addMapLayers([centroides, buffers, municipios])
A partir de la capa municipios. Seleccionar los municipios de "Molinos, Los", "Cercedilla" y "Guadarrama". Con estos tres municipios crear una capa (SierraNorte.shp) que tenga la geometría de la unión de los tres. Establecer el sistema de coordenadas WGS 84 para las capas Municipios y SierraNorte. Añadir las dos capas al mapa
Text
#Importamos las librerias
from qgis.analysis import QgsGeometryAnalyzer
from qgis.core import *
#Creamos el objeto QgsVectorLayer de Municipios
municipios = QgsVectorLayer("C:\MasterGIS\GeoSpatialTraining\Curso_PyQGIS\Datos\MUNICIPIOS.shp", "Municipios", "ogr")
#Creamos una liata para almacenar los ids de las entidades a seleccionar
iteraResult = []
#Iteramos por todas las entidades de la capa municipios
for i in municipios.getFeatures():
#Creamos una variable con el nombre del municipio
municipioName = i.attribute("MUNICIPIO")
#Comprobamos si el nombre se corresponde con alguno de los buscados
if (municipioName == "Molinos, Los"):
#Si el nombre se corresponde lo agregamos a la lista
iteraResult.append(i.id())
elif (municipioName == "Cercedilla"):
iteraResult.append(i.id())
elif (municipioName == "Guadarrama"):
iteraResult.append(i.id())
print iteraResult
#Seleccionamos las entidades en la capa municipios
municipios.setSelectedFeatures(iteraResult)
#Utilizamos el metodo disolve para que una las entidades seleccionadas
QgsGeometryAnalyzer().dissolve(municipios, "C:\MasterGIS\GeoSpatialTraining\Curso_PyQGIS\Datos\SierraNorte.shp", True)
#Creamos un objeto QgsVectorLayer con la nueva capa
SierraNorte = QgsVectorLayer("C:\MasterGIS\GeoSpatialTraining\Curso_PyQGIS\Datos\SierraNorte.shp", "Sierra Norte", "ogr")
#Creamos una lista con las capas a incluir en el mapa
layers = [SierraNorte, municipios]
#Recorremos la lista para aplicarles el CRS EPSG:4326
for layer in layers:
layer.setCrs( QgsCoordinateReferenceSystem(4326, QgsCoordinateReferenceSystem.EpsgCrsId) )
#Incorporamos las capas al mapa
QgsMapLayerRegistry.instance().addMapLayers(layers)
Luis Miguel Agudo
Jon Garrido
By Luis Miguel Agudo Bravo