# 编辑组件应用

在本教程中,你可以通过BIMFACE提供的Scene Module接口调用GIS场景编辑组件。 提示:本教程需要先创建场景并完成发布,场景的创建方法可参考场景创建 (opens new window)

# 教程

加载场景编辑组件首先需要引用BIMFACE的JavaScript显示组件库。在使用BIMFACE JSSDK之前,你需要新建一个HTML文件,并在浏览器中打开。

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>My first scene-editor in BIMFACE</title>
</head>

<body>
  <script src="https://static.bimface.com/api/BimfaceSDKLoader/BimfaceSDKLoader@latest-release.js" charset="utf-8"></script>
  <script>
    // 在这里输入BIMFACE JavaScript SDK提供的方法
  </script>
</body>

</html>

# 新建DOM元素

BIMFACE使用了HTML5的div元素作为容器,你可以方便的在Web上进行场景的二次开发。这里,我们在HTML中新建一个div元素,将其ID设定为“domId”,并设置其宽度和高度。

这里,你可以将div元素的ID修改为其他值,但需要注意在之后根据ID获取div元素的时候也相应的做出修改。

<div id="domId" style="width:800px; height:600px"></div>

# 构造Loader并指定待加载的场景

BimfaceSDKLoader是BIMFACE提供的SDK加载器(loader)对象,loader会根据所需加载的资源来加载对应的js组件。你需要调用loader的加载方法,并利用View Token为其指定所要加载的场景资源。

为了在网页中显示指定的场景,需要其View Token作为标识。需要注意的是,View Token只是一个临时的访问凭证,有效期为12小时。当超过12小时后,你需要重新进行获取View Token (opens new window)的操作。

调用加载方法前,你需要先构造BimfaceSDKLoaderConfig作为loader的配置项,并设置相关的参数。这里我们先只指定View Token,你也可以在配置项中设置SDK版本号、SDK语言版本等(目前支持了中文、英文、瑞典语、繁体中文四种),具体内容相见相关接口文档 (opens new window)

// 声明viewToken变量,填入想要显示的场景token
let viewToken = '<yourViewToken>';

// 构造BimfaceSDKLoaderConfig对象
let loaderConfig = new BimfaceSDKLoaderConfig();
// 设置BimfaceSDKLoaderConfig的viewToken
loaderConfig.viewToken = viewToken;

// 调用BimfaceSDKLoader的load方法加载模型
BimfaceSDKLoader.load(loaderConfig, successCallback, failureCallback);

# 设置加载资源后的回调函数

相信你已经注意到,我们在调用BimfaceSDKLoader.load方法时,分别传入了successCallback、failureCallback两个未定义的参数,它们分别是SDKLoader加载指定资源成功及失败的回调函数。这里,我们开始对其进行定义。

我们先定义加载成功的回调函数。对于场景编辑组件的调用,可以分为以下几个步骤

  • 获取html中添加的div元素
  • 构造EditorConfig对象,并与指定的div元素关联,定义对应参数
  • 初始化Editor对象
// 加载成功回调函数
function successCallback(viewMetaData) {
  // 获取DOM元素
  let domShow = document.getElementById('domId');
  // 创建EditorConfig
  let editorConfig = new Glodon.Bimface.Module.Scene.EditorConfig();
  // 设置EditorConfig的dom对象
  editorConfig.domElement = domShow;
  // 设置待加载编辑的场景viewToken
  editorConfig.viewToken = viewToken;
  // 设置组件中获取待添加资源列表的方法,方法需返回一个Promise对象,具体定义见下一小节
  editorConfig.getResourceListHandler = getResourceListHandler;
  // 设置组件中添加资源时获取对应viewToken的方法,方法需返回一个Promise对象,具体定义见下一小节
  editorConfig.getViewTokenHandler = getViewTokenHandler;
  // 创建Editor
  editor = new Glodon.Bimface.Module.Scene.Editor(editorConfig);
};

最后,我们定义加载失败的回调函数。

// 加载失败回调函数
function failureCallback(error) {
  console.log(error);
};

# 场景编辑组件初始化

