FilteredElementCollector Issues? (Site Coordination)

Hello everyone,

I am trying to push the project location of my host file to all the Revit links that are in that file.

I have been able to create a project location within the host file.

However, trying to do the same with linked Revit documents has been unsuccessful.


The empty lists in my debugging output lead me to believe that this is an issue with the collector, as the only document that shows is the current one although there are two revit links in the project.

# Import RevitAPI
import clr
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *

# Import DocumentManager and TransactionManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

#TransactionManager.Instance.EnsureInTransaction(doc)

collector = Autodesk.Revit.DB.FilteredElementCollector(doc)

siteLoc = collector.OfClass(Autodesk.Revit.DB.SiteLocation)
#I may get this id from a specific site location later
siteLocId = siteLoc.FirstElementId()

linkInstances = collector.OfClass(Autodesk.Revit.DB.RevitLinkInstance)

linkDocs = []
for i in linkInstances:
	linkDocs.append(i.GetLinkDocument())

#temporary for testing
linkDocs.append(doc) 

newLocs = []
z = 0
for i in linkDocs:
	TransactionManager.Instance.EnsureInTransaction(i)
	newLocs.append(Autodesk.Revit.DB.ProjectLocation.Create(i, siteLocId, "Master Shared Site (DO NOT UNCLIP, MODIFY OR DUPLICATE)Test1"))
	z = z +1

#TransactionManager.Instance.TransactionTaskDone()

#Assign your output to the OUT variable.

#janky debug tool :) (uncomment '#debug = true' to change state)
debug = False
debug = True
if debug:
	OUT = (['doc', 'collector', 'siteLoc', 'siteLocId', 'linkInstances', 'linkDocs', 'newLocs'],[doc, collector, siteLoc, siteLocId, linkInstances, linkDocs, newLocs])
else:
	OUT = (newLocs)

UpdateSharedSitePython.dyn (6.5 KB)

Revit test files
Host/Coordination:dyn cs test file.rvt (372 KB)
Linked Revit:CS Circle.rvt (392 KB)

-Thanks

P.S. I know an enumerate would work better than my z variable on my for loop, however I come from C# and I’m still working on learning python, I would like to see how it is done though :wink:

Hmm I just copied and pasted a second collector and it started actually giving warnings. One of them I seem to reproduce said something along the lines of “can’t open a transaction in a linked document.” I’ve toggled on and off the TransactionManager lines and it doesn’t work.

On:


Off:

Transactions remain and enigma to me (so all of my lines involving them is guesswork :face_with_raised_eyebrow:), and they seem to be what is in my way currently.

Here’s my most recent python, I really only added the collector2 object.

# Import RevitAPI
import clr
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *

# Import DocumentManager and TransactionManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

#TransactionManager.Instance.EnsureInTransaction(doc)

collector = Autodesk.Revit.DB.FilteredElementCollector(doc)
collector2 = Autodesk.Revit.DB.FilteredElementCollector(doc)

siteLoc = collector.OfClass(Autodesk.Revit.DB.SiteLocation)
#I may get this id from a specific site location later
siteLocId = siteLoc.FirstElementId()

linkInstances = collector2.OfClass(Autodesk.Revit.DB.RevitLinkInstance)

linkDocs = []
for i in linkInstances:
	linkDocs.append(i.GetLinkDocument())

#temporary for testing
linkDocs.append(doc) 

newLocs = []
z = 0
for i in linkDocs:
#I've tried this line both on and off, it doesn't seem to work either way... 
    #TransactionManager.Instance.EnsureInTransaction(i)
	newLocs.append(Autodesk.Revit.DB.ProjectLocation.Create(i, siteLocId, "Master Shared Site (DO NOT UNCLIP, MODIFY OR DUPLICATE)Test1"))
	z = z +1

#TransactionManager.Instance.TransactionTaskDone()

#Assign your output to the OUT variable.

#janky debug tool :) (uncomment '#debug = true' to change state)
debug = False
debug = True
if debug:
	OUT = (['doc', 'collector', 'siteLoc', 'siteLocId', 'linkInstances', 'linkDocs', 'newLocs'],[doc, collector, siteLoc, siteLocId, linkInstances, linkDocs, newLocs])
else:
	OUT = (newLocs)

I would love to hear everyone’s suggestions. (that means you python wizards)

I’ve had some issues with using the TransactionManager outside of the current document. Try manually managing the transactions for each document like this:

