I want to generate multiple maps with one script. I define a dictionary map_configurations with names for each map and the wanted layers.
I try to loop through the function create_map but my selected layers do not make it in the map.
What am I missing?
The legend shows all of the layers in the dictionary. But in the maps themself on the first layer is displayed.
The complete code is below.
#import aller erforderlichen Klassen für die Karte
from qgis.core import (
QgsProject,
QgsLayoutManager,
QgsPrintLayout,
QgsLayoutItemMap,
QgsLayoutItemLegend,
QgsLayoutSize,
QgsLayoutExporter,
QgsRectangle,
QgsLayoutPoint,
QgsUnitTypes,
QgsLayoutMeasurement,
QgsLayoutItemPicture,
QgsLayoutItemScaleBar,
QgsLayerTreeLayer,
QgsUnitTypes,
QgsLayoutPoint,
QgsLayoutItem
)
from qgis.utils import iface
from qgis.PyQt.QtGui import (
QColor,
QFont
)
from qgis.PyQt.QtWidgets import QFileDialog
import os #imports OS GUI functions
# === CONFIGURATION: Define all maps to be created; creating dictonaries with name and included maps
map_configurations = [ #always included © OpenStreetMap contributors
{
"name": "karte1",
"layers": ["BISKO-Sektor", "OSM Standard"] # Replace with actual layer names
},
{
"name": "karte2",
"layers": ["Gepuffert", "OSM Standard"] # Replace with actual layer names
},
{
"name": "karte3",
"layers": ["gross", "OSM Standard"] # Replace with actual layer names
},
# {
# "name": "Karte Baublock Waermedichte",
# "layers": ["Gemeindegrenze", "Wärmeverbrauchsdichte in MWh/ha a", "© OpenStreetMap contributor"]
# },
# {
# "name": "MapN",
# "layers": ["LayerX", "LayerY", "OSM Standard"]
# }
]
# === Prompt for export folder ===
export_folder = QFileDialog.getExistingDirectory(None, "Select folder to save PNG maps")
if not export_folder:
print("Export cancelled by user.")
exit()
# === Get current project ===
project = QgsProject.instance()
all_layers = project.mapLayers().values() #loads all layers from propject
print("Verfügbare Layer im Projekt:") #gets available layers in the project
for l in all_layers:
print("-", l.name())
# # === Frage, ob das Skript fortgesetzt werden soll, Prüfung ob alle Layer da sind ===
# response = QMessageBox.question(
# None,
# "Skript fortsetzen?",
# "Möchten Sie das Skript fortsetzen?",
# QMessageBox.Yes | QMessageBox.No,
# QMessageBox.No
# )
# if response != QMessageBox.Yes:
# print("Skript wurde vom Benutzer gestoppt.")
# # Skript einfach beenden, ohne QGIS zu schließen
# raise Exception("Benutzerabbruch.")
# === Store current extent (same for all maps) ===
canvas = iface.mapCanvas()
current_extent = canvas.extent()
center_x = current_extent.center().x()
center_y = current_extent.center().y()
gui_aspect_ratio = current_extent.width() / current_extent.height()
a4_aspect_ratio = 297 / 210 #sets for A4 format
if gui_aspect_ratio > a4_aspect_ratio:
new_width = current_extent.width()
new_height = new_width / a4_aspect_ratio
else:
new_height = current_extent.height()
new_width = new_height * a4_aspect_ratio
adjusted_extent = QgsRectangle(
center_x - new_width / 2,
center_y - new_height / 2,
center_x + new_width / 2,
center_y + new_height / 2
)
# === Function to create individual map ===
def create_map(map_config):
global export_folder
map_name = map_config["name"]
target_layer_names = map_config["layers"]
print(f"Creating {map_name}...")
print(f"Export folder in function: {export_folder}")
print("Funktion create_map erfolgreich definiert")
# Filter layers for this map
selected_layers = [layer for layer in all_layers if layer.name() in target_layer_names]
if not selected_layers:
print(f"Warning: No matching layers found for {map_name}")
return False
# Create or replace the layout
layout_manager = project.layoutManager()
layout_name = f"{map_name}"
for l in layout_manager.printLayouts():
if l.name() == layout_name:
layout_manager.removeLayout(l)
layout = QgsPrintLayout(project)
layout.initializeDefaults()
layout.setName(layout_name)
layout_manager.addLayout(layout)
# Configure page and map
page = layout.pageCollection().page(0)
page.setPageSize(QgsLayoutSize(297, 210, QgsUnitTypes.LayoutMillimeters)) # A4 landscape
map_item = QgsLayoutItemMap(layout)
map_item.setLayers(selected_layers) # only layers
map_item.attemptMove(QgsLayoutPoint(0, 0, QgsUnitTypes.LayoutMillimeters))
map_item.attemptResize(QgsLayoutSize(297, 210, QgsUnitTypes.LayoutMillimeters))
map_item.setExtent(adjusted_extent)
layout.addLayoutItem(map_item)
# === LEGEND ===
legend = QgsLayoutItemLegend(layout)
legend.setLinkedMap(map_item)
legend.setResizeToContents(True)
legend.setBackgroundEnabled(True)
legend.setBackgroundColor(QColor(255, 255, 255, 255))
legend.setFrameEnabled(True)
legend.setFrameStrokeWidth(QgsLayoutMeasurement(0.4, QgsUnitTypes.LayoutMillimeters))
legend.setBoxSpace(4.0)
# # Add only wanted layer in the legend
# for layer in selected_layers:
# legend.model().rootGroup().addLayer(layer)
# legend.setResizeToContents(True)
# legend.refresh()
# Positionieren: unten rechts
legend.setReferencePoint(QgsLayoutItem.LowerRight)
legend.attemptMove(QgsLayoutPoint(297, 210, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(legend)
# Add 4mm margin around legend content
legend.setBoxSpace(4.0)
# make sure legend has its final size
legend.setResizeToContents(True)
legend.refresh()
#switch the legend's reference point to its own lower right corner
legend.setReferencePoint(QgsLayoutItem.LowerRight)
# after you set up and add the legend, then…
page = layout.pageCollection().page(0)
page_size = page.pageSize() # QgsLayoutSize(297, 210, LayoutMillimeters)
# bottom‑right of map (same as bottom‑right of page)
map_br_x = page_size.width() # 297 mm
map_br_y = page_size.height() # 210 mm
# make legend size final…
legend.setResizeToContents(True)
legend.refresh()
# anchor legend's LowerRight to that point
legend.setReferencePoint(QgsLayoutItem.LowerRight)
legend.attemptMove(QgsLayoutPoint(map_br_x, map_br_y, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(legend) #adds the legend in the bottom right corner
# Add north arrow
north_arrow_path = "C:/Users/Benjamin/AppData/Roaming/QGIS/QGIS3/profiles/default/resource_sharing/collections/north_arrow (Kapildev's Repository)/svg/north_06.svg"
north_arrow = QgsLayoutItemPicture(layout)
north_arrow.setPicturePath(north_arrow_path)
north_arrow.attemptMove(QgsLayoutPoint(297 - 11, 2, QgsUnitTypes.LayoutMillimeters))
north_arrow.attemptResize(QgsLayoutSize(18, 18, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(north_arrow)
# Add scale bar
scale_bar = QgsLayoutItemScaleBar(layout)
scale_bar.setStyle('Single Box')
scale_bar.setUnits(QgsUnitTypes.DistanceKilometers)
scale_bar.setUnitLabel("km")
scale_bar.setFont(QFont("Arial", 9))
# QGIS versions earlier than 3.40, löschen wenn 3.40 oder älter
scale_bar.setSegmentSizeMode(QgsScaleBarSettings.SegmentSizeFitWidth)
# QGIS version 3.40 or later, hier mit 3.36 geschrieben
# scale_bar.setSegmentSizeMode(QgsScaleBarSettings.SegmentSizeMode.FitWidth)
scale_bar.setNumberOfSegments(3)
scale_bar.setNumberOfSegmentsLeft(0)
scale_bar.setMinimumBarWidth(30)
scale_bar.setMaximumBarWidth(50)
scale_bar.attemptMove(QgsLayoutPoint(5, 195, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(scale_bar)
scale_bar.setLinkedMap(map_item)
scale_bar.refresh()
# Refresh layout items
layout.refresh()
map_item.refresh()
legend.refresh()
# Export PNG
file_path = os.path.join(export_folder, f"{map_name}.png")
print(f"Exporting {map_name} to: {file_path}")
print(f"Folder exists: {os.path.exists(export_folder)}")
if os.path.exists(file_path):
try:
os.remove(file_path)
print(f"Alte Datei gelöscht: {file_path}")
except Exception as e:
print(f"Fehler beim Löschen der alten Datei: {e}")
exporter = QgsLayoutExporter(layout)
settings = QgsLayoutExporter.ImageExportSettings()
settings.dpi = 100 # Set 100 DPI
result = exporter.exportToImage(file_path, settings)
print(f"Export result code for {map_name}: {result}")
if result == QgsLayoutExporter.Success:
print(f"{map_name} successfully exported to: {file_path}")
return True
else:
print(f"Export failed for {map_name}!")
return False
# === Create all maps ===
successful_exports = 0
total_maps = len(map_configurations)
for map_config in map_configurations: #iterates through collection of map configurations and attempts to create a map for each one, keeping track of how many succeed.
if create_map(map_config):
successful_exports += 1
# === Summary ===
print(f"\n=== Layout Creation Summary ===")
print(f"Successfully created: {successful_exports}/{total_maps} maps")
# Open layout designer for last created layout
if map_configurations and successful_exports > 0:
layout_manager = project.layoutManager()
last_layout_name = map_configurations[-1]['name']
last_layout = layout_manager.layoutByName(last_layout_name)
if last_layout:
print(f"Opening layout designer for: {last_layout_name}")
iface.openLayoutDesigner(last_layout)
else:
print(f"Warning: Could not find layout '{last_layout_name}' to open")