在BimfaceSDKLoader.load的加载成功回调函数中,对场景编辑组件进行了初始化定义,该小节我们将对场景编辑组件的初始化参数进行详细说明。

首先,在构造了EditorConfig后,需要设置待编辑的场景viewToken、组件放置容器DIV,并定义getResourceListHandler、getViewTokenHandler两个回调函数。

其中getResourceListHandler为场景编辑中添加资源时获取待添加资源列表的方法;getViewTokenHandler为添加资源时获取待添加资源viewToken的方法。

# getResourceListHandler

基于编辑组件在场景中增加资源时,将通过该方法获取可添加至场景中的资源列表。该方法需带有Object类型的参数,如图所示,在资源添加过程中,切换添加资源类型、选中列表中对应文件夹、进行文件搜索等操作时会传递对应的参数至该方法中。

添加资源列表

基于传入参数,可调用获取转换资源列表 (opens new window)批量查询集成状态 (opens new window)的后端接口获取符合条件的可添加资源。需要注意的是,GIS场景中仅支持添加与场景处于同一项目且加载模式为流式加载的已转换/集成成功的资源,即在后端接口body中需限制projectId、outputFormat、status。

# 传入参数
name type description
config Object 传入参数
config.type String 添加资源类型,'file':单文件模型,'integrate':集成模型
config.parentId String 添加资源-点选对应文件夹时,返回该文件夹Id
config.searchKeyword String 添加资源-搜索对应资源时,传入对应搜索关键词
# 返回参数

该方法需返回一个Promise对象,在rosolve中返回符合条件的资源列表。

type description
Promise<Array> 返回Promise对象,resolve中的第一个参数返回需要展示的资源列表数组

返回列表及子项的格式为:

[
  {
    // 文件ID
    id: '10000007070001',
    // 文件名称
    name: 'Bimface示例模型-1.rvt',
    // 是否为文件夹
    isFolder: false
  },
  {
    id: '10000007070002',
    name: '文件夹-1',
    isFolder: true,  
  }
]
# 代码示例
async function getResourceListHandler (config){
  let url;
  let body;
  let headers = new Headers();
  // 基于accessToken发起后端接口调用
  headers = {
    "Authorization":"Bearer cn-e9725999-0b36-4c0e-bdca-38ea88888888",
    "Content-Type":"application/json"
  };
  if (config.type == 'file'){
    // 需传入场景所在的项目ID,获取转换资源列表。后端接口文档参考:https://bimface.com/docs/model-derivative/v1/api-reference/getResourceListUsingPOST.html
    url = 'https://api.bimface.com/v1/10000000006016/translations';
    // 后端接口的请求body需限制资源为流式加载模式且状态为转换成功
    body = {
    // 限制资源为流式加载模式
    "outputFormat" : "bimtiles",
    // 限制状态为转换成功或集成成功
    "status":99,
    // 搜索模式时,基于名称进行模糊匹配或基于ID进行精准匹配
    "keyword": config.searchKeyword,
    // 获取指定文件夹下的资源
    "parentId": config.parentId,
    // 搜索模式时,递归整个项目下的内容
    "recursive":true
    }
  }else{
    // 获取集成资源,后端接口文档参考:https://bimface.com/docs/model-derivative/v1/api-reference/getIntegratesUsingPOST.html
    url = 'https://api.bimface.com/integrateDetails';
    // 后端接口的请求body需限制资源为流式加载模式且状态为集成成功,集成模型无文件夹层级,故无parentId参数
    body = {
      // 需传入场景所在的项目ID
      "projectId" : "10000000006016",
      // 限制资源为流式加载模式
      "outputFormat" : "bimtiles",
      // 限制状态为转换成功或集成成功
      "status":99,
      // 搜索模式时,基于名称进行模糊匹配或基于ID进行精准匹配
      "keyword": config.searchKeyword
    }
  }
  // 获取接口返回值
  let res = await fetch(url, {method: 'POST', headers:headers, body: JSON.stringify(body)});
  let resJson = await res.json();
  let result = [];
  resJson.data && resJson.data.list.forEach((item) => {
    // 记录文件ID、文件名称、是否为文件夹
    result.push({
      id: item.fileId || item.integrateId,
      name: item.name,
      isFolder: item.isFolder
    });
  });
  return result;
}

