Geometry from linked file, by shared coordinates, appears on the wrong place in Dynamo

hello what was the solution? i have same issue, the rooms of another building appear on top of another building on the side overlapped, crazy…


Never skip the Primer

Its all there

1 Like

if that would be so simple I would have resolved already and i would not be here writing stupid post comment, i saw this but one of the nodes does not work in my revit 2023 Aligning linked geometry using Dynamo for Revit! - YouTube @GavinNicholls @Marcel_Rijsmus

I tried this following the video and still does not work correctly:

# IronPython 2.7 - Dynamo for Revit (no required inputs)
# OUT:
# [0] RevitLinkInstance[]
# [1] Types[]
# [2] CoordinateSystem[] (host-space, from each link's total transform)
# [3] elements[i][j] -> elements from linked doc for instance i, category j
# [4] dsGeom[i][j]   -> DS geometry in link-space
# [5] dsGeomHost[i][j] -> DS geometry transformed to host-space

import clr, System

# --- guard IN so default-arg crashes never happen if node has no inputs
try:
    IN
except:
    IN = [None]

# Revit Services / API
clr.AddReference("RevitServices")
from RevitServices.Persistence import DocumentManager

clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import (
    FilteredElementCollector, RevitLinkInstance, ElementCategoryFilter,
    BuiltInCategory, Category, ElementId, Options, ViewDetailLevel,
    GeometryInstance, Solid, Mesh, SpatialElement, SpatialElementGeometryCalculator
)

# Dynamo geometry
clr.AddReference("ProtoGeometry")
from Autodesk.DesignScript.Geometry import CoordinateSystem, Point, Vector, Geometry

# Dynamo Revit wrappers/extensions
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)

doc = DocumentManager.Instance.CurrentDBDocument

# ----------------- helpers -----------------

def _unwrap(x):
    try:
        return UnwrapElement(x)
    except:
        return x

def _as_list(x):
    return x if isinstance(x, list) else [x]

def _unique_by_eid(elems):
    seen = set(); out = []
    for e in elems:
        try: k = e.Id.IntegerValue
        except: k = id(e)
        if k not in seen:
            seen.add(k); out.append(e)
    return out

def _to_bic_list(maybe_cats):
    """Accept None | Category | ElementId | BuiltInCategory | int | 'Rooms' | 'OST_Rooms' | list of any -> list[BuiltInCategory]."""
    if not maybe_cats:
        return [BuiltInCategory.OST_Rooms]
    cats = _as_list(maybe_cats)
    bics = []
    for c in cats:
        c = _unwrap(c)
        # BuiltInCategory?
        if str(type(c)).endswith("BuiltInCategory"):
            bics.append(c); continue
        # Category -> BIC
        if isinstance(c, Category):
            try:
                bics.append(System.Enum.ToObject(BuiltInCategory, c.Id.IntegerValue)); continue
            except: pass
        # ElementId -> BIC
        if isinstance(c, ElementId):
            try:
                bics.append(System.Enum.ToObject(BuiltInCategory, c.IntegerValue)); continue
            except: pass
        # int -> BIC
        if isinstance(c, int):
            try:
                bics.append(System.Enum.ToObject(BuiltInCategory, c)); continue
            except: pass
        # string
        if isinstance(c, basestring):
            s = c.strip()
            if s == "Rooms":
                bics.append(BuiltInCategory.OST_Rooms); continue
            try:
                bics.append(getattr(BuiltInCategory, s)); continue
            except: pass
    if not bics:
        bics = [BuiltInCategory.OST_Rooms]
    return bics

# Geometry extraction from DB.Element -> list[DB.GeometryObject]
_opts = Options()
_opts.IncludeNonVisibleObjects = False
_opts.ComputeReferences = False
_opts.DetailLevel = ViewDetailLevel.Fine

# cache SpatialElementGeometryCalculator per document
_calc_cache = {}

def _get_room_solid(elem, rdoc):
    try:
        calc = _calc_cache.get(rdoc, None)
        if calc is None:
            calc = SpatialElementGeometryCalculator(rdoc)
            _calc_cache[rdoc] = calc
        res = calc.CalculateSpatialElementGeometry(elem)
        return res.GetGeometry()  # DB.Solid
    except:
        return None

