基於WebGL架構的ThingJS視覺化平臺—城市地下管線3D視覺化
前言
城市管網是城市最重要的公共基礎設施之一,與城市的發展和居民日常生活息息相關。根據不同的市政建設,管網分供水、排汙、供暖、通訊、電力等多種類別,其廣泛分佈遍及地下。隨著城市發展建設所衍生出空間分佈複雜,變化大,種類繁多等問題,視覺化管理是未來發展最好的解決方案。
實現
第一步,載入場景
//載入場景程式碼 var app = new THING.App({ // 場景地址 "url": "http://www.thingjs.com/./uploads/wechat/S2Vyd2lu/scene/Demo_地下管線", //背景設定 "skyBox": "BlueSky" });
第二步,建立管線以及建立管線資訊面板。這裡PolygonLine這種型別我們在之前的Demo中使用過沒看過的同學可以點進去看一下,當然那篇比較糙還是沒有這篇好看的,可以兩篇對比一下效果,文章地址。這裡這個renderOrder屬性劃重點,這裡將line的visible屬性設定為false,當場景載入完成後我們的管線就已經建立好了為了能更好的配合我們的面板所以給他先隱藏掉了,等到開關觸發再對他進行展示。
function createLine(name, color, points) { var line = null; line = app.create({ type: 'PolygonLine', name: name, points: points, style: { color: color, }, }); line.renderOrder = -10000; line.scrollUV = true; line.visible = false; return line; } function createInfo(obj, position) { var panel = new THING.widget.Panel({ template: 'default2', width: '120px', cornerType: 'polyline' }) var dataObj = { name: obj.name, color: obj.style.color } panel.addString(dataObj, 'name').caption('名稱'); panel.addString(dataObj, 'color').caption('顏色'); var uiAnchor = app.create({ type: 'UIAnchor', parent: obj, element: panel.domElement, position: [position[0], -1, position[2]], pivot: [-0.2, 2.1] }); return uiAnchor; }
第三步,建立功能面板以及建立各種類管線。
//管線的模擬資料 var dataJson = [{ 'id': 'BJ002', 'starId': '2TAG001', 'stopId': '2TAG002', 'starDeep': '-1.5', 'stopDeep': '-1.5', 'material': 'ASTNX01', 'pressure': '120' }, { 'id': 'BJ002', 'starId': '2TAG001', 'stopId': '2TAG002', 'starDeep': '-1.5', 'stopDeep': '-1.5', 'material': 'ASTNX01', 'pressure': '120' }, { 'id': 'BJ002', 'starId': '2TAG001', 'stopId': '2TAG002', 'starDeep': '-1.5', 'stopDeep': '-1.5', 'material': 'ASTNX01', 'pressure': '120' }];
app.on('load', function () { // init_camera app.camera.flyTo({ 'position': [-6.199233956799988, 49.47465259648085, 113.74453304853118], 'target': [-4.002967317456267, 26.055382001258867, 37.65111202780902], 'time': 2000, }); //建立場景 var controlPanel = new THING.widget.Panel({ titleText: '地下管線Demo', hasTitle: true, // 是否有標題 zIndex: 999, // 設定層級 }); var data = { totalOpen: false, viewOpen: false, waterOpen: false, blowOffOpen: false, heatOpen: false }; //openTotal按鈕控制 var totalOpen = controlPanel.addBoolean(data, 'totalOpen').caption('狀態切換'); totalOpen.on('change', function (ev) { if (ev) { //將campus下所有的obj的opacity = 0.4,將name = Uncorrelated 的obj的visiable = false app.query('Campus')[0].style.opacity = 0.3; app.query('Uncorrelated')[0].visible = false; } else { //反之初始化 app.query('Campus')[0].style.opacity = 1; app.query('Uncorrelated')[0].visible = true; } }); //viewOpen視角控制 var viewOpen = controlPanel.addBoolean(data, 'viewOpen').caption('2D/3D'); viewOpen.on('change', function (ev) { if (ev) { app.camera.viewMode = THING.CameraView.TopView; } else { app.camera.viewMode = THING.CameraView.Normal; app.skyBox = 'BlueSky'; } }); //waterOpen供水管線 color #0000FF var waterOpen = controlPanel.addBoolean(data, 'waterOpen').caption('供水管線'); var waterLine = []; waterLine.push(createLine('供水管線', 'https://thingjs.com/static/images/poly_line_04.png', [[-61.736, -1.5, 10], [74.408, -1.5, 10]])); waterLine.push(createLine('供水管線', 'https://thingjs.com/static/images/poly_line_04.png', [[-61.736, -1.50, 8], [-16.126, -1.50, 8], [-16.126, -1.50, -20], [-10.126, -1.50, -20], [-10.126, -1.50, 8], [74.408, -1.5, 8]])); waterLine.push(createLine('供水管線', 'https://thingjs.com/static/images/poly_line_04.png', [[-61.736, -1.50, 12], [-45.001, -1.5, 15.755], [-20.736, -1.5, 12], [74.408, -1.5, 12]])); waterLine.push(createLine('供水管線', 'https://thingjs.com/static/images/poly_line_04.png', [[4, -1.5, 46], [4, -1.50, 13.809], [4, -2, 13.809], [4, -2, 6], [4, -1.50, 6], [4, -1.5, -34]])); waterLine.push(createLine('供水管線', 'https://thingjs.com/static/images/poly_line_04.png', [[6, -1.50, 46], [6, -1.50, 13.809], [6, -2, 13.809], [6, -2, 6], [6, -1.50, 6], [6, -1.5, -34]])); waterLine.push(createLine('供水管線', 'https://thingjs.com/static/images/poly_line_04.png', [[8, -1.50, 46], [8, -1.50, 13.809], [8, -2, 13.809], [8, -2, 6], [8, -1.50, 6], [8, -1.5, -34]])); createInfo(waterLine[0], dataJson[0],[-28.847, -1.5, 7.957]); waterOpen.on('change', function (ev) { waterLine.forEach(function (obj) { obj.visible = ev; }) }); //blowOffLine排汙管線 color #FFEC8B var blowOffOpen = controlPanel.addBoolean(data, 'blowOffOpen').caption('排汙管線'); var blowOffLine = []; blowOffLine.push(createLine('排汙管線', 'https://thingjs.com/static/images/poly_line_03.png', [[-2, -3, 46], [-2, -3, -34]])); blowOffLine.push(createLine('排汙管線', 'https://thingjs.com/static/images/poly_line_03.png', [[0, -3, 46], [0, -3, -34]])); blowOffLine.push(createLine('排汙管線', 'https://thingjs.com/static/images/poly_line_03.png', [[-61.736, -3, 4], [74.408, -3, 4]])); blowOffLine.push(createLine('排汙管線', 'https://thingjs.com/static/images/poly_line_03.png', [[-61.736, -3, 2], [74.408, -3, 2]])); createInfo(blowOffLine[0], dataJson[1],[15.299, -1.5, 1.87]); blowOffOpen.on('change', function (ev) { blowOffLine.forEach(function (obj) { obj.visible = ev; }) }); //heatLine供熱管線 color #FF7F24 var heatOpen = controlPanel.addBoolean(data, 'heatOpen').caption('供熱管線'); var heatLine = []; heatLine.push(createLine('供熱管線', 'https://thingjs.com/static/images/poly_line_02.png', [[-61.736, -2, 0], [65.736, -2, 0], [74.408, -2, -8]])); heatLine.push(createLine('供熱管線', 'https://thingjs.com/static/images/poly_line_02.png', [[-61.736, -2, -2], [65.736, -2, -2], [74.408, -2, -10]])); heatLine.push(createLine('供熱管線', 'https://thingjs.com/static/images/poly_line_02.png', [[-4, -2, 46], [-4, -2, -34]])); heatLine.push(createLine('供熱管線', 'https://thingjs.com/static/images/poly_line_02.png', [[-6, -2, 46], [-6, -2, -34]])); createInfo(heatLine[0], dataJson[2],[-6.041, -1.817, 8.865]); heatOpen.on('change', function (ev) { heatLine.forEach(function (obj) { obj.visible = ev; }) });
小結
這個取點座標的方法在上文中沒有告訴大家,只是因為我原來用的方法就比較傻了,我是自己寫了一個全域性的singleclick事件,觸發之後打印出當前滑鼠的pickedPosistion記錄下來再寫進陣列中,這無疑的是很麻煩的操作。
其實ThingJS已經給我們寫好了一個工具叫做拾取場景座標,他的使用方式下面我會給大家貼幾張圖。整個程式碼100+行還是比較簡潔了(作者能力有限,已經努力精簡了下文可以給作者多提意見,虛心接受)。最後附上完整程式碼!
完整程式碼
//載入場景程式碼 var app = new THING.App({ // 場景地址 "url": "http://www.thingjs.com/./uploads/wechat/S2Vyd2lu/scene/Demo_地下管線", //背景設定 "skyBox": "BlueSky" }); app.on('load', function () { // init_camera app.camera.flyTo({ 'position': [-6.199233956799988, 49.47465259648085, 113.74453304853118], 'target': [-4.002967317456267, 26.055382001258867, 37.65111202780902], 'time': 2000, }); //建立場景 var controlPanel = new THING.widget.Panel({ titleText: '地下管線Demo', hasTitle: true, // 是否有標題 zIndex: 999, // 設定層級 }); var data = { totalOpen: false, viewOpen: false, waterOpen: false, blowOffOpen: false, heatOpen: false }; //openTotal按鈕控制 var totalOpen = controlPanel.addBoolean(data, 'totalOpen').caption('狀態切換'); totalOpen.on('change', function (ev) { if (ev) { //將campus下所有的obj的opacity = 0.4,將name = Uncorrelated 的obj的visiable = false app.query('Campus')[0].style.opacity = 0.3; app.query('Uncorrelated')[0].visible = false; } else { //反之初始化 app.query('Campus')[0].style.opacity = 1; app.query('Uncorrelated')[0].visible = true; } }); //viewOpen視角控制 var viewOpen = controlPanel.addBoolean(data, 'viewOpen').caption('2D/3D'); viewOpen.on('change', function (ev) { if (ev) { app.camera.viewMode = THING.CameraView.TopView; } else { app.camera.viewMode = THING.CameraView.Normal; app.skyBox = 'BlueSky'; } }); //waterOpen供水管線 color #0000FF var waterOpen = controlPanel.addBoolean(data, 'waterOpen').caption('供水管線'); var waterLine = []; waterLine.push(createLine('供水管線', 'https://thingjs.com/static/images/poly_line_04.png', [[-61.736, -1.5, 10], [74.408, -1.5, 10]])); waterLine.push(createLine('供水管線', 'https://thingjs.com/static/images/poly_line_04.png', [[-61.736, -1.50, 8], [-16.126, -1.50, 8], [-16.126, -1.50, -20], [-10.126, -1.50, -20], [-10.126, -1.50, 8], [74.408, -1.5, 8]])); waterLine.push(createLine('供水管線', 'https://thingjs.com/static/images/poly_line_04.png', [[-61.736, -1.50, 12], [-45.001, -1.5, 15.755], [-20.736, -1.5, 12], [74.408, -1.5, 12]])); waterLine.push(createLine('供水管線', 'https://thingjs.com/static/images/poly_line_04.png', [[4, -1.5, 46], [4, -1.50, 13.809], [4, -2, 13.809], [4, -2, 6], [4, -1.50, 6], [4, -1.5, -34]])); waterLine.push(createLine('供水管線', 'https://thingjs.com/static/images/poly_line_04.png', [[6, -1.50, 46], [6, -1.50, 13.809], [6, -2, 13.809], [6, -2, 6], [6, -1.50, 6], [6, -1.5, -34]])); waterLine.push(createLine('供水管線', 'https://thingjs.com/static/images/poly_line_04.png', [[8, -1.50, 46], [8, -1.50, 13.809], [8, -2, 13.809], [8, -2, 6], [8, -1.50, 6], [8, -1.5, -34]])); createInfo(waterLine[0], dataJson[0],[-28.847, 0, 7.957]); waterOpen.on('change', function (ev) { waterLine.forEach(function (obj) { obj.visible = ev; }) }); //blowOffLine排汙管線 color #FFEC8B var blowOffOpen = controlPanel.addBoolean(data, 'blowOffOpen').caption('排汙管線'); var blowOffLine = []; blowOffLine.push(createLine('排汙管線', 'https://thingjs.com/static/images/poly_line_03.png', [[-2, -3, 46], [-2, -3, -34]])); blowOffLine.push(createLine('排汙管線', 'https://thingjs.com/static/images/poly_line_03.png', [[0, -3, 46], [0, -3, -34]])); blowOffLine.push(createLine('排汙管線', 'https://thingjs.com/static/images/poly_line_03.png', [[-61.736, -3, 4], [74.408, -3, 4]])); blowOffLine.push(createLine('排汙管線', 'https://thingjs.com/static/images/poly_line_03.png', [[-61.736, -3, 2], [74.408, -3, 2]])); createInfo(blowOffLine[0], dataJson[1],[15.299, 0, 1.87]); blowOffOpen.on('change', function (ev) { blowOffLine.forEach(function (obj) { obj.visible = ev; }) }); //heatLine供熱管線 color #FF7F24 var heatOpen = controlPanel.addBoolean(data, 'heatOpen').caption('供熱管線'); var heatLine = []; heatLine.push(createLine('供熱管線', 'https://thingjs.com/static/images/poly_line_02.png', [[-61.736, -2, 0], [65.736, -2, 0], [74.408, -2, -8]])); heatLine.push(createLine('供熱管線', 'https://thingjs.com/static/images/poly_line_02.png', [[-61.736, -2, -2], [65.736, -2, -2], [74.408, -2, -10]])); heatLine.push(createLine('供熱管線', 'https://thingjs.com/static/images/poly_line_02.png', [[-4, -2, 46], [-4, -2, -34]])); heatLine.push(createLine('供熱管線', 'https://thingjs.com/static/images/poly_line_02.png', [[-6, -2, 46], [-6, -2, -34]])); createInfo(heatLine[0], dataJson[2],[-6.041, 0, 8.865]); heatOpen.on('change', function (ev) { heatLine.forEach(function (obj) { obj.visible = ev; }) }); }); function createLine(name, image, points) { var line = null; line = app.create({ type: 'PolygonLine', name: name, points: points, image: image }); line.renderOrder = -10000; line.scrollUV = true; line.visible = false; return line; } function createInfo(obj,json, position) { var panel = new THING.widget.Panel({ template: 'default2', width: '200px', cornerType: 'polyline' }) // panel.zIndex = -10000; panel.renderOrder = -1000; var dataObj = { name: ' ', id: json.id, starId: json.starId, stopId: json.stopId, starDeep: json.starDeep, stopDeep: json.stopDeep, material: json.material, pressure: json.pressure, } panel.addString(dataObj, 'name').caption(obj.name); panel.addString(dataObj, 'id').caption('管線編號'); panel.addString(dataObj, 'starId').caption('起點編號'); panel.addString(dataObj, 'stopId').caption('終點編號'); panel.addString(dataObj, 'starDeep').caption('起點深度'); panel.addString(dataObj, 'stopDeep').caption('終點深度'); panel.addString(dataObj, 'material').caption('材料'); panel.addString(dataObj, 'pressure').caption('壓力'); var uiAnchor = app.create({ type: 'UIAnchor', parent: obj, element: panel.domElement, position: [position[0], 0, position[2]], pivot: [-0.2, 2.1] }); return uiAnchor; } var dataJson = [{ 'id': 'BJ002', 'starId': '2TAG001', 'stopId': '2TAG002', 'starDeep': '-1.5', 'stopDeep': '-1.5', 'material': 'ASTNX01', 'pressure': '120' }, { 'id': 'BJ002', 'starId': '2TAG001', 'stopId': '2TAG002', 'starDeep': '-1.5', 'stopDeep': '-1.5', 'material': 'ASTNX01', 'pressure': '120' }, { 'id': 'BJ002', 'starId': '2TAG001', 'stopId': '2TAG002', 'starDeep': '-1.5', 'stopDeep': '-1.5', 'material': 'ASTNX01', 'pressure': '120' }];