Script for Floor to Follow Topography

Hi all,

I am trying to build a script that will make a floor follow a topo surface when both of them are selected.

This is the code that I have at the moment but it’s not working.

from pyrevit import revit, DB
from pyrevit import script
from pyrevit import forms

# Function to get the elevation of toposurface at a given point
def get_toposurface_elevation(toposurface, point):
    location = DB.XYZ(point.X, point.Y, 0)
    elevation = toposurface.GetElevationAtPoint(location)
    return elevation

# Function to create a floor at a specific elevation
def create_floor(document, level, elevation):
    floor_type = None  # Set your floor type here
    floor_profile = None  # Set your floor profile here

    floor = DB.Floor.Create(document, floor_profile, floor_type)
    level_param = floor.get_Parameter(DB.BuiltInParameter.LEVEL_PARAM)
    level_param.Set(level.Id)

    offset_param = floor.get_Parameter(DB.BuiltInParameter.FLOOR_HEIGHTABOVELEVEL_PARAM)
    offset_param.Set(elevation)

    return floor

def main():
    # Prompt user to select a toposurface
    toposurface = forms.select_element_by_category(
        DB.BuiltInCategory.OST_Topography,
        multiple=False,
        msg="Select Toposurface"
    )

    if not toposurface:
        return

    # Prompt user to select a level for the floor
    level = forms.select_level()

    if not level:
        return

    # Get the boundary points of the toposurface
    boundary_points = toposurface.GetBoundaryPoints()

    # Create floors at the elevations of the toposurface
    with revit.Transaction("Create Floors on Toposurface"):
        for point in boundary_points:
            elevation = get_toposurface_elevation(toposurface, point)
            create_floor(revit.doc, level, elevation)

if __name__ == "__main__":
    main()


@rivindub ,

Do you have any Errors and warnings to show?
Which Revit Version do you run?

KR

Andreas

Hi @andreasd811

I am running Revit 2023.

This is the error that I’m getting.

Thanks

@rivindub ,

looks like a external library it is function, seems to be it is not imported.
do you want select a element or several elements by selection? or rectengular selection ? and only one category?

KR

Andreas

I’ll help you analyze that error report.
You basically do these 2 things for most of the errors you encounter.
I’ve used some OCR to convert your image to text, so there’s some serious garbled text in there , but the points are the same

Step 1, look at the top paragraph of the traceback (Most of the time the essential error info is contained here)
So we want to look just at the IronPython Tracback

IronPython Traceback :
Traceback (most recent call last):
File
"C : \Users RivinduB \digitaldes ign. extens ion\digitaldes ign. tab\WIP. panel \C01umn1. stack
line 52, in <module>
File
"C : RivinduB \digitaldesign. extens ign. tab\WIP. panel \C01umn1. stack
line 27, in main
AttributeError: •module • object has no attribute 'select_element_by_category•

Step 2
Read that traceback from bottom to top
The traceback is printed in ‘order of execution’ by python.
Each line is a step in the python execution up until you encountered the error. The last line is usually the line containing the error.

Step 3
Identify the issue by looking at what the bottom line says. In this case it says

File C:\.....\Column1.stack\yourscriptname.pushbutton\script.py line 27, in main
AttributeError: 'module'object has no attribute 'select_element_by_category

There’s some usefull info in here.
1st. It says which python file of your script is causing the issue.
2nd It says that at line 27 in your file (in def main ) an error has occured
3rd It says that it’s an attribute error. You are asking for select_element_by_category, but the module does not know a function called that.

So put those together and it means that you are calling a pyrevit function from froms that does not exist.

Sidenote:
When walking up through the traceback, you sometimes get errors inside modules that you didn’t create or write. In those cases it’s best to skip those lines and keep going up right until you find a point in your own code where the error messages begin.

2 Likes

check this
https://github.com/search?q=repo%3Aeirannejad%2FpyRevit+by_category&type=code

the forms module does not have such a method select_element_by_category() (It feels like an hallucination from chatGPT :stuck_out_tongue: )

but only these methods:
pick_element_by category()
or
pick_elements_by_category()

Is this a ChatGPT script? There seems to be a lot of funky things with this code, like I can’t find any method for toposurfaces call GetElevationAtPoint(). and also it looks like the code is attempting to make a new floor for every point? You shouldn’t even be creating a floor if you want it to match an existing one to the topo.

I’ve done something similar to this, and it not a simple feat.

I think this would be the layout of the script:

  1. Get floor and topo
  2. Get points of floor
  3. Project those points up to intersect with the topo.
  4. Edit the floor shape to move the points to the new locations.

@Archibum @Jean-Marc,

Thanks for the responses, yes it was a gpt script that I was using. I’ve decided to have a look at it again.

I’ve managed to get close by editing the sub elements and moving the vertex of the floor but I don’t think my intersection of the floor points with the topo is working. I also think I should be adding more points onto the floor and then doing it that way.

