Autodesk Forge External ID for Navisworks File - autodesk-forge

We are trying to export dimensional properties for elements using Forge Viewer with getBulkProperties method. For Revit files, the method works fine, but for Navisworks files, we cannot get any useful properties directly.
As we investigate into the problem, we found that the all the externalId for Navisworks file are in the format of slash separated integers (e.g. 1/2/2/1/1). If we chop off the last integer from externalId (in this case, using 1/2/2/1), and get the properties of the corresponding element, then we can have some useful dimensional properties, and the value matches the information we see on desktop version of Navisworks.
Does the externalId for Navisworks encoding implies a tree structure? (I am assuming 1/2/2/1 is the parent of 1/2/2/1/1 in this case). What could explain our issue that by just chopping off the last integer, we can get the information we need? Is this a reliable way to get dimensional properties?
A small experiment to reproduce this in Chrome console:
selected = AutodeskViewer.getSelection()[0]
AutodeskViewer.getProperties(selected, console.log)
/* {dbId: Array(1), properties: Array(0), externalId: "12/6/0/0/0/0"} */
/* We don't have any useful properties here */
// Get External ID mapping
map = {}; AutodeskViewer.model.getExternalIdMapping(ext_map => {map = ext_map})
// Chopoff the last integer, and run getProperties again
AutodeskViewer.getProperties(map['12/6/0/0/0'], console.log)
/* {dbId: 78085, properties: Array(151), externalId: "12/6/0/0/0", name: "Floor"} */
/* We get useful dimensional properties */

Related

Getting the model object properties in the right display units

I need to show all the properties of a selected object in the viewer. Is there a method that translates the native model information taking in consideration the units selected by the user in the viewer config? That is, if the model contains information expressed in imperial units and the viewer's Display Unit is set as millimeters, is there a way to automatically convert the different units?
I found the autodesk.unit.unit list in this post related to Revit but I haven't found useful information about this topic in Forge documentation.
If you want to change the display unit by Viewer API, here is the code snippet:
// Display units
viewer.prefs.set(Autodesk.Viewing.Private.Prefs.DISPLAY_UNITS, 'm');
// Display Units Precision
viewer.prefs.set(Autodesk.Viewing.Private.Prefs.DISPLAY_UNITS_PRECISION, 4);
//Get human-readable unit names
Autodesk.Viewing.Private.displayUnits
//Get values for changing display units
Autodesk.Viewing.Private.displayUnitsEnum
//Get human-readable Display Units Precisions
Autodesk.Viewing.Private.displayUnitsPrecision
//Get values for changing Display Units Precisions
Autodesk.Viewing.Private.displayUnitsPrecisionEnum
Or you can also consider using Profile settings: https://forge.autodesk.com/en/docs/viewer/v7/developers_guide/advanced_options/profiles/
const customIfcProfileSettings = {
settings: {
displayUnits: 'm',
displayUnitsPrecision: 4
}
};
const customIfcProfile = new Autodesk.Viewing.Profile(customIfcProfileSettings);
// Updates viewer settings encapsulated witihn a Profile.
// This method will also load and unload extensions referenced by the Profile.
viewer.setProfile(customIfcProfile);​

Missing revit ID in instance elements

When processing a translated model's data using the get properties request in the forge model derivative API, a model I'm testing doesn't return the revit ID as part of any instance element. The format I'm used to seeing is something like "Railing [5707296]" for a railing instance element, but the properties JSON response don't show the [5707296] in the result. Is this a recent change with the API? Is there a setting I need to enable this? The revit ID is a useful value for us and we'd like to retrieve it. It's strange that the viewer shows the ID, as expected, but that same entity in the get properties request does not show the ID. This is from a Revit 2022 model.
JSON response:
...
{
"objectid": 410,
"name": "Railing", // <-- I would expect this to be "Railing [5707296]"
"externalId": "1c277e31-8d23-4dc2-96e6-b1ac60f1c07a-0053ce49",
"properties": {
...
}
},
...
Same element selected in the viewer shows the name I would expect:
Thanks to Eason & Jeremy, I learned that the last substring of the externalId is a hex-encoded representation of the Revit element ID. This means that I can derive the Revit element ID from the externalId, and I don't need to parse it from the element name.
const externalId = '110717dd-74ae-4656-b586-fb1c03e9905a-00030954';
const splitArr = externalId.split('-');
console.log(parseInt(splitArr[splitArr.length - 1], 16)); // 198996 ✅
Source: https://thebuildingcoder.typepad.com/blog/2009/02/uniqueid-dwf-and-ifc-guid.html
Please note that the Forge externalId probably corresponds to the Revit UniqueId. I would recommend always using the unique id to identify elements, if possible, since it is more reliable than the element id.
I think this is happening because of SVF2/OTG. The Revit ID is also missing in the viewer when forcing the new format with https://forge.autodesk.com/en/docs/model-derivative/v2/developers_guide/notes/
Seems like there is no simple way to get the Revit ID at the moment. Maybe a workaround is possible using something like https://forge.autodesk.com/blog/temporary-workaround-mapping-between-svf1-and-svf2-ids (see Alex's accepted answer for a good workaround)
Edit: I just realized that the missing Revit Id in my viewer was the result of a poorly implemented custom property panel. Everything works as expected using the default property panel.

