Last active
March 17, 2022 19:08
-
-
Save jtornero/4ae64933e99e21d27f5044b4f42e78ec to your computer and use it in GitHub Desktop.
Vector layer batch creation with PyQGis
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Ejemplo de cómo crear capas en QGIS y dotarlas de simbología basada en reglas | |
# Las fuentes en las que me he basado para crearlo son principalmente el QGIS Cookbook, | |
# algún artículo de Anita Grase y otras fuentes como | |
# https://gis.stackexchange.com/questions/86770/how-to-programmatically-set-a-rule-based-renderer-in-qgis | |
# Cuando pueda subir un artículo más detallado a la wiki de qgis | |
# se acompañará de estas fuentes en detalle con enlaces,etc, para referencia y consulta | |
# En mi caso creo una capa por cada una de estas especies de peces, | |
que meto en un diccionario junto al color que tendrá la capa | |
especies={'ANE':'#00ff00', | |
'PIL':'#0000ff', | |
'MAS':'#00ffff', | |
'MAC':'#ff0000', | |
'HOM':'#ffff00', | |
'HMM':'#ffffc0', | |
'JAA':'#ff7f00', | |
'BOG':'#c000c0', | |
'MAV':'#b5fe5f', | |
'SNS':'#c8c8c8', | |
'POA':'#c8c8c8', | |
'BOC':'#c8c8c8'} | |
# Esto es una función para crear el renderizador basado en reglas | |
# para representar NASC (Nautical Area Scattering Coefficient) | |
# Le pasamos el color del símbolo y el máximo de NASC para crear | |
# la simbología. | |
# Al principio lo intenté haciendo una subclase de QgsRuleBasedRenderer, | |
# pero según comento Víctor Olaya es necesario reimplementar unos cuantos métodos | |
# lo que queda fuera de mi alcance, por lo que se crea un QgsRuleBasedRenderer | |
# y luego se va modificando. | |
def creaRendererNASC(color,maxNASC): | |
# Se crea un objeto QgsRuleBasedRenderer, con un símbolo por defecto | |
# que es al que luego iremos añadiendo capas de simbología, etc. según corresponda | |
renderer=QgsRuleBasedRenderer(QgsSymbol.defaultSymbol(0)) | |
# Tenemos que convertir el marker color a un QColor y añadirle transparencia | |
markerColor=QColor(color) | |
markerColor.setAlpha(127) | |
# La simbología consta de varias capas dependiendo ddel rango de valores. | |
# Pero hay un caso especial, el valor 0 que lo representamos como una cruz | |
# Y el punto central de cada símbolo, que es un punto pequeño. | |
# Esto lo creamos como capas de símbolo e iremos poniendo cada una donde toque | |
# Regla 0, shape=11 significa que el simbolo basico es una cruz | |
markerZero=QgsSimpleMarkerSymbolLayer(shape=11,size=0.7) | |
# Punto central, el marcador es un punto muy pequeño | |
markerDot=QgsSimpleMarkerSymbolLayer(size=0.2) | |
# En esta simbología cada regla tiene un símbolo de un tamaño dependiendo del | |
# rango de valores. En esta lista se almacenan los tamaños del símbolo principal de la capa | |
markerSizes=[0.5,1.83,3.96,6.55,9.55,12.90] | |
# Mediante el logaritmo del valor maximo hallamos el rango | |
# máximo del NASC, que luego servirá como índice para buscar el tamaño | |
# del símbolo y también para hallar el intervalo de la regla | |
rangeNASC=math.log(maxNASC,10) | |
if rangeNASC < 0: | |
rangeNASC = 0 | |
elif rangeNASC >= 0: | |
rangeNASC = math.trunc(rangeNASC)+1 | |
if rangeNASC>5: | |
rangeNASC = 5 | |
#Dejamos el numero de decimales en funcion del valor de maxNASC | |
if rangeNASC==0: | |
maxNASC=round(maxNASC,2) | |
elif rangeNASC==1: | |
maxNASC=round(maxNASC,1) | |
else: | |
maxNASC=math.trunc(maxNASC) | |
# Tomamos todos los valores de tamaño de marcador hasta el rangeNASC | |
# y después apartamos el último ya que tiene un tratamiento diferente | |
sizes=markerSizes[0:rangeNASC+1] | |
lastMarker=sizes.pop() | |
# Esto lo explica bastante bien el cookbook y otro artículos. | |
# Basicamente lo que hacemos es tomar la regla base del nuevo renderizador | |
# Para clonarla después, algo así como usarla de plantilla para el resto de reglas | |
root_rule=renderer.rootRule() | |
# En primer lugar añadimos la regla correspomndiente al valor 0 | |
rule=root_rule.children()[0].clone() | |
rule.symbol().appendSymbolLayer(markerZero) | |
rule.symbol().deleteSymbolLayer(0) | |
rule.setFilterExpression('nasc_especie=0') | |
rule.setLabel('0') | |
root_rule.appendChild(rule) | |
# Ahora creamos cada una de las demás reglas, | |
# siguiendo el mismo esquema de antes | |
for indice,markerSize in enumerate(sizes): | |
rule=root_rule.children()[0].clone() | |
print (markerSize,indice) | |
rule.symbol().appendSymbolLayer(QgsSimpleMarkerSymbolLayer(size=markerSize,color=markerColor)) | |
rule.symbol().deleteSymbolLayer(0) | |
if indice==0: #La regla de filtrado es diferente y la etiqueta también | |
rule.setFilterExpression('nasc_especie<1') | |
rule.setLabel('<1') | |
root_rule.appendChild(rule) | |
else: | |
rangeMin=10**(indice-1) | |
rangeMax=10**(indice) | |
rule.setLabel('%i - %i' %(rangeMin,rangeMax)) | |
rule.setFilterExpression('nasc_especie>=%i and nasc_especie<%i' %(rangeMin,rangeMax)) | |
root_rule.appendChild(rule) | |
# La última regla es un poco diferente, por el el tope de la regla | |
@ es el valor máximo y, como el caso O, se trata aparte | |
rule=root_rule.children()[0].clone() | |
rule.symbol().appendSymbolLayer(markerDot) | |
rule.symbol().appendSymbolLayer(QgsSimpleMarkerSymbolLayer(size=lastMarker,color=markerColor)) | |
rule.symbol().deleteSymbolLayer(0) | |
rangeMin=10**(rangeNASC-1) | |
rangeMax=10**(rangeNASC) | |
rule.setLabel('%i - %s (%s)' %(rangeMin,rangeMax,maxNASC)) | |
rule.setFilterExpression('nasc_especie>=%i and nasc_especie<%i' %(rangeMin,rangeMax)) | |
root_rule.appendChild(rule) | |
root_rule.removeChildAt(0) | |
return renderer | |
# Con esta función creamos el sql para luego establecer el origen de datos al crear la capa, | |
# Se podría haber creado dentro de la función de creación de capas pero preferí hacer así | |
def creaSql(camp,especie): | |
"""Crea el sql para generar las capas que servirán como base para los mapas de NASC""" | |
sql="""(with rclasses as( select interv as intervalo, array_to_string(array_agg(DISTINCT region_class),',') as region_classes from regiones where regiones.camp='{0}' group by interv ) SELECT generalmillas.id,generalmillas.intervalo, generalmillas.profundidad, coalesce(round(generalmillas.{1},4),0) as nasc_especie, case when coalesce(nasc_total,0)>0 then round({1}/nasc_total,4) else coalesce(round(nasc_total,4),0) end as porc_nasc, region_classes, generalmillas.millapun FROM generalmillas left join rclasses using (intervalo) WHERE generalmillas.camp='{0}' and generalmillas.valida=true ORDER BY generalmillas.intervalo )""".format(camp,especie) | |
return sql | |
# Esta es la función que crea la capa vectorial, el renderizador, se lo asigna y carga la capa | |
# generada en QGIS | |
def creaCapaNASC(camp,especie,colorCapa): | |
nombreCapa='%s_NASC_%s' %(camp,especie) | |
# Creamos la consulta que nos servirá como origen de datos | |
sql=creaSql(camp,especie) | |
# Primero creamos el URI y luego la capa | |
uriCapa=QgsDataSourceUri() | |
uriCapa.setConnection('localhost','5432','biofuturo','antares','2$trabuco') | |
uriCapa.setDataSource("",sql,"millapun","","id") | |
layer=QgsVectorLayer(uriCapa.uri(),nombreCapa,"postgres") | |
# Obtenemos el valor máximo de NASC para la capa, se lo tendremos que pasar | |
# a la funciń que crea el rederizador | |
nascMax=layer.maximumValue(layer.fields().indexFromName('nasc_especie')) | |
# Añadimos la capa e intentamos convertir a float. Esta es la versión | |
# chapucera para evitar que la cosa casque si en algún momento devolviese un null | |
# Cosa que no debería hacer ya que introduje en la consulta un coalesce para evitar | |
# precistamente esto, pero bueno | |
try: | |
nascMax=float(nascMax) | |
except: | |
nascMax=0 | |
# Con el dato de nascMax creamos el rederizador, se lo acoplamos a la capa | |
# y cargamos la capa en QGIS | |
renderer=creaRendererNASC(colorCapa,nascMax) | |
layer.setRenderer(renderer) | |
QgsProject.instance().addMapLayer(layer) | |
# Para hacer el lote de capas, procedemos así | |
spes=especies.keys() | |
for spe in spes: | |
creaCapaNASC('ECOCADIZ-R-2019',spe,especies[spe]) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment