Tag piles as per coordinate X and Y

Edit Moderation:

Tag piles as per coordinate X and Y value so it take X value in ascending order till Y is constant and move to new row (for a new higher value of Y) Y value and tag piles again for X(lower to high value) till the new constant Y value

import clr

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

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

clr.AddReference('System')
from System.Collections.Generic import List

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument
uidoc = DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument

# Function to get the X, Y coordinates using direct access to Location property with unit conversion
def get_location_coordinates(element):
    location = UnwrapElement(element).Location
    if isinstance(location, LocationPoint):
        point = location.Point
        # Convert from feet to millimeters
        x_mm = point.X * 304.8  # 1 foot = 304.8 millimeters
        y_mm = point.Y * 304.8
        return x_mm, y_mm

# Retrieve selected pile elements from the model
selected_elements = UnwrapElement(IN[0])
if not isinstance(selected_elements, list):
    selected_elements = [selected_elements]

# Retrieve coordinates using direct access to Location property with unit conversion
coordinates = [get_location_coordinates(element) for element in selected_elements]


# Dictionary to store tag values based on columns
tag_values_by_column = {}

# Assign pile tags and update the "Mark" parameter
TransactionManager.Instance.EnsureInTransaction(doc)

# Counter for assigning tag values
tag_counter = 1

for x, y in coordinates:
    # Check if the X coordinate is already in the dictionary
    if x not in tag_values_by_column:
        tag_values_by_column[x] = {}

    # Check if the Y coordinate is already assigned a tag in the column
    if y not in tag_values_by_column[x]:
        tag_value = f'CP{tag_counter:03d}'
        tag_values_by_column[x][y] = tag_value
        tag_counter += 1

    # Find the corresponding pile based on coordinates
    pile = next(pile for pile, coord in zip(selected_elements, coordinates) if coord == (x, y))

    # Assign tag value to the pile
    pile.get_Parameter(BuiltInParameter.ALL_MODEL_MARK).Set(tag_values_by_column[x][y])

TransactionManager.Instance.TransactionTaskDone()

# Refresh the view to see the changes
uidoc.RefreshActiveView()

# Output the sorted piles and their coordinates
OUT = [(x, y) for pile, (x, y) in zip(selected_elements, coordinates)]

@Vinay ,

i embedded my transaction in a try statement…

KR

Andreas

Hi Vinay, welcome onboard the Dynamo forum :handshake:

I’m finding your code somewhat convoluted, as I understand what you are wanting to do is

  • Select piles in the current document
  • Get the pile location x & y coordinates in mm (project units)
  • Sort the piles based on x, then y
  • Tag the piles in order with CP000, CP001,...

Some comments on your code

  • Remove code that is not required - there are a lot of imports that can be removed like 'System' and 'RevitNodes'
  • The get_location_coordinates function uses hard coded conversion - use the API UnitUtils to do this dynamically (see my code below)
  • Iterating through the coordinates and building a dictionary, then checking every pile in the zip function is using a lot of cycles as we are doing it every time we test a coordinate - these can be separate operations
  • Your OUT variable does not need the list comprehension as it is only selecting coordinates - just use the zip

Here’s another way to do this operation

import sys

import clr

clr.AddReference("RevitServices")
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import BuiltInParameter, SpecTypeId, UnitUtils

def to_proj_units(val):
    units = doc.GetUnits().GetFormatOptions(SpecTypeId.Length).GetUnitTypeId()
    return UnitUtils.ConvertFromInternalUnits(val, units)
    
def location(obj, tol=0):
    loc = obj.Location.Point
    return round(to_proj_units(loc.X), tol), round(to_proj_units(loc.Y), tol)

def sort(obj, tol=-3):
    # Use rounding tolerance to deal with minor variances (-3 = meter)
    # -Y, X sorting Top left -> Bottom Right
    x, y = location(obj, tol)
    return -y, x

doc = DocumentManager.Instance.CurrentDBDocument

piles = UnwrapElement(IN[0]) if isinstance(IN[0], list) else [UnwrapElement(IN[0])]

sorted_piles = sorted(piles, key=sort)  # Sort list of piles based on location function

TransactionManager.Instance.EnsureInTransaction(doc)

for cp, pile in enumerate(sorted_piles, 1):  # Use enumerate to count items
    mark = pile.get_Parameter(BuiltInParameter.ALL_MODEL_MARK)
    if not mark.IsReadOnly:
        mark.Set(f"CP{cp:03d}")
    
TransactionManager.Instance.TransactionTaskDone()

OUT = [(p, location(p)) for p in sorted_piles]
3 Likes

thanks Mike, it has changes coordinate values to sort the piles
this had happened to me as well when i am trying to sort pile as per coordinates this keep changing values and assigning coordinates values to diff piles

i want to sort piles like this, but without changing the coordinate values

for the first row, N value (Y coordinate) is constant and E value (X coordinate) is increasing in that row
and so on

i am inputing my selected piles manually in IN[0] of pyhton scripts

Hello if you remove the - before the y in the return of the sort function,
this should rank in the direction of increasing y

Sincerely
christian.stan

2 Likes

@christian.stan which line, could you please add a snip

hello

def sort(obj, tol=-3):
    # Use rounding tolerance to deal with minor variances (-3 = meter)
    # Y, X sorting From bottom left to top right
    x, y = location(obj, tol)
    return y, x

cordially
christian.stan

1 Like