3  Model Derivative

A DWG file sitting in a bucket is just bytes. The Model Derivative API is what turns those bytes into something useful, OBJ and STL meshes you can analyse in R, SVF viewables you can embed in a browser, or structured metadata you can query with dplyr. This chapter covers the full translation workflow.

The pattern is always the same: submit a job, poll until it finishes, then pull the output.

flowchart LR
  A[Upload file\nuploadFile] --> B[Translate\ntranslateObj / translateSvf]
  B --> C{checkFile}
  C -- pending / inprogress --> C
  C -- success --> D[Get output URN\ngetOutputUrn]
  D --> E[Download\ndownloadFile]

3.1 Translate to OBJ

First get a token with data:read and data:write scopes. Note that only certain source formats can produce OBJ output (DWG, IPT, IAM, IPN, F3D, FBX. See the APS supported formats list for the full matrix).

resp <- getToken(
  id     = Sys.getenv("client_id"),
  secret = Sys.getenv("client_secret"),
  scope  = "data:read data:write"
)
myToken <- resp$content$access_token

APS requires the file’s URN to be Base-64 encoded before it’ll accept it — jsonlite::base64_enc() handles that:

myEncodedUrn <- jsonlite::base64_enc(myUrn)
myEncodedUrn
#> [1] "dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6bXlidWNrZXQvYWVyaWFsLmR3Zw=="

Kick off the translation:

resp <- translateObj(urn = myEncodedUrn, token = myToken)
resp$content
#> $result
#> [1] "created"
#>
#> $urn
#> [1] "dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6bXlidWNrZXQvYWVyaWFsLmR3Zw=="
#>
#> $acceptedJobs$output$formats[[1]]$type
#> [1] "obj"
Warning

OBJ translation only accepts certain source formats. Submitting an unsupported format returns a 415 Unsupported Media Type:

resp$status_code
#> [1] 415
resp$content$diagnostic
#> [1] "File type not supported for OBJ translation."

Poll checkFile() until status hits "success". The job can take anywhere from a few seconds to a few minutes depending on file complexity:

resp <- checkFile(urn = myEncodedUrn, token = myToken)
resp$content
#> $status
#> [1] "success"
#>
#> $progress
#> [1] "complete"
#>
#> $region
#> [1] "US"
Note

Translation status moves through "pending""inprogress""success". Don’t try to download before you see "success" — there’s nothing there yet. Use the aps_retry() helper from the Authentication chapter to handle rate limiting while you wait.

Retrieve the output URN, then download:

resp <- getOutputUrn(urn = myUrn, token = Sys.getenv("token"))
myOutputUrn        <- resp$content$derivatives[[1]]$children[[1]]$urn
myEncodedOutputUrn <- jsonlite::base64_enc(myOutputUrn)

resp <- downloadFile(
  urn        = myEncodedUrn,
  output_urn = myEncodedOutputUrn,
  token      = myToken,
  destfile   = "aerial.obj"
)
resp$status_code
#> [1] 200

3.2 Translate to STL

Same pattern as OBJ, different function:

resp <- translateStl(urn = myEncodedUrn, token = myToken)
resp$content$acceptedJobs$output$formats[[1]]$type
#> [1] "stl"

# Poll until done
resp <- checkFile(urn = myEncodedUrn, token = myToken)
resp$content$status
#> [1] "success"

3.3 Extract Metadata from a File

SVF format unlocks the structured metadata inside a model. The object tree, property sets, layer information. This is the format the Viewer also uses.

resp <- translateSvf(urn = myEncodedUrn, token = myToken)
resp$content$result
#> [1] "created"

# Wait for it...
resp <- checkFile(urn = myEncodedUrn, token = myToken)
resp$content$status
#> [1] "success"

getMetadata() returns the viewable GUIDs. You’ll need the GUID for every subsequent metadata call:

resp   <- getMetadata(urn = myEncodedUrn, token = myToken)
myGuid <- resp$content$data$metadata[[1]]$guid
resp$content$data
#> $type
#> [1] "metadata"
#>
#> $metadata[[1]]$name
#> [1] "aerial"
#>
#> $metadata[[1]]$guid
#> [1] "a7b3c9d2-4e1f-4a8b-b6c2-9d3e7f5a1b4c"
#>
#> $metadata[[1]]$isMasterView
#> [1] TRUE

getObjectTree() returns the model’s full object hierarchy. For a DWG, the children of the root node are its layers:

resp <- getObjectTree(guid = myGuid, urn = myEncodedUrn, token = myToken)
resp$content$data$objects[[1]]
#> $objectid
#> [1] 1
#>
#> $name
#> [1] "aerial.dwg"
#>
#> $objects[[1]]$name
#> [1] "Layer: A-SITE"
#>
#> $objects[[2]]$name
#> [1] "Layer: A-BLDG"
#>
#> $objects[[3]]$name
#> [1] "Layer: A-ROAD"

getData() pulls all geometry properties and material data, bounding boxes, layer assignments, custom attributes. This is the raw material for the Layer Structure Analysis chapter:

resp <- getData(guid = myGuid, urn = myEncodedUrn, token = myToken)
resp$content$data$collection[[1]]
#> $objectid
#> [1] 2
#>
#> $name
#> [1] "Layer: A-SITE"
#>
#> $properties$`Layer and Material`$Layer
#> [1] "A-SITE"
#>
#> $properties$Geometry$`Bounding Box Min X`
#> [1] -142.83
#>
#> $properties$Geometry$`Bounding Box Max X`
#> [1]  318.62
Warning

getData() can be large. Complex models return thousands of objects in a single response. For very large DWGs, consider filtering the object tree first to identify only the layers or elements you care about.