I would like to get the THREE.Mesh object of an element in Autodesk Forge Viewer. Here is the code:
var dbId; // geometry node Id of an element
var viewer; // GuiViewer3D
var mesh = viewer.impl.getRenderProxy(viewer.model, dbId);
The return mesh object is a THREE.Mesh object but with null Geometry and Material, so it is useless. How can I get the real THREE.Mesh object?
Thank you.
It depends what you want to do with the mesh: if you want to change the render style, you need to get the renderProxy, if you want to transform the component position or rotation, you need to get the fragmentProxy.
Those methods take as input the fragment ids not the dbId.
Find examples for both at:
Viewing.Extension.Material
Viewing.Extension.Transform
You get the fragment Ids for a given dbId either from the selection event, as illustrated in the above samples, or by using enumNodeFragments:
var instanceTree = model.getData().instanceTree
var fragIds = []
instanceTree.enumNodeFragments(dbId, function(fragId){
fragIds.push(fragId)
})
// to change material or transform, need to iterate all
// fragments of a given dbId and apply same material/transform
fragIds.forEach(function(fragId) {
var renderProxy = viewer.impl.getRenderProxy(viewer.model, fragId)
var fragmentproxy = viewer.impl.getFragmentProxy(viewer.model, fragId)
})
Related
from the Forge viewer is it possible to retrieve the absolute coordinates of the objects?
With the following code I was able to obtain the relative coordinates of the selected object:
const viewer = NOP_VIEWER;
const dbId = NOP_VIEWER.getSelection()[0];
const selSet = viewer.getSelection();
const targetElem = selSet[0];
const model = viewer.model;
const instanceTree = model.getData().instanceTree;
const fragList = model.getFragmentList();
let bounds = new THREE.Box3();
instanceTree.enumNodeFragments( dbId, ( fragId ) => {
let box = new THREE.Box3();
fragList.getWorldBounds( fragId, box );
bounds.union( box );
}, true );
const position = bounds.center();
With the getAecModelData method I was able to retrieve the refPointTransformation:
What do the values in this array refer to?
TIA
Alder
I'm not sure what exactly you mean by absolute coordinates but if we're talking about geolocation, note that there's actually a Geolocation extension for the viewer:
https://forge.autodesk.com/en/docs/viewer/v6/reference/Extensions/GeolocationExtension
The extension leverages the refPointTransformation in AEC Model Data, letting you transform lat/long (WGS84) coordinates to viewer coordinates and vice versa.
Here's a related blog post that might also help: https://forge.autodesk.com/blog/mini-map-geolocation-extension.
I am trying to find x,y,z coordinates of an object inside nwc model, and I am using below code.
Despite that this code was working for rvt files, it is not working for nwc model.
Is there a way to get x,y,z coordinates from a nwc model?
getFragmentWorldMatrixByNodeId(nodeId) {
let result = {
fragId: [],
matrix: [],
};
let viewer = this.viewer;
this.tree.enumNodeFragments(nodeId, function (frag) {
let fragProxy = viewer.impl.getFragmentProxy(viewer.model, frag);
let matrix = new THREE.Matrix4();
fragProxy.getWorldMatrix(matrix);
result.fragId.push(frag);
result.matrix.push(matrix);
});
return result;
}
You mentioned you are looking for the "x,y,z coordinates of an object". What exactly do you mean by that? I'm going to assume that you want the coordinates of the center point of the object's bounding box, as that is what people ask for usually. In your code snippet, however, you're retrieving the entire transformation matrix, not a position.
If the center point of a bounding box works for you, you could obtain it like so:
function getObjectBoundingBox(model, dbid) {
const tree = model.getInstanceTree();
const frags = model.getFragmentList();
let totalBounds = new THREE.Box3();
tree.enumNodeFragments(dbid, function (fragid) {
let fragBounds = new THREE.Box3();
frags.getWorldBounds(fragid, fragBounds);
totalBounds.union(fragBounds);
}, true);
return totalBounds;
}
getObjectBoundingBox(viewer.model, 123).center();
How we can show object tree / components tree along with viewer? So that user can click on the tree node and then he can see the object/dbid selected in viewer?
Any thoughts on this?
So, you wanted to build a similar model tree aside by Forge Viewer. The demo you shared is using JStree library to list the files in BIM 360. I believe you are familiar with JsTree.
To dump the model tree nodes of Forge Viewer, the code below could be a reference. It enumerates the hierarchy and gets the nodes name and dbId one by one.
function getAllLeafComponents(viewer, callback) {
var cbCount = 0;
var tree;
var jsData = []
function getLeafComponentsRec(current,parent) {
cbCount++;
if (tree.getChildCount(current) != 0) {
tree.enumNodeChildren(current, function (children) {
getLeafComponentsRec(children,current);
}, false);
}
var nodeName = viewer.model.getInstanceTree().getNodeName(current)
jsData.push({id:current,parent:parent,text:nodeName})
if (--cbCount == 0) callback(jsData);
}
viewer.getObjectTree(function (objectTree) {
tree = objectTree;
var rootId = tree.getRootId()
var nodeName = viewer.model.getInstanceTree().getNodeName(rootId)
jsData.push({id:rootId,parent:'#',text:nodeName})
var allLeafComponents = getLeafComponentsRec(rootId,'#');
});
}
To use the function,
getAllLeafComponents(viewer, function (jsonData) {
console.log(jsonData);
})
It dumps the tree, which can be used with JSTree. Since the data tells DbId, when the JStree node is clicked, get out dbId, and call
viewer.fitToView([dbId])
It will zoom to the object.
I'd need to have the complete model use a default material (e.g. grey color) and then use externally defined materials for each node.
So I'm looking for some advice on two points:
1) Setting a default material on all nodes.
2) Setting the material / color for given nodes after they're fetched from an external source.
Could this be done at some point before the model is loaded into the viewer? (i.e. server-side)? If not, can it be done in the viewer?
All geometry coming from Forge will always have some material defined for it, but you can iterate over dbIDs of all objects on the model and set a custom THREE.js material for them using something along these lines:
function setCustomMaterial(viewer, dbids) {
const material = new THREE.MeshPhongMaterial({
color: 0xAB00EE,
specular: 0xEEABEE
});
viewer.impl.matman().addMaterial('CustomMaterial', material, true);
const fragList = viewer.model.getFragmentList();
const instanceTree = viewer.model.getData().instanceTree;
for (let dbid of dbids) {
instanceTree.enumNodeFragments(dbid, function(frag) {
fragList.setMaterial(frag, material);
});
}
}
In Google Earth Engine, I have loaded in a Featurecollection as a JSON which contains a few polygons. I would like to add columns to this FeatureCollection which gives me the mean values of two bands for each polygon and from each of the multiple images contained within the Image Collection.
Here is the code I have so far.
//Polygons
var polygons = ee.FeatureCollection('ft:1_z8-9NMZnJie34pXG6l-3StxlcwSKSTJFfVbrdBA');
Map.addLayer(polygons);
//Date of interest
var start = ee.Date('2008-01-01');
var finish = ee.Date('2010-12-31');
//IMPORT Landsat IMAGEs
var Landsat = ee.ImageCollection('LANDSAT/LT05/C01/T1') //Landsat images
.filterBounds(polygons)
.filterDate(start,finish)
.select('B4','B3');
//Add ImageCollection to Map
Map.addLayer(Landsat);
//Map the function over the collection and display the result
print(Landsat);
// Empty Collection to fill
var ft = ee.FeatureCollection(ee.List([]))
var fill = function(img, ini) {
// type cast
var inift = ee.FeatureCollection(ini)
// gets the values for the points in the current img
var mean = img.reduceRegions({
collection:polygons,
reducer: ee.Reducer.mean(),
});
// Print the first feature, to illustrate the result.
print(ee.Feature(mean.first()).select(img.bandNames()));
// writes the mean in each feature
var ft2 = polygons.map(function(f){return f.set("mean", mean)})
// merges the FeatureCollections
return inift.merge(ft2)
// gets the date of the img
var date = img.date().format()
// writes the date in each feature
var ft3 = polygons.map(function(f){return f.set("date", date)})
// merges the FeatureCollections
return inift.merge(ft3)
}
// Iterates over the ImageCollection
var newft = ee.FeatureCollection(Landsat.iterate(fill, ft))
// Export
Export.table.toDrive(newft,
"anyDescription",
"anyFolder",
"test")
In the console I get an error message
Element (Error)
Failed to decode JSON.
Error: Field 'value' of object '{"type":"ArgumentRef","value":null}' is missing or null.
Object: {"type":"ArgumentRef","value":null}.
In my csv file which is generated I get a new column called mean but this is populated with and no actual values.
There is no reason to use iterate() here. What you can do is a nested map(). Over polygons and then over images. You can flatten the resulting list of lists to turn it into a single list like this:
// compute mean band values by mapping over polygons and then over images
var results = polygons.map(function(f) {
return images.map(function(i) {
var mean = i.reduceRegion({
geometry: f.geometry(),
reducer: ee.Reducer.mean(),
});
return f.setMulti(mean).set({date: i.date()})
})
})
// flatten
results = results.flatten()
Script: https://code.earthengine.google.com/b65a731c78f78a6f9e08300dcf552dff
The same approach can be used with reduceRegions() as well, mapping over images and then over regions. However, you will have to map over the resulting features to set dates.
images.filterBounds(f) can be probably also added if your features cover a larger area.
PS: your table is not shared