美文网首页
基于i3s obb的在线编辑场景图层放置

基于i3s obb的在线编辑场景图层放置

作者: Xuds | 来源:发表于2020-09-28 17:25 被阅读0次

    简介

    很多规划领域的用户在线修改规划设计方案时,需要修改建筑位置和朝向,想要进行平移旋转和改变基面高度操作,以重新放置建筑。
    ArcGIS 10.8.1 发布的场景图层(Scene Layer)在自身的产品体系中,不具有在浏览器上进行放置编辑例如平移、旋转、拉高等整体编辑的功能。但由于场景图层遵循i3s(index 3d scene layer)开放标准,使得开发者可以自己开发这项功能。

    获取所有节点的i3s obb

    在上一篇文章中,我们已经介绍了如何将i3s的obb转换为Graphic。在实际使用中,必须同步编辑所有OBB才能实现Scene Layer的整体平移旋转。所以请求nodepage得到全部obb后,将每个obb转换为一个graphic,创建一个GraphicsLayer添加并容纳所有的graphic。

    var satelliteLayer = new GraphicsLayer();
    esriRequest(sceneLayerUrl + "/layers/0/nodepages/0", {
        responseType: "json"
    }).then(function(response) {
        var nodepage = response.data.nodes;
        console.log(response.data.nodes);
        for (var i = 0; i < nodepage.length; i++) {
            const obb = nodepage[i].obb;
            var satelliteLoc = new Point({
                type: "point",
                // autocasts as new Point()
                x: obb.center[0],
                y: obb.center[1],
                z: obb.center[2],
                spatialReference: {
                    wkid: wkid
                }
            });
            //console.log(obb);
            //three.js          
            var vectorQuaternion = new THREE.Quaternion();
            vectorQuaternion.w = obb.quaternion[3];
            vectorQuaternion.x = obb.quaternion[0];
            vectorQuaternion.y = obb.quaternion[1];
            vectorQuaternion.z = obb.quaternion[2];
            var vectorEuler = new THREE.Euler(0, 0, 0, eulerOrder);
            vectorEuler.setFromQuaternion(vectorQuaternion, eulerOrder);
            if (satelliteLoc !== null) {
                let graphic = new Graphic({
                    geometry: satelliteLoc,
                    symbol: {
                        type: "point-3d",
                        // autocasts as new PointSymbol3D()
                        symbolLayers: [{
                            type: "object",
                            // autocasts as new ObjectSymbol3DLayer()
                            width: obb.halfSize[0] * 2,
                            // diameter of the object from east to west in meters
                            height: obb.halfSize[2] * 2,
                            // height of the object in meters
                            depth: obb.halfSize[1] * 2,
                            // diameter of the object from north to south in meters
                            resource: {
                                primitive: "cube"
                            },
                            material: {
                                color: [255, 0, 0, 0.5]
                            },
                            tilt: radToSpecific(vectorEuler.x),
                            //around x axis
                            roll: radToSpecific(vectorEuler.y),
                            // around the y axis
                            heading: radToSpecific(vectorEuler.z),
                            //around z      
                        }]
                    },
                    attributes: {
                        ObjectId: nodepage[i].index,
                        all: nodepage[i]
                    }
                });
                satelliteLayer.add(graphic);
    
            }
        }
        return satelliteLayer;
    }).then(function(satelliteLayer) {
        //obb对象的集合
    });
    

    创建Sketch微件

    通常使用Sketch微件对于三维样式的Graphic进行编辑。由于被编辑的obb对象必须先请求服务器并且实例化为graphic后,才能指定Sketch微件以特定的模式开始编辑,所以这里使用异步的方法创建Sketch并且在异步的请求回调里对graphic指定编辑模式。
    为了一次编辑操作,整体移动,只需绑定一个root节点的obb进行编辑,其他所有节点的obb应根据平移或旋转操作进行联动。root节点一般情况下nodepage.index==0即graphic.attributes.ObjectId == 0。

    .then(function(satelliteLayer){
        myFirstPromise.then(function(sketch){
             let graphic=satelliteLayer.graphics.find(function(graphic){
               return graphic.attributes.ObjectId === 0;
             });
              sketch.update(graphic,{
                tool: "transform",                      
                enableScaling: false,
                multipleSelectionEnabled:false
              });
        });
    });
    let sketch;
    var myFirstPromise = new Promise(function(resolve, reject){
        sketch = new Sketch({
            layer: satelliteLayer,
            view: view
        });
        sketch.on("update", onMove);
        sketch.on("undo",undo);
        resolve(sketch);
    });
    

    现在,只需将代表根节点obb的Graphic进行平移旋转和改变基面高度的操作,对其他obb同步平移旋转改变基面高度,并且暂存,然后转化为i3s obb提交入库持久化存储即可。

    编辑和联动计算

    在创建Sketch时,已经指定了监听更新事件和撤销编辑事件的方法。首先开发编辑事件。编辑包括平移(水平和垂直)、旋转。

    sketch.on("update", onMove);
    sketch.on("undo",undo);
    //撤销编辑用的有序json数组,每次操作加一条,每次撤销减去一条最新的。
    let operationList=[];
    //初始坐标
    let ddx = 0;
    let ddy = 0;
    let ddz = 0;
    //平移坐标差
    let dddx = 0;
    let dddy = 0;
    let dddz = 0;
    //旋转开始方向角
    let dangleyuanshi = 0;
    //旋转角度差
    let dangle = 0;
    let graplinshi1;
    function onMove(event) {
        // If the edge graphic is moving, keep the center graphic
        if (event.toolEventInfo) {
            const toolType = event.toolEventInfo.type;
            if (toolType === "move-start") {}
            else if (toolType === "move") {} 
            else if (toolType === "move-stop") {}
            else if (toolType === "rotate-start") {}
            else if (toolType === "rotate-stop") {}
            if (event.state === "complete") {}
        }
    }
    

    平移OBB

    首先在平移开始时记录初始obb中心点坐标,在结束时,通过移动后的obb中心点坐标求出移动的坐标差包括x,y,z的差值。遍历所有OBB也就是GraphicsLayer,让所有OBB的中心点都加上x,y,z的差值,从而实现同步。最后记录move操作日志到operationList中。

    if (toolType === "move-start") {
        ddx = event.toolEventInfo.mover.geometry.x;
        ddy = event.toolEventInfo.mover.geometry.y;
        ddz = event.toolEventInfo.mover.geometry.z;
    } else if (toolType === "move-stop") {
        let grflinshi = event.toolEventInfo.mover;
        dddx = grflinshi.geometry.x - ddx;
        dddy = grflinshi.geometry.y - ddy;
        dddz = grflinshi.geometry.z - ddz;
        satelliteLayer.graphics.forEach(function(item, i) {
            if (grflinshi.attributes['ObjectId'] != item.attributes['ObjectId']) {
                //对item做了修改
                item.geometry = new Point({
                    type: "point",
                    // autocasts as new Point()
                    x: item.geometry.x + dddx,
                    y: item.geometry.y + dddy,
                    z: item.geometry.z + dddz,
                    spatialReference: {
                        wkid: wkid
                    }
                });
            }
        });
        dangle = 0;
        let operation1 = {
            "caozuo": "move",
            "dddx": dddx,
            "dddy": dddy,
            "dddz": dddz
        }
        operationList.push(operation1);
    }
    

    旋转OBB

    首先在旋转开始时记录初始方向角heading(正北顺时针)。在旋转结束时记录结束时的方向角,求出角度差dangle。遍历所有非root节点,让他们的symbolLyaer的heading属性做同样旋转。
    这时,使用高中数学知识推导以root节点obb的中心点为圆心,在平面上旋转heading角度后,求所有其他节点的坐标的方法。


    计算一点绕另一点旋转angle度后的坐标.png(来源CSDN)

    求出其他节点obb的中心点在旋转后的坐标覆盖写入GraphicsLayer。最后将旋转操作记录到操作记录operationList中。

    else if (toolType === "rotate-start") {
        dangleyuanshi = event.graphics[0].symbol.symbolLayers.items[0].heading;
    } else if (toolType === "rotate-stop") {
        dangle = event.graphics[0].symbol.symbolLayers.items[0].heading - dangleyuanshi;
        satelliteLayer.graphics.forEach(function(item, i) {
            //对item做了修改
            let grflinshi = event.graphics[0];
            if (grflinshi.attributes['ObjectId'] != item.attributes['ObjectId']) {
                let symbolyresp = {
                    type: "point-3d",
                    // autocasts as new PointSymbol3D()
                    symbolLayers: [{
                        type: "object",
                        width: item.symbol.symbolLayers.items[0].width,
                        height: item.symbol.symbolLayers.items[0].height,
                        depth: item.symbol.symbolLayers.items[0].depth,
                        resource: item.symbol.symbolLayers.items[0].resource,
                        material: item.symbol.symbolLayers.items[0].material,
                        tilt: item.symbol.symbolLayers.items[0].tilt,
                        //around x axis
                        roll: item.symbol.symbolLayers.items[0].roll,
                        // around the y axis
                        heading: item.symbol.symbolLayers.items[0].heading + dangle,
                        //around z      
                    }]
                }
                item.symbol = symbolyresp;
                let x = item.geometry.x;
                let y = item.geometry.y;
                let xr = grflinshi.geometry.x;
                let yr = grflinshi.geometry.y;
                let xnew = xr + (x - xr) * Math.cos(specificToRad( - dangle)) - (y - yr) * Math.sin(specificToRad( - dangle));
                let ynew = yr + (x - xr) * Math.sin(specificToRad( - dangle)) + (y - yr) * Math.cos(specificToRad( - dangle));
                item.geometry = new Point({
                    type: "point",
                    x: xnew,
                    y: ynew,
                    z: item.geometry.z,
                    spatialReference: {
                        wkid: wkid
                    }
                });
            }
        });
        dddx = 0;
        dddy = 0;
        let operation2 = {
            "caozuo": "rotate",
            "dangle": dangle
        }
        operationList.push(operation2);
    }
    

    撤销编辑

    撤销编辑的联动实现原理是给"undo"事件绑定方法。利用操作记录列表operationList去还原以前的操作。分为撤销平移操作 和 撤销旋转操作。

    //撤销编辑 弄个有序json数组,每次操作加一条,每次撤销减去一条最新的。
    //ctrl+z
    function undo(event) {
        console.log(event);
        if (event.tool == "transform") {
            let operation1 = operationList[operationList.length - 1];
            if (operation1.caozuo == "move") {
            //撤销平移操作
            } else if (operation1.caozuo == "rotate") {
            //撤销旋转操作
            }
        }
    
    }
    

    撤销平移操作

    针对平移操作。由于记录了xyz平移的差值。只需减去平移的差值,就可以还原平移前的obb。最后执行operationList.pop()方法删除已还原的操作记录。

    //撤销编辑 使用有序json数组,每次操作加一条,每次撤销减去一条最新的。
    //ctrl+z也可以触发
            let operation1 = operationList[operationList.length - 1];
            if (operation1.caozuo == "move") {
                let grflinshi = event.graphics[0];
                satelliteLayer.graphics.forEach(function(item, i) {
                    // Do something here to each graphic like calculate area of its geometry
                    //calculateArea(item.geometry);
                    //console.log(grfslinshi[0].attributes['ObjectId']);
                    if (grflinshi.attributes['ObjectId'] != item.attributes['ObjectId']) {
                        //对item做了修改
                        item.geometry = new Point({
                            type: "point",
                            // autocasts as new Point()
                            x: item.geometry.x - operation1.dddx,
                            y: item.geometry.y - operation1.dddy,
                            z: item.geometry.z - operation1.dddz,
                            spatialReference: {
                                wkid: wkid
                            }
                        });
                    }
                });
                operationList.pop();
            } 
    

    撤销旋转操作。

    针对旋转操作。由于记录了旋转的角度和方向(正和负),只需减去已旋转角度即可还原原始角度。而非root节点的obb还原其空间位置也是使用原来的旋转算法,传入负的dangle即可。

    else if (operation1.caozuo == "rotate") {
        console.log(operation1.dangle);
        satelliteLayer.graphics.forEach(function(item, i) {
            //对item做了修改
            let grflinshi = event.graphics[0];
            if (grflinshi.attributes['ObjectId'] != item.attributes['ObjectId']) {
                //item.symbol.symbolLayers.items[0].heading=item.symbol.symbolLayers.items[0].heading+operation1.dangle
                let symbolyresp = {
                    type: "point-3d",
                    // autocasts as new PointSymbol3D()
                    symbolLayers: [{
                        type: "object",
                        // autocasts as new ObjectSymbol3DLayer()
                        width: item.symbol.symbolLayers.items[0].width,
                        // diameter of the object from east to west in meters
                        height: item.symbol.symbolLayers.items[0].height,
                        // height of the object in meters
                        depth: item.symbol.symbolLayers.items[0].depth,
                        // diameter of the object from north to south in meters
                        resource: item.symbol.symbolLayers.items[0].resource,
                        material: item.symbol.symbolLayers.items[0].material,
                        tilt: item.symbol.symbolLayers.items[0].tilt,
                        //around x axis
                        roll: item.symbol.symbolLayers.items[0].roll,
                        // around the y axis
                        heading: item.symbol.symbolLayers.items[0].heading - operation1.dangle,
                        //around z      
                    }]
                };
                item.symbol = symbolyresp;
                let x = item.geometry.x;
                let y = item.geometry.y;
                let xr = grflinshi.geometry.x;
                let yr = grflinshi.geometry.y;
                let xnew = xr + (x - xr) * Math.cos(specificToRad(operation1.dangle)) - (y - yr) * Math.sin(specificToRad(operation1.dangle));
                let ynew = yr + (x - xr) * Math.sin(specificToRad(operation1.dangle)) + (y - yr) * Math.cos(specificToRad(operation1.dangle));
                item.geometry = new Point({
                    type: "point",
                    // autocasts as new Point()
                    x: xnew,
                    y: ynew,
                    z: item.geometry.z,
                    spatialReference: {
                        wkid: wkid
                    }
                });
            }
        });
        operationList.pop();
    }
    

    入库

    入库是将编辑后的GraphicsLayer进行数据转换并且保存入CouchDB库。由于中间件Server(端口80/443/6080/6443)未提供任何增删改接口,只能直连CouchDB(端口29081/29080),对所有改变的obb进行修改。CouchDB原生支持JSON,数据录入还是非常方便的。当然,最安全的方式还是自己写个中间件。这里只说明原理。

    入库前期准备

    1. 配置couchDB允许跨域访问。通过Datastore安装目录tools下的listadminusers命令获取tile cache库的admin用户名和密码,并在浏览器登录。


      获得CouchDB的管理员用户密码

    在Configuration中选择CORS,点击Enable CORS。开启CouchDB的跨域访问。


    开启CouchDB跨域访问
    1. 通过listmanageduser命令获得couchDB的user用户名密码。并记录到程序中。


      获得CouchDB使用者的用户名密码
    2. 访问ArcGIS Server的配置目录下面的
      /directories/arcgiscache/Hosted/服务名称SceneServer下面的cache_config.json
      例如:得到信息为
      {"layers":[{"id":0,"uuid":"9d2024ca250b44c7828d3309b4cee2c8","cache_name":"building3dobject_wkid_slpk"}]}
      Couchdb里db名称是这段json信息的拼接,拼接格式为cache_name+"
      "+id+"_"+uuid 结果为building3dobject_wkid_slpk_0_9d2024ca250b44c7828d3309b4cee2c8
      将这个db名称和其他所有信息一起填入程序中相应位置。

    //服务地址
    let sceneLayerUrl = "https://linux111.esrichina.com/server/rest/services/Hosted/Building3DObject_WKID/SceneServer";
    //couchdb地址
    let couchdb = "http://linux111.esrichina.com:29080/"
    //couchDB的服务对应db的地址,通常位于:/directories/arcgiscache/Hosted/Building3DObject_WKID_SceneServer下面的cache_config.json
    //{"layers":[{"id":0,"uuid":"9d2024ca250b44c7828d3309b4cee2c8","cache_name":"building3dobject_wkid_slpk"}]}
    //db的拼接格式为cache_name+"_"+id+"_"+uuid
    let db = couchdb + "building3dobject_wkid_slpk_0_9d2024ca250b44c7828d3309b4cee2c8/";
    let username = "usr_29fbe";
    let password = "ln5ff18p51";
    

    入库代码实现

    针对I3S的平移旋转后的obb入库,同事已经有基于python语言的成果。不妨拿来参考和改写为JavaScript语言。
    python核心代码为

            if 'nodepage' in name:
                doc=d.get(i)
                jsondoc=json.dumps(doc)
                jsondict=json.loads(jsondoc)
                
                nodes=jsondict['nodes']
                for q in range(0,len(nodes)):
                    nodepageObbx=nodes[q]['obb']['center'][0]
                    nodepageObby=nodes[q]['obb']['center'][1]
                    nodepageObbz=nodes[q]['obb']['center'][2]
                    nodes[q]['obb']['center'][0]=nodepageObbx+xoffset
                    nodes[q]['obb']['center'][1]=nodepageObby+yoffset
                    nodes[q]['obb']['center'][2]=nodepageObbz+zoffset
                    # print(name,nodepageObbx,nodepageObby,nodepageObbz)
                d.save(jsondict)
            elif 'shared' not in name and 'resources' not in name and 'features' not in name and 'nodepage' not in name:
                doc=d.get(i)
                jsondoc=json.dumps(doc)
                jsondict=json.loads(jsondoc)  
                try:
                    mbsx=jsondict['mbs'][0]
                    mbsy=jsondict['mbs'][1]
    
                    obbx=jsondict['obb']['center'][0]
                    obby=jsondict['obb']['center'][1]
    
                    jsondict['mbs'][0]=mbsx+xoffset
                    jsondict['mbs'][1]=mbsy+yoffset
                    jsondict['obb']['center'][0]=obbx+xoffset
                    jsondict['obb']['center'][1]=obby+yoffset
                    d.save(jsondict)
                    if jsondict['parentNode']:
                        try:
                            mbsx1=jsondict['parentNode']['mbs'][0]
                            mbsy1=jsondict['parentNode']['mbs'][1]
                            obbx1=jsondict['parentNode']['obb']['center'][0]
                            obby1=jsondict['parentNode']['obb']['center'][1]
                            jsondict['parentNode']['mbs'][0]=mbsx1+xoffset
                            jsondict['parentNode']['mbs'][1]=mbsy1+yoffset
                            jsondict['parentNode']['obb']['center'][0]=obbx1+xoffset
                            jsondict['parentNode']['obb']['center'][1]=obby1+yoffset
                            d.save(jsondict)
                        except:
                            continue
                    
                    elif jsondict['children']:
                        count1=len(jsondict['children'])
                        for w in range(count1):
                            try:
                                mbsx3=jsondict['children'][w]['mbs'][0]
                                mbsy3=jsondict['children'][w]['mbs'][1]
                                obbx3=jsondict['children'][w]['obb']['center'][0]
                                obby3=jsondict['children'][w]['obb']['center'][1]
                                jsondict['children'][w]['mbs'][0]=mbsx3+xoffset
                                jsondict['children'][w]['mbs'][1]=mbsy3+yoffset
                                jsondict['children'][w]['obb']['center'][0]=obbx3+xoffset
                                jsondict['children'][w]['obb']['center'][0]=obby3+yoffset
                                d.save(jsondict)
                            except:
                                continue
                except:
                    continue
    

    改写为JavaScript代码。由于没有使用任何类库,是原生写的JavaScript,和导入了couchdb包的python代码的优雅性没法比。
    大致思路是:首先,请求couchdb的登录方法得到session并写入cookie。
    然后查询所有doc,一方面,找到nodepage的doc并查询内容,以GraphicsLayer为基础,改写nodepage里的所有obb信息,再写入覆盖nodepage;
    另一方面,查询所有node节点,替换其中所有obb信息,具有父节点和子节点的node的obb也要根据i3s node的id和Graphics的objectId的对应情况将Graphics里的obb写入到couchdb的i3s node的obb里。

    //服务地址
    let sceneLayerUrl = "https://linux111.esrichina.com/server/rest/services/Hosted/Building3DObject_WKID/SceneServer";
    //couchdb地址
    let couchdb = "http://linux111.esrichina.com:29080/"
    //couchDB的服务对应db的地址,通常位于:/directories/arcgiscache/Hosted/Building3DObject_WKID_SceneServer下面的cache_config.json
    //{"layers":[{"id":0,"uuid":"9d2024ca250b44c7828d3309b4cee2c8","cache_name":"building3dobject_wkid_slpk"}]}
    //db的拼接格式为cache_name+"_"+id+"_"+uuid
    let db = couchdb + "building3dobject_wkid_slpk_0_9d2024ca250b44c7828d3309b4cee2c8/";
    let username = "usr_29fbe";
    let password = "ln5ff18p51";
    function savedb() {
        //satelliteLayer.graphics的修改在这里生效了吗
        let grfs = satelliteLayer.graphics.toArray()
        //在这里加循环,把graphics按id更上去
        let jsonData = {
            'username': username,
            'password': password
        };
        fetch(couchdb + '_session', {
            method: 'POST',
            credentials: 'include',
            headers: {
                'Content-Type': 'application/json;charset=UTF-8'
            },
            body: JSON.stringify(jsonData),
        }).then(function(res1) {
            ddx = 0;
            ddy = 0;
            dangle = 0;
            //按py思路先查询所有doc
            fetch(db + '_all_docs', {
                method: 'GET',
                credentials: 'include',
                //body: JSON.stringify(jsonData),
            }).then(function(res) {
                return res.json().then((data) = >{
                    console.log(data.rows);
                    let docs = data.rows;
                    for (let i = 0; i < docs.length; i++) {
                        //找到nodepage并修改它
                        let name = docs[i].id;
                        if (name.indexOf("nodepage") > -1) {
                            console.log(docs[i].id);
                            //先查询再对应写
                            fetch(db + name, {
                                method: 'GET',
                                credentials: 'include',
                                //body: JSON.stringify(jsonData),
                            }).then(function(res) {
                                return res.json().then((data) = >{
                                    console.log(data);
                                    let nodepage1 = data.nodes;
                                    for (let j = 0; j < nodepage1.length; j++) {
                                        //event.graphics
                                        for (let k = 0; k < grfs.length; k++) {
                                            if (nodepage1[j].index == grfs[k].attributes.ObjectId) {
                                                let grf = grfs[k];
                                                let symbolLayer = grf.symbol.symbolLayers.items[0] let vectorQuaternionresp = new THREE.Quaternion();
                                                let vectorEulerresp = new THREE.Euler(specificToRad(symbolLayer.tilt), specificToRad(symbolLayer.roll), specificToRad(symbolLayer.heading), eulerOrder);
                                                vectorQuaternionresp.setFromEuler(vectorEulerresp);
                                                let obbresp = {
                                                    "center": [grf.geometry.x, grf.geometry.y, grf.geometry.z],
                                                    "halfSize": [symbolLayer.width / 2, symbolLayer.height / 2, symbolLayer.depth / 2],
                                                    "quaternion": [vectorQuaternionresp.x, vectorQuaternionresp.y, vectorQuaternionresp.z, vectorQuaternionresp.w]
                                                }
                                                //console.log(obbresp);
                                                nodepage1[j].obb = obbresp;
    
                                            }
                                        }
                                        if (j == nodepage1.length - 1) {
                                            data.nodes = nodepage1;
                                            return data
                                        }
                                    }
                                }).then((data) = >{
                                    //这里就是改变后的data了。
                                    console.log(data)
                                    //得到了nodepage1再写入couchdb里
                                    let jsonData1 = data fetch(db + name, {
                                        method: 'PUT',
                                        credentials: 'include',
                                        body: JSON.stringify(jsonData1),
                                    }).then(function(res) {
                                        return res.json().then((data1) = >{
                                            console.log(data1);
                                        })
                                    });
                                });
                            });
                        } else if (name.indexOf('shared') == -1 && name.indexOf('resources') == -1 && name.indexOf('features') == -1 && name.indexOf('nodepage') == -1) {
                            fetch(db + name, {
                                method: 'GET',
                                credentials: 'include',
                                //body: JSON.stringify(jsonData),
                            }).then(function(res) {
                                return res.json().then((data) = >{
                                    for (let k = 0; k < grfs.length; k++) {
                                        let grf = grfs[k];
                                        if ((data.id == "root" && grf.attributes.ObjectId == 0) || (parseInt(data.id) + 1) == grf.attributes.ObjectId) {
                                            console.log(parseInt(data.id) + 1);
                                            data.mbs[0] = grf.geometry.x data.mbs[1] = grf.geometry.y data.mbs[2] = grf.geometry.z let symbolLayer = grf.symbol.symbolLayers.items[0];
                                            let vectorQuaternionresp = new THREE.Quaternion();
                                            let vectorEulerresp = new THREE.Euler(specificToRad(symbolLayer.tilt), specificToRad(symbolLayer.roll), specificToRad(symbolLayer.heading), eulerOrder);
                                            vectorQuaternionresp.setFromEuler(vectorEulerresp);
                                            let obbresp = {
                                                "center": [grf.geometry.x, grf.geometry.y, grf.geometry.z],
                                                "halfSize": [symbolLayer.width / 2, symbolLayer.height / 2, symbolLayer.depth / 2],
                                                "quaternion": [vectorQuaternionresp.x, vectorQuaternionresp.y, vectorQuaternionresp.z, vectorQuaternionresp.w]
                                            }
                                            data.obb = obbresp;
                                        }
                                        if (data.hasOwnProperty("parentNode")) {
                                            if ((data.parentNode.id == "root" && grf.attributes.ObjectId == 0) || (parseInt(data.parentNode.id) + 1) == grf.attributes.ObjectId) {
                                                data.parentNode.mbs[0] = grf.geometry.x data.parentNode.mbs[1] = grf.geometry.y data.parentNode.mbs[2] = grf.geometry.z let symbolLayer = grf.symbol.symbolLayers.items[0];
                                                let vectorQuaternionresp = new THREE.Quaternion();
                                                let vectorEulerresp = new THREE.Euler(specificToRad(symbolLayer.tilt), specificToRad(symbolLayer.roll), specificToRad(symbolLayer.heading), eulerOrder);
                                                vectorQuaternionresp.setFromEuler(vectorEulerresp);
                                                let obbresp = {
                                                    "center": [grf.geometry.x, grf.geometry.y, grf.geometry.z],
                                                    "halfSize": [symbolLayer.width / 2, symbolLayer.height / 2, symbolLayer.depth / 2, ],
                                                    "quaternion": [vectorQuaternionresp.x, vectorQuaternionresp.y, vectorQuaternionresp.z, vectorQuaternionresp.w]
                                                }
                                                data.parentNode.obb = obbresp;
                                            }
                                        }
                                        if (data.hasOwnProperty("children")) {
                                            for (let l = 0; l < data.children.length; l++) {
                                                let children = data.children[l];
                                                if ((children.id == "root" && grf.attributes.ObjectId == 0) || (parseInt(children.id) + 1) == grf.attributes.ObjectId) {
                                                    children.mbs[0] = grf.geometry.x children.mbs[1] = grf.geometry.y children.mbs[2] = grf.geometry.z let symbolLayer = grf.symbol.symbolLayers.items[0];
                                                    let vectorQuaternionresp = new THREE.Quaternion();
                                                    let vectorEulerresp = new THREE.Euler(specificToRad(symbolLayer.tilt), specificToRad(symbolLayer.roll), specificToRad(symbolLayer.heading), eulerOrder);
                                                    vectorQuaternionresp.setFromEuler(vectorEulerresp);
                                                    let obbresp = {
                                                        "center": [grf.geometry.x, grf.geometry.y, grf.geometry.z],
                                                        "halfSize": [symbolLayer.width / 2, symbolLayer.height / 2, symbolLayer.depth / 2],
                                                        "quaternion": [vectorQuaternionresp.x, vectorQuaternionresp.y, vectorQuaternionresp.z, vectorQuaternionresp.w]
                                                    }
                                                    children.obb = obbresp;
                                                }
                                            }
                                        }
                                        if (k == grfs.length - 1) {
                                            return data;
                                        }
                                    }
    
                                }).then((data) = >{
                                    //这里就是改变后的data了。
                                    console.log(data)
                                    //得到了nodepage1再写入couchdb里
                                    let jsonData1 = data fetch(db + name, {
                                        method: 'PUT',
                                        credentials: 'include',
                                        body: JSON.stringify(jsonData1),
                                    }).then(function(res) {
                                        return res.json().then((data1) = >{
                                            console.log(data1);
                                        })
                                    });
                                });
                            })
    
                        }
                    }
                })
            });
    
        });
    }
    

    成果展示

    1.首先介绍右上角的4个功能操作按钮。

    (1)开始编辑。点击后会发送nodepage的请求,获取每个node的obb并放在一个GraphicsLayer里。并且激活Sketch微件,可以进行拖拽、旋转等操作。

    (2)保存编辑。将编辑后的GraphicsLayer转换为I3S Node的obb和obs的相关信息,并入库。

    (3)撤销上一步操作。在编辑过程中,如果想要撤销上一步,可以点击此按钮。激活sketch的undo事件,实际上编辑过程中点击ctrl+z也是可以撤销上一步操作的。可多次撤销。注意:如果点击了空白处相当于触发了Sketch的编辑完成的事件,之前的操作不可撤销。如需撤销,请刷新该网页。

    (4)移除定向包围盒。可以移除掉开始编辑时加载的定向包围盒。


    4个按钮

    2.平移操作

    鼠标放在内圈,可出现东西南北四个方向标,点击方向标拖拽可按一个方向定向位移。松开鼠标,平移停止。


    平移操作

    同时,也可以点选中间的橘黄色实心圆,任意拖动,平移场景图层的OBB。松开鼠标,平移停止。


    选中橘黄色实心圆

    长按中间的小白点,可以延Z轴上下平移。

    沿Z轴平移

    2.旋转操作

    长按外圈可以顺时针和逆时针旋转。松开鼠标,旋转停止。


    旋转操作

    3.撤销上一步编辑操作

    前提:Sketch微件必须在激活状态才能依次撤销本次编辑的所有步骤。

    参考sketch的API:https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-Sketch.html

    4.点击保存即可保存SceneLayer。但需要注意,保存的效果虽然在服务器端立即生效,并且在其他初次浏览这个模型的用户那里生效。对于此浏览器的用户来说,需清除浏览器缓存后才会生效。

    相关文章

      网友评论

          本文标题:基于i3s obb的在线编辑场景图层放置

          本文链接:https://www.haomeiwen.com/subject/mhmisktx.html