Hi Jeremy,
Thanks for the update.
In the meantime I have been able to come up with an acceptable workaround to this RevitId => AutoCAD XDATA mapping issue.
The workaround:
Instead of relying on the Revit Id value that originates in a Linked model to be correctly pushed to AutoCAD XDATA, I've implemented a different and simple way of being able to correctly identify an AutoCAD BlockReference associated with the originating Revit FamilyInstance element. Which is to just identify an AutoCAD BlockReference entity by the X,Y in the drawing which does appear to be an identical match to a FamilyInstance Location in the model. It seems to work very well, and gets us to where we need to be, which is what counts at this point in time.
Background info of this particular bit of functionality:
Our core applications, which are distributed globally, are Browser/Mobile device interfaces.
Some of the core functionality we deliver is to support Space Management, Asset Management, etc.
Our users expect a graphical interface in the Browser/Mobile that display both 2D floor plans as well as 3D models.
(I'm very aware of Autodesk's Forge based APIs, but for a huge part of our client base, for many different legitimate reasons are not either ready or planning on moving to the cloud).
The building/floor plan graphics originate from Revit/AutoCAD, which we push to different output file formats to satisfy our core applications. In particular, this issue is in regards to publishing the 2D floor plans.
The overall flow of this functionality looks like this:
Revit/AutoCAD => Intermediate Format => Targeted Output formats
In the above flow, DXF was not even part of the equation until we discovered that it is not possible with the Revit API to determine if an element in a linked model is visible in a view in the main model. Meaning it was not possible to push the correct linked graphics to our Intermediate Format.
So we shoehorned in the DXF process into our overall process something like this:
Revit/AutoCAD => DXFShoehornedIn => Intermediate Format => Targeted Output formats
The following is an example of the Revit code for getting at list of elements to work with from the main model (pretty standard logic):
FilteredElementCollector collector = new FilteredElementCollector(document, view.Id);
IEnumerator enumerator = collector.WherePasses(
new LogicalOrFilter(
new ElementIsElementTypeFilter(false),
new ElementIsElementTypeFilter(true))).GetElementIdIterator();
But the above is not able to get the elements in the linked models. Of course there are ways of getting the elements and the associated graphics from the linked models, but there is no way to determine if an element in a linked model is visible in the current model's view. Per a discussion I had at Forge last year with Revit development, it is not exposed in the Revit API. e.g. no such capability as:
GetVisibleElementsContainedInLinkedModelPerViewInMainModel(mainModelView);
Based on that limitation with not being able to correctly identify which elements are visible in the linked model, we were able to determine that DXF would solve this problem for us. That is, it does a very nice job of pushing all of the geometry, whether originating from the main model or one of the linked models, into a single and nicely packaged DXF file. And the bonus was that the Revit => DXF process conveniently also provided a way of mapping the generated AutoCAD entity back to the originating Revit element, via the Revit Id. But that then lead us to discover, it doesn't push the Revit Id from a linked model correctly, which is what lead to this discussion in the first place.
The workaround, based on the DXF logic that now maps the X,Y values:
private void DXFToIntermediateLogicPsuedoCode()
{
// Create a list of the linked in model identifiers
List<string> linkedModelIds = GetLinkedModelIds();
// Create a list of Asset information to be passed to the DXF process
List<Asset> assets = new List<Asset>();
assets.AddRange(GetElementsInMainModelWeAreInterestedIn().Select(e => ToAsset(e)));
assets.AddRange(GetLinkedElementsWeAreInterestedIn().Select(e => ToAsset(e)));
// Call the Revit API for exporting to DXF
ExportDXF();
// The DXF process
var dxfModel = LoadDXF();
foreach (var entity in dxfModel.Entities)
{
if (entity is BlockReference && linkedModelIds.Contains(entity.Name))
ProcessEntities(assets, entity.Entities);
else
ProcessEntity(assets, entity);
}
}
private void ProcessEntity(IEnumerable<Asset> assets, Entity entity)
{
int RevitId = entity.GetRevitId();
var asset = assets.FirstOrDefault(a => a.Id == RevitId);
// The logic that will now test per X,Y
if (asset == null)
asset = assets.FirstOrDefault(a => a.Point == entity.Point);
if (asset != null)
PushAssetGraphics(asset);
}
public class Asset
{
public int Id; // The Revit ElementId
public Point Point; // The Revit Location
// Other data we keep track of
}
private Asset ToAsset(Element element)
{
// If from a linked model, get the X,Y values
// + taking into consideration the revitLinkInstance.GetTotalTransform() values
return new Asset();
}
The above code is obviously a lightweight description of the process, but you get the basic idea.
Based on this workaround, we are good to go.
Thanks again for your time with this,
Joe