for i in linkDocs:
	t = Transaction(i, 'Create Project Location')
    t.Start()
    try:
        newLoc = ProjectLocation.Create(i, siteLocId, "Master Shared Site (DO NOT UNCLIP, MODIFY OR DUPLICATE)Test1")
    except:
        newLocs.append('Could not create project location')
        t.Rollback()
    else:
        newLocs.append(newLoc)
        t.Commit()

I’m mixing single and double quotes here because the site isn’t formatting the text properly otherwise.

Side note: You are already importing everything from Autodesk.Revit.DB, so you do not need to explicitly write Autodesk.Revit.DB before each class.

3 Likes

Side note, if you are using Revit 2018 and above, you can use PublishCoordinates method or AcquireCoordinates Method to publish or acquire coordinates from link file. i believe this approach will be more much simpler as compared to your current one

1 Like

I assume this also works for unloaded links which would not work in OP’s case, as GetLinkDocument returns null for links which are not loaded.

and also i tried using your workflow and it still cant be create, i think you will need to open the document in background using the openDocumentFile() method, then commit the transaction there

EDIT: I take this back as i realise i couldnt open a link file document when the parent file is open.

Ah, you’re right. You need to unload all links before being able to open them.

There is a reason for not simply using publish and acquire coordinates, this is that the shared site feature of link instances requires identical shared site names and survey points I believe. The script needs the ability to be able to work even with less than ideal circumstances (i.e. the file you get doesn’t have the same shared site name and project position/survey point. Files often have absolute northings and eastings, but aren’t based of the same survey point)

Our goal is to auto coordinate revit files even with different survey point locations. (Which isn’t too uncommon with large sites)

Well you are in luck, i happen to be playing around with the API for this particular function and i had managed to get it work i guess? Test it in your model and give me any feedback. The dyn file is attached

Get Desired ProjectLocation With Specific Name:

import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

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

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

# Import ToDSType(bool) extension method
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
# Import ToProtoType, ToRevitType geometry conversion extension methods
clr.ImportExtensions(Revit.GeometryConversion)

# Import DSCore nodes in Dynamo
clr.AddReference('DSCoreNodes')
import DSCore
from DSCore import *

# Import python library
import sys
pyt_path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
sys.path.append(pyt_path)
import os
import shutil
import math
# Import math library
from math import *

#Import Data and Time
from datetime import datetime
now = datetime.now()

#Import System Library
import System
from System.Collections.Generic import *
from System.IO import Directory, Path

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application

#Preparing input from dynamo to revit

result = None
#Do some action in a Transaction

projLocs = FilteredElementCollector(doc).OfClass(ProjectLocation).ToElements()

for projLoc in projLocs:
	if projLoc.Name == IN[0]:
		result = projLoc
		break
		
OUT = result

Python Script | Create Project Location At Revit Links:

import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

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

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

# Import ToDSType(bool) extension method
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
# Import ToProtoType, ToRevitType geometry conversion extension methods
clr.ImportExtensions(Revit.GeometryConversion)

# Import DSCore nodes in Dynamo
clr.AddReference('DSCoreNodes')
import DSCore
from DSCore import *

# Import python library
import sys
pyt_path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
sys.path.append(pyt_path)
import os
import shutil
import math
# Import math library
from math import *

#Import Data and Time
from datetime import datetime
now = datetime.now()

#Import System Library
import System
from System.Collections.Generic import *
from System.IO import Directory, Path

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application

#Preparing input from dynamo to revit
rvtLinkInstances = UnwrapElement(IN[0])
result = []
#Do some action in a Transaction

projSite = None
projLocName = None

ProLoc = UnwrapElement(IN[1])
orgin = XYZ(0, 0, 0)
projPos = ProLoc.get_ProjectPosition(orgin)
projLocName = ProLoc.Name
projSite = ProLoc.GetSiteLocation()
projSiteLat = projSite.Latitude
projSiteLong = projSite.Longitude
projSiteTimeZ = projSite.TimeZone