# getViewTokenHandler

勾选完待添加的资源后,点击添加文件的按钮时将通过该方法获取待添加资源的viewToken。该方法需带有Array类型的参数,如图所示,在点击“添加单文件模型”或“添加集成模型”按钮时会传递对应的多个模型信息至该方法中。

基于传入参数,可调用获取模型的View Token (opens new window)后端接口获取对应的View Token。

资源viewToken列表

# 传入参数
name type description
list Array 传入参数,由需要获取viewToken的模型信息组成的数组

列表及子项的格式为:

[
  {
    // 文件ID
    id: '10000007070001',
    // 文件名称
    name: 'Bimface示例模型-1.rvt',
    // 是否为文件夹
    isFolder: false,
    // 文件类型,file对应单文件模型,integrate对应集成模型
    type: 'file'
  },
  {
    id: '10000007070002',
    name: 'Bimface示例模型-2.rvt',
    isFolder: false,
    type: 'file'
  }
]
# 返回参数

该方法需返回一个Promise对象,在resolve中返回获取到的多个viewToken信息。

type description
Promise<Array> 返回Promise对象,resolve中的第一个参数返回待添加的资源数组

返回列表及子项的格式为:

  [
    {
      // 文件ID
      id: '10000007070001',
      // 文件名称
      name: 'Bimface示例模型-1.rvt',
      // 文件的View Token
      viewToken: 'c6eec7b8b4724da2926959b17c351622',
    },
    {
      id: '10000007070002',
      name: 'Bimface示例模型-2.rvt',
      viewToken: 'c6eec7b8b4724da2926959b17c351633',
    },
  ]
# 代码示例
// 获取ViewToken的回调
function getViewTokenHandler (list){
  let result = [];
  return new Promise((resolve) => {
    Promise.all(list.map((item) => getViewToken(item.id, item.name, item.isFolder, item.type))).then((data) => {
      data.forEach((item) => result.push(...item));
      resolve(result);
    })
  });
} 

// 获取单个资源的ViewToken
async function getViewToken (id, name, isFolder, type) {
  // 判断传入对象是否为文件夹。若为文件夹,则需先获取文件夹内的资源列表,再获取各资源的viewToken
  if(isFolder){
    // 获取文件夹内的资源列表
    let folderFileList = await getResourceListHandler({type: 'file', parentId: id});
    let list = [];
    // getResourceListHandler函数的返回值不包含type字段,需补全该字段。由于只有单文件模型涉及文件夹,所以type固定为‘file’
    folderFileList.forEach((item)=>{
      let newItem = {
        type: 'file',
        ...item
      };
      list.push(newItem);
    });
    let viewTokenList = await getViewTokenHandler(list); 
    return viewTokenList;
  }
  let headers = new Headers();
  // 基于accessToken发起后端接口调用
  headers = {
    "Authorization":"Bearer cn-e9725999-0b36-4c0e-bdca-38ea88888888",
    "Content-Type":"application/json"
  };
  // 基于后端接口获取模型的viewToken,后端接口文档参考:https://bimface.com/docs/authentication/v1/api-reference/getViewTokenByFileIdUsingGET.html
  let url = `https://api.bimface.com/view/token?${type}Id=${id}`;
  // 获取后端接口返回值
  let res = await fetch(url, {method: 'GET',headers:headers});
  let resJson = await res.json();
  let result = [];
  result.push({
    id: id,
    name: name,
    viewToken: resJson.data
  });
  return result;
}

# 场景编辑组件发布方式

通过编辑组件完成场景的编辑后,需要发布更改内容进行场景信息的更新保存。因此需要在页面中添加一个按钮,用来控制场景的发布。在class为buttons的div下输入如下内容:

<button class='button' onclick="publish()">发布场景</button>

我们可以通过css代码对按钮的样式进行定义:

.button {
  background-color: #eee;
  color: #1890ff;
  border: 1px solid #1890ff;
  padding: 10px;
}

