6ab698286f
Neue Spalte "Nachbarn" (kommaseparierte TeileId-Liste) in export_csv.py: zwei Elemente gelten als benachbart, wenn ihre Bounding-Boxes (x/y-Ebene) sich ueberschneiden. Toleranz konfigurierbar in mm ueber cfg/export.cfg [Nachbarschaft] -> toleranz_mm, siehe lib/export_neighbors.py. Nur EXPORTCSV betroffen, EXPORTSIVAS bleibt unveraendert. shapely als neue Abhaengigkeit in requirements.txt ergaenzt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
73 lines
2.7 KiB
Python
73 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
export_neighbors.py - Ermittelt benachbarte Elemente ueber die Bounding-Box (STRtree).
|
|
|
|
Zwei Elemente gelten als benachbart, wenn ihre Bounding-Boxes sich in der
|
|
x/y-Ebene (Grundriss, Z/Hoehe wird ignoriert) ueberschneiden. Um auch nah
|
|
beieinander liegende, aber nicht direkt beruehrende Elemente (z.B. Kreisel)
|
|
als benachbart zu erkennen, wird jede Bounding-Box vor der Pruefung um die
|
|
Haelfte der konfigurierten Toleranz nach allen Seiten erweitert - zwei
|
|
Elemente mit einem Abstand <= toleranz_mm gelten dann ebenfalls als
|
|
benachbart. Die Toleranz wird aus cfg/export.cfg ([Nachbarschaft] ->
|
|
toleranz_mm) gelesen.
|
|
|
|
Nur von export_csv.py genutzt (EXPORTCSV), nicht von export_sivas.py.
|
|
"""
|
|
|
|
import configparser
|
|
|
|
from shapely.geometry import box
|
|
from shapely.strtree import STRtree
|
|
|
|
from export_blockpatterns import cfg_path_from_env
|
|
|
|
|
|
def load_neighbor_tolerance_mm(cfg_path=None):
|
|
"""Liest die Nachbarschafts-Toleranz (mm) aus export.cfg. Default: 0."""
|
|
if cfg_path is None:
|
|
cfg_path = cfg_path_from_env()
|
|
parser = configparser.ConfigParser(inline_comment_prefixes=("#",))
|
|
parser.read(cfg_path, encoding="utf-8")
|
|
return parser.getfloat("Nachbarschaft", "toleranz_mm", fallback=0.0)
|
|
|
|
|
|
def compute_neighbor_ids(items, tolerance_mm):
|
|
"""Ermittelt je Item die IDs (item["teileid"]) ueberschneidender Nachbarn.
|
|
|
|
items = Liste von dict mit "teileid" und optional "_bbox"
|
|
({"cx","cy","dx","dy",...}, siehe export.lsp csv:get-bbox / export_csv.py
|
|
bbox_columns). Items ohne _bbox (z.B. die synthetische Omniflo-Sum-Zeile)
|
|
haben keine Nachbarn.
|
|
|
|
Rueckgabe: Liste von kommaseparierten Nachbar-ID-Strings, positionsgleich
|
|
zu items (nicht ueber die ID dedupliziert, da TeileId nicht zwingend
|
|
eindeutig ist).
|
|
"""
|
|
idx_with_bbox = [i for i, it in enumerate(items) if it.get("_bbox")]
|
|
result = [""] * len(items)
|
|
if len(idx_with_bbox) < 2:
|
|
return result
|
|
|
|
half_tol = tolerance_mm / 2.0
|
|
geoms = []
|
|
for i in idx_with_bbox:
|
|
bb = items[i]["_bbox"]
|
|
minx = bb["cx"] - bb["dx"] / 2.0 - half_tol
|
|
maxx = bb["cx"] + bb["dx"] / 2.0 + half_tol
|
|
miny = bb["cy"] - bb["dy"] / 2.0 - half_tol
|
|
maxy = bb["cy"] + bb["dy"] / 2.0 + half_tol
|
|
geoms.append(box(minx, miny, maxx, maxy))
|
|
|
|
tree = STRtree(geoms)
|
|
for local_i, global_i in enumerate(idx_with_bbox):
|
|
hits = tree.query(geoms[local_i], predicate="intersects")
|
|
neighbor_ids = [
|
|
items[idx_with_bbox[int(local_j)]]["teileid"]
|
|
for local_j in hits
|
|
if int(local_j) != local_i
|
|
]
|
|
result[global_i] = ", ".join(neighbor_ids)
|
|
|
|
return result
|