trigger = True
#TransactionManager.Instance.EnsureInTransaction(doc)
for rvtLinkInstance in rvtLinkInstances:
	linkdoc = rvtLinkInstance.GetLinkDocument()
	linkdocpath = linkdoc.PathName
	message = "Project Location of specific name already exist"
	rvtlinktype = doc.GetElement(rvtLinkInstance.GetTypeId())
	TransactionManager.Instance.ForceCloseTransaction()
	rvtlinktype.Unload(None)	
	newdoc = app.OpenDocumentFile(linkdocpath)
	currentLinkLocation = newdoc.ActiveProjectLocation
	Linklocations = newdoc.ProjectLocations
	for Linklocation in Linklocations:
		if Linklocation.Name == projLocName:
			trigger = False
			break
		else: continue
	if trigger:
		t = Transaction(newdoc, "Create Project Location")
		t.Start()
		try: 
			newloc = currentLinkLocation.Duplicate(projLocName)
			newloc.set_ProjectPosition(orgin,projPos)
			newlocsite = newloc.GetSiteLocation()
			newlocsite.Latitude = projSiteLat
			newlocsite.Longitude = projSiteLong
			newlocsite.TimeZone = projSiteTimeZ
			#newloc = ProjectLocation.Create(newdoc, projSite.Id, projLocName)
			message = "Project Location successfully duplicated/created"
			t.Commit()
		except Exception as e: 
			message = "Could not create project location due to: " + str(e)
			t.RollBack() 
	newdoc.Close()
	rvtlinktype.Reload() 
	result.append(message)
#TransactionManager.Instance.TransactionTaskDone()
#Final output
OUT = result

CopyProjectLocationToLinkFile.dyn (7.8 KB)

EDIT: If any of you are wondering why i split out the python script, is because i wanna utilize the element binding in dynamo so as when i open the link doc, my element data will not be gone.

2 Likes

@stillgotme Your dyn is working correctly on my file, thanks! :+1:


Now for some more robust testing, I may need to figure out how to get it to work together with the survey point origin to survey-point origin positioning script that I made earlier this year.
-> How do you rotate around a specified center of rotation?

Hey @stillgotme,

I got my positioning script working before your project location script makes it’s changes. :+1:


ProjectCoordination.dyn (58.0 KB) (Clockwork and possibly archi-lab dependency)

Can we now publish and reconcile the new coordinate system after we reload the link? I’m missisng something though, does “newloc” not exist outside of that loop?

Thanks

newloc is only defined within the try block. If an exception is raised, newloc will not be a valid variable. Your error implies that something within the try block didn’t work, although there’s quite a lot going on there so it may be difficult to debug.

I was running the script more than once to test some things, I would delete the project location and try to run the script again and it wouldn’t work. I just discovered that it seems to hold on to the project locations until you re-save making it where it would appear to be clear yet still block the creation of a new project location of the same name.

even if the try block works, newloc will not be valid as the link document is already closed and dispose of. Thats why i didnt append the newloc as my OUT. you can make use of the “message” and include some string properties i guess? like for example a name or such.

1 Like

hahaha, this is because of the app.Opendocument method. the link document is opened in the background when the try block encounter an error. The lines were executed sequentially until an error occurs :rofl:

1 Like

I just ran the for loop again to retrieve the project locations instead of using the newloc object, however I’m having trouble getting the PublishCoordinates method to work. I can’t seem to provide the correct transaction for it.

Previous script...
import clr*
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import 

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

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

# Import ToDSType(bool) extension method
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
# Import ToProtoType, ToRevitType geometry conversion extension methods
clr.ImportExtensions(Revit.GeometryConversion)

# Import DSCore nodes in Dynamo
clr.AddReference('DSCoreNodes')
import DSCore
from DSCore import *

# Import python library
import sys
pyt_path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
sys.path.append(pyt_path)
import os
import shutil
import math
# Import math library
from math import *

#Import Data and Time
from datetime import datetime
now = datetime.now()

#Import System Library
import System
from System.Collections.Generic import *
from System.IO import Directory, Path

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application

#Preparing input from dynamo to revit
rvtLinkInstances = UnwrapElement(IN[0])
result = []
#Do some action in a Transaction

projSite = None
projLocName = None

ProLoc = UnwrapElement(IN[1])
orgin = XYZ(0, 0, 0)
projPos = ProLoc.get_ProjectPosition(orgin)
projLocName = ProLoc.Name
projSite = ProLoc.GetSiteLocation()
projSiteLat = projSite.Latitude
projSiteLong = projSite.Longitude
projSiteTimeZ = projSite.TimeZone

