diff --git a/functions/knickpunkt_pruefung.py b/functions/knickpunkt_pruefung.py index afdf23b..edf635a 100644 --- a/functions/knickpunkt_pruefung.py +++ b/functions/knickpunkt_pruefung.py @@ -277,21 +277,20 @@ def _fehlende_knickpunkte_via_verbindungslinien( return 0, [] # ------------------------------------------------------------------ - # Außenringe extrahieren + # Alle Ringe extrahieren (Außen- und Innenringe) # ------------------------------------------------------------------ - exterior_rings: List[List[object]] = [] + all_rings: List[List[object]] = [] if bror_union.isMultipart(): for poly in bror_union.asMultiPolygon(): - if poly: - exterior_rings.append([QgsPointXY(x, y) for x, y in poly[0]]) + for ring in poly: + all_rings.append([QgsPointXY(x, y) for x, y in ring]) else: - poly = bror_union.asPolygon() - if poly: - exterior_rings.append([QgsPointXY(x, y) for x, y in poly[0]]) + for ring in bror_union.asPolygon(): + all_rings.append([QgsPointXY(x, y) for x, y in ring]) # Schlusspunkt (= Startpunkt, Ring geschlossen) entfernen cleaned_rings: List[List[object]] = [] - for ring in exterior_rings: + for ring in all_rings: if len(ring) >= 2: if ( abs(ring[-1].x() - ring[0].x()) < 1e-9 @@ -661,7 +660,7 @@ def erstelle_korrigiertes_polygon( return None, None # ------------------------------------------------------------------ - # Alle BROR-Außenringe extrahieren (Multipolygon-sicher) + # Alle Polygon-Teile mit allen Ringen (Außen- und Innenringe) # ------------------------------------------------------------------ try: bror_feats = [ @@ -673,97 +672,101 @@ def erstelle_korrigiertes_polygon( return None, None if bror_union.isMultipart(): - all_raw_rings = [ - poly[0] for poly in bror_union.asMultiPolygon() if poly - ] + all_poly_parts = [poly for poly in bror_union.asMultiPolygon() if poly] else: poly = bror_union.asPolygon() - all_raw_rings = [poly[0]] if poly else [] + all_poly_parts = [poly] if poly else [] except Exception: return None, None - if not all_raw_rings: + if not all_poly_parts: return None, None all_line_segments: List[Tuple[object, object]] = [] all_polygon_feats: List[object] = [] # ------------------------------------------------------------------ - # Pro Polygon-Teil: Identisch-Punkte verorten, Verlauf aufbauen + # Pro Polygon-Teil: alle Ringe (Außen + Innen) verarbeiten # ------------------------------------------------------------------ - for raw_ring in all_raw_rings: - ring_pts = [QgsPointXY(x, y) for x, y in raw_ring] - if len(ring_pts) >= 2 and ( - abs(ring_pts[-1].x() - ring_pts[0].x()) < 1e-9 - and abs(ring_pts[-1].y() - ring_pts[0].y()) < 1e-9 - ): - ring_pts = ring_pts[:-1] + for poly_rings in all_poly_parts: + corrected_qgs_rings: List[List[object]] = [] - if len(ring_pts) < 3: - continue + for raw_ring in poly_rings: + ring_pts = [QgsPointXY(x, y) for x, y in raw_ring] + if len(ring_pts) >= 2 and ( + abs(ring_pts[-1].x() - ring_pts[0].x()) < 1e-9 + and abs(ring_pts[-1].y() - ring_pts[0].y()) < 1e-9 + ): + ring_pts = ring_pts[:-1] - # Identisch-Punkte auf diesem Ring lokalisieren - on_ring: List[Tuple[int, object]] = [] - for pt in identisch_pts: - for i, rpt in enumerate(ring_pts): - dist_sq = (rpt.x() - pt.x()) ** 2 + (rpt.y() - pt.y()) ** 2 - if dist_sq <= KOORDINATEN_TOLERANZ_M ** 2: - on_ring.append((i, pt)) - break + if len(ring_pts) < 3: + continue - on_ring.sort(key=lambda t: t[0]) + # Identisch-Punkte auf diesem Ring lokalisieren + on_ring: List[Tuple[int, object]] = [] + for pt in identisch_pts: + for i, rpt in enumerate(ring_pts): + dist_sq = (rpt.x() - pt.x()) ** 2 + (rpt.y() - pt.y()) ** 2 + if dist_sq <= KOORDINATEN_TOLERANZ_M ** 2: + on_ring.append((i, pt)) + break - if len(on_ring) < 3: - # Fallback: Original-Ring dieses Teilgebiets verwenden - ordered_pts: List[object] = ring_pts - for j in range(len(ring_pts)): - all_line_segments.append( - (ring_pts[j], ring_pts[(j + 1) % len(ring_pts)]) - ) - else: - # Korrigierten Verlauf für diesen Teil aufbauen - ordered_pts = [] - n_part = len(on_ring) + on_ring.sort(key=lambda t: t[0]) - for k in range(n_part): - _, pt1 = on_ring[k] - _, pt2 = on_ring[(k + 1) % n_part] - - dx = pt2.x() - pt1.x() - dy = pt2.y() - pt1.y() - len_sq = dx * dx + dy * dy - - between: List[Tuple[float, object]] = [] - if len_sq > 0: - for opt in orange_pts: - t = ( - (opt.x() - pt1.x()) * dx + (opt.y() - pt1.y()) * dy - ) / len_sq - clamped_t = max(0.0, min(1.0, t)) - foot_x = pt1.x() + clamped_t * dx - foot_y = pt1.y() + clamped_t * dy - dist = ( - (opt.x() - foot_x) ** 2 + (opt.y() - foot_y) ** 2 - ) ** 0.5 - if dist <= VERBINDUNGSLINIE_PUFFER_M: - between.append((t, opt)) - - between.sort(key=lambda x: x[0]) - - segment_pts = [pt1] + [p for _, p in between] - ordered_pts.extend(segment_pts) - - all_pts_in_seg = segment_pts + [pt2] - for j in range(len(all_pts_in_seg) - 1): + if len(on_ring) < 3: + # Fallback: Original-Ring verwenden + ordered_pts: List[object] = ring_pts + for j in range(len(ring_pts)): all_line_segments.append( - (all_pts_in_seg[j], all_pts_in_seg[j + 1]) + (ring_pts[j], ring_pts[(j + 1) % len(ring_pts)]) ) + else: + # Korrigierten Verlauf für diesen Ring aufbauen + ordered_pts = [] + n_part = len(on_ring) - if len(ordered_pts) < 3: + for k in range(n_part): + _, pt1 = on_ring[k] + _, pt2 = on_ring[(k + 1) % n_part] + + dx = pt2.x() - pt1.x() + dy = pt2.y() - pt1.y() + len_sq = dx * dx + dy * dy + + between: List[Tuple[float, object]] = [] + if len_sq > 0: + for opt in orange_pts: + t = ( + (opt.x() - pt1.x()) * dx + (opt.y() - pt1.y()) * dy + ) / len_sq + clamped_t = max(0.0, min(1.0, t)) + foot_x = pt1.x() + clamped_t * dx + foot_y = pt1.y() + clamped_t * dy + dist = ( + (opt.x() - foot_x) ** 2 + (opt.y() - foot_y) ** 2 + ) ** 0.5 + if dist <= VERBINDUNGSLINIE_PUFFER_M: + between.append((t, opt)) + + between.sort(key=lambda x: x[0]) + + segment_pts = [pt1] + [p for _, p in between] + ordered_pts.extend(segment_pts) + + all_pts_in_seg = segment_pts + [pt2] + for j in range(len(all_pts_in_seg) - 1): + all_line_segments.append( + (all_pts_in_seg[j], all_pts_in_seg[j + 1]) + ) + + if len(ordered_pts) >= 3: + corrected_qgs_rings.append(ordered_pts + [ordered_pts[0]]) + + if not corrected_qgs_rings: continue - polygon_ring = ordered_pts + [ordered_pts[0]] - polygon_geom = QgsGeometry.fromPolygonXY([polygon_ring]) + # Erstes Element = Außenring, weitere = Innenringe (Löcher) + polygon_geom = QgsGeometry.fromPolygonXY(corrected_qgs_rings) if polygon_geom and not polygon_geom.isEmpty(): pf = QgsFeature() pf.setGeometry(polygon_geom)