def _expand_gobj(go):
    out = []
    if isinstance(go, Solid):
        # keep non-empty solids
        try:
            if go.Volume > 1e-9:
                out.append(go)
            else:
                # even "empty" solids may have faces
                faces = list(go.Faces)
                if len(faces) > 0: out.append(go)
        except:
            out.append(go)
    elif isinstance(go, Mesh):
        out.append(go)
    elif isinstance(go, GeometryInstance):
        ig = go.GetInstanceGeometry()
        if ig:
            for sub in ig:
                out.extend(_expand_gobj(sub))
    else:
        # curves/symbolic lines often not useful for volume; skip
        pass
    return out

def _db_element_to_db_geom(elem, rdoc):
    geos = []
    try:
        g = elem.get_Geometry(_opts)
        if g:
            for go in g:
                geos.extend(_expand_gobj(go))
    except:
        pass
    # Rooms: robust solid via SpatialElementGeometryCalculator
    if isinstance(elem, SpatialElement) and not geos:
        rs = _get_room_solid(elem, rdoc)
        if rs is not None:
            geos.append(rs)
    return geos

def _dbgeom_to_ds(dbgeom_list):
    ds = []
    for go in dbgeom_list:
        try:
            ds.append(go.ToProtoType())
        except:
            # some geometry types may fail; skip safely
            pass
    return ds

# ----------------- main -----------------

# 1) link instances
instances = list(FilteredElementCollector(doc).OfClass(RevitLinkInstance))
instances = _unique_by_eid(instances)

# 2) types
types = []
for inst in instances:
    try:
        types.append(inst.Document.GetElement(inst.GetTypeId()))
    except:
        types.append(None)

# 3) coordinate systems from each instance's total transform (host-space)
coord_systems = []
for inst in instances:
    t = inst.GetTotalTransform()
    o = t.Origin; bx, by, bz = t.BasisX, t.BasisY, t.BasisZ
    p  = Point.ByCoordinates(o.X,  o.Y,  o.Z)
    vx = Vector.ByCoordinates(bx.X, bx.Y, bx.Z)
    vy = Vector.ByCoordinates(by.X, by.Y, by.Z)
    vz = Vector.ByCoordinates(bz.X, bz.Y, bz.Z)
    coord_systems.append(CoordinateSystem.ByOriginVectors(p, vx, vy, vz))

# category selection (default Rooms) — can wire IN[0] with Categories/BICs if desired
requested_bics = _to_bic_list(IN[0])

# 4) elements per instance per category
elements = []           # elements[i][j]
dsGeom = []             # dsGeom[i][j] in link-space
dsGeomHost = []         # dsGeomHost[i][j] transformed to host-space

CS_identity = CoordinateSystem.ByOrigin(0, 0, 0)

for idx, inst in enumerate(instances):
    try:
        ldoc = inst.GetLinkDocument()
    except:
        ldoc = None

    inst_elems_per_cat = []
    inst_ds_per_cat = []
    inst_ds_host_per_cat = []

    if ldoc is None:
        # Unloaded link: fill empties per requested category
        for _ in requested_bics:
            inst_elems_per_cat.append([])
            inst_ds_per_cat.append([])
            inst_ds_host_per_cat.append([])
    else:
        for bic in requested_bics:
            try:
                coll = FilteredElementCollector(ldoc).WhereElementIsNotElementType().WherePasses(ElementCategoryFilter(bic))
                cat_elems = list(coll.ToElements())
            except:
                cat_elems = []

            inst_elems_per_cat.append(cat_elems)

            # Convert to DS geometry (flatten by element)
            link_ds_geo_flat = []
            for e in cat_elems:
                db_geo = _db_element_to_db_geom(e, ldoc)
                link_ds_geo_flat.extend(_dbgeom_to_ds(db_geo))

            inst_ds_per_cat.append(link_ds_geo_flat)

            # Transform DS geometry to host using the instance CS
            host_ds_geo_flat = []
            cs_target = coord_systems[idx] if idx < len(coord_systems) else CS_identity
            for g in link_ds_geo_flat:
                try:
                    host_ds_geo_flat.append(Geometry.Transform(g, CS_identity, cs_target))
                except:
                    # If any geometry fails to transform, skip it
                    pass
            inst_ds_host_per_cat.append(host_ds_geo_flat)

    elements.append(inst_elems_per_cat)
    dsGeom.append(inst_ds_per_cat)
    dsGeomHost.append(inst_ds_host_per_cat)

OUT = instances, types, coord_systems, elements, dsGeom, dsGeomHost

1 Like

Hi! Did you see this post: Geometry from linked file, by shared coordinates, appears on the wrong place in Dynamo - #12 by danail.momchilov

You could also send your graph here, along with a set of sample files and I could fix it for you