This is what I have so far:

from Autodesk.Revit.DB import (
    BuiltInCategory, XYZ, ReferenceIntersector,
    FindReferenceTarget, Transaction, Options, Solid,
    GeometryInstance, UV, SlabShapeEditor, SlabShapeVertex
)
from Autodesk.Revit.UI.Selection import ObjectType, ISelectionFilter
import clr
clr.AddReference("System")
from System.Collections.Generic import List

uidoc = __revit__.ActiveUIDocument
doc = uidoc.Document

class FloorSelectionFilter(ISelectionFilter):
    def AllowElement(self, element):
        return element.Category.Id.IntegerValue == int(BuiltInCategory.OST_Floors)
    def AllowReference(self, refer, point):
        return False

class TopoSelectionFilter(ISelectionFilter):
    def AllowElement(self, element):
        return element.Category.Id.IntegerValue == int(BuiltInCategory.OST_Topography)
    def AllowReference(self, refer, point):
        return False

try:
    floor_selection_filter = FloorSelectionFilter()
    selected_floor_ref = uidoc.Selection.PickObject(
        ObjectType.Element,
        floor_selection_filter,
        'Select a floor to modify.'
    )
    selected_floor = doc.GetElement(selected_floor_ref.ElementId)

    topo_selection_filter = TopoSelectionFilter()
    selected_topo_ref = uidoc.Selection.PickObject(
        ObjectType.Element,
        topo_selection_filter,
        'Select a topography surface.'
    )
    selected_topo = doc.GetElement(selected_topo_ref.ElementId)

    t = Transaction(doc, "Modify Floor Shape")
    t.Start()

    boundary_points = []
    options = Options()
    options.ComputeReferences = True
    options.IncludeNonVisibleObjects = True
    geometry_element = selected_floor.get_Geometry(options)

    for geometry_object in geometry_element:
        if isinstance(geometry_object, Solid):
            solid = geometry_object
            for face in solid.Faces:
                normal = face.ComputeNormal(UV(0.5, 0.5))
                if abs(normal.Z) > 0.99 and normal.Z < 0:
                    edge_loops = face.EdgeLoops
                    for edgeArray in edge_loops:
                        for edge in edgeArray:
                            curve = edge.AsCurve()
                            start_point = curve.GetEndPoint(0)
                            boundary_points.append(start_point)
        elif isinstance(geometry_object, GeometryInstance):
            instance_geometry = geometry_object.GetInstanceGeometry()
            for inst_geom_obj in instance_geometry:
                if isinstance(inst_geom_obj, Solid):
                    solid = inst_geom_obj
                    for face in solid.Faces:
                        normal = face.ComputeNormal(UV(0.5, 0.5))
                        if abs(normal.Z) > 0.99 and normal.Z < 0:
                            edge_loops = face.EdgeLoops
                            for edgeArray in edge_loops:
                                for edge in edgeArray:
                                    curve = edge.AsCurve()
                                    start_point = curve.GetEndPoint(0)
                                    boundary_points.append(start_point)

    unique_points = []
    for pt in boundary_points:
        if not any(pt.IsAlmostEqualTo(existing_pt) for existing_pt in unique_points):
            unique_points.append(pt)

    print('Boundary points: ', unique_points)

    direction_up = XYZ(0, 0, 1)
    direction_down = XYZ(0, 0, -1)
    reference_intersector = ReferenceIntersector(
        selected_topo.Id,
        FindReferenceTarget.Face,
        doc.ActiveView
    )

    new_points = []

    for point in unique_points:
        results_up = reference_intersector.Find(point, direction_up)
        results_down = reference_intersector.Find(point, direction_down)

        intersection_point = None
        if results_up:
            intersection_point = results_up[0].GetIntersection().XYZPoint
        elif results_down:
            intersection_point = results_down[0].GetIntersection().XYZPoint
        else:
            intersection_point = point

        new_points.append(intersection_point)

    print('New points: ', new_points)

    slab_shape_editor = selected_floor.SlabShapeEditor

    slab_shape_editor.Enable()

    for pt in unique_points:
        slab_shape_editor.DrawPoint(pt)

    vertices = slab_shape_editor.SlabShapeVertices

    print('Vertices: ', vertices)

    for vertex in vertices:
        vertex_pt = vertex.Position
        min_dist = None
        closest_pt = None
        for pt in new_points:
            dist = vertex_pt.DistanceTo(pt)
            if min_dist is None or dist < min_dist:
                min_dist = dist
                closest_pt = pt
                print("Closest point: ", closest_pt.Z)
        if closest_pt:
            slab_shape_editor.ModifySubElement(vertex, closest_pt.Z)  

    t.Commit()
    print("Floor shape modified successfully.")

except Exception as e:
    import traceback
    print("Selection cancelled or an error occurred: {}".format(e))
    traceback.print_exc()

Thanks