Forge viewer: how to isolate custom objects?

We use custom objects to visualize ifc space data (mostly rooms). As a guidance, we used this very helpful blog. After drawing the objects, we would also like to select the custom objects from outside and isolate them in the viewer. As the tutorial suggests, we change the model builder's changeFragmentsDbId function to set DbIds that do not exist yet and therefor do not overlap with already existing DbIds. One approach is to use the negative space [-1, -2, -3...] for our custom objects DbIds like this:
const roomFragId = this.modelBuilder.addFragment(roomGeometryId, materialName, transform);
this.modelBuilder.changeFragmentsDbId(roomFragId, -roomFragId);
Another one is to find the maximum DbId (eg. 4905) and use numbers higher than this maximum DbId for our custom objects DbIds (eg. [4906, 4907, 4908...]):
const roomFragId = this.modelBuilder.addFragment(roomGeometryId, materialName, transform);
this.modelBuilder.changeFragmentsDbId(roomFragId, maxDbId + roomFragId);
However, when we try to isolate a custom drawn object (viewer.isolate(-1) or viewer.isolate(4906)), the viewer kind of refreshs itself, but no object gets isolated...
Thus, we would like to know how we can isolate custom objects?
The other way, when we select the object in the viewer works for the negative space approach => we get the DbId (eg. -1) in the aggregate selection event.
Thank you for any kind of help!
To isolate or select custom objects created by the SceneBuilder ext, you need to pass model object to Viewer3D#isolate / Viewer3D#select like the below. Otherwise, viewer will use viewer.model instead.
viewer.isolate( [4906, 4907, 4908...], this.modelBuilder.model )
viewer.select( [4906, 4907, 4908...], this.modelBuilder.model )

combine model on Autodesk Forge

I have couple of questions about combine model on forge viewer (load list urn to 1 viewer):
when i combine model. i only can get data from 1 main model in that combine. for instance, 
var instanceTree = GlobalViewer.model.getData().instanceTree;
var allDbIdsStr = Object.keys(instanceTree.nodeAccess.dbIdToIndex);
var list = allDbIdsStr.map(function (id) { return parseInt(id) });
list will return all dbid of main model, how can i access all data of all model when i combine?
what is the unique id for object in combine model. i do some function with dbid and i realize it can appear in others model too.
When i combine 3d model(revit) with 2d model(autocad). it has 2 case: if 3d model load first i can rotate like normal, if 2d model load first i cant rotate the model any more. how can i force it always can rotate?
Autocad unit seems different with model in viewer. it always scale down compare with the model. how can i fix that?
Appreciate any comments,
Regarding #1: viewer.model obviously only references one of the models (I believe it's the last one you loaded), but you can use viewer.getVisibleModels() or viewer.getHiddenModels() to get other loaded models as well.
Regarding #2: dbIDs are only unique within a single model; many of the viewer methods accept an additional parameter specifying the model on which to operate, for example, you could say viewer.select([123, 456], oneOfMyModels).
Regarding #3: that's a good question; loading a 2D model first puts the viewer into 2D viewing mode (where only zoom and pan is allowed); if you know you will be working with 3D models, I'd recommend always loading those first
Regarding #4: yes, each loaded model can have different units; when loading a model using the loadDocumentNode method you can specify additional options (for example, a placement transform for the loaded geometries), and one of them is an object called applyScaling, for example, like so:
viewer.loadDocumentNode(doc, viewable, {
applyScaling: { to: 'mm' }
});

How can I load a unique database ID onto a MarkupCore extension markup?

I am using the MarkupsCore extension to build a cloud-based annotation system. I am able to successfully store markups in the database individually, and load them back as one whole SVG string. However, I am confused on being able to delete them. Ordinarily, I would attach a database ID to the markup, and delete it by that. But, I do not know how I would do that in this case. Are there any unique attributes that I could store that are part of the markups to use to identify them to delete them with?
Also, is there a particular reason that the MarkupsCore extension doesn't have an event that is fired when a markup is created? I was able to resolve this problem myself, but I am just curious.
If you want to bypass the standard storage mechanism (using the generateData() and loadMarkups() methods on the MarkupCore extension), you could potentially store the data separately, and re-create the markup procedurally using the following approach:
viewer.loadExtension('Autodesk.Viewing.MarkupsCore').then((extension) => {
const CoreNS = Autodesk.Viewing.Extensions.Markups.Core;
extension.clear();
extension.enterEditMode();
let rect = new CoreNS.MarkupRectangle(123 /* your custom ID */, extension);
extension.addMarkup(rect);
rect.setSize({ x: 10, y: 10 }, 100 /* width */, 100 /* height */);
extension.leaveEditMode();
console.log('markup data', extension.generateData());
});