Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a7196ebb9 | ||
|
|
543ba605a7 | ||
|
|
730c013799 | ||
|
|
d1b2ee80bb | ||
|
|
1bc6c90559 | ||
|
|
34d422b807 | ||
|
|
d50d10f830 | ||
|
|
d4552b5afe | ||
|
|
e036d5a178 | ||
|
|
f6328be375 | ||
|
|
d346640947 |
@@ -1,15 +1,4 @@
|
|||||||
---
|
---
|
||||||
Version 26.6.1-unstable:
|
|
||||||
- VLN-API zum Laden der Plan41-Maßnahmen integriert
|
|
||||||
---
|
|
||||||
Version 26.5.2-testing:
|
|
||||||
---
|
|
||||||
Version 26.5.1-testing:
|
|
||||||
---
|
|
||||||
Version 26.5.1-unstable:
|
|
||||||
---
|
|
||||||
Version 26.4.8-unstable:
|
|
||||||
---
|
|
||||||
Version 26.4.7-unstable:
|
Version 26.4.7-unstable:
|
||||||
---
|
---
|
||||||
Version 26.4.6-unstable:
|
Version 26.4.6-unstable:
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
{
|
||||||
|
"template_name": "baufreigabe",
|
||||||
|
"description": "Baufreigabe-Layout mit 80mm Kartenschild (DIN A3/A4 Querformat)",
|
||||||
|
"kartenschild_width_mm": 80.0,
|
||||||
|
"kartenschild_height_mm": 58.0,
|
||||||
|
"kartenschild_line_offsets_y": [-10.0, -20.0, -30.0, -46.0],
|
||||||
|
"labels": [
|
||||||
|
{
|
||||||
|
"id": "Herausgeber",
|
||||||
|
"object_name": "Herausgeber",
|
||||||
|
"text": "Teilnehmergemeinschaft [%@sn_bezeichnung %] [%@sn_name %]",
|
||||||
|
"x_mm": 14.0,
|
||||||
|
"y_mm": -48.0,
|
||||||
|
"width_mm": 74.0,
|
||||||
|
"height_mm": 8.0,
|
||||||
|
"format": "Format_1",
|
||||||
|
"html_mode": false,
|
||||||
|
"expression_enabled": true,
|
||||||
|
"v_align": "bottom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Bezeichnung_Flurbereinigung",
|
||||||
|
"object_name": "Bezeichnung_Flurbereinigung",
|
||||||
|
"text": "[% @sn_bezeichnung %]",
|
||||||
|
"x_mm": 14.0,
|
||||||
|
"y_mm": -41.0,
|
||||||
|
"width_mm": 74.0,
|
||||||
|
"height_mm": 4.0,
|
||||||
|
"format": "Format_1",
|
||||||
|
"html_mode": false,
|
||||||
|
"expression_enabled": true,
|
||||||
|
"v_align": "top"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Name_Flurbereinigung",
|
||||||
|
"object_name": "Name_Flurbereinigung",
|
||||||
|
"text": "[% @sn_name %]",
|
||||||
|
"x_mm": 14.0,
|
||||||
|
"y_mm": -32.0,
|
||||||
|
"width_mm": 74.0,
|
||||||
|
"height_mm": 5.0,
|
||||||
|
"format": "Format_2",
|
||||||
|
"html_mode": false,
|
||||||
|
"expression_enabled": true,
|
||||||
|
"v_align": "top"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Kartenname",
|
||||||
|
"object_name": "Kartenname",
|
||||||
|
"text": "[% @sn_kartenname %]",
|
||||||
|
"x_mm": 14.0,
|
||||||
|
"y_mm": -23.0,
|
||||||
|
"width_mm": 74.0,
|
||||||
|
"height_mm": 6.0,
|
||||||
|
"format": "Format_3",
|
||||||
|
"html_mode": false,
|
||||||
|
"expression_enabled": true,
|
||||||
|
"v_align": "top"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Ausgabedatum",
|
||||||
|
"object_name": "Ausgabedatum",
|
||||||
|
"text": "Ausgabedatum: [%format_date(now(), 'dd.MM.yyyy')%]",
|
||||||
|
"x_mm": 14.0,
|
||||||
|
"y_mm": -13.0,
|
||||||
|
"width_mm": 74.0,
|
||||||
|
"height_mm": 4.0,
|
||||||
|
"format": "Format_1",
|
||||||
|
"html_mode": false,
|
||||||
|
"expression_enabled": true,
|
||||||
|
"v_align": "top"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Massstab",
|
||||||
|
"object_name": "Massstab",
|
||||||
|
"text": "Ma\u00dfstab: 1 : [%round(map_get(item_variables('Hauptkarte'),'map_scale'),0)%]",
|
||||||
|
"x_mm": 14.0,
|
||||||
|
"y_mm": -3.0,
|
||||||
|
"width_mm": 74.0,
|
||||||
|
"height_mm": 4.0,
|
||||||
|
"format": "Format_1",
|
||||||
|
"html_mode": false,
|
||||||
|
"expression_enabled": true,
|
||||||
|
"v_align": "top"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
{
|
||||||
|
"template_name": "standard",
|
||||||
|
"description": "Standard-Layout mit 180mm Kartenschild fuer §38/§41 FlurbG Karten",
|
||||||
|
"kartenschild_width_mm": 180.0,
|
||||||
|
"kartenschild_height_mm": 92.0,
|
||||||
|
"kartenschild_line_offsets_y": [-10.0, -20.0, -30.0, -46.0, -62.0, -82.0],
|
||||||
|
"labels": [
|
||||||
|
{
|
||||||
|
"id": "Herausgeber",
|
||||||
|
"object_name": "Herausgeber",
|
||||||
|
"text": "Teilnehmergemeinschaft [%@sn_bezeichnung %] [%@sn_name %]",
|
||||||
|
"text_if_kartenname_38": "[%@sn_landkreis_user%] [%@sn_behoerde%]",
|
||||||
|
"x_mm": 16.0,
|
||||||
|
"y_mm": -84.0,
|
||||||
|
"width_mm": 171.0,
|
||||||
|
"height_mm": 4.0,
|
||||||
|
"format": "Format_1",
|
||||||
|
"html_mode": false,
|
||||||
|
"expression_enabled": true,
|
||||||
|
"v_align": "top"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Bezeichnung_Flurbereinigung",
|
||||||
|
"object_name": "Bezeichnung_Flurbereinigung",
|
||||||
|
"text": "[% @sn_bezeichnung %]",
|
||||||
|
"x_mm": 16.0,
|
||||||
|
"y_mm": -75.0,
|
||||||
|
"width_mm": 171.0,
|
||||||
|
"height_mm": 4.0,
|
||||||
|
"format": "Format_1",
|
||||||
|
"html_mode": false,
|
||||||
|
"expression_enabled": true,
|
||||||
|
"v_align": "top"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Name_Flurbereinigung",
|
||||||
|
"object_name": "Name_Flurbereinigung",
|
||||||
|
"text": "[% @sn_name %]",
|
||||||
|
"x_mm": 16.0,
|
||||||
|
"y_mm": -64.0,
|
||||||
|
"width_mm": 171.0,
|
||||||
|
"height_mm": 5.0,
|
||||||
|
"format": "Format_2",
|
||||||
|
"html_mode": false,
|
||||||
|
"expression_enabled": true,
|
||||||
|
"v_align": "top"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Stadt_Gemeinde",
|
||||||
|
"object_name": "Stadt_Gemeinde",
|
||||||
|
"text": "[% @sn_gemeinden %]",
|
||||||
|
"x_mm": 16.0,
|
||||||
|
"y_mm": -56.0,
|
||||||
|
"width_mm": 171.0,
|
||||||
|
"height_mm": 4.0,
|
||||||
|
"format": "Format_1",
|
||||||
|
"html_mode": false,
|
||||||
|
"expression_enabled": true,
|
||||||
|
"v_align": "top"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Landkreis",
|
||||||
|
"object_name": "Landkreis",
|
||||||
|
"text": "[% @sn_landkreise_proj %]",
|
||||||
|
"x_mm": 16.0,
|
||||||
|
"y_mm": -48.0,
|
||||||
|
"width_mm": 171.0,
|
||||||
|
"height_mm": 4.0,
|
||||||
|
"format": "Format_1",
|
||||||
|
"html_mode": false,
|
||||||
|
"expression_enabled": true,
|
||||||
|
"v_align": "top"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Kartenname",
|
||||||
|
"object_name": "Kartenname",
|
||||||
|
"text": "[% @sn_kartenname %]",
|
||||||
|
"x_mm": 16.0,
|
||||||
|
"y_mm": -32.0,
|
||||||
|
"width_mm": 137.0,
|
||||||
|
"height_mm": 12.0,
|
||||||
|
"format": "Format_3",
|
||||||
|
"html_mode": false,
|
||||||
|
"expression_enabled": true,
|
||||||
|
"v_align": "top"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Gefertigt",
|
||||||
|
"object_name": "Gefertigt",
|
||||||
|
"text": "Gefertigt: ",
|
||||||
|
"x_mm": 16.0,
|
||||||
|
"y_mm": -23.0,
|
||||||
|
"width_mm": 81.0,
|
||||||
|
"height_mm": 4.0,
|
||||||
|
"format": "Format_1",
|
||||||
|
"html_mode": false,
|
||||||
|
"expression_enabled": true,
|
||||||
|
"v_align": "top"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Gepr\u00fcft",
|
||||||
|
"object_name": "Gepr\u00fcft",
|
||||||
|
"text": "Geprueft: ",
|
||||||
|
"x_mm": 101.0,
|
||||||
|
"y_mm": -23.0,
|
||||||
|
"width_mm": 81.0,
|
||||||
|
"height_mm": 4.0,
|
||||||
|
"format": "Format_1",
|
||||||
|
"html_mode": false,
|
||||||
|
"expression_enabled": true,
|
||||||
|
"v_align": "top"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Massstab",
|
||||||
|
"object_name": "Massstab",
|
||||||
|
"text": "Ma\u00dfstab: 1 : [%round(map_get(item_variables('Hauptkarte'),'map_scale'),0)%]",
|
||||||
|
"x_mm": 16.0,
|
||||||
|
"y_mm": -13.0,
|
||||||
|
"width_mm": 81.0,
|
||||||
|
"height_mm": 4.0,
|
||||||
|
"format": "Format_1",
|
||||||
|
"html_mode": false,
|
||||||
|
"expression_enabled": true,
|
||||||
|
"v_align": "top"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Ausgabedatum",
|
||||||
|
"object_name": "Ausgabedatum",
|
||||||
|
"text": "Ausgabedatum: [%format_date(now(), 'dd.MM.yyyy')%]",
|
||||||
|
"x_mm": 101.0,
|
||||||
|
"y_mm": -13.0,
|
||||||
|
"width_mm": 81.0,
|
||||||
|
"height_mm": 4.0,
|
||||||
|
"format": "Format_1",
|
||||||
|
"html_mode": false,
|
||||||
|
"expression_enabled": true,
|
||||||
|
"v_align": "top"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Aktenzeichen",
|
||||||
|
"object_name": "Aktenzeichen",
|
||||||
|
"text": "Aktenzeichen: XXX",
|
||||||
|
"x_mm": 16.0,
|
||||||
|
"y_mm": -3.0,
|
||||||
|
"width_mm": 171.0,
|
||||||
|
"height_mm": 4.0,
|
||||||
|
"format": "Format_1",
|
||||||
|
"html_mode": false,
|
||||||
|
"expression_enabled": true,
|
||||||
|
"v_align": "top"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"Format_1": {
|
||||||
|
"font_family": "Arial",
|
||||||
|
"font_size": 11,
|
||||||
|
"font_style": "normal"
|
||||||
|
},
|
||||||
|
"Format_2": {
|
||||||
|
"font_family": "Arial",
|
||||||
|
"font_size": 13,
|
||||||
|
"font_style": "bold"
|
||||||
|
},
|
||||||
|
"Format_3": {
|
||||||
|
"font_family": "Arial",
|
||||||
|
"font_size": 16,
|
||||||
|
"font_style": "bold"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ from .ly_metadata_wrapper import (
|
|||||||
is_layer_editable,
|
is_layer_editable,
|
||||||
)
|
)
|
||||||
from .ly_style_wrapper import apply_style
|
from .ly_style_wrapper import apply_style
|
||||||
from .dialog_wrapper import ask_yes_no, ask_overwrite_append_cancel_custom, ask_ergaenzen_entfernen_ersetzen_abbrechen, ask_detailpruefung_oder_vg_laden
|
from .dialog_wrapper import ask_yes_no, ask_overwrite_append_cancel_custom, ask_ergaenzen_entfernen_ersetzen_abbrechen
|
||||||
|
|
||||||
from .message_wrapper import (
|
from .message_wrapper import (
|
||||||
_get_message_bar,
|
_get_message_bar,
|
||||||
|
|||||||
@@ -182,56 +182,6 @@ def ask_ergaenzen_entfernen_ersetzen_abbrechen(
|
|||||||
return "abbrechen"
|
return "abbrechen"
|
||||||
|
|
||||||
|
|
||||||
def ask_detailpruefung_oder_vg_laden(
|
|
||||||
parent: Any,
|
|
||||||
title: str,
|
|
||||||
message: str,
|
|
||||||
) -> Literal["detailpruefung", "als_vg_laden", "abbrechen"]:
|
|
||||||
"""Zeigt Dialog mit drei Optionen nach erkannter Flächenabweichung.
|
|
||||||
|
|
||||||
Optionen:
|
|
||||||
- **Detailprüfung starten**: Knickpunktbasierte Prüfung des BROR-Umrings
|
|
||||||
- **Bodenordnungsobjekt als VG laden**: BROR direkt als Verfahrensgebiet übernehmen
|
|
||||||
- **Abbrechen**: Vorgang beenden ohne weitere Aktion
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
parent :
|
|
||||||
Eltern-Widget oder None.
|
|
||||||
title : str
|
|
||||||
Dialog-Titel.
|
|
||||||
message : str
|
|
||||||
Hauptmeldung mit Flächen-Informationen.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Literal["detailpruefung", "als_vg_laden", "abbrechen"]
|
|
||||||
Genaue Entscheidung des Nutzers.
|
|
||||||
"""
|
|
||||||
if QT_VERSION == 0: # Mock-Modus
|
|
||||||
print(f"Mock-Modus: ask_detailpruefung_oder_vg_laden('{title}') → abbrechen")
|
|
||||||
return "abbrechen"
|
|
||||||
|
|
||||||
msg = QMessageBox(parent)
|
|
||||||
msg.setIcon(ICON_QUESTION)
|
|
||||||
msg.setWindowTitle(title)
|
|
||||||
msg.setText(message)
|
|
||||||
|
|
||||||
detail_btn = msg.addButton("Detailprüfung starten", QMessageBox.ButtonRole.AcceptRole)
|
|
||||||
vg_btn = msg.addButton("Bodenordnungsobjekt als Verfahrensgebiet laden", QMessageBox.ButtonRole.ActionRole)
|
|
||||||
msg.addButton("Abbrechen", QMessageBox.ButtonRole.RejectRole)
|
|
||||||
|
|
||||||
exec_dialog(msg)
|
|
||||||
|
|
||||||
clicked = msg.clickedButton()
|
|
||||||
if clicked == detail_btn:
|
|
||||||
return "detailpruefung"
|
|
||||||
elif clicked == vg_btn:
|
|
||||||
return "als_vg_laden"
|
|
||||||
else:
|
|
||||||
return "abbrechen"
|
|
||||||
|
|
||||||
|
|
||||||
class ProgressDialog:
|
class ProgressDialog:
|
||||||
def __init__(self, total: int, title: str = "Fortschritt", label: str = "Verarbeite..."):
|
def __init__(self, total: int, title: str = "Fortschritt", label: str = "Verarbeite..."):
|
||||||
self.total = max(total, 1)
|
self.total = max(total, 1)
|
||||||
@@ -324,73 +274,7 @@ class ProgressDialog:
|
|||||||
if self._dlg is not None:
|
if self._dlg is not None:
|
||||||
self._dlg.close()
|
self._dlg.close()
|
||||||
|
|
||||||
def raise_to_front(self) -> None:
|
|
||||||
"""Bringt den Fortschrittsdialog in den Vordergrund.
|
|
||||||
|
|
||||||
Wird aufgerufen, nachdem ein modaler Subdialog (z. B. Auswahlbox)
|
|
||||||
geschlossen wurde und der Fortschrittsdialog wieder sichtbar sein soll.
|
|
||||||
"""
|
|
||||||
if QT_VERSION == 0:
|
|
||||||
return
|
|
||||||
if self._dlg is not None:
|
|
||||||
try:
|
|
||||||
self._dlg.raise_()
|
|
||||||
self._dlg.activateWindow()
|
|
||||||
QCoreApplication.processEvents()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def create_progress_dialog(total: int, title: str = "Fortschritt", label: str = "Verarbeite...") -> ProgressDialog:
|
def create_progress_dialog(total: int, title: str = "Fortschritt", label: str = "Verarbeite...") -> ProgressDialog:
|
||||||
return ProgressDialog(total, title, label)
|
return ProgressDialog(total, title, label)
|
||||||
|
|
||||||
|
|
||||||
def ask_bror_kombination(
|
|
||||||
parent: Any,
|
|
||||||
kombinationen: list[tuple[str, str]],
|
|
||||||
) -> Optional[tuple[str, str]]:
|
|
||||||
"""Zeigt Dialog zur Auswahl einer BROR-Kombination (adv_name + bezeichnung).
|
|
||||||
|
|
||||||
Wird aufgerufen, wenn der WFS mehrere verschiedene Kombinationen aus
|
|
||||||
``adv_name`` und ``bezeichnung`` zurückliefert, sodass der Nutzer
|
|
||||||
entscheiden muss, welche Objekte in die Pipeline einfließen sollen.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
parent :
|
|
||||||
Eltern-Widget oder None.
|
|
||||||
kombinationen :
|
|
||||||
Sortierte Liste von ``(adv_name, bezeichnung)``-Tupeln.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Optional[tuple[str, str]]
|
|
||||||
Das gewählte ``(adv_name, bezeichnung)``-Tupel oder ``None`` bei
|
|
||||||
Abbruch bzw. leerer Eingabe.
|
|
||||||
"""
|
|
||||||
if not kombinationen:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if QT_VERSION == 0: # Mock-Modus
|
|
||||||
print(f"Mock-Modus: ask_bror_kombination → {kombinationen[0]}")
|
|
||||||
return kombinationen[0]
|
|
||||||
|
|
||||||
items = [f"{adv} | {bez}" for adv, bez in kombinationen]
|
|
||||||
item, ok = QInputDialog.getItem(
|
|
||||||
parent,
|
|
||||||
"BROR-Objekt auswählen",
|
|
||||||
"Mehrere BauRaumOderBodenordnungsrecht-Objekte gefunden.\n"
|
|
||||||
"Bitte wählen Sie die gewünschte Kombination\n"
|
|
||||||
"aus ADV-Name und Bezeichnung:",
|
|
||||||
items,
|
|
||||||
0,
|
|
||||||
False,
|
|
||||||
)
|
|
||||||
if not ok:
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
idx = items.index(item)
|
|
||||||
return kombinationen[idx]
|
|
||||||
except ValueError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,25 +5,6 @@ from sn_basis.functions.sys_wrapper import get_plugin_root, join_path
|
|||||||
from sn_basis.modules.stilpruefer import Stilpruefer
|
from sn_basis.modules.stilpruefer import Stilpruefer
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
def apply_style_from_path(layer, style_path: str) -> bool:
|
|
||||||
"""
|
|
||||||
Wendet einen Layerstil anhand eines vollständigen Pfades an.
|
|
||||||
|
|
||||||
Im Gegensatz zu :func:`apply_style` wird der Pfad extern bereitgestellt;
|
|
||||||
es findet keine eigene Stilprüfung statt.
|
|
||||||
"""
|
|
||||||
if not layer_exists(layer):
|
|
||||||
return False
|
|
||||||
try:
|
|
||||||
ok, _ = layer.loadNamedStyle(str(style_path))
|
|
||||||
if ok:
|
|
||||||
getattr(layer, "triggerRepaint", lambda: None)()
|
|
||||||
return True
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def apply_style(layer, style_name: str) -> bool:
|
def apply_style(layer, style_name: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Wendet einen Layerstil an, sofern er gültig ist.
|
Wendet einen Layerstil an, sofern er gültig ist.
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ QgsFeature: Type[Any]
|
|||||||
QgsField: Type[Any]
|
QgsField: Type[Any]
|
||||||
QgsGeometry: Type[Any]
|
QgsGeometry: Type[Any]
|
||||||
QgsFeatureRequest: Type[Any]
|
QgsFeatureRequest: Type[Any]
|
||||||
QgsRectangle: Type[Any]
|
|
||||||
QgsCoordinateTransform: Type[Any]
|
QgsCoordinateTransform: Type[Any]
|
||||||
QgsCoordinateReferenceSystem: Type[Any]
|
QgsCoordinateReferenceSystem: Type[Any]
|
||||||
QgsPrintLayout: Type[Any]
|
QgsPrintLayout: Type[Any]
|
||||||
@@ -38,12 +37,6 @@ QgsLayoutPoint: Type[Any]
|
|||||||
QgsLayoutSize: Type[Any]
|
QgsLayoutSize: Type[Any]
|
||||||
QgsUnitTypes: Type[Any]
|
QgsUnitTypes: Type[Any]
|
||||||
QgsLayoutItem: Type[Any]
|
QgsLayoutItem: Type[Any]
|
||||||
QgsBlockingNetworkRequest: Type[Any]
|
|
||||||
QgsJsonExporter: Type[Any]
|
|
||||||
QgsJsonUtils: Type[Any]
|
|
||||||
GEOM_POINT: Any = None
|
|
||||||
GEOM_LINE: Any = None
|
|
||||||
GEOM_POLYGON: Any = None
|
|
||||||
|
|
||||||
MAP_GRID_STYLE_MARKERS: Any = None
|
MAP_GRID_STYLE_MARKERS: Any = None
|
||||||
MAP_GRID_STYLE_FRAME_ANNOTATIONS: Any = None
|
MAP_GRID_STYLE_FRAME_ANNOTATIONS: Any = None
|
||||||
@@ -83,7 +76,6 @@ try:
|
|||||||
QgsField as _QgsField,
|
QgsField as _QgsField,
|
||||||
QgsGeometry as _QgsGeometry,
|
QgsGeometry as _QgsGeometry,
|
||||||
QgsFeatureRequest as _QgsFeatureRequest,
|
QgsFeatureRequest as _QgsFeatureRequest,
|
||||||
QgsRectangle as _QgsRectangle,
|
|
||||||
QgsCoordinateTransform as _QgsCoordinateTransform,
|
QgsCoordinateTransform as _QgsCoordinateTransform,
|
||||||
QgsCoordinateReferenceSystem as _QgsCoordinateReferenceSystem,
|
QgsCoordinateReferenceSystem as _QgsCoordinateReferenceSystem,
|
||||||
QgsPrintLayout as _QgsPrintLayout,
|
QgsPrintLayout as _QgsPrintLayout,
|
||||||
@@ -97,9 +89,6 @@ try:
|
|||||||
QgsLayoutSize as _QgsLayoutSize,
|
QgsLayoutSize as _QgsLayoutSize,
|
||||||
QgsUnitTypes as _QgsUnitTypes,
|
QgsUnitTypes as _QgsUnitTypes,
|
||||||
QgsLayoutItem as _QgsLayoutItem,
|
QgsLayoutItem as _QgsLayoutItem,
|
||||||
QgsBlockingNetworkRequest as _QgsBlockingNetworkRequest,
|
|
||||||
QgsJsonExporter as _QgsJsonExporter,
|
|
||||||
QgsJsonUtils as _QgsJsonUtils,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
QgsProject = _QgsProject
|
QgsProject = _QgsProject
|
||||||
@@ -113,7 +102,6 @@ try:
|
|||||||
QgsField = _QgsField
|
QgsField = _QgsField
|
||||||
QgsGeometry = _QgsGeometry
|
QgsGeometry = _QgsGeometry
|
||||||
QgsFeatureRequest = _QgsFeatureRequest
|
QgsFeatureRequest = _QgsFeatureRequest
|
||||||
QgsRectangle = _QgsRectangle
|
|
||||||
QgsCoordinateTransform = _QgsCoordinateTransform
|
QgsCoordinateTransform = _QgsCoordinateTransform
|
||||||
QgsCoordinateReferenceSystem = _QgsCoordinateReferenceSystem
|
QgsCoordinateReferenceSystem = _QgsCoordinateReferenceSystem
|
||||||
QgsPrintLayout = _QgsPrintLayout
|
QgsPrintLayout = _QgsPrintLayout
|
||||||
@@ -127,32 +115,6 @@ try:
|
|||||||
QgsLayoutSize = _QgsLayoutSize
|
QgsLayoutSize = _QgsLayoutSize
|
||||||
QgsUnitTypes = _QgsUnitTypes
|
QgsUnitTypes = _QgsUnitTypes
|
||||||
QgsLayoutItem = _QgsLayoutItem
|
QgsLayoutItem = _QgsLayoutItem
|
||||||
QgsBlockingNetworkRequest = _QgsBlockingNetworkRequest
|
|
||||||
QgsJsonExporter = _QgsJsonExporter
|
|
||||||
QgsJsonUtils = _QgsJsonUtils
|
|
||||||
|
|
||||||
# Geometrietyp-Konstanten (QGIS 4 / QGIS 3.30+ vs. ältere QGIS-3)
|
|
||||||
def _resolve_geom_type(*paths):
|
|
||||||
for obj, *attrs in paths:
|
|
||||||
val = obj
|
|
||||||
for attr in attrs:
|
|
||||||
val = getattr(val, attr, None)
|
|
||||||
if val is None:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
if val is not None:
|
|
||||||
return val
|
|
||||||
return None
|
|
||||||
|
|
||||||
GEOM_POINT = _resolve_geom_type(
|
|
||||||
(_Qgis, "GeometryType", "Point"),
|
|
||||||
) or getattr(__import__("qgis.core", fromlist=["QgsWkbTypes"]), "QgsWkbTypes", type("_", (), {"PointGeometry": 0})).PointGeometry
|
|
||||||
GEOM_LINE = _resolve_geom_type(
|
|
||||||
(_Qgis, "GeometryType", "Line"),
|
|
||||||
) or getattr(__import__("qgis.core", fromlist=["QgsWkbTypes"]), "QgsWkbTypes", type("_", (), {"LineGeometry": 1})).LineGeometry
|
|
||||||
GEOM_POLYGON = _resolve_geom_type(
|
|
||||||
(_Qgis, "GeometryType", "Polygon"),
|
|
||||||
) or getattr(__import__("qgis.core", fromlist=["QgsWkbTypes"]), "QgsWkbTypes", type("_", (), {"PolygonGeometry": 2})).PolygonGeometry
|
|
||||||
|
|
||||||
def _resolve_qgis_enum(*paths: tuple[Any, ...]) -> Any:
|
def _resolve_qgis_enum(*paths: tuple[Any, ...]) -> Any:
|
||||||
for path in paths:
|
for path in paths:
|
||||||
@@ -812,60 +774,6 @@ except Exception:
|
|||||||
|
|
||||||
QgsVectorFileWriter = _MockQgsVectorFileWriter
|
QgsVectorFileWriter = _MockQgsVectorFileWriter
|
||||||
|
|
||||||
# Geometrietyp-Konstanten (Mock-Werte entsprechen QgsWkbTypes)
|
|
||||||
GEOM_POINT = 0
|
|
||||||
GEOM_LINE = 1
|
|
||||||
GEOM_POLYGON = 2
|
|
||||||
|
|
||||||
class _MockQgsBlockingNetworkRequest:
|
|
||||||
NoError = 0
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self._reply = None
|
|
||||||
|
|
||||||
def get(self, request: Any) -> int:
|
|
||||||
return self.NoError
|
|
||||||
|
|
||||||
def post(self, request: Any, data: bytes) -> int:
|
|
||||||
return self.NoError
|
|
||||||
|
|
||||||
def put(self, request: Any, data: bytes) -> int:
|
|
||||||
return self.NoError
|
|
||||||
|
|
||||||
def reply(self) -> Any:
|
|
||||||
return self._reply
|
|
||||||
|
|
||||||
def errorMessage(self) -> str:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
QgsBlockingNetworkRequest = _MockQgsBlockingNetworkRequest
|
|
||||||
|
|
||||||
class _MockQgsJsonUtils:
|
|
||||||
@staticmethod
|
|
||||||
def stringToFields(text: str) -> Any:
|
|
||||||
return type("_Fields", (), {"toList": lambda self: []})()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def stringToFeatureList(text: str, fields: Any) -> list:
|
|
||||||
return []
|
|
||||||
|
|
||||||
QgsJsonUtils = _MockQgsJsonUtils
|
|
||||||
|
|
||||||
class _MockQgsJsonExporter:
|
|
||||||
def __init__(self, layer: Any = None):
|
|
||||||
self._layer = layer
|
|
||||||
|
|
||||||
def setSourceCrs(self, crs: Any) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def setTransformGeometries(self, transform: bool) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def exportFeatures(self, features: list) -> str:
|
|
||||||
return '{"type":"FeatureCollection","features":[]}'
|
|
||||||
|
|
||||||
QgsJsonExporter = _MockQgsJsonExporter
|
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# Netzwerk
|
# Netzwerk
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
@@ -1043,79 +951,3 @@ def layer_exists_in_gpkg(gpkg_path: str, layer_name: str) -> bool:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
|
||||||
# QGIS-3/4-kompatible Geometrie-Hilfsfunktionen
|
|
||||||
# ---------------------------------------------------------
|
|
||||||
|
|
||||||
def polygon_zu_linie(geom: Any) -> Optional[Any]:
|
|
||||||
"""Konvertiert ein Polygon/MultiPolygon zur Umringslinie (QGIS 3 + 4).
|
|
||||||
|
|
||||||
In QGIS 3.x existiert ``QgsGeometry.boundary()``, in QGIS 4.x wurde sie
|
|
||||||
entfernt. Diese Funktion kapselt die Versionsunterschiede und stellt dem
|
|
||||||
Fachplugin eine stabile API bereit.
|
|
||||||
|
|
||||||
Strategie (dreistufig):
|
|
||||||
1. ``geom.boundary()`` – funktioniert in QGIS 3.x
|
|
||||||
2. ``geom.convertToType(1, False)`` – ``1`` = LineGeometry-Wert,
|
|
||||||
identisch in ``QgsWkbTypes.LineGeometry`` (3.x) und
|
|
||||||
``Qgis.GeometryType.Line`` (4.x)
|
|
||||||
3. Manuelle Ring-Extraktion via ``asPolygon()`` / ``asMultiPolygon()``
|
|
||||||
+ ``QgsGeometry.fromPolylineXY()``
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
geom :
|
|
||||||
Polygon- oder MultiPolygon-``QgsGeometry``.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Optional[QgsGeometry]
|
|
||||||
Liniengeometrie (Umring) oder ``None`` bei Fehler / leerer Eingabe.
|
|
||||||
"""
|
|
||||||
if geom is None:
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
if geom.isEmpty():
|
|
||||||
return None
|
|
||||||
except Exception:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Stufe 1: QGIS 3.x – .boundary() existiert und liefert alle Ringe korrekt
|
|
||||||
if hasattr(geom, "boundary"):
|
|
||||||
try:
|
|
||||||
result = geom.boundary()
|
|
||||||
if result is not None and not result.isEmpty():
|
|
||||||
return result
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Stufe 2: Manuelle Ring-Extraktion (primärer QGIS-4.x-Pfad).
|
|
||||||
# convertToType(1, False) würde bei MultiPolygon nur den ersten Außenring
|
|
||||||
# zurückgeben und alle weiteren Polygone sowie Innenringe verwerfen → daher
|
|
||||||
# direkt die vollständige manuelle Extraktion als nächste Stufe.
|
|
||||||
lines = []
|
|
||||||
try:
|
|
||||||
if geom.isMultipart():
|
|
||||||
for polygon in geom.asMultiPolygon():
|
|
||||||
for ring in polygon:
|
|
||||||
lines.append(QgsGeometry.fromPolylineXY(ring))
|
|
||||||
else:
|
|
||||||
for ring in geom.asPolygon():
|
|
||||||
lines.append(QgsGeometry.fromPolylineXY(ring))
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
if lines:
|
|
||||||
return QgsGeometry.unaryUnion(lines)
|
|
||||||
|
|
||||||
# Stufe 3: Letzter Fallback – convertToType mit destMultipart=True, damit
|
|
||||||
# zumindest alle Außenringe eines MultiPolygons erfasst werden.
|
|
||||||
try:
|
|
||||||
result = geom.convertToType(1, True)
|
|
||||||
if result is not None and not result.isEmpty():
|
|
||||||
return result
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|||||||
+2
-32
@@ -45,7 +45,6 @@ QComboBox: Type[Any] = object
|
|||||||
QCheckBox: Type[Any] = object
|
QCheckBox: Type[Any] = object
|
||||||
QHBoxLayout: Type[Any] = object
|
QHBoxLayout: Type[Any] = object
|
||||||
QFont: Type[Any] = object
|
QFont: Type[Any] = object
|
||||||
QSettings: Type[Any] = object
|
|
||||||
|
|
||||||
|
|
||||||
def exec_dialog(dialog: Any) -> Any:
|
def exec_dialog(dialog: Any) -> Any:
|
||||||
@@ -100,8 +99,7 @@ try:
|
|||||||
QUrl as _QUrl,
|
QUrl as _QUrl,
|
||||||
QCoreApplication as _QCoreApplication,
|
QCoreApplication as _QCoreApplication,
|
||||||
Qt as _Qt,
|
Qt as _Qt,
|
||||||
QVariant as _QVariant,
|
QVariant as _QVariant
|
||||||
QSettings as _QSettings,
|
|
||||||
)
|
)
|
||||||
from qgis.PyQt.QtNetwork import (
|
from qgis.PyQt.QtNetwork import (
|
||||||
QNetworkRequest as _QNetworkRequest,
|
QNetworkRequest as _QNetworkRequest,
|
||||||
@@ -142,7 +140,6 @@ try:
|
|||||||
QVariant = _QVariant
|
QVariant = _QVariant
|
||||||
QHBoxLayout = _QHBoxLayout
|
QHBoxLayout = _QHBoxLayout
|
||||||
QFont = _QFont
|
QFont = _QFont
|
||||||
QSettings = _QSettings
|
|
||||||
# ✅ QT6 ENUMS
|
# ✅ QT6 ENUMS
|
||||||
YES = QMessageBox.StandardButton.Yes
|
YES = QMessageBox.StandardButton.Yes
|
||||||
NO = QMessageBox.StandardButton.No
|
NO = QMessageBox.StandardButton.No
|
||||||
@@ -202,8 +199,7 @@ except (ImportError, AttributeError):
|
|||||||
QUrl as _QUrl,
|
QUrl as _QUrl,
|
||||||
QCoreApplication as _QCoreApplication,
|
QCoreApplication as _QCoreApplication,
|
||||||
Qt as _Qt,
|
Qt as _Qt,
|
||||||
QVariant as _QVariant,
|
QVariant as _QVariant
|
||||||
QSettings as _QSettings,
|
|
||||||
)
|
)
|
||||||
from PyQt5.QtNetwork import (
|
from PyQt5.QtNetwork import (
|
||||||
QNetworkRequest as _QNetworkRequest,
|
QNetworkRequest as _QNetworkRequest,
|
||||||
@@ -242,7 +238,6 @@ except (ImportError, AttributeError):
|
|||||||
QVariant = _QVariant
|
QVariant = _QVariant
|
||||||
QHBoxLayout= _QHBoxLayout
|
QHBoxLayout= _QHBoxLayout
|
||||||
QFont = _QFont
|
QFont = _QFont
|
||||||
QSettings = _QSettings
|
|
||||||
|
|
||||||
# ✅ PYQT5 ENUMS
|
# ✅ PYQT5 ENUMS
|
||||||
YES = QMessageBox.Yes
|
YES = QMessageBox.Yes
|
||||||
@@ -565,31 +560,6 @@ except (ImportError, AttributeError):
|
|||||||
pass
|
pass
|
||||||
QHBoxLayout = _MockQHBoxLayout
|
QHBoxLayout = _MockQHBoxLayout
|
||||||
|
|
||||||
class _MockQSettings:
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self._data: dict = {}
|
|
||||||
self._group = ""
|
|
||||||
|
|
||||||
def beginGroup(self, group: str) -> None:
|
|
||||||
self._group = group
|
|
||||||
|
|
||||||
def endGroup(self) -> None:
|
|
||||||
self._group = ""
|
|
||||||
|
|
||||||
def setValue(self, key: str, value: Any) -> None:
|
|
||||||
full = "%s/%s" % (self._group, key) if self._group else key
|
|
||||||
self._data[full] = value
|
|
||||||
|
|
||||||
def value(self, key: str, default: Any = "") -> Any:
|
|
||||||
full = "%s/%s" % (self._group, key) if self._group else key
|
|
||||||
return self._data.get(full, default)
|
|
||||||
|
|
||||||
def remove(self, key: str) -> None:
|
|
||||||
full = "%s/%s" % (self._group, key) if self._group else key
|
|
||||||
self._data.pop(full, None)
|
|
||||||
|
|
||||||
QSettings = _MockQSettings
|
|
||||||
|
|
||||||
class _MockQCheckBox:
|
class _MockQCheckBox:
|
||||||
def __init__(self, text: str = "", *args, **kwargs):
|
def __init__(self, text: str = "", *args, **kwargs):
|
||||||
self._text = text
|
self._text = text
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[general]
|
[general]
|
||||||
version=
|
version=26.4.7-unstable
|
||||||
name=LNO Sachsen | Plugin Basisfunktionen
|
name=LNO Sachsen | Plugin Basisfunktionen
|
||||||
description=Plugin mit Basisfunktionen
|
description=Plugin mit Basisfunktionen
|
||||||
author=Daniel Helbig, Michael Otto
|
author=Daniel Helbig, Michael Otto
|
||||||
@@ -209,7 +209,7 @@ class Datenschreiber:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
layer_name_raw = thema or str(ident)
|
layer_name_raw = thema or str(ident)
|
||||||
layer_name = re.sub(r"[^A-Za-z0-9_ÄäÖöÜüß]+", "_", layer_name_raw).strip("_")
|
layer_name = re.sub(r"[^A-Za-z0-9_]+", "_", layer_name_raw).strip("_")
|
||||||
if not layer_name:
|
if not layer_name:
|
||||||
layer_name = f"layer_{ident}"
|
layer_name = f"layer_{ident}"
|
||||||
|
|
||||||
|
|||||||
+146
-61
@@ -4,7 +4,9 @@ sn_basis/modules/print_layout.py - Aufbau von Einzelblatt-Drucklayouts.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import importlib
|
import importlib
|
||||||
|
import json
|
||||||
import math
|
import math
|
||||||
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from sn_basis.functions.qt_wrapper import QFont, Qt
|
from sn_basis.functions.qt_wrapper import QFont, Qt
|
||||||
@@ -45,13 +47,19 @@ MM = QgsUnitTypes.LayoutMillimeters
|
|||||||
|
|
||||||
|
|
||||||
class PrintLayout:
|
class PrintLayout:
|
||||||
"""Erzeugt ein QGIS-Einzelblatt-Layout fuer den Druck."""
|
"""Erzeugt ein QGIS-Einzelblatt-Layout fuer den Druck.
|
||||||
|
|
||||||
|
Der optionale Parameter config_name waehlt ein Schriftfeld-Template aus
|
||||||
|
config/label_configs/{config_name}.json. Wird kein Name angegeben, wird
|
||||||
|
"standard" verwendet. Neue Templates koennen ohne Code-Aenderungen durch
|
||||||
|
Erstellen einer neuen JSON-Datei hinzugefuegt werden.
|
||||||
|
"""
|
||||||
|
|
||||||
_FRAME_STROKE_WIDTH_MM = 0.5
|
_FRAME_STROKE_WIDTH_MM = 0.5
|
||||||
_LEGEND_WIDTH_MM = 180.0
|
|
||||||
_GRID_INTERVAL_MAP_UNITS = 500.0
|
_GRID_INTERVAL_MAP_UNITS = 500.0
|
||||||
|
|
||||||
_TEXT_FORMAT_TEMPLATES = {
|
# Eingebaute Fallback-Formate fuer den Fall, dass text_formats.json fehlt
|
||||||
|
_TEXT_FORMAT_TEMPLATES_DEFAULT = {
|
||||||
"Format_1": {"font_family": "Arial", "font_size": 11, "font_style": "normal"},
|
"Format_1": {"font_family": "Arial", "font_size": 11, "font_style": "normal"},
|
||||||
"Format_2": {"font_family": "Arial", "font_size": 13, "font_style": "bold"},
|
"Format_2": {"font_family": "Arial", "font_size": 13, "font_style": "bold"},
|
||||||
"Format_3": {"font_family": "Arial", "font_size": 16, "font_style": "bold"},
|
"Format_3": {"font_family": "Arial", "font_size": 16, "font_style": "bold"},
|
||||||
@@ -91,9 +99,51 @@ class PrintLayout:
|
|||||||
"mit teilweise angepasster Signatur<br>"
|
"mit teilweise angepasster Signatur<br>"
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, project: Any | None = None) -> None:
|
def __init__(self, project: Any | None = None, config_name: str = "standard") -> None:
|
||||||
self.project = project or QgsProject.instance()
|
self.project = project or QgsProject.instance()
|
||||||
self._current_layout: Any | None = None
|
self._current_layout: Any | None = None
|
||||||
|
self.config_name = config_name
|
||||||
|
self._label_config = self._load_label_config(config_name)
|
||||||
|
self._TEXT_FORMAT_TEMPLATES = self._load_text_formats()
|
||||||
|
self._LEGEND_WIDTH_MM = float(
|
||||||
|
self._label_config.get("kartenschild_width_mm", 180.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_config_dir() -> Path:
|
||||||
|
return Path(__file__).parent.parent / "config"
|
||||||
|
|
||||||
|
def _load_label_config(self, config_name: str) -> dict:
|
||||||
|
"""Laedt Schriftfeld-Konfiguration aus config/label_configs/{config_name}.json.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
FileNotFoundError: Wenn die Konfigurationsdatei nicht existiert.
|
||||||
|
ValueError: Wenn die JSON-Datei nicht geparst werden kann.
|
||||||
|
"""
|
||||||
|
config_file = self._get_config_dir() / "label_configs" / f"{config_name}.json"
|
||||||
|
if not config_file.exists():
|
||||||
|
raise FileNotFoundError(
|
||||||
|
f"Label-Config '{config_name}' nicht gefunden: {config_file}"
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
with open(config_file, encoding="utf-8") as fh:
|
||||||
|
return json.load(fh)
|
||||||
|
except json.JSONDecodeError as exc:
|
||||||
|
raise ValueError(f"JSON-Fehler in {config_file}: {exc}") from exc
|
||||||
|
|
||||||
|
def _load_text_formats(self) -> dict:
|
||||||
|
"""Laedt zentrale Schriftformat-Definitionen aus config/text_formats.json.
|
||||||
|
|
||||||
|
Faellt auf eingebaute Standardwerte zurueck, wenn die Datei fehlt.
|
||||||
|
"""
|
||||||
|
formats_file = self._get_config_dir() / "text_formats.json"
|
||||||
|
if not formats_file.exists():
|
||||||
|
return dict(self._TEXT_FORMAT_TEMPLATES_DEFAULT)
|
||||||
|
try:
|
||||||
|
with open(formats_file, encoding="utf-8") as fh:
|
||||||
|
return json.load(fh)
|
||||||
|
except (json.JSONDecodeError, OSError):
|
||||||
|
return dict(self._TEXT_FORMAT_TEMPLATES_DEFAULT)
|
||||||
|
|
||||||
def _create_map_item(
|
def _create_map_item(
|
||||||
self,
|
self,
|
||||||
@@ -247,17 +297,51 @@ class PrintLayout:
|
|||||||
map_height_mm: float,
|
map_height_mm: float,
|
||||||
kartenname_auswahl: str = "",
|
kartenname_auswahl: str = "",
|
||||||
) -> None:
|
) -> None:
|
||||||
|
"""Richtet Legende, Quellenangabe und Kartenschild ein.
|
||||||
|
|
||||||
|
Delegiert an _setup_layout_objects_from_config() unter Aufloesung
|
||||||
|
des optionalen kartenname_auswahl-Parameters fuer §38-spezifische Texte.
|
||||||
|
"""
|
||||||
|
self._setup_layout_objects_from_config(
|
||||||
|
kartenfenster_untere_rechte_ecke_mm,
|
||||||
|
hauptkarte,
|
||||||
|
map_top_mm,
|
||||||
|
map_height_mm,
|
||||||
|
kartenname_auswahl=kartenname_auswahl,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _setup_layout_objects_from_config(
|
||||||
|
self,
|
||||||
|
kartenfenster_untere_rechte_ecke_mm: tuple,
|
||||||
|
hauptkarte: Any,
|
||||||
|
map_top_mm: float,
|
||||||
|
map_height_mm: float,
|
||||||
|
kartenname_auswahl: str = "",
|
||||||
|
) -> None:
|
||||||
|
"""Erstellt Legende, Quellenangabe, Kartenschild und alle Labels aus der JSON-Config.
|
||||||
|
|
||||||
|
Die Label-Definitionen werden vollstaendig aus self._label_config geladen.
|
||||||
|
Einzige Ausnahme: Das Feld "text_if_kartenname_38" in einem Label-Eintrag
|
||||||
|
erlaubt einen alternativen Text speziell fuer kartenname_auswahl == '§38'.
|
||||||
|
"""
|
||||||
if self._current_layout is None:
|
if self._current_layout is None:
|
||||||
raise RuntimeError("Kein Layout aktiv.")
|
raise RuntimeError("Kein Layout aktiv.")
|
||||||
|
|
||||||
def _coords_rel_untere_rechte_ecke(rel_x_mm: float, rel_y_mm: float) -> tuple[float, float]:
|
kartenschild_width_mm = float(self._label_config.get("kartenschild_width_mm", 180.0))
|
||||||
|
kartenschild_height_mm = float(self._label_config.get("kartenschild_height_mm", 92.0))
|
||||||
|
kartenschild_line_offsets_y: list[float] = [
|
||||||
|
float(v) for v in self._label_config.get("kartenschild_line_offsets_y", [-10.0, -20.0, -30.0, -46.0, -62.0, -82.0])
|
||||||
|
]
|
||||||
|
|
||||||
|
def _coords(rel_x_mm: float, rel_y_mm: float) -> tuple[float, float]:
|
||||||
return (
|
return (
|
||||||
kartenfenster_untere_rechte_ecke_mm[0] + rel_x_mm,
|
kartenfenster_untere_rechte_ecke_mm[0] + rel_x_mm,
|
||||||
kartenfenster_untere_rechte_ecke_mm[1] + rel_y_mm,
|
kartenfenster_untere_rechte_ecke_mm[1] + rel_y_mm,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Legende
|
||||||
legende_x = kartenfenster_untere_rechte_ecke_mm[0] + 10.0
|
legende_x = kartenfenster_untere_rechte_ecke_mm[0] + 10.0
|
||||||
legende_height_mm = max(map_height_mm - 122.0, 20.0)
|
legende_height_mm = 50.0 # Klein anfangen, fitBoxToContents() wird es vergrößern
|
||||||
legende_y = map_top_mm
|
legende_y = map_top_mm
|
||||||
legende = self._create_legend_item(
|
legende = self._create_legend_item(
|
||||||
self._current_layout,
|
self._current_layout,
|
||||||
@@ -265,36 +349,36 @@ class PrintLayout:
|
|||||||
hauptkarte,
|
hauptkarte,
|
||||||
legende_x,
|
legende_x,
|
||||||
legende_y,
|
legende_y,
|
||||||
width_mm=self._LEGEND_WIDTH_MM,
|
width_mm=kartenschild_width_mm,
|
||||||
height_mm=legende_height_mm,
|
height_mm=legende_height_mm,
|
||||||
object_name="Legende",
|
object_name="Legende",
|
||||||
)
|
)
|
||||||
|
|
||||||
legende_hoehe_mm = self._configure_legend_size(
|
legende_hoehe_mm = self._configure_legend_size(
|
||||||
legende,
|
legende,
|
||||||
width_limit_mm=self._LEGEND_WIDTH_MM,
|
width_limit_mm=kartenschild_width_mm,
|
||||||
column_count=1,
|
column_count=1,
|
||||||
equal_column_width=False,
|
equal_column_width=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
quellenangabe_x, quellenangabe_y = _coords_rel_untere_rechte_ecke(10.0, -102.0)
|
quellenangabe_x, quellenangabe_y = _coords(10.0, -102.0)
|
||||||
quellenangabe_hoehe_mm = 20.0
|
quellenangabe_obere_y = quellenangabe_y - 20.0
|
||||||
quellenangabe_obere_y = quellenangabe_y - quellenangabe_hoehe_mm
|
|
||||||
legende_untere_y = legende_y + legende_hoehe_mm
|
legende_untere_y = legende_y + legende_hoehe_mm
|
||||||
|
|
||||||
if legende_untere_y > quellenangabe_obere_y:
|
if legende_untere_y > quellenangabe_obere_y:
|
||||||
self._configure_legend_size(
|
legende_hoehe_mm = self._configure_legend_size(
|
||||||
legende,
|
legende,
|
||||||
width_limit_mm=self._LEGEND_WIDTH_MM,
|
width_limit_mm=kartenschild_width_mm,
|
||||||
column_count=2,
|
column_count=2,
|
||||||
equal_column_width=True,
|
equal_column_width=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Quellenangabe
|
||||||
self.add_text_label(
|
self.add_text_label(
|
||||||
self._QUELLENANGABE_TEXT,
|
self._QUELLENANGABE_TEXT,
|
||||||
quellenangabe_x,
|
quellenangabe_x,
|
||||||
quellenangabe_y,
|
quellenangabe_y,
|
||||||
width_mm=self._LEGEND_WIDTH_MM,
|
width_mm=kartenschild_width_mm,
|
||||||
height_mm=20.0,
|
height_mm=20.0,
|
||||||
text_format="Format_1",
|
text_format="Format_1",
|
||||||
html_mode=True,
|
html_mode=True,
|
||||||
@@ -302,72 +386,52 @@ class PrintLayout:
|
|||||||
object_name="Quellenangabe",
|
object_name="Quellenangabe",
|
||||||
)
|
)
|
||||||
|
|
||||||
kartenschild_x, kartenschild_y = _coords_rel_untere_rechte_ecke(10.0, 0.0)
|
# Kartenschild-Rahmen
|
||||||
|
kartenschild_x, kartenschild_y = _coords(10.0, 0.0)
|
||||||
self._create_rectangle_box_item(
|
self._create_rectangle_box_item(
|
||||||
self._current_layout,
|
self._current_layout,
|
||||||
"Kartenschild",
|
"Kartenschild",
|
||||||
kartenschild_x,
|
kartenschild_x,
|
||||||
kartenschild_y,
|
kartenschild_y,
|
||||||
self._LEGEND_WIDTH_MM,
|
kartenschild_width_mm,
|
||||||
92.0,
|
kartenschild_height_mm,
|
||||||
stroke_width_mm=self._FRAME_STROKE_WIDTH_MM,
|
stroke_width_mm=self._FRAME_STROKE_WIDTH_MM,
|
||||||
)
|
)
|
||||||
|
|
||||||
kartenschild_line_x, _ = _coords_rel_untere_rechte_ecke(10.0, 0.0)
|
# Kartenschild-Trennlinien
|
||||||
kartenschild_line_offsets_y = (-10.0, -20.0, -30.0, -46.0, -62.0, -82.0)
|
kartenschild_line_x, _ = _coords(10.0, 0.0)
|
||||||
for index, offset_y in enumerate(kartenschild_line_offsets_y, start=1):
|
for index, offset_y in enumerate(kartenschild_line_offsets_y, start=1):
|
||||||
_, line_y = _coords_rel_untere_rechte_ecke(0.0, offset_y)
|
_, line_y = _coords(0.0, offset_y)
|
||||||
self._create_rectangle_box_item(
|
self._create_rectangle_box_item(
|
||||||
self._current_layout,
|
self._current_layout,
|
||||||
f"Kartenschild_Linie_{index}",
|
f"Kartenschild_Linie_{index}",
|
||||||
kartenschild_line_x,
|
kartenschild_line_x,
|
||||||
line_y,
|
line_y,
|
||||||
self._LEGEND_WIDTH_MM,
|
kartenschild_width_mm,
|
||||||
0.0,
|
0.0,
|
||||||
stroke_width_mm=0.1,
|
stroke_width_mm=0.1,
|
||||||
)
|
)
|
||||||
|
|
||||||
if kartenname_auswahl == "§38":
|
# Labels aus Config
|
||||||
herausgeber_text = "[%@sn_landkreis_user%] [%@sn_behoerde%]"
|
for label_def in self._label_config.get("labels", []):
|
||||||
else:
|
# Alternativer Text fuer §38-Karten (falls in Config definiert)
|
||||||
herausgeber_text = "Teilnehmergemeinschaft [%@sn_bezeichnung %] [%@sn_name %]"
|
if kartenname_auswahl == "§38" and "text_if_kartenname_38" in label_def:
|
||||||
|
text = label_def["text_if_kartenname_38"]
|
||||||
|
else:
|
||||||
|
text = label_def["text"]
|
||||||
|
|
||||||
labels = [
|
x, y = _coords(float(label_def["x_mm"]), float(label_def["y_mm"]))
|
||||||
(herausgeber_text, 16.0, -84.0, 171.0, 4.0, "Format_1", "Herausgeber"),
|
|
||||||
("[% @sn_bezeichnung %]", 16.0, -75.0, 171.0, 4.0, "Format_1", "Bezeichnung_Flurbereinigung"),
|
|
||||||
("[% @sn_name %]", 16.0, -64.0, 171.0, 5.0, "Format_2", "Name_Flurbereinigung"),
|
|
||||||
("[% @sn_gemeinden %]", 16.0, -56.0, 171.0, 4.0, "Format_1", "Stadt_Gemeinde"),
|
|
||||||
("[% @sn_landkreise_proj %]", 16.0, -48.0, 171.0, 4.0, "Format_1", "Landkreis"),
|
|
||||||
("[% @sn_kartenname %]", 16.0, -32.0, 137.0, 12.0, "Format_3", "Kartenname"),
|
|
||||||
("Gefertigt: ", 16.0, -23.0, 81.0, 4.0, "Format_1", "Gefertigt"),
|
|
||||||
("Geprueft: ", 101.0, -23.0, 81.0, 4.0, "Format_1", "Geprüft"),
|
|
||||||
(
|
|
||||||
"Maßstab: 1 : [%round(map_get(item_variables('Hauptkarte'),'map_scale'),0)%]",
|
|
||||||
16.0,
|
|
||||||
-13.0,
|
|
||||||
81.0,
|
|
||||||
4.0,
|
|
||||||
"Format_1",
|
|
||||||
"Massstab",
|
|
||||||
),
|
|
||||||
("Ausgabedatum: [%format_date(now(), 'dd.MM.yyyy')%]", 101.0, -13.0, 81.0, 4.0, "Format_1", "Ausgabedatum"),
|
|
||||||
("Aktenzeichen: ", 16.0, -3.0, 171.0, 4.0, "Format_1", "Aktenzeichen"),
|
|
||||||
]
|
|
||||||
|
|
||||||
for entry in labels:
|
|
||||||
text, rx, ry, w, h, fmt, obj_name = entry[:7]
|
|
||||||
v_align = entry[7] if len(entry) > 7 else "top"
|
|
||||||
x, y = _coords_rel_untere_rechte_ecke(rx, ry)
|
|
||||||
self.add_text_label(
|
self.add_text_label(
|
||||||
text,
|
text,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
width_mm=w,
|
width_mm=float(label_def["width_mm"]),
|
||||||
height_mm=h,
|
height_mm=float(label_def["height_mm"]),
|
||||||
text_format=fmt,
|
text_format=label_def.get("format", "Format_1"),
|
||||||
expression_enabled=True,
|
html_mode=bool(label_def.get("html_mode", False)),
|
||||||
object_name=obj_name,
|
expression_enabled=bool(label_def.get("expression_enabled", True)),
|
||||||
v_align=v_align,
|
object_name=label_def.get("object_name") or label_def.get("id"),
|
||||||
|
v_align=label_def.get("v_align", "top"),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _create_label_item(
|
def _create_label_item(
|
||||||
@@ -661,6 +725,14 @@ class PrintLayout:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Deaktiviere Auto-Fit: Größe an Inhalt anpassen soll aus sein
|
||||||
|
set_resize_to_contents = getattr(legend, "setResizeToContents", None)
|
||||||
|
if callable(set_resize_to_contents):
|
||||||
|
try:
|
||||||
|
set_resize_to_contents(False)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
set_column_count = getattr(legend, "setColumnCount", None)
|
set_column_count = getattr(legend, "setColumnCount", None)
|
||||||
if callable(set_column_count):
|
if callable(set_column_count):
|
||||||
try:
|
try:
|
||||||
@@ -682,17 +754,30 @@ class PrintLayout:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
adjust_box_size = getattr(legend, "adjustBoxSize", None)
|
# Versuche fitBoxToContents() zuerst, dann Fallback zu adjustBoxSize()
|
||||||
if callable(adjust_box_size):
|
fit_box_to_contents = getattr(legend, "fitBoxToContents", None)
|
||||||
|
if callable(fit_box_to_contents):
|
||||||
try:
|
try:
|
||||||
adjust_box_size()
|
fit_box_to_contents()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
else:
|
||||||
|
adjust_box_size = getattr(legend, "adjustBoxSize", None)
|
||||||
|
if callable(adjust_box_size):
|
||||||
|
try:
|
||||||
|
adjust_box_size()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
_, updated_height_mm = self._get_item_size_mm(legend)
|
_, updated_height_mm = self._get_item_size_mm(legend)
|
||||||
if updated_height_mm is None:
|
if updated_height_mm is None:
|
||||||
updated_height_mm = 20.0
|
updated_height_mm = 20.0
|
||||||
|
|
||||||
|
# Breite auf Kartenschild-Breite setzen, Höhe an Inhalt angepasst behalten
|
||||||
|
legend.attemptResize(
|
||||||
|
QgsLayoutSize(width_limit_mm, updated_height_mm, MM)
|
||||||
|
)
|
||||||
|
|
||||||
if callable(set_locked):
|
if callable(set_locked):
|
||||||
try:
|
try:
|
||||||
set_locked(True)
|
set_locked(True)
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
"""
|
"""
|
||||||
sn_basis/modules/print_layout_baufreigabe.py - Layout-Erstellung fuer Baufreigabe-Vorlagen.
|
sn_basis/modules/print_layout_baufreigabe.py - Layout-Erstellung fuer Baufreigabe-Vorlagen.
|
||||||
|
|
||||||
Baufreigabe-Layouts verwenden immer feste DIN A3/A4 Seitengroessen im Querformat,
|
Baufreigabe-Layouts verwenden immer feste DIN A3/A4 Seitengroessen im Querformat.
|
||||||
ein 120 mm breites Kartenschild (statt 180 mm) und erbenerben die Standard-Logik
|
Das Schriftfeld (80mm breit) wird ueber config/label_configs/baufreigabe.json definiert.
|
||||||
fuer Hauptkarte, Quellenangabe und Legende vom PrintLayout.
|
|
||||||
|
|
||||||
Atlas-Integration erfolgt nach Layouterstellung durch externe Konfiguration
|
Atlas-Integration erfolgt nach Layouterstellung durch externe Konfiguration
|
||||||
in der PrintLogic.
|
in der PrintLogic.
|
||||||
@@ -12,11 +11,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from sn_basis.functions.qt_wrapper import QFont
|
|
||||||
from sn_basis.functions.qgiscore_wrapper import (
|
from sn_basis.functions.qgiscore_wrapper import (
|
||||||
QgsLayoutItem,
|
|
||||||
QgsLayoutItemLabel,
|
|
||||||
QgsLayoutItemLegend,
|
|
||||||
QgsLayoutItemMap,
|
QgsLayoutItemMap,
|
||||||
QgsLayoutPoint,
|
QgsLayoutPoint,
|
||||||
QgsLayoutSize,
|
QgsLayoutSize,
|
||||||
@@ -32,60 +27,19 @@ MM = QgsUnitTypes.LayoutMillimeters
|
|||||||
|
|
||||||
|
|
||||||
class PrintLayoutBaufreigabe(PrintLayout):
|
class PrintLayoutBaufreigabe(PrintLayout):
|
||||||
"""Erzeugt QGIS-Einzelblatt-Layouts mit fester DIN A3/A4 Querformat-Groesse und 80mm Kartenschild."""
|
"""Erzeugt QGIS-Einzelblatt-Layouts mit fester DIN A3/A4 Querformat-Groesse.
|
||||||
|
|
||||||
|
Schriftfeld-Definition kommt aus config/label_configs/baufreigabe.json.
|
||||||
|
"""
|
||||||
|
|
||||||
_FRAME_STROKE_WIDTH_MM = 0.5
|
|
||||||
_KARTENSCHILD_WIDTH_MM = 80.0
|
|
||||||
_KARTENSCHILD_HEIGHT_MM = 58.0
|
|
||||||
_ATLAS_FRAME_MARGIN_MM = 10.0
|
_ATLAS_FRAME_MARGIN_MM = 10.0
|
||||||
_GRID_INTERVAL_MAP_UNITS = 500.0
|
|
||||||
|
|
||||||
# DIN-Formate: nur A3 und A4 im Querformat
|
# DIN-Formate: nur A3 und A4 im Querformat
|
||||||
_DIN_A3_LANDSCAPE = (420, 297) # Breite x Hoehe (Querformat)
|
_DIN_A3_LANDSCAPE = (420, 297) # Breite x Hoehe (Querformat)
|
||||||
_DIN_A4_LANDSCAPE = (297, 210) # Querformat
|
_DIN_A4_LANDSCAPE = (297, 210) # Querformat
|
||||||
|
|
||||||
_TEXT_FORMAT_TEMPLATES = {
|
def __init__(self, project: Any | None = None, config_name: str = "baufreigabe") -> None:
|
||||||
"Format_1": {"font_family": "Arial", "font_size": 11, "font_style": "normal"},
|
super().__init__(project, config_name=config_name)
|
||||||
"Format_2": {"font_family": "Arial", "font_size": 13, "font_style": "bold"},
|
|
||||||
"Format_3": {"font_family": "Arial", "font_size": 16, "font_style": "bold"},
|
|
||||||
}
|
|
||||||
|
|
||||||
_OBJECT_TEMPLATES = {
|
|
||||||
"hauptkarte": {"id": "Hauptkarte", "object_name": "Hauptkarte"},
|
|
||||||
"quellenangabe": {
|
|
||||||
"id": "Quellenangabe",
|
|
||||||
"object_name": "Quellenangabe",
|
|
||||||
"size_mm": (80.0, 100.0),
|
|
||||||
"format": "Format_1",
|
|
||||||
},
|
|
||||||
"textfeld": {
|
|
||||||
"id": "Textfeld",
|
|
||||||
"object_name": "Textfeld",
|
|
||||||
"size_mm": (60.0, 8.0),
|
|
||||||
"format": "Format_1",
|
|
||||||
"html_mode": True,
|
|
||||||
"text": "",
|
|
||||||
},
|
|
||||||
"legende": {
|
|
||||||
"id": "Legende",
|
|
||||||
"object_name": "Legende",
|
|
||||||
"size_mm": (80.0, 60.0),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
_QUELLENANGABE_TEXT = (
|
|
||||||
"Quelle Geobasisdaten: GeoSN, "
|
|
||||||
"<a href=\"https://www.govdata.de/dl-de/by-2-0\">dl-de/by-2-0</a><br>"
|
|
||||||
"Quelle Fachdaten: Darstellung auf der Grundlage von Daten und mit Erlaubnis des "
|
|
||||||
"Sächsischen Landesamtes für Umwelt, Landwirtschaft und Geologie<br>"
|
|
||||||
"Basemap:"
|
|
||||||
"© GeoBasis-DE / <a href=\"https://www.bkg.bund.de\">BKG</a> ([%year($now)%]) "
|
|
||||||
"<a href=\"https://creativecommons.org/licences/by/4.0/\">CC BY 4.0</a> "
|
|
||||||
"mit teilweise angepasster Signatur<br>"
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, project: Any | None = None) -> None:
|
|
||||||
super().__init__(project)
|
|
||||||
|
|
||||||
def create_baufreigabe_layout(
|
def create_baufreigabe_layout(
|
||||||
self,
|
self,
|
||||||
@@ -110,9 +64,10 @@ class PrintLayoutBaufreigabe(PrintLayout):
|
|||||||
plotmassstab: Plotmassstab (z.B. 5000 fuer 1:5000)
|
plotmassstab: Plotmassstab (z.B. 5000 fuer 1:5000)
|
||||||
thema: Name des Visibility-Presets (optional)
|
thema: Name des Visibility-Presets (optional)
|
||||||
fit_extent_to_map: Extent bei Bedarf auf das Karten-Seitenverhaeltnis erweitern
|
fit_extent_to_map: Extent bei Bedarf auf das Karten-Seitenverhaeltnis erweitern
|
||||||
|
atlas_driven: Atlas-Steuerung fuer die Hauptkarte aktivieren
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
QgsPrintLayout objektInstance (wird im Layout-Dialog geoeffnet)
|
QgsPrintLayout-Instanz (wird im Layout-Designer geoeffnet)
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValueError: Falls Name bereits existiert oder Format ungueltig ist
|
ValueError: Falls Name bereits existiert oder Format ungueltig ist
|
||||||
@@ -174,7 +129,6 @@ class PrintLayoutBaufreigabe(PrintLayout):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
# Atlas-Steuerung nur aktivieren, wenn Atlas tatsaechlich genutzt wird.
|
# Atlas-Steuerung nur aktivieren, wenn Atlas tatsaechlich genutzt wird.
|
||||||
# Andernfalls bleiben Extent/Scale unveraendert (z.B. Canvas-Ausschnitt).
|
|
||||||
if atlas_driven:
|
if atlas_driven:
|
||||||
set_atlas_driven_fn = getattr(hauptkarte, "setAtlasDriven", None)
|
set_atlas_driven_fn = getattr(hauptkarte, "setAtlasDriven", None)
|
||||||
if callable(set_atlas_driven_fn):
|
if callable(set_atlas_driven_fn):
|
||||||
@@ -247,7 +201,7 @@ class PrintLayoutBaufreigabe(PrintLayout):
|
|||||||
|
|
||||||
self._configure_hauptkarte_grids(hauptkarte)
|
self._configure_hauptkarte_grids(hauptkarte)
|
||||||
|
|
||||||
self._setup_layout_objects_baufreigabe(
|
self._setup_layout_objects(
|
||||||
kartenfenster_untere_rechte_ecke_mm,
|
kartenfenster_untere_rechte_ecke_mm,
|
||||||
hauptkarte,
|
hauptkarte,
|
||||||
map_top_mm,
|
map_top_mm,
|
||||||
@@ -259,138 +213,6 @@ class PrintLayoutBaufreigabe(PrintLayout):
|
|||||||
open_layout_designer(layout)
|
open_layout_designer(layout)
|
||||||
return layout
|
return layout
|
||||||
|
|
||||||
def _setup_layout_objects_baufreigabe(
|
|
||||||
self,
|
|
||||||
kartenfenster_untere_rechte_ecke_mm: tuple,
|
|
||||||
hauptkarte: Any,
|
|
||||||
map_top_mm: float,
|
|
||||||
map_height_mm: float,
|
|
||||||
) -> None:
|
|
||||||
"""Erstellt Legende, Quellenangabe und Kartenschild fuer Baufreigabe-Layout.
|
|
||||||
|
|
||||||
Angepasst fuer 80mm Kartenschild statt 180mm.
|
|
||||||
"""
|
|
||||||
if self._current_layout is None:
|
|
||||||
raise RuntimeError("Kein Layout aktiv.")
|
|
||||||
|
|
||||||
def _coords_rel_untere_rechte_ecke(rel_x_mm: float, rel_y_mm: float) -> tuple[float, float]:
|
|
||||||
return (
|
|
||||||
kartenfenster_untere_rechte_ecke_mm[0] + rel_x_mm,
|
|
||||||
kartenfenster_untere_rechte_ecke_mm[1] + rel_y_mm,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Legende: 80mm Breite, hoehe adaptive wie Standard
|
|
||||||
legende_x = kartenfenster_untere_rechte_ecke_mm[0] + 10.0
|
|
||||||
legende_height_mm = max(map_height_mm - 122.0, 20.0)
|
|
||||||
legende_y = map_top_mm
|
|
||||||
legende = self._create_legend_item(
|
|
||||||
self._current_layout,
|
|
||||||
"legende",
|
|
||||||
hauptkarte,
|
|
||||||
legende_x,
|
|
||||||
legende_y,
|
|
||||||
width_mm=self._KARTENSCHILD_WIDTH_MM,
|
|
||||||
height_mm=legende_height_mm,
|
|
||||||
object_name="Legende",
|
|
||||||
)
|
|
||||||
|
|
||||||
legende_hoehe_mm = self._configure_legend_size(
|
|
||||||
legende,
|
|
||||||
width_limit_mm=self._KARTENSCHILD_WIDTH_MM,
|
|
||||||
column_count=1,
|
|
||||||
equal_column_width=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Quellenangabe: 80mm Breite
|
|
||||||
quellenangabe_x, quellenangabe_y = _coords_rel_untere_rechte_ecke(10.0, -102.0)
|
|
||||||
quellenangabe_hoehe_mm = 20.0
|
|
||||||
quellenangabe_obere_y = quellenangabe_y - quellenangabe_hoehe_mm
|
|
||||||
legende_untere_y = legende_y + legende_hoehe_mm
|
|
||||||
|
|
||||||
if legende_untere_y > quellenangabe_obere_y:
|
|
||||||
self._configure_legend_size(
|
|
||||||
legende,
|
|
||||||
width_limit_mm=self._KARTENSCHILD_WIDTH_MM,
|
|
||||||
column_count=2,
|
|
||||||
equal_column_width=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.add_text_label(
|
|
||||||
self._QUELLENANGABE_TEXT,
|
|
||||||
quellenangabe_x,
|
|
||||||
quellenangabe_y,
|
|
||||||
width_mm=self._KARTENSCHILD_WIDTH_MM,
|
|
||||||
height_mm=20.0,
|
|
||||||
text_format="Format_1",
|
|
||||||
html_mode=True,
|
|
||||||
expression_enabled=True,
|
|
||||||
object_name="Quellenangabe",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Kartenschild: 80mm Breite
|
|
||||||
kartenschild_x, kartenschild_y = _coords_rel_untere_rechte_ecke(10.0, 0.0)
|
|
||||||
self._create_rectangle_box_item(
|
|
||||||
self._current_layout,
|
|
||||||
"Kartenschild",
|
|
||||||
kartenschild_x,
|
|
||||||
kartenschild_y,
|
|
||||||
self._KARTENSCHILD_WIDTH_MM,
|
|
||||||
self._KARTENSCHILD_HEIGHT_MM,
|
|
||||||
stroke_width_mm=self._FRAME_STROKE_WIDTH_MM,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Kartenschild-Linien: 6 Trennlinien (Positionen gleich)
|
|
||||||
kartenschild_line_x, _ = _coords_rel_untere_rechte_ecke(10.0, 0.0)
|
|
||||||
kartenschild_line_offsets_y = (-10.0, -20.0, -30.0, -46.0)
|
|
||||||
for index, offset_y in enumerate(kartenschild_line_offsets_y, start=1):
|
|
||||||
_, line_y = _coords_rel_untere_rechte_ecke(0.0, offset_y)
|
|
||||||
self._create_rectangle_box_item(
|
|
||||||
self._current_layout,
|
|
||||||
f"Kartenschild_Linie_{index}",
|
|
||||||
kartenschild_line_x,
|
|
||||||
line_y,
|
|
||||||
self._KARTENSCHILD_WIDTH_MM,
|
|
||||||
0.0,
|
|
||||||
stroke_width_mm=0.1,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Textfelder im Kartenschild ohne Skalierungslogik.
|
|
||||||
# Positionen werden bewusst als feste Werte gesetzt und bei Bedarf manuell angepasst.
|
|
||||||
|
|
||||||
labels = [
|
|
||||||
("Teilnehmergemeinschaft [%@sn_bezeichnung %] [%@sn_name %]", 14.0, -48.0, 74.0, 8.0, "Format_1", "Herausgeber","Bottom"),
|
|
||||||
("[% @sn_bezeichnung %]", 14.0, -41.0, 74.0, 4.0, "Format_1", "Bezeichnung_Flurbereinigung"),
|
|
||||||
("[% @sn_name %]", 14.0, -32.0, 74.0, 5.0, "Format_2", "Name_Flurbereinigung"),
|
|
||||||
("[% @sn_kartenname %]", 14.0, -23.0, 74.0, 6.0, "Format_3", "Kartenname"),
|
|
||||||
("Ausgabedatum: [%format_date(now(), 'dd.MM.yyyy')%]", 14.0, -13.0, 74.0, 4.0, "Format_1", "Ausgabedatum"),
|
|
||||||
(
|
|
||||||
"Maßstab: 1 : [%round(map_get(item_variables('Hauptkarte'),'map_scale'),0)%]",
|
|
||||||
14.0,
|
|
||||||
-3.0,
|
|
||||||
74.0,
|
|
||||||
4.0,
|
|
||||||
"Format_1",
|
|
||||||
"Massstab",
|
|
||||||
),
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
||||||
for entry in labels:
|
|
||||||
text, rx, ry, w, h, fmt, obj_name = entry[:7]
|
|
||||||
v_align = entry[7] if len(entry) > 7 else "top"
|
|
||||||
x, y = _coords_rel_untere_rechte_ecke(rx, ry)
|
|
||||||
self.add_text_label(
|
|
||||||
text,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
width_mm=w,
|
|
||||||
height_mm=h,
|
|
||||||
text_format=fmt,
|
|
||||||
expression_enabled=True,
|
|
||||||
object_name=obj_name,
|
|
||||||
v_align=v_align,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _calc_atlas_margin_percent(self, map_width_mm: float, map_height_mm: float) -> float:
|
def _calc_atlas_margin_percent(self, map_width_mm: float, map_height_mm: float) -> float:
|
||||||
"""Berechnet Atlas-Margin in Prozent, sodass etwa 10 mm Rand sichtbar bleiben."""
|
"""Berechnet Atlas-Margin in Prozent, sodass etwa 10 mm Rand sichtbar bleiben."""
|
||||||
margin_mm = self._ATLAS_FRAME_MARGIN_MM
|
margin_mm = self._ATLAS_FRAME_MARGIN_MM
|
||||||
|
|||||||
@@ -1166,6 +1166,7 @@ class PrintLogic:
|
|||||||
zielgroesse: str,
|
zielgroesse: str,
|
||||||
formfaktor: bool,
|
formfaktor: bool,
|
||||||
druckbereich_auswahl: str,
|
druckbereich_auswahl: str,
|
||||||
|
schriftfeld_template: str = "",
|
||||||
) -> dict:
|
) -> dict:
|
||||||
resolved_layer = layer or self._resolve_layer_from_project()
|
resolved_layer = layer or self._resolve_layer_from_project()
|
||||||
|
|
||||||
@@ -1270,8 +1271,9 @@ class PrintLogic:
|
|||||||
try:
|
try:
|
||||||
if kartenname_auswahl == KARTENNAME_BAUFREIGABE:
|
if kartenname_auswahl == KARTENNAME_BAUFREIGABE:
|
||||||
# Baufreigabe: feste DIN A3/A4 Querformat-Logik mit 80mm Kartenschild
|
# Baufreigabe: feste DIN A3/A4 Querformat-Logik mit 80mm Kartenschild
|
||||||
|
config_to_use = schriftfeld_template or "baufreigabe"
|
||||||
din_format = "DIN A3" if auto_page_w >= 420.0 else "DIN A4"
|
din_format = "DIN A3" if auto_page_w >= 420.0 else "DIN A4"
|
||||||
PrintLayoutBaufreigabe().create_baufreigabe_layout(
|
PrintLayoutBaufreigabe(config_name=config_to_use).create_baufreigabe_layout(
|
||||||
name=vorlage_name if vorlage_name != kartenname else "Baufreigabe",
|
name=vorlage_name if vorlage_name != kartenname else "Baufreigabe",
|
||||||
din_format=din_format,
|
din_format=din_format,
|
||||||
map_width_mm=auto_map_w,
|
map_width_mm=auto_map_w,
|
||||||
@@ -1284,7 +1286,8 @@ class PrintLogic:
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# Standard-Layout: dynamische Groessenanpassung
|
# Standard-Layout: dynamische Groessenanpassung
|
||||||
PrintLayout().create_single_page_layout(
|
config_to_use = schriftfeld_template or "standard"
|
||||||
|
PrintLayout(config_name=config_to_use).create_single_page_layout(
|
||||||
name=vorlage_name,
|
name=vorlage_name,
|
||||||
page_width_mm=auto_page_w,
|
page_width_mm=auto_page_w,
|
||||||
page_height_mm=auto_page_h,
|
page_height_mm=auto_page_h,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ sn_basis/ui/tabs/print_tab.py - Zentraler Druck-Tab fuer sn-Plugins.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from sn_basis.functions.qt_wrapper import (
|
from sn_basis.functions.qt_wrapper import (
|
||||||
@@ -85,6 +86,7 @@ class PrintTab(QWidget):
|
|||||||
self._atlas_erzeugen_cb: Optional[Any] = None
|
self._atlas_erzeugen_cb: Optional[Any] = None
|
||||||
self._atlas_button: Optional[Any] = None
|
self._atlas_button: Optional[Any] = None
|
||||||
self._atlas_content: Optional[Any] = None
|
self._atlas_content: Optional[Any] = None
|
||||||
|
self._schriftfeld_combo: Optional[Any] = None
|
||||||
|
|
||||||
self._build_ui()
|
self._build_ui()
|
||||||
self._restore_state()
|
self._restore_state()
|
||||||
@@ -256,6 +258,16 @@ class PrintTab(QWidget):
|
|||||||
self._atlas_content = atlas_content
|
self._atlas_content = atlas_content
|
||||||
main_layout.addWidget(atlas_content)
|
main_layout.addWidget(atlas_content)
|
||||||
|
|
||||||
|
schriftfeld_label = QLabel("Schriftfeld-Template")
|
||||||
|
schriftfeld_label.setStyleSheet("font-weight: bold; margin-top: 6px;")
|
||||||
|
main_layout.addWidget(schriftfeld_label)
|
||||||
|
|
||||||
|
schriftfeld_combo = QComboBox(self)
|
||||||
|
schriftfeld_combo.addItems(self._get_available_templates())
|
||||||
|
schriftfeld_combo.currentTextChanged.connect(self._on_schriftfeld_changed)
|
||||||
|
self._schriftfeld_combo = schriftfeld_combo
|
||||||
|
main_layout.addWidget(schriftfeld_combo)
|
||||||
|
|
||||||
btn_vorlage_erstellen = QPushButton("Vorlage erstellen", self)
|
btn_vorlage_erstellen = QPushButton("Vorlage erstellen", self)
|
||||||
btn_vorlage_erstellen.clicked.connect(self._on_vorlage_erstellen)
|
btn_vorlage_erstellen.clicked.connect(self._on_vorlage_erstellen)
|
||||||
self._btn_vorlage_erstellen = btn_vorlage_erstellen
|
self._btn_vorlage_erstellen = btn_vorlage_erstellen
|
||||||
@@ -329,6 +341,14 @@ class PrintTab(QWidget):
|
|||||||
self._atlas_erzeugen_cb.setChecked(atlas_checked)
|
self._atlas_erzeugen_cb.setChecked(atlas_checked)
|
||||||
self._on_atlas_erzeugen_changed(1 if atlas_checked else 0)
|
self._on_atlas_erzeugen_changed(1 if atlas_checked else 0)
|
||||||
|
|
||||||
|
if self._schriftfeld_combo:
|
||||||
|
saved_schriftfeld = get_variable("tab_b_schriftfeld_template", scope="project")
|
||||||
|
available_templates = self._get_available_templates()
|
||||||
|
if saved_schriftfeld in available_templates:
|
||||||
|
self._schriftfeld_combo.setCurrentText(saved_schriftfeld)
|
||||||
|
else:
|
||||||
|
self._schriftfeld_combo.setCurrentText("standard")
|
||||||
|
|
||||||
self._restore_atlas_layer_selection()
|
self._restore_atlas_layer_selection()
|
||||||
|
|
||||||
def _on_kartenname_changed(self, value: str) -> None:
|
def _on_kartenname_changed(self, value: str) -> None:
|
||||||
@@ -436,6 +456,7 @@ class PrintTab(QWidget):
|
|||||||
zielgroesse=self._zielgroesse_combo.currentText() if self._zielgroesse_combo else DIN_STANDARD,
|
zielgroesse=self._zielgroesse_combo.currentText() if self._zielgroesse_combo else DIN_STANDARD,
|
||||||
formfaktor=self._endlosrolle_cb.isChecked() if self._endlosrolle_cb else False,
|
formfaktor=self._endlosrolle_cb.isChecked() if self._endlosrolle_cb else False,
|
||||||
druckbereich_auswahl=self._druckbereich_combo.currentText() if self._druckbereich_combo else DRUCKBEREICH_VERFAHRENSGEBIET,
|
druckbereich_auswahl=self._druckbereich_combo.currentText() if self._druckbereich_combo else DRUCKBEREICH_VERFAHRENSGEBIET,
|
||||||
|
schriftfeld_template=self._schriftfeld_combo.currentText() if self._schriftfeld_combo else "",
|
||||||
)
|
)
|
||||||
|
|
||||||
def _disconnect_project_signals(self) -> None:
|
def _disconnect_project_signals(self) -> None:
|
||||||
@@ -706,3 +727,29 @@ class PrintTab(QWidget):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
self._atlas_layer_combo.blockSignals(False)
|
self._atlas_layer_combo.blockSignals(False)
|
||||||
|
|
||||||
|
def _get_available_templates(self) -> list[str]:
|
||||||
|
"""Liest verfügbare Schriftfeld-Vorlagen aus dem config/label_configs Verzeichnis."""
|
||||||
|
config_dir = Path(__file__).parent.parent / "config" / "label_configs"
|
||||||
|
templates: list[str] = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
if config_dir.exists() and config_dir.is_dir():
|
||||||
|
for json_file in config_dir.glob("*.json"):
|
||||||
|
template_name = json_file.stem
|
||||||
|
if template_name and template_name not in templates:
|
||||||
|
templates.append(template_name)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not templates:
|
||||||
|
templates = ["standard", "baufreigabe"]
|
||||||
|
elif "standard" not in templates:
|
||||||
|
templates.insert(0, "standard")
|
||||||
|
|
||||||
|
return sorted(templates)
|
||||||
|
|
||||||
|
def _on_schriftfeld_changed(self, template_name: str) -> None:
|
||||||
|
"""Handler wenn Schriftfeld-Template wechselt."""
|
||||||
|
set_variable("tab_b_schriftfeld_template", template_name, scope="project")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user