Importante

La traducción es un esfuerzo comunitario al que puedes unirte. Esta página está actualmente traducida en 100.00%.

10. Representación del Mapa e Impresión

Consejo

Los fragmentos de código en esta página necesitan las siguientes importaciones:

 1import os
 2
 3from qgis.core import (
 4    QgsGeometry,
 5    QgsMapSettings,
 6    QgsPrintLayout,
 7    QgsMapSettings,
 8    QgsMapRendererParallelJob,
 9    QgsLayoutItemLabel,
10    QgsLayoutItemLegend,
11    QgsLayoutItemMap,
12    QgsLayoutItemPolygon,
13    QgsLayoutItemScaleBar,
14    QgsLayoutExporter,
15    QgsLayoutItem,
16    QgsLayoutPoint,
17    QgsLayoutSize,
18    QgsUnitTypes,
19    QgsProject,
20    QgsFillSymbol,
21    QgsAbstractValidityCheck,
22    check,
23)
24
25from qgis.PyQt.QtGui import (
26    QPolygonF,
27    QColor,
28)
29
30from qgis.PyQt.QtCore import (
31    QPointF,
32    QRectF,
33    QSize,
34)

Por lo general, hay dos enfoques cuando los datos de entrada deben representarse como un mapa: hacerlo de manera rápida usando QgsMapRendererJob o producir una salida más ajustada componiendo el mapa con la clase QgsLayout.

10.1. Representación Simple

La renderización se realiza creando un objeto QgsMapSettings para definir la configuración de renderizado, y luego construir una clase QgsMapRendererJob con esos ajustes. Este último se utiliza para crear la imagen resultante.

Aquí hay un ejemplo:

 1image_location = os.path.join(QgsProject.instance().homePath(), "render.png")
 2
 3vlayer = iface.activeLayer()
 4settings = QgsMapSettings()
 5settings.setLayers([vlayer])
 6settings.setBackgroundColor(QColor(255, 255, 255))
 7settings.setOutputSize(QSize(800, 600))
 8settings.setExtent(vlayer.extent())
 9
10render = QgsMapRendererParallelJob(settings)
11
12def finished():
13    img = render.renderedImage()
14    # save the image; e.g. img.save("/Users/myuser/render.png","png")
15    img.save(image_location, "png")
16
17render.finished.connect(finished)
18
19# Start the rendering
20render.start()
21
22# The following loop is not normally required, we
23# are using it here because this is a standalone example.
24from qgis.PyQt.QtCore import QEventLoop
25loop = QEventLoop()
26render.finished.connect(loop.quit)
27loop.exec_()

10.2. Representando capas con diferente SRC

Si tiene más de una capa y tienen un SRC diferente, el simple ejemplo anterior probablemente no funcionará: para obtener los valores correctos de los cálculos de extensión, debe establecer explícitamente el SRC de destino

layers = [iface.activeLayer()]
settings = QgsMapSettings()
settings.setLayers(layers)
settings.setDestinationCrs(layers[0].crs())

10.3. Salida usando diseño de impresión

El diseño de impresión es una herramienta muy útil si desea realizar una salida más sofisticada que la simple representación mostrada anteriormente. Es posible crear diseños de mapas complejos compuestos por vistas de mapas, etiquetas, leyendas, tablas y otros elementos que suelen estar presentes en los mapas en papel. Los diseños pueden exportarse a PDF, SVG, imágenes rasterizadas o imprimirse directamente en una impresora.

El diseño consta de varias clases. Todos pertenecen a la biblioteca principal. La aplicación QGIS tiene una GUI conveniente para la ubicación de los elementos, aunque no está disponible en la biblioteca de la GUI. Si no está familiarizado con Qt Graphics View framework, entonces le recomendamos que consulte la documentación ahora, porque el diseño se basa en ella.

La clase central del diseño es la clase QgsLayout, que deriva de la clase de Qt QGraphicsScene . Creemos una instancia de ello:

