Instances.ByLine - Different levels

Hello!

When inserting a line-based family, we specify the host level. Everything works fine; you just need to use the “Levels” block. The problem arises when we are dealing with objects at different levels, and we want each object to have its own specified level.

I tried extracting this information from the “Work Plane,” but I get a string, which I cannot connect to the block for placing the family by line. I’m looking for a solution to obtain a list for each object with the assigned level, similar to how it works with the “Levels” block, but instead of one level, I want each object to have its designated level that the selected family is hosted on.

1 Like

@krzysztof.jadrzyk

just create a list of levels…

i tested with one line …

1 Like

thank you draxl for your answer!
However, this method does not solve the problem, because I need to somehow extract the level information for each object, which will allow me to connect it to the “instance families by line” block. I believe that using “get element parameter” is a good clue and all that is missing here is extracting the level information further.
Why do I need this methodology ? Because it simultaneously assumes selecting e.g. 500/800 objects on different levels, often with different names depending on the Project. Therefore, I am not able and cannot predefine the level in the script. It must be defined by the element that I have marked by “select model elements”

Extract the level from the element as you were before. Then build a dictionary of all levels using their name as the key. You can then pull the relevant level from the dictionary using the parameter value, and that resulting list can feed I to the creation node without issue.

If you are just referencing the Z elevation of a Family/Line/Element then you can use a method that checks the location relative to the levels above/below via the use of a Collector and a couple of small definitions (this example is set to prefer identifying the level below, but has a toggle to set the reverse).

# Import Namespace References
import clr
import sys

# Revit API
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *

# Revit Services for Transactions
clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager

# Document Essentials
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application
uidoc = uiapp.ActiveUIDocument

# Revit API UI
clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import *

def closest_value(lst, num, above=False):
    """Find the closest value in a list to a number, either below or above."""
    filtered = [i for i in lst if i <= num] if not above else [i for i in lst if i > num]
    return max(filtered) if filtered else None

def convert_from_internal_unit_length(unitL):
    if unitL == 0:
        return 0
    revit_version = int(app.VersionNumber)
    uid = UnitTypeId.Millimeters if revit_version >= 2021 else DisplayUnitType.DUT_MILLIMETERS
    return UnitUtils.ConvertFromInternalUnits(unitL, uid)

def find_closest_level(elevations, prefer_below=True):
    # Gather all Levels in the Project
    levels = FilteredElementCollector(doc).OfClass(Level).ToElements()
    
    # Create a dictionary mapping elevations to levels
    level_dict = {round(convert_from_internal_unit_length(l.ProjectElevation)): l for l in levels}
    
    # Sort the elevations
    sorted_elevations = sorted(level_dict.keys())
    
    def find_level(elevation):
        # First, try to find a level below
        below = closest_value(sorted_elevations, elevation, above=False)
        if below is not None:
            return level_dict[below]
        
        # If no level below is found, look for a level above
        above = closest_value(sorted_elevations, elevation, above=True)
        return level_dict[above] if above is not None else None

    return [find_level(elevation) for elevation in elevations]

# Main execution
elements_z = IN[0]
OUT = find_closest_level(elements_z)

I drew the effect I’m trying to achieve.

Dictionary.ByKeysValues goes in the ?, with the levels themselves as the value and the level names as they key.

Dictionary.ValueAtKey goes after that where you sketched the nodes content, with the new dictionary as the dictionary input and the names from above as the key input.

Instead of a String.Split and flatten node use a String.Replace node to remove the prefix.

Probably something here could work as well, guess the same as @Ewan_Opie mean :wink:

1 Like

But what if model is on floor 1 but from elevation floor 2 is closest ? Then the script will match the level wrong?. I want the workplane to be identical. And the heights of the objects will do the trick by reading the appropriate parameters from the family

For example floor 1 is on +0.000
Floor 2 is on +4.000
And famile elevation is on +3900 ? Then i want still to instance new object on floor 1 according to selected work plane familie

ok what kind of element are you select ? have you then tried what Jacob suggest??

Gentlemens, it is already working. Just use “Element.Host+” thank you for your help !

2 Likes