Swisstopo Iter 2 + hierarchische Ebenen + 0-Kote m.ü.M
Swisstopo
- swissBUILDINGS3D 3.0 + Variant-Toggle (separated/solid) im Dialog
- Auto-Fallback auf 2.0 wenn 3.0-Tiles ueber 200 MB sind (Stadt-Fall)
- Defensiver Variant-Filter auf 3 Ebenen (Item, Asset, ZIP-Extract) — keine
Doppelimporte mehr
- Auto-Skala korrigiert jetzt die importierten Objekte (×1000) statt die
User-bbox zu schrumpfen — Buildings bleiben in m-Doc-Skala
- merge_grids: XYZ-Tiles werden vor dem Mesh-Bau vereint, kein 1m-Streifen
zwischen Tiles mehr
- Layer-Konsolidierung: Build_*/Roof_*/Wall_*/Floor_* DWG-Source-Layer
werden auf Sub-Sub-Layer unter 81_Swissbuildings/{Build,Roof,Wall,Floor}
gemappt; solid-Variante landet flach direkt auf dem Parent
- 0-Kote m.ü.M (Projekt-Nullpunkt) wird beim Import als Z-Offset angewandt
Hierarchische Ebenen
- dossier_ebenen unterstuetzt jetzt 'children'-Array (rekursiv)
- layer_builder.build_layers rekursiv (Parent + Children unter jedem Geschoss)
- apply_visibility/update_layer_style/set_ebene_visible/set_ebene_locked
walken den Tree (Sub-Sub-Layer mit gleichem Code-Prefix werden mit-gepflegt)
- EbenenManager mit Chevron-Toggle + Indent pro Level + Context-Menue-Item
'Sub-Ebene hinzufuegen'
- rhinoBridge.applyVisibility schickt Children-Tree (nicht nur Top-Level) —
sonst kommen Sub-Toggles nicht beim Backend an
- Visibility-Key in App.jsx rekursiv durch Children — useEffect feuert jetzt
auch bei Sub-Eye-Toggles
0-Kote m.ü.M
- Eingabefeld im Geschoss-Settings-Dialog (projektweit)
- Speicherung als dossier_project_zero_mum in doc.Strings
- Wird im Swisstopo-Import als Z-Offset (m + doc-units) angewandt
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+127
-90
@@ -287,19 +287,84 @@ def _apply_section_style(doc, layer, section_cfg, layer_color):
|
||||
print(diag, "OK applied")
|
||||
|
||||
|
||||
def walk_ebenen(ebenen, parent_path=()):
|
||||
"""Iteriert Ebenen-Baum (flach + Children). Liefert Tuples
|
||||
(path, ebene) wobei path ein Tuple der Codes von der Root bis zu dieser
|
||||
Ebene ist (inkl. eigener Code). Beispiel:
|
||||
walk_ebenen([{'code':'20','children':[{'code':'01'}]}])
|
||||
→ [(('20',), e20), (('20','01'), e01)]"""
|
||||
out = []
|
||||
if not ebenen: return out
|
||||
for e in ebenen:
|
||||
if not isinstance(e, dict): continue
|
||||
code = e.get("code")
|
||||
if not code: continue
|
||||
path = parent_path + (code,)
|
||||
out.append((path, e))
|
||||
children = e.get("children")
|
||||
if isinstance(children, list) and children:
|
||||
out.extend(walk_ebenen(children, path))
|
||||
return out
|
||||
|
||||
|
||||
def _build_ebene_layer(doc, parent_id, e, diag_prefix=""):
|
||||
"""Findet/erstellt einen Sublayer fuer eine Ebene unter parent_id.
|
||||
Liefert den layer_idx oder -1. Setzt Farbe/LW/Section-Style."""
|
||||
code = e.get("code") or ""
|
||||
name = e.get("name") or "Ebene"
|
||||
sub_name = "{}_{}".format(code, name) if code else name
|
||||
col = _color(e.get("color"))
|
||||
lw = float(e.get("lw", 0.13))
|
||||
sub_idx = _find_sublayer_by_code(doc, parent_id, code) if code else -1
|
||||
if sub_idx < 0:
|
||||
sub_idx = _add_layer(doc, sub_name, parent_id, col, lw)
|
||||
if sub_idx >= 0 and code:
|
||||
doc.Layers[sub_idx].SetUserString("dossier_code", code)
|
||||
else:
|
||||
sub = doc.Layers[sub_idx]
|
||||
if sub.Name != sub_name: sub.Name = sub_name
|
||||
sub.Color = col
|
||||
try:
|
||||
import massstab as _ms
|
||||
_ms.write_plotweight(doc, sub, float(lw))
|
||||
except Exception:
|
||||
sub.PlotWeight = lw
|
||||
if code: sub.SetUserString("dossier_code", code)
|
||||
# Section Style anwenden (Py3-only — IPy 2.7 no-op)
|
||||
try:
|
||||
_apply_section_style(doc, doc.Layers[sub_idx],
|
||||
e.get("section"), e.get("color"))
|
||||
except Exception as ex:
|
||||
print("[EBENEN] section-style apply ({}{}): {}".format(
|
||||
diag_prefix, sub_name, ex))
|
||||
return sub_idx
|
||||
|
||||
|
||||
def _build_ebenen_recursive(doc, parent_id, ebenen, diag_prefix=""):
|
||||
"""Rekursive Ebenen-Erstellung: jeder Eintrag wird als Sublayer angelegt,
|
||||
seine 'children' werden unter dem neu erstellten Sublayer angelegt."""
|
||||
if not ebenen: return
|
||||
for e in ebenen:
|
||||
if not isinstance(e, dict): continue
|
||||
sub_idx = _build_ebene_layer(doc, parent_id, e, diag_prefix=diag_prefix)
|
||||
if sub_idx < 0: continue
|
||||
children = e.get("children")
|
||||
if isinstance(children, list) and children:
|
||||
child_parent_id = doc.Layers[sub_idx].Id
|
||||
_build_ebenen_recursive(doc, child_parent_id, children,
|
||||
diag_prefix=diag_prefix + e.get("name", "") + "/")
|
||||
|
||||
|
||||
def build_layers(doc, zeichnungsebenen, ebenen):
|
||||
"""
|
||||
Stellt sicher dass fuer jede Zeichnungsebene ein Parent-Layer existiert
|
||||
und unter jedem alle Ebenen als Sublayer angelegt/aktualisiert sind.
|
||||
"""
|
||||
"""Stellt sicher dass fuer jede Zeichnungsebene ein Parent-Layer existiert
|
||||
und unter jedem alle Ebenen (rekursiv inkl. children) als Sublayer angelegt
|
||||
/ aktualisiert sind."""
|
||||
for z in zeichnungsebenen:
|
||||
z_id = z["id"]
|
||||
z_name = z["name"]
|
||||
|
||||
# Parent finden oder anlegen
|
||||
idx = _find_top_by_id(doc, z_id)
|
||||
if idx < 0:
|
||||
idx = _find_top_by_name(doc, z_name)
|
||||
if idx < 0: idx = _find_top_by_name(doc, z_name)
|
||||
if idx < 0:
|
||||
idx = _add_layer(doc, z_name)
|
||||
doc.Layers[idx].SetUserString("dossier_id", z_id)
|
||||
@@ -308,78 +373,53 @@ def build_layers(doc, zeichnungsebenen, ebenen):
|
||||
if parent.Name != z_name:
|
||||
parent.Name = z_name
|
||||
parent.SetUserString("dossier_id", z_id)
|
||||
|
||||
parent_id = doc.Layers[idx].Id
|
||||
|
||||
# Sublayer pro Ebene
|
||||
for e in ebenen:
|
||||
sub_name = "{}_{}".format(e["code"], e["name"])
|
||||
col = _color(e.get("color"))
|
||||
lw = float(e.get("lw", 0.13))
|
||||
sub_idx = _find_sublayer_by_code(doc, parent_id, e["code"])
|
||||
if sub_idx < 0:
|
||||
sub_idx = _add_layer(doc, sub_name, parent_id, col, lw)
|
||||
doc.Layers[sub_idx].SetUserString("dossier_code", e["code"])
|
||||
else:
|
||||
sub = doc.Layers[sub_idx]
|
||||
if sub.Name != sub_name:
|
||||
sub.Name = sub_name
|
||||
sub.Color = col
|
||||
try:
|
||||
import massstab as _ms
|
||||
_ms.write_plotweight(doc, sub, float(lw))
|
||||
except Exception:
|
||||
sub.PlotWeight = lw
|
||||
sub.SetUserString("dossier_code", e["code"])
|
||||
|
||||
# Section Style anwenden (Py3-only — IPy 2.7 no-op)
|
||||
try:
|
||||
_apply_section_style(doc, doc.Layers[sub_idx],
|
||||
e.get("section"), e.get("color"))
|
||||
except Exception as ex:
|
||||
print("[EBENEN] section-style apply ({}): {}".format(sub_name, ex))
|
||||
|
||||
_build_ebenen_recursive(doc, parent_id, ebenen,
|
||||
diag_prefix=z_name + "/")
|
||||
doc.Views.Redraw()
|
||||
print("[EBENEN] {} Zeichnungsebenen x {} Ebenen aktualisiert".format(
|
||||
len(zeichnungsebenen), len(ebenen)))
|
||||
n_total = len(walk_ebenen(ebenen))
|
||||
print("[EBENEN] {} Zeichnungsebenen x {} Ebenen aktualisiert (inkl. {} Sub)".format(
|
||||
len(zeichnungsebenen), len(ebenen), max(0, n_total - len(ebenen))))
|
||||
|
||||
|
||||
def _layer_matches_code(layer, code):
|
||||
"""True wenn der Layer zu der Ebene mit `code` gehoert. Akzeptiert
|
||||
sowohl Top-Sub-Layer (Geschoss/CODE_Name) als auch Sub-Sub-Layer
|
||||
(Geschoss/Parent/CODE_Name) — Match via Name-Prefix `code_`."""
|
||||
if _is_top_level(layer): return False
|
||||
return layer.Name.startswith(code + "_")
|
||||
|
||||
|
||||
def update_layer_style(doc, code, color_hex=None, lw=None):
|
||||
"""Aendert Farbe und/oder Stiftdicke fuer alle Sublayer mit dem gegebenen Code."""
|
||||
"""Aendert Farbe und/oder Stiftdicke fuer alle Sublayer mit dem gegebenen
|
||||
Code — auch tief verschachtelte (Sub-Sub-Layer mit gleichem Code-Prefix)."""
|
||||
col = _color(color_hex) if color_hex else None
|
||||
try:
|
||||
import massstab as _ms
|
||||
except Exception:
|
||||
_ms = None
|
||||
for i, layer in enumerate(doc.Layers):
|
||||
if _is_top_level(layer):
|
||||
continue
|
||||
if layer.Name.startswith(code + "_"):
|
||||
if col is not None:
|
||||
layer.Color = col
|
||||
if lw is not None:
|
||||
if _ms is not None:
|
||||
_ms.write_plotweight(doc, layer, float(lw))
|
||||
else:
|
||||
layer.PlotWeight = float(lw)
|
||||
for layer in doc.Layers:
|
||||
if not _layer_matches_code(layer, code): continue
|
||||
if col is not None: layer.Color = col
|
||||
if lw is not None:
|
||||
if _ms is not None:
|
||||
_ms.write_plotweight(doc, layer, float(lw))
|
||||
else:
|
||||
layer.PlotWeight = float(lw)
|
||||
doc.Views.Redraw()
|
||||
|
||||
|
||||
def set_ebene_visible(doc, code, visible):
|
||||
"""Schaltet alle Sublayer mit Code in/aus Zeichnungsebenen."""
|
||||
for i, layer in enumerate(doc.Layers):
|
||||
if _is_top_level(layer):
|
||||
continue
|
||||
if layer.Name.startswith(code + "_"):
|
||||
"""Schaltet alle Sublayer mit Code in/aus (auch tief verschachtelte)."""
|
||||
for layer in doc.Layers:
|
||||
if _layer_matches_code(layer, code):
|
||||
layer.IsVisible = visible
|
||||
doc.Views.Redraw()
|
||||
|
||||
|
||||
def set_ebene_locked(doc, code, locked):
|
||||
for i, layer in enumerate(doc.Layers):
|
||||
if _is_top_level(layer):
|
||||
continue
|
||||
if layer.Name.startswith(code + "_"):
|
||||
for layer in doc.Layers:
|
||||
if _layer_matches_code(layer, code):
|
||||
layer.IsLocked = locked
|
||||
doc.Views.Redraw()
|
||||
|
||||
@@ -631,10 +671,16 @@ def apply_visibility(doc, zeichnungsebenen, ebenen, active_z_id, active_code, z_
|
||||
"""
|
||||
Kombinierte Sichtbarkeit aus Z-Mode (Zeichnungsebenen) und E-Mode (Ebenen).
|
||||
Beide Modi: 'all' | 'active' | 'grey' | 'grey_locked'
|
||||
|
||||
Versteht den hierarchischen Ebenen-Baum: Children erben ParentLayerId vom
|
||||
Sub-Layer (nicht vom Geschoss). Sub-Sub-Layer werden rekursiv mitgepflegt.
|
||||
"""
|
||||
canonical = {e["code"]: _color(e.get("color")) for e in ebenen}
|
||||
e_eye_vis = {e["code"]: e.get("visible", True) for e in ebenen}
|
||||
e_eye_locked = {e["code"]: e.get("locked", False) for e in ebenen}
|
||||
# Flat walk durch Ebenen-Tree (top + children) — alle Codes mit ihren
|
||||
# Eye/Lock-Flags.
|
||||
flat_ebenen = [e for _path, e in walk_ebenen(ebenen)]
|
||||
canonical = {e["code"]: _color(e.get("color")) for e in flat_ebenen}
|
||||
e_eye_vis = {e["code"]: e.get("visible", True) for e in flat_ebenen}
|
||||
e_eye_locked = {e["code"]: e.get("locked", False) for e in flat_ebenen}
|
||||
|
||||
id_to_top, name_to_top, children_by_parent = {}, {}, {}
|
||||
for layer in doc.Layers:
|
||||
@@ -693,17 +739,15 @@ def apply_visibility(doc, zeichnungsebenen, ebenen, active_z_id, active_code, z_
|
||||
if not p_vis:
|
||||
continue # Children erben Parent-Hidden
|
||||
|
||||
# E-Mode -> Sublayer-Zustand
|
||||
for child in children:
|
||||
if "_" not in child.Name:
|
||||
continue
|
||||
# E-Mode → Sub-Layer (rekursiv durch Tree; Sub-Sub-Layer haben Parent
|
||||
# = Sub-Layer, nicht das Geschoss — also iterativ in die Tiefe).
|
||||
def _apply_to_sublayer(child, p_grey_eff):
|
||||
if "_" not in child.Name: return
|
||||
code = child.Name.split("_", 1)[0]
|
||||
if code not in canonical:
|
||||
continue
|
||||
if code not in canonical: return
|
||||
is_active_e = (code == active_code)
|
||||
eye_v = e_eye_vis.get(code, True)
|
||||
eye_l = e_eye_locked.get(code, False)
|
||||
|
||||
if is_active_e:
|
||||
e_vis, e_grey, e_lock = True, False, False
|
||||
elif e_mode == "active":
|
||||
@@ -716,35 +760,28 @@ def apply_visibility(doc, zeichnungsebenen, ebenen, active_z_id, active_code, z_
|
||||
e_vis, e_grey, e_lock = True, True, True
|
||||
else: # grey
|
||||
e_vis, e_grey, e_lock = True, True, False
|
||||
|
||||
# Kombination
|
||||
child_vis = e_vis
|
||||
child_grey = p_grey or e_grey
|
||||
child_grey = p_grey_eff or e_grey
|
||||
child_lock = e_lock or eye_l
|
||||
|
||||
changed = False
|
||||
if child.IsVisible != child_vis:
|
||||
child.IsVisible = child_vis
|
||||
changed = True
|
||||
child.IsVisible = child_vis; changed = True
|
||||
if child.IsLocked != child_lock:
|
||||
child.IsLocked = child_lock
|
||||
changed = True
|
||||
child.IsLocked = child_lock; changed = True
|
||||
if child_grey:
|
||||
if child.Color != GREY:
|
||||
child.Color = GREY
|
||||
changed = True
|
||||
child.Color = GREY; changed = True
|
||||
else:
|
||||
canon = canonical.get(code)
|
||||
if canon is not None and child.Color != canon:
|
||||
child.Color = canon
|
||||
changed = True
|
||||
# In neueren Rhino-Versionen committed der Property-Setter direkt,
|
||||
# in manchen Faellen (besonders auf Mac) wird IsLocked nicht
|
||||
# persistiert ohne explizites Modify. Defensiv:
|
||||
child.Color = canon; changed = True
|
||||
if changed:
|
||||
try:
|
||||
doc.Layers.Modify(child, child.LayerIndex, True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try: doc.Layers.Modify(child, child.LayerIndex, True)
|
||||
except Exception: pass
|
||||
# Sub-Sub-Layer rekursiv (Children dieses Sub-Layers).
|
||||
# Sub-Sub-Layer erben den 'grey'-Zustand des Parents.
|
||||
for grand in children_by_parent.get(child.Id, []):
|
||||
_apply_to_sublayer(grand, child_grey)
|
||||
for child in children:
|
||||
_apply_to_sublayer(child, p_grey)
|
||||
doc.Views.Redraw()
|
||||
|
||||
Reference in New Issue
Block a user