project = QgsProject.instance()
layout = QgsPrintLayout(project)
layout.initializeDefaults()

Esto inicializa el diseño con algunas configuraciones predeterminadas, específicamente agregando una página A4 vacía al diseño. Puede crear diseños sin llamar al método initializeDefaults(), pero deberá encargarse de agregar páginas al diseño usted mismo.

El código anterior crea un diseño «temporal» que no es visible en la GUI. Puede ser útil p.Ej. agregue rápidamente algunos elementos y exporte sin modificar el proyecto en sí ni exponer estos cambios al usuario. Si desea que el diseño se guarde/restaure junto con el proyecto y esté disponible en el administrador de diseño, agregue:

layout.setName("MyLayout")
project.layoutManager().addLayout(layout)

Ahora podemos agregar varios elementos (mapa, etiqueta, …) al diseño. Todos estos objetos están representados por clases que heredan de la clase base QgsLayoutItem.

Aquí hay una descripción de algunos de los elementos de diseño principales que se pueden agregar a un diseño.

  • mapa — Aquí creamos un mapa de un tamaño personalizado y renderizamos el lienzo del mapa actual.

    1map = QgsLayoutItemMap(layout)
    2# Set map item position and size (by default, it is a 0 width/0 height item placed at 0,0)
    3map.attemptMove(QgsLayoutPoint(5,5, QgsUnitTypes.LayoutMillimeters))
    4map.attemptResize(QgsLayoutSize(200,200, QgsUnitTypes.LayoutMillimeters))
    5# Provide an extent to render
    6map.zoomToExtent(iface.mapCanvas().extent())
    7layout.addLayoutItem(map)
    
  • etiqueta — permite mostrar etiquetas. Es posible modificar su letra, color, alineación y margen.

    label = QgsLayoutItemLabel(layout)
    label.setText("Hello world")
    label.adjustSizeToText()
    layout.addLayoutItem(label)
    
  • legend

    legend = QgsLayoutItemLegend(layout)
    legend.setLinkedMap(map) # map is an instance of QgsLayoutItemMap
    layout.addLayoutItem(legend)
    
  • barra de escala

    1item = QgsLayoutItemScaleBar(layout)
    2item.setStyle('Numeric') # optionally modify the style
    3item.setLinkedMap(map) # map is an instance of QgsLayoutItemMap
    4item.applyDefaultSize()
    5layout.addLayoutItem(item)
    
  • forma basada en nodos

     1polygon = QPolygonF()
     2polygon.append(QPointF(0.0, 0.0))
     3polygon.append(QPointF(100.0, 0.0))
     4polygon.append(QPointF(200.0, 100.0))
     5polygon.append(QPointF(100.0, 200.0))
     6
     7polygonItem = QgsLayoutItemPolygon(polygon, layout)
     8layout.addLayoutItem(polygonItem)
     9
    10props = {}
    11props["color"] = "green"
    12props["style"] = "solid"
    13props["style_border"] = "solid"
    14props["color_border"] = "black"
    15props["width_border"] = "10.0"
    16props["joinstyle"] = "miter"
    17
    18symbol = QgsFillSymbol.createSimple(props)
    19polygonItem.setSymbol(symbol)
    

Una vez que un elemento es añadido a la composiciónm puede ser movida y redimensionadas:

item.attemptMove(QgsLayoutPoint(1.4, 1.8, QgsUnitTypes.LayoutCentimeters))
item.attemptResize(QgsLayoutSize(2.8, 2.2, QgsUnitTypes.LayoutCentimeters))

Un cuadro es dibujado alrededor de cada elemento por defecto. Puede borrarlo como sigue:

# for a composer label
label.setFrameEnabled(False)

Además de crear los elementos de diseño a mano, QGIS tiene soporte para plantillas de diseño que son esencialmente composiciones con todos sus elementos guardados en un archivo .qpt (con sintaxis XML).

