【AUTODESK Platform Services】APSを使って.rvtファイルをWebブラウザ上で表示してみる

### 本記事の内容

AUTODESK Platform Services(APS)の提供しているAPIを利用して、Revitファイル(.rvt)をWebブラウザ上で表示するまでのプロセスについて記載しています。

https://aps.autodesk.com/developer/documentation

AUTODESK Platform Services(APS)とは？については以下の記事をご覧ください

https://qiita.com/maixhashi/items/47da7ed7cb7c4e37b9d4

### 前提について
前提について記します。
 - 前提①
   - AUTODESK Platform Servicesのアプリを作成済で、下記を取得済
  (取得方法については[こちら](https://get-started.aps.autodesk.com/))
     - Client ID
     - Client Secret 
 - 前提②
   - Revitファイルを取得済である
     - 本記事では取得済のRevitファイルを`RoofSampleFile.rvt`とします。
   （[こちら](https://help.autodesk.com/view/RVT/2023/JPN/?guid=GUID-785EDA82-B8DD-4BF3-A508-E7FC4860E24D)からダウンロードしたものです）
 - 前提③
   - AUTODESK Construction Cloud（ACC）/ BIM360などは使わない。

AUTODESK Construction Cloud（ACC）/ BIM360などは使わないため、[Data Management API](https://aps.autodesk.com/en/docs/data/v2/developers_guide/basics/)のドキュメントにある下図のうちでは、OSSのみがアクセス対象になります。

![スクリーンショット 2025-06-03 14.50.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606327/c27e6e52-6020-4eae-960e-6b56e06d1919.png)


### やりたいこと

[こちら](https://qiita.com/maixhashi/items/47da7ed7cb7c4e37b9d4)で掲載したコードを再掲します。


```none:フォルダ構成
.
├── node_modules
├── package-lock.json
├── package.json
├── public
│   ├── index.html　　　　　// ビューワー
├── .env                  // 環境変数の設定
└── server.js　　　　　　　 // 環境変数のロード
```

```html:index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Forge Viewer - Minimal</title>
  <script src="https://developer.api.autodesk.com/modelderivative/v2/viewers/7.*/viewer3D.js"></script>
  <link rel="stylesheet" href="https://developer.api.autodesk.com/modelderivative/v2/viewers/7.*/style.min.css" />
  <style>
    html, body {
      margin: 0;
      overflow: hidden;
      height: 100%;
    }
    #forgeViewer {
      width: 100%;
      height: 100%;
      position: relative;
    }
    .viewer-label {
      position: absolute;
      top: 10px;
      left: 10px;
      background-color: rgba(0, 0, 0, 0.7);
      color: white;
      padding: 5px 10px;
      border-radius: 4px;
      font-family: Arial, sans-serif;
      z-index: 100;
    }
  </style>
</head>
<body>
  <div id="forgeViewer">
    <div class="viewer-label">Autodesk Platform Services Viewer</div>
  </div>

  <script>
    let forgeViewer;
    
    // サーバーから環境変数を取得
    fetch('/config')
      .then(response => response.json())
      .then(config => {
        const urn = config.FORGE_MODEL_URN;
        const options = {
          env: 'AutodeskProduction',
          accessToken: config.FORGE_ACCESS_TOKEN
        };

        Autodesk.Viewing.Initializer(options, () => {
          forgeViewer = new Autodesk.Viewing.GuiViewer3D(document.getElementById('forgeViewer'));
          forgeViewer.start();
          const documentId = 'urn:' + urn;
          Autodesk.Viewing.Document.load(documentId, (doc) => {
            const defaultModel = doc.getRoot().getDefaultGeometry();
            forgeViewer.loadDocumentNode(doc, defaultModel);
          }, (err) => console.error(err));
        });
      })
      .catch(error => console.error('設定の読み込みに失敗しました:', error));
  </script>
</body>
</html>
```


``` js:server.js
const express = require('express');
const path = require('path');
const dotenv = require('dotenv');

// Load environment variables
dotenv.config();

const app = express();
const PORT = process.env.PORT || 3000;

// Serve static files
app.use(express.static(path.join(__dirname, 'public')));

// Model Derivative APIの変換設定を調整
const createManifest = async (objectId) => {
  const job = {
    input: {
      urn: Buffer.from(objectId).toString('base64')
    },
    output: {
      formats: [
        {
          type: 'svf',
          views: ['2d', '3d'],
          advanced: {
            materialMode: 2, // マテリアル情報を保持するモード
            renderPrecision: 'detailed' // 詳細な描画精度
          }
        }
      ]
    }
  };
  
  // Model Derivative APIを呼び出し
  return await new Promise((resolve, reject) => {
    // APIコール実装
  });
};

// Endpoint to provide configuration to the client
app.get('/config', (req, res) => {
  res.json({
    FORGE_ACCESS_TOKEN: process.env.FORGE_ACCESS_TOKEN,
    FORGE_MODEL_URN: process.env.FORGE_MODEL_URN
  });
});

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
```

``` .env
FORGE_ACCESS_TOKEN=xxx # Autodesk Platform Services で取得したアクセス トークン（API 認証用）
FORGE_MODEL_URN=yyy    # Autodesk Platform Services で使用するモデルの URN（対象モデルの識別子）
```

`.env`内の`FORGE_MODEL_URN`はビューワーで描画したいファイルのモデルに対応するURNです。

やりたいことは次の通りです
 - **AUTODESK Platform Servicesの提供するAPIを利用して、URNを取得**
 - **ビューワーファイルindex.htmlでモデルをWebブラウザ上で描画する**

### AUTODESK Platform ServiceのAPIを使ってURNを取得しよう

流れをまとめた図です。
![autodesk platform services api.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606327/78c7db76-d422-4f61-97f4-fcfd4b6d1347.jpeg)

大まかな流れは、次のようになります。
 - Authentication(OAuth)
   - アクセストークンを取得（各エンドポイントで使用）
 - Data Management API
   - バケット（Bucket）の作成
   - 描画したいファイルに対応するオブジェクト（Object）をバケットに格納
 - Model Derivative API
   - オブジェクトとして格納されたデータを違うフォーマットに翻訳したデータのURNを取得


各エンドポイントごとに処理を細分すると
 - Authentication(OAuth)
   - **① アクセストークンを取得**　　　　　　　　　　　　　　　　　　[[公式ドキュメント](https://aps.autodesk.com/en/docs/oauth/v2/reference/http/gettoken-POST/)]
     - 各エンドポイントで使用するアクセストークンを取得します
     - エンドポイント： [POST] /token
 - Data Management API
   - **② バケット（Bucket）の作成** 　　　　　　　　　　　　　　　　[[公式ドキュメント](https://aps.autodesk.com/en/docs/data/v2/reference/http/buckets-POST/)]
     - オブジェクトを格納するバケットを作成します
     - エンドポイント： [POST] /buckets
   - **③ オブジェクト（Object）アップロード用のS3署名用URLの取得**　[[公式ドキュメント](https://aps.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-:objectKey-signeds3upload-POST/)]
     - アップロードするファイルと同じ名前のオブジェクトに対するS3署名付きURLを
     レスポンスから取得します
     - エンドポイント： [GET] /buckets/{{bucket_key}}/objects/{{object_key}}/signeds3upload
       - {{bucket_key}} は②で作成したバケットのbucket_id
       - {{object_key}} は④でアップロードするファイル名（拡張子含む）
   - **④ファイルをS3S3署名付きURLにアップロード**　　　　　　　　　[[公式ドキュメント](https://aps.autodesk.com/en/docs/data/v2/tutorials/upload-file/#step-5-upload-a-file-to-the-signed-url)] 
     - ③のレスポンスから取得したS3署名付きURLにファイルをアップロードします
     - エンドポイント： [PUT] {{signed_url}}
       - {{signed_url}} は③のレスポンスから取得したS3署名付きURL
   - **⑤ S3署名付きURLへのアップロードを完了させる**　　　　　　　　[[公式ドキュメント](https://aps.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-:objectKey-signeds3upload-POST/)]
     - S3署名付きURLへのアップロードを完了したオブジェクトのobject_idをbase64エンコードしたものをURNとして取得します
     - エンドポイント： [POST] /buckets/{{bucket_key}}/objects/{{object_key}}/signeds3upload
       - {{bucket_key}} は②で作成したバケットのbucket_id
       - {{object_key}} は④でアップロードするファイル名（拡張子含む）
 - Model Derivative API
   - **⑥ファイル変換ジョブを送信**　　　　　　　　　　　　　　　　　[[公式ドキュメント](https://aps.autodesk.com/en/docs/model-derivative/v2/reference/http/jobs/job-POST/)]
     - 違うファイル形式への変換ジョブを送信します 
     - エンドポイント： [POST] /jobs
   - **⑦ファイル変換結果を取得**　　　　　　　　　　　　　　　　　　[[公式ドキュメント](https://aps.autodesk.com/en/docs/model-derivative/v2/reference/http/manifest/urn-manifest-GET/)]
     - 違うファイル形式への変換ジョブを完了したUR Nを取得します 
     - エンドポイント： [POST] {{base64_encoded_urn}}/jobs
       - {{base64_encoded_urn}}は⑤でbase64エンコードしたURNです

以上のプロセスを経て⑦のレスポンスから取得したURNを
先述のコードの`.env` に環境変数として設定すると、
無事ビューワーに描画することができました😭

今回は　.rvt →　.svf に変換することに成功したことになります。

### 終わりに
はじめに参照した資料がすでに廃止されたAPI v1のもので悲しみに暮れました。
AUTODESK Platform Servicesではすでにv2となっておりドキュメントを1から参照し格闘しました🥊
英語ドキュメントにはDeepLが必須ですな🧐