美文网首页
基于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的在线编辑场景图层放置

    简介 很多规划领域的用户在线修改规划设计方案时,需要修改建筑位置和朝向,想要进行平移旋转和改变基面高度操作,以重新...

  • 日常整理

    主要项目: 基于 Vue + Vuex 的在线简历编辑器项目 基于 NodeJS + Express 的在线便利贴...

  • PhotoShop图层使用快捷方式技巧提示

    1、转换图层样式 将图层样式转换为普通的基于像素的图层可让您更好地控制内容的编辑。为此,请添加样式,然后右键单击“...

  • ArcGIS 图层

    简介 ArcGIS的图层大体可以分成三大类:在线图层、离线图层、交互图层。 在线主要包括静态图层(TiledLay...

  • 强大在线 Markdown 编辑器,支持流程图、甘特图、时序图、

    在线 Markdown 编辑器 是一款基于Vue为未来而构建的在线 Markdown 编辑器;轻量且强大:内置粘贴...

  • 办公文档-帮助说明

    办公文档在线编辑,海量模板,智能云存储! 适用于手机办公、简历制作、表格制作、汇报ppt制作等场景! *在线编辑 ...

  • arcgis api for js入门开发系列十九图层在线编辑

    本篇主要讲述的是利用arcgis api实现图层在线编辑功能模块,效果图如下: 实现思路: 1.arcgis se...

  • 004-坐标系

    1、和视图一样,图层在"图层树"当中也是相对于父图层按层级关系放置,一个图层的position依赖于它父图层的bo...

  • Cocos2d-x 基础知识

    导演Director、场景Scene、布景层Layer、精灵Sprite 导演控制场景,场景控制图层,图层控制精灵...

  • AR电商十大经典案例盘点(上)

    近日,商业图片在线交易网站Shutterstock推出其首个AR功能,用户可以虚拟放置图片到墙上。 该应用程序基于...

网友评论

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

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