接下来就需要在“发布场景”按钮的点击事件中实现场景发布流程。

场景编辑组件中提供了获取编辑后的场景信息数据的接口:getPublishData(),该接口能够返回发布场景的相关数据信息,包括场景的配置信息、图层资源数据等。基于返回的数据可以调用后端接口更新场景 (opens new window)进行场景发布。

function publish(){
  let body = {
    // 基于编辑组件返回的场景数据进行场景发布更新
    ...editor.getPublishData()
  };
  // 基于后端接口进行场景更新,需将ID替换为当前进行编辑的场景ID
  let url = 'https://api.bimface.com/scene/2017707858581668',
  let headers = new Headers();
  // 基于accessToken发起后端接口调用
  headers = {
    "Authorization":"Bearer cn-e9725999-0b36-4c0e-bdca-38ea88888888",
    "Content-Type":"application/json"
  };
  fetch(url, {method: 'PATCH',headers:headers,body:JSON.stringify(body)}).then(function(response) {
    if(response.ok){
      alert('发布成功');
    }
  });
}

# 完整代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>SceneEditor</title>
  <style type="text/css">
    * {
        margin: 0;
        padding: 0
    }
    .main {
        height: 90vh;
        width: 90%;
        margin: 1%;
        padding: 10px;
        border: 1px solid #eee;
    }

    .model {
        width: 100%;
        height: calc(100% - 50px);
    }

    .footer {
      padding: 10px;
      display: flex;
      flex-direction: row;
      justify-content: flex-end;
    }

    .button {
      background-color: #eee;
      color: #1890ff;
      border: 1px solid #1890ff;
      padding: 10px;
    }
  </style>
  <!-- 引用BIMFACE的JavaScript显示组件库 -->
  <script src="https://static.bimface.com/api/BimfaceSDKLoader/BimfaceSDKLoader@latest-release.js" charset="utf-8"></script>
</head>