trigger = True
#TransactionManager.Instance.EnsureInTransaction(doc)
for rvtLinkInstance in rvtLinkInstances:
	linkdoc = rvtLinkInstance.GetLinkDocument()
	linkdocpath = linkdoc.PathName
	message = "Project Location of specific name already exist"
	rvtlinktype = doc.GetElement(rvtLinkInstance.GetTypeId())
	TransactionManager.Instance.ForceCloseTransaction()
	rvtlinktype.Unload(None)	
	newdoc = app.OpenDocumentFile(linkdocpath)
	currentLinkLocation = newdoc.ActiveProjectLocation
	Linklocations = newdoc.ProjectLocations
	for Linklocation in Linklocations:
		if Linklocation.Name == projLocName:
			trigger = False
			break
		else: continue
	if trigger:
		t = Transaction(newdoc, "Create Project Location")
		t.Start()
		try: 
			newloc = currentLinkLocation.Duplicate(projLocName)
			newloc.set_ProjectPosition(orgin,projPos)
			newlocsite = newloc.GetSiteLocation()
			newlocsite.Latitude = projSiteLat
			newlocsite.Longitude = projSiteLong
			newlocsite.TimeZone = projSiteTimeZ
			#newloc = ProjectLocation.Create(newdoc, projSite.Id, projLocName)
			message = "Project Location successfully duplicated/created"
			t.Commit()
		except Exception as e: 
			message = "Could not create project location due to: " + str(e)
			t.RollBack() 
	newdoc.Close()
	rvtlinktype.Reload() 
	result.append(message)

#START OF NEW SECTION#
#Publish newloc 

#Modifying is forbidden because document has no open transaction#
"""for rvtLinkInstance in rvtLinkInstances:
	linkdoc = rvtLinkInstance.GetLinkDocument()
	message2 = "Project Location already Published"
	TransactionManager.Instance.ForceCloseTransaction()
	Linklocations = linkdoc.ProjectLocations
	for Linklocation in Linklocations:
		if Linklocation.Name == projLocName:
			try: 
				linkdoc.PublishCoordinates(LinkElementId(Linklocation.Id))
				message = "Project Location successfully published"
			except Exception as e: 
				message2 = "Could not publish project location due to: " + str(e)
		else: continue	
	result.append(message2)"""

#Modifying is forbidden beacause document has no open transaction#
"""for rvtLinkInstance in rvtLinkInstances:
	linkdoc = rvtLinkInstance.GetLinkDocument()
	message2 = "Project Location already Published"
	TransactionManager.Instance.ForceCloseTransaction()
	Linklocations = linkdoc.ProjectLocations
	for Linklocation in Linklocations:
		if Linklocation.Name == projLocName:
			t = Transaction(doc, "Publish Project Location")
			t.Start()
			try: 
				linkdoc.PublishCoordinates(LinkElementId(Linklocation.Id))
				message = "Project Location successfully published"
				t.Commit()
			except Exception as e: 
				message2 = "Could not publish project location due to: " + str(e)
				t.RollBack() 
		else: continue	
	result.append(message2)"""

#Transactions can only be used in primary documents#
"""for rvtLinkInstance in rvtLinkInstances:
	linkdoc = rvtLinkInstance.GetLinkDocument()
	message2 = "Project Location already Published"
	TransactionManager.Instance.ForceCloseTransaction()
	Linklocations = linkdoc.ProjectLocations
	for Linklocation in Linklocations:
		if Linklocation.Name == projLocName:
			t = Transaction(linkdoc, "Publish Project Location")
			t.Start()
			try: 
				linkdoc.PublishCoordinates(LinkElementId(Linklocation.Id))
				message = "Project Location successfully published"
				t.Commit()
			except Exception as e: 
				message2 = "Could not publish project location due to: " + str(e)
				t.RollBack() 
		else: continue 	
	result.append(message2)"""

#LocationID does not contain valid linkinstance id (Due to Open and Unload from host)
"""for rvtLinkInstance in rvtLinkInstances:
	linkdoc = rvtLinkInstance.GetLinkDocument()
	linkdocpath = linkdoc.PathName
	message2 = "Project Location already Published"
	rvtlinktype = doc.GetElement(rvtLinkInstance.GetTypeId())
	TransactionManager.Instance.ForceCloseTransaction()
	rvtlinktype.Unload(None)	
	newdoc = app.OpenDocumentFile(linkdocpath)
	Linklocations = newdoc.ProjectLocations
	for Linklocation in Linklocations:
		if Linklocation.Name == projLocName:
			t = Transaction(newdoc, "Publish Project Location")
			t.Start()
			try: 
				newdoc.PublishCoordinates(LinkElementId(Linklocation.Id))
				message = "Project Location successfully published"
				t.Commit()
			except Exception as e: 
				message2 = "Could not publish project location due to: " + str(e)
				t.RollBack() 
		else: continue
	newdoc.Close()
	rvtlinktype.Reload() 	
	result.append(message2)"""
	
#ReconileCoordinates(couldn't find) 
#END OF NEW SECTION#

#TransactionManager.Instance.TransactionTaskDone()
#Final output
OUT = result

Once again, thanks for all your help. :grinning: