From 4d0dcc0310fb0c182d25527e23ccf8f73e595f32 Mon Sep 17 00:00:00 2001 From: daniel Date: Fri, 20 Mar 2026 22:56:10 +0100 Subject: [PATCH] Atlasobjekte sind jetzt weniger langgestrckt --- ui/tab_b_logic.py | 123 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/ui/tab_b_logic.py b/ui/tab_b_logic.py index 14157f8..a1d1b35 100644 --- a/ui/tab_b_logic.py +++ b/ui/tab_b_logic.py @@ -94,6 +94,93 @@ class TabBLogic: except Exception: pass + @staticmethod + def _find_tile_grid_for_roll_atlas( + kartenbild_w_mm: float, + kartenbild_h_mm: float, + din_dims: tuple[int, int], + respect_max_sheet_size: bool = False, + ) -> tuple[int, int, float, float]: + """Bestimmt ein Atlas-Raster für Endlosrolle, das Kacheln statt Streifen erzeugt. + + Ziel: Kachel-Seitenverhältnis möglichst nah am Einzelblatt-Kartenfenster + (inkl. Rändern) bei zugleich moderater Seitenanzahl. + """ + if kartenbild_w_mm <= 0 or kartenbild_h_mm <= 0: + return 1, 1, max(1.0, kartenbild_w_mm), max(1.0, kartenbild_h_mm) + + # Einzelblatt-Kartenfenster für beide Orientierungen + dim_w, dim_h = float(din_dims[0]), float(din_dims[1]) + orientation_candidates: list[tuple[float, float]] = [ + (dim_w - 210.0, dim_h - 20.0), + (dim_h - 210.0, dim_w - 20.0), + ] + + best_score = math.inf + best_result: tuple[int, int, float, float] | None = None + + for target_w, target_h in orientation_candidates: + if target_w <= 0 or target_h <= 0: + continue + + target_aspect = target_w / target_h + px0 = max(1, int(round(kartenbild_w_mm / target_w))) + py0 = max(1, int(round(kartenbild_h_mm / target_h))) + + for pages_x in range(max(1, px0 - 3), px0 + 4): + for pages_y in range(max(1, py0 - 3), py0 + 4): + tile_w = kartenbild_w_mm / pages_x + tile_h = kartenbild_h_mm / pages_y + if tile_w <= 0 or tile_h <= 0: + continue + + # Im Blatt-Modus darf die resultierende Atlasseite die + # gewählte Zielgröße (inkl. Orientierung) nicht überschreiten. + if respect_max_sheet_size and (tile_w > target_w or tile_h > target_h): + continue + + tile_aspect = tile_w / tile_h + # 0 bei perfekter Übereinstimmung; symmetrisch für >1/<1 + aspect_error = abs(math.log(tile_aspect / target_aspect)) + + # Streifen bestrafen + strip_penalty = 0.0 + if tile_aspect < 0.5: + strip_penalty = abs(math.log(tile_aspect / 0.5)) + elif tile_aspect > 2.0: + strip_penalty = abs(math.log(tile_aspect / 2.0)) + + page_count = pages_x * pages_y + + # Gewichtung: zuerst Formatnähe, dann Streifenvermeidung, + # danach Seitenzahl minimieren. + score = (aspect_error * 12.0) + (strip_penalty * 6.0) + (page_count * 0.20) + + if score < best_score: + best_score = score + best_result = (pages_x, pages_y, tile_w, tile_h) + + if best_result is None: + if respect_max_sheet_size: + fallback_candidates: list[tuple[int, int, float, float, int]] = [] + for target_w, target_h in orientation_candidates: + if target_w <= 0 or target_h <= 0: + continue + pages_x = max(1, math.ceil(kartenbild_w_mm / target_w)) + pages_y = max(1, math.ceil(kartenbild_h_mm / target_h)) + tile_w = kartenbild_w_mm / pages_x + tile_h = kartenbild_h_mm / pages_y + fallback_candidates.append((pages_x, pages_y, tile_w, tile_h, pages_x * pages_y)) + + if fallback_candidates: + fallback_candidates.sort(key=lambda entry: (entry[4], abs(math.log((entry[2] / entry[3]) if entry[3] > 0 else 1.0)))) + fx, fy, fw, fh, _ = fallback_candidates[0] + return fx, fy, fw, fh + + return 1, 1, kartenbild_w_mm, kartenbild_h_mm + + return best_result + def _create_atlasobjekte_layer( self, layer: Any, @@ -396,6 +483,42 @@ class TabBLogic: pages_x = math.ceil(kartenbild_w / seite_karte_w) pages_y = math.ceil(kartenbild_h / seite_karte_h) + + # Für Atlas in beiden Modi (Endlosrolle + Blatt): Seitenraster so wählen, + # dass Atlasobjekte näher am Einzelblattformat liegen und keine Streifen entstehen. + opt_pages_x, opt_pages_y, opt_tile_w_mm, opt_tile_h_mm = self._find_tile_grid_for_roll_atlas( + kartenbild_w_mm=kartenbild_w, + kartenbild_h_mm=kartenbild_h, + din_dims=din_dims, + respect_max_sheet_size=(not formfaktor), + ) + + # Endlosrolle: Nur die Breite darf wachsen, die Höhe muss innerhalb + # der gewählten Zielhöhe bleiben. + if formfaktor: + max_tile_h_mm = max(1.0, ziel_h - 20.0) + if opt_tile_h_mm > max_tile_h_mm: + required_pages_y = max(1, math.ceil(kartenbild_h / max_tile_h_mm)) + opt_pages_y = max(opt_pages_y, required_pages_y) + opt_tile_h_mm = kartenbild_h / opt_pages_y + + pages_x = max(1, opt_pages_x) + pages_y = max(1, opt_pages_y) + seite_karte_w = max(1.0, opt_tile_w_mm) + seite_karte_h = max(1.0, opt_tile_h_mm) + modus = "Endlosrolle" if formfaktor else "Blatt" + print( + f"[TabBLogic] {modus} Rasteroptimierung: pages_x={pages_x}, pages_y={pages_y}, " + f"tile_mm=({seite_karte_w:.1f}x{seite_karte_h:.1f}), " + f"tile_aspect={seite_karte_w / seite_karte_h:.3f}" + ) + if formfaktor: + max_tile_h_mm = max(1.0, ziel_h - 20.0) + print( + f"[TabBLogic] Endlosrolle Höhenlimit: tile_h={seite_karte_h:.1f}mm, " + f"max_tile_h={max_tile_h_mm:.1f}mm, within_limit={seite_karte_h <= max_tile_h_mm}" + ) + anzahl_seiten = pages_x * pages_y ja = self.pruefmanager.frage_ja_nein(