Files
Plugin_SN_Plan41/ui/tab_b_logic.py

253 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
sn_plan41/ui/tab_b_logic.py Fachlogik für Tab B (Druck)
"""
from __future__ import annotations
import math
from sn_basis.functions.variable_wrapper import set_variable, get_variable
from sn_basis.functions.qgiscore_wrapper import get_layer_extent
from sn_basis.modules.Pruefmanager import Pruefmanager
from sn_basis.modules.layerpruefer import Layerpruefer
from sn_plan41.ui.layout import Layout
KARTENNAME_VAR = "sn_kartenname"
PLOTMASSSTAB_VAR = "sn_plotmassstab"
VIEW_VAR = "sn_view"
ZIELGROESSE_VAR = "sn_zielgroesse"
FORMFAKTOR_VAR = "sn_formfaktor"
KARTENNAME_38 = "§38"
KARTENNAME_41 = "§41"
MASSSTAB_WIE_KARTENFENSTER = "Wie Kartenfenster"
THEMA_WIE_KARTENFENSTER = "wie kartenfenster"
KARTENNAME_BY_AUSWAHL = {
KARTENNAME_38: "Planungsübersicht §38 FlurbG",
KARTENNAME_41: "Karte zum Plan über die gemeinschaftlichen und öffentlichen Anlagen (§ 41 FlurbG)",
}
PLOTMASSSTAB_BY_AUSWAHL = {
"1:5.000": "5000",
"1:10.000": "10000",
"1:15.000": "15000",
"1:20.000": "20000",
"1:25.000": "25000",
"1:50.000": "50000",
"1:100.000": "100000",
}
# Breite x Höhe in mm (Hochformat, DIN-Standard)
DIN_GROESSEN: dict[str, tuple[int, int]] = {
"DIN A0": (841, 1189),
"DIN A1": (594, 841),
"DIN A2": (420, 594),
"DIN A3": (297, 420),
"DIN A4": (210, 297),
}
DIN_STANDARD = "DIN A0"
class TabBLogic:
"""
Kapselt die Fachlogik von Tab B.
"""
def __init__(self, pruefmanager: Pruefmanager) -> None:
self.pruefmanager = pruefmanager
def set_kartenname_for_auswahl(self, auswahl: str) -> None:
"""Setzt die Projektvariable ``sn_kartenname`` anhand der Kartennamen-Auswahl."""
kartenname = KARTENNAME_BY_AUSWAHL.get(auswahl, "")
set_variable(KARTENNAME_VAR, kartenname, scope="project")
def set_plotmassstab_for_auswahl(self, auswahl: str, aktueller_massstab: float | None = None) -> None:
"""Setzt die Projektvariable ``sn_plotmassstab`` anhand der Maßstabsauswahl."""
if auswahl == MASSSTAB_WIE_KARTENFENSTER:
if aktueller_massstab and aktueller_massstab > 0:
set_variable(PLOTMASSSTAB_VAR, str(int(round(aktueller_massstab))), scope="project")
else:
set_variable(PLOTMASSSTAB_VAR, "", scope="project")
return
value = PLOTMASSSTAB_BY_AUSWAHL.get(auswahl, "")
set_variable(PLOTMASSSTAB_VAR, value, scope="project")
def set_view_for_auswahl(self, auswahl: str) -> None:
"""Setzt ``sn_view`` auf ``aktuell`` oder den Namen des gewählten Layerthemas."""
if auswahl == THEMA_WIE_KARTENFENSTER:
set_variable(VIEW_VAR, "aktuell", scope="project")
return
set_variable(VIEW_VAR, auswahl or "", scope="project")
def set_zielgroesse_for_auswahl(self, auswahl: str) -> None:
"""Setzt ``sn_zielgroesse`` auf den gewählten DIN-Namen."""
set_variable(ZIELGROESSE_VAR, auswahl if auswahl in DIN_GROESSEN else DIN_STANDARD, scope="project")
def set_formfaktor(self, endlosrolle: bool) -> None:
"""Setzt ``sn_formfaktor`` auf ``Endlosrolle`` oder ``Blatt``."""
set_variable(FORMFAKTOR_VAR, "Endlosrolle" if endlosrolle else "Blatt", scope="project")
# ─────────────────────────────────────────────────────────────────────────
# Pipeline: Druckvorlage_anlegen
# ─────────────────────────────────────────────────────────────────────────
def druckvorlage_anlegen(
self,
layer: object,
kartenname_auswahl: str,
massstab_auswahl: str,
zielgroesse: str,
formfaktor: bool,
) -> dict:
"""Pipeline 'Druckvorlage_anlegen'.
Prüft Parameter, berechnet Plotgröße und stellt bei Bedarf Atlas-Rückfrage.
Parameters
----------
layer:
Aktuell gewählter Verfahrensgebiet-Layer aus Tab A.
Returns
-------
dict
``ok`` (bool): Ob die Pipeline erfolgreich durchlaufen werden soll.
``switch_to_tab_a`` (bool): Ob Tab A aktiviert werden soll.
``atlas_seiten`` (int): Anzahl benötigter Seiten (1 = kein Atlas).
"""
# ─── 1. Verfahrensgebiet-Layer prüfen ─────────────────────────────
lp = Layerpruefer(layer=layer)
ergebnis = lp.pruefe()
if not ergebnis.ok:
self.pruefmanager.zeige_hinweis(
"Verfahrensgebiets-Layer angeben",
"Verfahrensgebiets-Layer angeben",
)
return {"ok": False, "switch_to_tab_a": True, "atlas_seiten": 0}
layer_id = getattr(layer, "id", lambda: "")() or ""
set_variable("sn_verfahrensgebietslayer", layer_id, scope="project")
# ─── 2. Kartenname prüfen ─────────────────────────────────────────
if kartenname_auswahl not in (KARTENNAME_38, KARTENNAME_41):
self.pruefmanager.zeige_hinweis(
"Kartennamen wählen",
"Kartennamen wählen",
)
return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0}
# ─── 3. Maßstab ermitteln ─────────────────────────────────────────
if massstab_auswahl == MASSSTAB_WIE_KARTENFENSTER:
massstab_str = get_variable(PLOTMASSSTAB_VAR, scope="project") or ""
try:
massstab_zahl = float(massstab_str)
except (ValueError, TypeError):
massstab_zahl = 0.0
else:
massstab_zahl = float(PLOTMASSSTAB_BY_AUSWAHL.get(massstab_auswahl, 0))
if massstab_zahl <= 0:
self.pruefmanager.zeige_hinweis(
"Maßstab fehlt",
"Kein gültiger Maßstab angegeben.",
)
return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0}
# ─── 4. Kartenbild berechnen ──────────────────────────────────────
# Der Layer wird als metrisch projiziert (Einheit: m) vorausgesetzt,
# wie es für deutsche Planungslagen (z.B. EPSG:25832) üblich ist.
extent = get_layer_extent(layer)
if extent is None:
self.pruefmanager.zeige_hinweis(
"Fehler",
"Layer-Ausdehnung konnte nicht ermittelt werden.",
)
return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0}
# Naturgröße (m) → Papiergröße (mm): mm = m * 1000 / massstab
kartenbild_w = extent.width() * 1000.0 / massstab_zahl
kartenbild_h = extent.height() * 1000.0 / massstab_zahl
# ─── 5. Plotgröße = Kartenbild + Randabstand (x+210 mm, y+20 mm) ──
plotgroesse_w = kartenbild_w + 210.0
plotgroesse_h = kartenbild_h + 20.0
# ─── 6. Zielgröße bestimmen ───────────────────────────────────────
din_dims = DIN_GROESSEN.get(zielgroesse, DIN_GROESSEN[DIN_STANDARD])
if formfaktor: # Endlosrolle: X-Richtung entspricht der Plotgröße
ziel_w, ziel_h = plotgroesse_w, float(min(din_dims))
else:
ziel_w, ziel_h = float(din_dims[0]), float(din_dims[1])
# ─── 7. Passt auf ein Blatt? -> Layout erzeugen ───────────────────
print(f"[TabBLogic] plotgroesse=({plotgroesse_w:.1f}x{plotgroesse_h:.1f}), "
f"zielgroesse=({ziel_w:.1f}x{ziel_h:.1f}), passt={plotgroesse_w <= ziel_w and plotgroesse_h <= ziel_h}")
if plotgroesse_w <= ziel_w and plotgroesse_h <= ziel_h:
kartenname = get_variable(KARTENNAME_VAR, scope="project") or KARTENNAME_BY_AUSWAHL.get(
kartenname_auswahl, "Vorlage"
)
thema = get_variable(VIEW_VAR, scope="project") or ""
print(f"[TabBLogic] frage_text aufrufen, default='{kartenname}', thema='{thema}'")
vorlage_name, bestaetigt = self.pruefmanager.frage_text(
"Neue Vorlage anlegen",
"Bezeichnung der Vorlage:",
default_text=kartenname,
)
print(f"[TabBLogic] frage_text Ergebnis: name='{vorlage_name}', bestaetigt={bestaetigt}")
if not bestaetigt:
return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0}
vorlage_name = (vorlage_name or "").strip() or kartenname
print(f"[TabBLogic] Rufe Layout().create_single_page_layout auf: name='{vorlage_name}', "
f"page=({ziel_w}x{ziel_h}), map=({kartenbild_w:.1f}x{kartenbild_h:.1f}), "
f"massstab={massstab_zahl}, thema='{thema}'")
try:
Layout().create_single_page_layout(
name=vorlage_name,
page_width_mm=ziel_w,
page_height_mm=ziel_h,
map_width_mm=kartenbild_w,
map_height_mm=kartenbild_h,
extent=extent,
plotmassstab=massstab_zahl,
thema=thema,
)
print("[TabBLogic] create_single_page_layout erfolgreich abgeschlossen")
except ValueError as exc:
print(f"[TabBLogic] ValueError: {exc}")
self.pruefmanager.zeige_hinweis("Vorlage anlegen", str(exc))
return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0}
except Exception as exc:
print(f"[TabBLogic] Exception: {exc!r}")
self.pruefmanager.zeige_hinweis("Vorlage anlegen", f"Die Vorlage konnte nicht angelegt werden: {exc}")
return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0}
return {"ok": True, "switch_to_tab_a": False, "atlas_seiten": 1}
# ─── 8. Atlas: Anzahl Seiten berechnen ────────────────────────────
# Nutzbarer Kartenbereich pro Atlasseite (abzüglich gleichem Randabstand)
seite_karte_w = ziel_w - 210.0
seite_karte_h = ziel_h - 20.0
if seite_karte_w <= 0 or seite_karte_h <= 0:
self.pruefmanager.zeige_hinweis(
"Blattgröße zu klein",
"Die gewählte Zielgröße ist kleiner als der Mindest-Randabstand. "
"Bitte eine größere Blattgröße wählen.",
)
return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0}
pages_x = math.ceil(kartenbild_w / seite_karte_w)
pages_y = math.ceil(kartenbild_h / seite_karte_h)
anzahl_seiten = pages_x * pages_y
ja = self.pruefmanager.frage_ja_nein(
"Ausdruck als Atlas anlegen?",
f"Für die ausgewählten Parameter sind {anzahl_seiten} Einzelseiten erforderlich.\n"
"Ausdruck als Atlas anlegen?",
default=True,
)
if not ja:
return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": anzahl_seiten}
return {"ok": True, "switch_to_tab_a": False, "atlas_seiten": anzahl_seiten}