<body>
  <!-- 定义DOM元素,用于在该DOM元素中显示场景 -->
  <div class='main'>
    <div class='model' id="domId"></div>
    <div class='footer'>
      <button class='button' onclick="publish()">发布场景</button>
    </div>
  </div>
  <script type="text/javascript">
    let viewer;
    let editor;

    // 声明viewToken变量,填入想要显示的场景token
    let viewToken = '<yourViewToken>';

    // 构造BimfaceSDKLoaderConfig对象
    let loaderConfig = new BimfaceSDKLoaderConfig();
    // 设置BimfaceSDKLoaderConfig的viewToken
    loaderConfig.viewToken = viewToken;

    // 调用BimfaceSDKLoader的load方法加载模型
    BimfaceSDKLoader.load(loaderConfig, successCallback, failureCallback);

    // 资源加载成功的回调函数
    function successCallback(viewMetaData) {
      let editorConfig = new Glodon.Bimface.Module.Scene.EditorConfig();
      editorConfig.domElement = document.getElementById("domId");
      editorConfig.viewToken = viewToken;
      editorConfig.getResourceListHandler = getResourceListHandler;
      editorConfig.getViewTokenHandler = getViewTokenHandler;
      editor = new Glodon.Bimface.Module.Scene.Editor(editorConfig);
    }

    // 资源加载失败的回调函数
    function failureCallback(error) {
      console.log(error);
    }

    // 获取待添加资源列表的回调
    async function getResourceListHandler (config){
      let url;
      let headers = new Headers();
      // 基于accessToken发起后端接口调用
      headers = {
        "Authorization":"Bearer cn-e9725999-0b36-4c0e-bdca-38ea88888888",
        "Content-Type":"application/json"
      };
      if (config.type == 'file'){
        // 需传入场景所在的项目ID,获取转换资源列表。后端接口文档参考:https://bimface.com/docs/model-derivative/v1/api-reference/getResourceListUsingPOST.html
        url = 'https://api.bimface.com/v1/10000000006016/translations';
        // 后端接口的请求body需限制资源为流式加载模式且状态为转换成功
        body = {
        // 限制资源为流式加载模式
        "outputFormat" : "bimtiles",
        // 限制状态为转换成功或集成成功
        "status":99,
        // 搜索模式时,基于名称进行模糊匹配或基于ID进行精准匹配
        "keyword": config.searchKeyword,
        // 获取指定文件夹下的资源
        "parentId": config.parentId,
        // 搜索模式时,递归整个项目下的内容
        "recursive":true
        }
      }else{
        // 获取集成资源,后端接口文档参考:https://bimface.com/docs/model-derivative/v1/api-reference/getIntegratesUsingPOST.html
        url = 'https://api.bimface.com/integrateDetails';
        // 后端接口的请求body需限制资源为流式加载模式且状态为集成成功,集成模型无文件夹层级,故无parentId参数
        body = {
          // 需传入场景所在的项目ID
          "projectId" : "10000000006016",
          // 限制资源为流式加载模式
          "outputFormat" : "bimtiles",
          // 限制状态为转换成功或集成成功
          "status":99,
          // 搜索模式时,基于名称进行模糊匹配或基于ID进行精准匹配
          "keyword": config.searchKeyword
        }
      }
      // 获取接口返回值
      let res = await fetch(url, {method: 'POST', headers:headers, body: JSON.stringify(body)});
      let resJson = await res.json();
      let result = [];
      resJson.data && resJson.data.list.forEach((item) => {
        // 记录文件ID、文件名称、是否为文件夹
        result.push({
          id: item.fileId || item.integrateId,
          name: item.name,
          isFolder: item.isFolder
        });
      });
      return result;
    }

    // 获取ViewToken的回调
    function getViewTokenHandler (list){
      let result = [];
      return new Promise((resolve) => {
        Promise.all(list.map((item) => getViewToken(item.id, item.name, item.isFolder, item.type))).then((data) => {
          data.forEach((item) => result.push(...item));
          resolve(result);
        })
      });
    } 

    // 获取单个资源的View Token
    async function getViewToken (id, name, isFolder, type) {
      // 判断传入对象是否为文件夹。若为文件夹,则需先获取文件夹内的资源列表,再获取各资源的viewToken
      if(isFolder){
        // 获取文件夹内的资源列表
        let folderFileList = await getResourceListHandler({type: 'file', parentId: id});
        let list = [];
        // getResourceListHandler函数的返回值不包含type字段,需补全该字段。由于只有单文件模型涉及文件夹,所以type固定为‘file’
        folderFileList.forEach((item)=>{
          let newItem = {
            type: 'file',
            ...item
          };
          list.push(newItem);
        });
        let viewTokenList = await getViewTokenHandler(list); 
        return viewTokenList;
      }
      let headers = new Headers();
      // 基于accessToken发起后端接口调用
      headers = {
        "Authorization":"Bearer cn-e9725999-0b36-4c0e-bdca-38ea88888888",
        "Content-Type":"application/json"
      };
      // 基于后端接口获取模型的viewToken
      let url = `https://api.bimface.com/view/token?${type}Id=${id}`;
      // 获取后端接口返回值
      let res = await fetch(url, {method: 'GET',headers:headers});
      let resJson = await res.json();
      let result = [];
      result.push({
        id: id,
        name: name,
        viewToken: resJson.data
      });
      return result;
    }

    // 发布场景的方法
    function publish(){
      let body = {
        // 基于编辑组件返回的场景数据进行场景发布更新
        ...editor.getPublishData()
      };
      // 基于后端接口进行场景更新,需将ID替换为当前进行编辑的场景ID
      let url = 'https://api.bimface.com/scene/2017707858581668';
      let headers = new Headers();
      // 基于accessToken发起后端接口调用
      headers = {
        "Authorization":"Bearer cn-e9725999-0b36-4c0e-bdca-38ea88888888",
        "Content-Type":"application/json"
      };
      fetch(url, {method: 'PATCH',headers:headers,body:JSON.stringify(body)}).then(function(response) {
        if(response.ok){
          alert('发布成功');
        }
      });
    }
  </script>
</body>

</html>

基于以上步骤,你就可以在浏览器中基于场景编辑组件对指定场景进行编辑、发布等操作了!