Showing results for 
Search instead for 
Did you mean: 
Showing results for 
Search instead for 
Did you mean: 

Community Tip - You can subscribe to a forum, label or individual post and receive email notifications when someone posts a new topic or reply. Learn more! X

Incorporate CAD Metadata Into an Experience ( version 8.5.13 and later)

100% helpful (6/6)

In Vuforia Studio version 8.5.13 a new metadata is intruduced. This will make the techniques described e.g. in the posts "How to extract the components with properties from a pvz file","Extracting the viewables and the seqnece steps information from a .pvz file for the usage in TWX" and "How to extract model data of 3d models in Vuforia Studio (without external Tools)?"  for the most cases not neccessarly any more. as In Vuforia Studio when a model is imported (add Resource) there is a new check button “Allow the Experience access to CAD metadata” :



The selection of this checkbox lead that when the model is loaded to the “app/resources/Uploaded” folder but also an a json object with the name <model name>.metadata.json
Example when we load the model “Coffee Maker Model_High.pvz” then we have also a json file named “Coffee Maker Model_High.metadata.json”. This JSON file contains the metadata to the coffee maker model



The metadata json file contains some different sections for each component /

  • For each component / CompPath Id  we have a sub object – comp obj


"/11":{"":{"Feature_Id":"11","Source_file_name": …

"/14":{"":{"Feature_Id":"14","Source_file_name": …

Where “/” means the root asm , “/11” and “/14” are components paths

  • The component object contains the following sections : general(empty string as key of the json obj) “”, "PROE Parameters" and "__PV_SystemProperties"

(see the attached example “Coffee Maker Model_High.metadata.json”)

The one possible approach could be , to use only this json object directly  but we can use also the PTC API which is provided starting with the Vuforia Studio 8.5.13

Metadata Access:

  • Read the json object into memory (javascript): 

In this case we can use the json object which was already created when the model data is omported in Studio.





var metaDataArray=[];
//========================== When Model Loaded Event
$rootScope.$on("modelLoaded", function() {
  if (arguments.length >0){ 
//================== if args >1 ======================
    var modelWidgetId=arguments[1]; 
    metaDataArray[modelWidgetId]={}; console.warn($scope.view.wdg);    
    let wdg= $scope.view.wdg[modelWidgetId];    
    let mdlsrc=$scope.getWidgetProp(modelWidgetId,'src');     console.log( "mdlsrc="+mdlSrc); 
    //==== extracts the model file name with extension
    let mdlNameExt= mdlSrc.replace(/^.*[\\\/]/, '');  console.log( "mdlNameExt="+mdlNameExt); 
    //==== extracts the model file name without extension
    var mdlName=mdlNameExt.replace(/\.[^/.]+$/, ""); console.log( "mdlName="+mdlName); 
   // ---adds the modelname to the array element for the widget
    // $https call of the JSON file form the UPLOAD folder
   $http.get('app/resources/Uploaded/' + metaDataArray[modelWidgetId]['mdlName']+'.metadata.json')
          .success(function(data, status, headers, config) {//-------- success fnc
 			angular.forEach(data , function(value ,key){ //-------- ForEach json loop
					});//--------end of ForEach json loop   
                        // print the data to the console
  		console.log("metaDataArray[]"); console.warn(metaDataArray);
														})//-------- end success fnic
  .error(function(data, status, headers, config) {console.log("problem in the http will create a new ");});    





 The code listed above will load the json file to the modelWidget/s (it will works also if we have many model widngets there - and each model widget ponts to model which is imported with metadata)

When we  test this code we can check the object in memory (console.warn() ) :



Now we can access the metadata using the normal JSON functionality - so we can   access the component data via the Comp Path Id / id paths - corresponds to the modelItem widget property occurrence:





$ function() {
 // visiting array with string index
    console.log(" model Widget = "+ key);
    let compList = metaDataArray[key]['CompIdList'];
    //select randomly from the coponent list 
    let randomNum= parseInt(Math.random()*metaDataArray[key]['CompIdList'].length)
    let randomComp= metaDataArray[key]['CompIdList'][randomNum];
    console.log("random component selected = " +randomComp);
   // let DispName = metadata.get(randomComp, 'Display Name')
    /*** you can use here one of the following   fields
    "Child Count","Component Name","Display Name","Model Extents (mm)","OL File Name","Part Depth""Part ID","Part ID Path","Part Name","Part Path"
    let DispName = metaDataArray[key]['data'][randomComp]['__PV_SystemProperties']['Display Name']
    console.log("DispName= "+ DispName)
    let model_extend_mm = metaDataArray[key]['data'][randomComp]['__PV_SystemProperties']['Model Extents (mm)']
     console.log("model_extend_mm= "+ model_extend_mm)
    let creaDate = metaDataArray[key]['data'][randomComp]['PROE Parameters']['CREATION_DATE']
     console.log("creaDate= "+ creaDate)
       console.log("model_extend_mm= "+ model_extend_mm)
    let designState = metaDataArray[key]['data'][randomComp]['PROE Parameters']['DESIGN_STATE']
     console.log(" designState= "+  designState)
     // make a selection string for this component
    let mdl_selection= key+"-"+randomComp;
    console.log ("mdl_selection="+mdl_selection)
    // generate some random rgbá color
    let r =parseInt(Math.random()*255);
    let g =parseInt(Math.random()*255);
    let b =parseInt(Math.random()*255);
    let a =parseInt(Math.random()*0.8)+0.2;
    //apply blink funciton for this component selection with random color





When we test the listed code above we will have in the chrome console window:


Using furhter the json  functionality we can also implement e.g. some user picks where you can associate it with the metadata :





angular.forEach($element.find('twx-dt-model'), function(value, key) {
   // find all model widget feature and perform funciton() 
   // for each model widget - key == modelWidgetId
//define the userpick function for each modelWidgetId 
angular.element(value).scope().$on('userpick',function(event,target,parent,edata) {
if (edata) {
    if ($scope.currentSelection) { // selection is not null make it undefined
       tml3dRenderer.setColor($scope.currentSelection, undefined);    }
    //create the selection string  <modelWidgetId>-<occurance Path Id>
    $scope.currentSelection = target + '-' + JSON.parse(edata).occurrence;
    //generate a random rgba color
        let r =parseInt(Math.random()*255);
    	let g =parseInt(Math.random()*255);
    	let b =parseInt(Math.random()*255);
    	let a =parseInt(Math.random()*0.8)+0.2;
    	//call blinkSelection function for the selected component
    //write the data SystemProperty of this compoonent in to a textArea Widget
     $scope.setWidgetProp('textArea-1','text',JSON.stringify( metaDataArray[target]['data'][JSON.parse(edata).occurrence]['__PV_SystemProperties']));
      }    })






This code will write the meta data to the selected component form the __PV_SystemPoperties section to a text area widget. Also the component will blink fwith random rgba color:


I attached an small example ( which should demonstrated the described techniques


  • Vuforia Studio metadata API

The first step is to call the metadata for a model widget. For this we can use the following construct e.g. WidgetId is ‘model-1’ :




(metadata) => {  
// <HERE  CALL YOUR CODE with metadata > 




So for example we can use get the “Display Name of the component with the path Id =’/0/6’:




PTC.Metadata.fromId('model-1').then( (metadata) => {  
Let disp_name= metadata.get('/0/6', 'Display Name');
console.log(“Display Name=”+disp_name);




The most of the  methods which could be applied to the metadata object and also some examples are described in the PTC Help (Incorporate CAD Metadata Into an Experience)

This functionality is available first with Vuforia Studio 8.5.13. To this article is also attached a PDF copy of the metioned link above.

There we can use 2 different approaches:

  1. To get a selected object or list of objects:
    let metaDATA=PTC.Metadata.fromId('model-1')
     metaDATA.then(function(meta) { console.log("success func of metaData");
     //a test with a fix model widget id  
        var designer = meta.get('/11','DESIGNER','PROE Parameters');
        console.log("Designer= " + designer);
        var ptc_mat = meta.get('/11','PTC_MATERIAL_NAME','PROE Parameters');
        console.log("PTC_MATERIAL_NAME= " + ptc_mat);
        var dispName = meta.get('/11','Display Name','__PV_SystemProperties');
        console.log("dispName= " + dispName);
        var DV_System_Categ= meta.get('/11'). getCategory ('__PV_SystemProperties')
        let myQuery=meta.find('Display Name').like('PRT').find('Part Depth').in(0,3);
        console.log("myQuery Name:"+myQuery._friendlyName);
         console.log("myQuery selected Paths :"+JSON.stringify(myQuery._selectedPaths));                          
        })  .catch(function(err) 
             {console.log("problem with the read of metadata ");console.warn(err);});  
  2. To call for the selected object a callback function where the Path id was passed as function argument:




$ function(){
let pathDepth=4
 PTC.Metadata.fromId( $
               {meta.find('Part Depth').lessThan(3).find('Display Name')

//========================== app.testCustomSelection
$ function(modelId,whereFunc,selectFunc,pathDepth) {
   var metaDATA= PTC.Metadata.fromId(modelId)
         .then(function(meta) {meta.findCustom(whereFunc,selectFunc);})
$ = function(idpath) {
      // scope var `this` is the metadata instance
      const depth = this.get(idpath, 'Part Depth')
      const name  = this.get(idpath, 'Display Name')
      return parseFloat(depth) > $ || 
                 (name &&'PRT') >= 0)
$ = function(idpath) {
   const name  = this.get(idpath, 'Display Name')
  console.log("app.selectFunc["+$"] idpath=" 
              +idpath+ " >>>Name: "+name);
  return this.get(idpath, 'Display Name');}





In the example above the query is done by the  where function and for all selected models the callback function select Func  is called where the pathId was passed to the function



I attached an small example ( which should demonstrated the described techniques


Hi @RolandRaytchev ,


I am using the methadata already and it works great. Now I want to add a functionality: When user clicks on a part, a text widget shall jump to the position of the part and display a custom text. Unfortunately my code isn't working, the text label jumps to a wrong position. Can you help here?


$timeout(function() {
angular.forEach($element.find('twx-dt-model'), function(value, key) {
angular.element(value).scope().$on('userpick',function(event,target,parent,edata) {
var data = $scope.modelData[target];

if (currenttool=="teilenummer"){
if (data != undefined) {
pathid = JSON.parse(edata).occurrence;
$scope.currentSelection = target + "-" + pathid;
var selection= target + "-" + pathid;
console.log("selection: ");
var loc=tml3dRenderer.GetObject(selection).GetWidget().GetLocation(); // this is the crucial part
console.log("loc: ");
var xloc= loc.position.x;
var rxloc= loc.orientation.x;
var yloc= loc.position.y;
var ryloc= loc.orientation.y;
var zloc= loc.position.z;
var rzloc= loc.orientation.z;
$scope.view.wdg['PosLabel'].x = xloc;
$scope.view.wdg['PosLabel'].rx = rxloc;
$scope.view.wdg['PosLabel'].y = yloc;
$scope.view.wdg['PosLabel'].ry = ryloc;
$scope.view.wdg['PosLabel'].z = zloc;
$scope.view.wdg['PosLabel'].rz = rzloc;


I realized, that the call for position might be insufficient, since it calls the position of the part only within the assembly group.

So, I added an iterator, that sums up all positions from all assembly groups.

Unfortunately, this still doesn't do the job.


var selection = target+'-'+path
var path_array = path.split('/')
var pathiterator;
var positerator= tml3dRenderer.GetObject("model-1").GetWidget().GetLocation();
var depth = parseInt(path_array.length)

 for (var i=1; i<depth; i++)
prev_path= prev_path +'/' + path_array[i];
var newPosition = tml3dRenderer.GetObject(pathiterator).GetWidget().GetLocation();
positerator.position.x += newPosition.position.x;
positerator.position.y += newPosition.position.y;
positerator.position.z += newPosition.position.z;
console.log("added up posx: "+positerator.position.x);



THANK YOU SO MUCH for this. I actually found a nasty bug in PTC.Metadata.find(id) that broke a very important experience I have been crafting. By showing the example of how to manually open a metadata.json file you made my day!

You are a credit to PTC!

Thank you buddy!

Hello Hector,

many thanks for your feedback.  I am happy to know that this was helpful for you.

I want to mention 2 points to your issue/related issues:

1.)  the problem with the  bug in PTC.Metadata.find(id) . Could you provide on extra community post or in a TSupport  case (if is reproducible only with specific data)  more details about this issue - so I could report it to PTC R&D and we can expect a fix soon.

2.) Regarding to the Jira Ticket VTS-683 -"Can we now derive metadata from PVZ file outside of the UI?"  The current status here is : the request for this feature was submitted a while back. we are hoping we can persuade PM team to prioritize it soon. 

Best Regards!

Version history
Last update:
‎Oct 19, 2020 02:58 AM
Updated by:
Labels (2)