Una vez que la composición está lista (los elementos de diseño se han creado y agregado a la composición), podemos proceder a producir una salida rasterizada y/o vectorial.

10.3.1. Comprobación de la validez del diseño

Un diseño está formado por un conjunto de elementos interconectados y puede ocurrir que estas conexiones se rompan durante las modificaciones (una leyenda conectada a un mapa eliminado, un elemento de imagen al que le falta el archivo fuente,…) o puede que desee aplicar restricciones personalizadas a los elementos del diseño. La QgsAbstractValidityCheck te ayuda a conseguirlo.

Una comprobación básica tiene este aspecto:

@check.register(type=QgsAbstractValidityCheck.TypeLayoutCheck)
def my_layout_check(context, feedback):
  results = ...
  return results

Aquí hay una comprobación que lanza una advertencia cada vez que un elemento de mapa de diseño se establece en la proyección mercator web:

 1@check.register(type=QgsAbstractValidityCheck.TypeLayoutCheck)
 2def layout_map_crs_choice_check(context, feedback):
 3  layout = context.layout
 4  results = []
 5  for i in layout.items():
 6    if isinstance(i, QgsLayoutItemMap) and i.crs().authid() == 'EPSG:3857':
 7      res = QgsValidityCheckResult()
 8      res.type = QgsValidityCheckResult.Warning
 9      res.title = 'Map projection is misleading'
10      res.detailedDescription = 'The projection for the map item {} is set to <i>Web Mercator (EPSG:3857)</i> which misrepresents areas and shapes. Consider using an appropriate local projection instead.'.format(i.displayName())
11      results.append(res)
12
13  return results

Y aquí hay un ejemplo más complejo, que lanza una advertencia si cualquier elemento de mapa de diseño se establece en un SRC que sólo es válido fuera de la extensión mostrada en ese elemento de mapa:

 1@check.register(type=QgsAbstractValidityCheck.TypeLayoutCheck)
 2def layout_map_crs_area_check(context, feedback):
 3    layout = context.layout
 4    results = []
 5    for i in layout.items():
 6        if isinstance(i, QgsLayoutItemMap):
 7            bounds = i.crs().bounds()
 8            ct = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:4326'), i.crs(), QgsProject.instance())
 9            bounds_crs = ct.transformBoundingBox(bounds)
10
11            if not bounds_crs.contains(i.extent()):
12                res = QgsValidityCheckResult()
13                res.type = QgsValidityCheckResult.Warning
14                res.title = 'Map projection is incorrect'
15                res.detailedDescription = 'The projection for the map item {} is set to \'{}\', which is not valid for the area displayed within the map.'.format(i.displayName(), i.crs().authid())
16                results.append(res)
17
18    return results

10.3.2. Exportando la composición

Para exportar una composición, la clase QgsLayoutExporter debe ser usada.

1base_path = os.path.join(QgsProject.instance().homePath())
2pdf_path = os.path.join(base_path, "output.pdf")
3
4exporter = QgsLayoutExporter(layout)
5exporter.exportToPdf(pdf_path, QgsLayoutExporter.PdfExportSettings())

Utilice exportToSvg() o exportToImage() en caso de que desee exportar respectivamente a un archivo SVG o de imagen en lugar de a un archivo PDF.

10.3.3. Exportar un atlas de diseño

Si desea exportar todas las páginas de un diseño que tiene la opción de atlas configurada y habilitada, debe usar el método atlas() en el exportador (QgsLayoutExporter) con pequeños ajustes. En el siguiente ejemplo, las páginas se exportan a imágenes PNG:

exporter.exportToImage(layout.atlas(), base_path, 'png', QgsLayoutExporter.ImageExportSettings())

Observe que las salidas se guardarán en la carpeta de ruta base, utilizando la expresión de nombre de archivo de salida configurada en atlas.