Potree 003 基於Potree Desktop創建自定義工程

1、第三方js庫

第三方庫js庫選擇dojo,其官網地址爲https://dojotoolkit.org/,git地址爲https://github.com/dojo/dojo,demo地址爲https://demos.dojotoolkit.org/demos/,如果打不開,可以多刷新幾次。

截圖.png

因爲使用ArcGIS API for js開發,接觸到了dojo,dojo是一個非常優秀的js框架庫,包含的內容非常全,做單頁面Web應用程序是一個非常不錯的選擇。

2、修改Mian.js文件

Main.js文件主要還是設置electron的一些參數,代碼如下。

var electron = require("electron");
var app = electron.app;
var BrowserWindow = electron.BrowserWindow;
var path = require("path");
var Menu = electron.Menu;
var myMainWindow;
function CreateWindow() {
    // 新建主窗體,設置圖標、加載主頁面
    myMainWindow = new BrowserWindow({
        webPreferences: {
            nodeIntegration: true,
            backgroundThrottling: false
        },
        show: false
    });
    myMainWindow.maximize();
    myMainWindow.setIcon(path.join(__dirname, "Res/Images/Ico64.png"));
    myMainWindow.loadFile(path.join(__dirname, "Index.html"));
    myMainWindow.show();
    //把electron帶的菜單隱藏
    Menu.setApplicationMenu(null);
    //屏蔽警告
    process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'
    myMainWindow.on("closed", function () {
        myMainWindow = null;
    });
}
app.on("ready", CreateWindow);
// Quit when all windows are closed.
app.on("window-all-closed", function () {
    if (process.platform !== "darwin") {
        app.quit()
    }
});
app.on("activate", function () {
    if (myMainWindow === null) {
        CreateWindow()
    }
});

我們在Potree Desktop代碼中的Main.js文件的基礎上上做了修改。通過引用electron,得到electron、app、BrowserWindow、path、Menu等引用,並定義了CreateWindow函數。

該函數是Main.js最主要的函數,在代碼中,我們創建了一個BrowserWindow窗體,並最大化該窗體。設置了窗體圖標、加載的頁面等。最後調用窗體的Show函數,打開窗體。BrowserWindow窗體自帶了一個菜單,通過Menu.setApplicationMenu(null);隱藏electron自帶的菜單。當窗體關閉的時候,設置myMainWindow變量的值爲null。

後面的代碼主要針對全局App,當系統環境準備好的時候,調用CreateWindow函數。當所有的窗體都關閉後,退出App。當App被激活的時候,如果myMainWindow變量爲null,則調用CreateWindow函數。這些邏輯基本上都是從Potree Desktop中拷貝過來的,修改了下變量名稱,邏輯未變。

代碼運行Mian.js,實例化BrowserWindow窗體,加載Index.html,此時系統就進入了Index.html頁面。

3、修改index.html文件

我們的目的是做一個Web單頁面應用程序,樣式以及操作方式要儘量能貼近普通的桌面應用程序。整體佈局包括菜單欄、工具欄、左側工程樹以及中間的主顯示區。

首先引用外部的js和css文件,包括dojo、Potree定義的文件以及我們系統中自己定義的一些文件,代碼如下。

<link rel="stylesheet" type="text/css" href="./libs/potree/potree.css">
<link rel="stylesheet" type="text/css" href="./libs/jquery-ui/jquery-ui.min.css">
<link rel="stylesheet" type="text/css" href="./libs/openlayers3/ol.css">
<link rel="stylesheet" type="text/css" href="./libs/spectrum/spectrum.css">
<link rel="stylesheet" type="text/css" href="./libs/jstree/themes/mixed/style.css">

<link rel="stylesheet" href="Res/dojo/dijit/themes/claro/claro.css" />
<link rel="stylesheet" href="Res/dojo/dojox/grid/resources/claroGrid.css" />
<link rel="stylesheet" href="Res/dojo/dojox/form/resources/CheckedMultiSelect.css" />
<link rel="stylesheet" href="Res/dojo/dojox/form/resources/RangeSlider.css" />
<link rel="stylesheet" href="Res/dojo/dojox/widget/ColorPicker/ColorPicker.css" />

<link rel="stylesheet" href="Index.css" />
<link rel="stylesheet" href="AppUI/AppMenuUI.css" />
<link rel="stylesheet" href="AppUI/AppToolBarUI.css" />

<script>
    if (typeof module === 'object') {
        window.module = module;
        module = undefined;
    }
</script>
<script src="./libs/jquery/jquery-3.1.1.min.js"></script>
<script src="./libs/spectrum/spectrum.js"></script>
<script src="./libs/jquery-ui/jquery-ui.min.js"></script>
<script src="./libs/other/BinaryHeap.js"></script>
<script src="./libs/tween/tween.min.js"></script>
<script src="./libs/d3/d3.js"></script>
<script src="./libs/proj4/proj4.js"></script>
<script src="./libs/openlayers3/ol.js"></script>
<script src="./libs/i18next/i18next.js"></script>
<script src="./libs/jstree/jstree.js"></script>
<script src="./libs/potree/potree.js"></script>
<script src="./libs/plasio/js/laslaz.js"></script>
<script src="./libs/three.js/build/three.js"></script>
<script src="./libs/three.js/build/three.js"></script>

接下來定義對electron和Nodejs中一些功能的引用。

<script>
    var Electron = require('electron');
    var ElectronDialog = require('electron').remote.dialog;
    var NodeFS = require('fs');
    var NodePath = require('path');
    var NodeChildProcess = require('child_process');
</script>

ElectronDialog可彈出本地對話框,包括消息對話框,是否對話框等,類似於.Net中的MessageBox。NodeFS模塊可對文件進行操作,類似於.Net中的File類。NodePath模塊可文件路徑進行操作,類似於.Net中的Path類。NodeChildProcess模塊可調用本地的exe文件,並可以傳入參數,捕捉輸出信息等。

接下來是使用dojo庫的一些配置以及Index.html頁面直接引用的一些js文件。

<script>
    var dojoConfig = {
        async: true,
        parseOnLoad: false,
        //用於頁面加載時立即加載的JS依賴
        deps: ["dojo/parser"],
        callback: function (parser) { },
        //加載一個模塊的請求超時時間,如果超時說明加載模塊失敗
        waitSeconds: 10,
        //如果爲true可以避免模塊緩存(原理就是在請求模塊的URL加上當前時間戳)
        //cacheBust: true,
    }
</script>
<script src="Res/dojo/dojo/dojo.js"></script>
<script src="Framework/URLHelper.js"></script>
<script src="Framework/DateFormat.js"></script>
<script src="Index.js"></script>

Index.html文件最後,是該頁面的佈局代碼,如下所示。

<body class="claro">
    <table id="UI_Main_Table">
        <tr style="height:30px">
            <td padding:0">
                <div id="UI_AppMenuUI_Div"></div>
            </td>
        </tr>
        <tr style="height:30px">
            <td padding:0">
                <div id="UI_AppToolBarUI_Div"></div>
            </td>
        </tr>
        <tr>
            <td style="padding:0;">
                <div data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="gutters:true, liveSplitters:false" style="width: 100%; height: 100%; margin: 0; padding: 0; ">
                    <div id="UI_AppTreeUI_Div" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="minSize:20, region:'leading', splitter:true" style="width: 260px;"></div>
                    <div id="UI_Center_BorderContainer" data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="region:'center'" style="width: 100%; height: 100%; margin: 0; padding: 0; ">
                        <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'center',splitter:true">
                            <div class="potree_container" style="height:100%">
                                <div id="potree_render_area"></div>
                            </div>
                        </div>
                    </div>
                </div>
            </td>
        </tr>
    </table>
</body>

我們在body中直接定義了一個Table作爲根元素,該表格分三行,第一行菜單欄,第二行工具條,第三行就是主顯示區。如果想在底部增加狀態欄,可再增加一行。

主顯示區使用了dojo中定義的dijit/layout/BorderContainer,在BorderContainer中添加了兩個dijit/layout/ContentPane,其中一個ContentPane的region屬性設置爲leading,另外一個ContentPane的region屬性設置爲center,並都設置splitter=true。這樣主顯示區域左側會添加一個區域,寬度爲260px,剩下的爲中間區域,並且兩區域可左右調整大小。

我們在Index.js中添加菜單。

4、添加菜單

菜單我們使用dojo中定義的dijit/MenuBar,如果我們添加文件子菜單,html代碼如下。

<div style="font-size:14px">
    <div data-dojo-type="dijit/MenuBar">
        <div data-dojo-type="dijit/PopupMenuBarItem">
            <span>文件</span>
            <div data-dojo-type="dijit/DropDownMenu" data-dojo-attach-point="UI_File_DropDownMenu"></div>
        </div>
    </div>
</div>

在js文件中,我們可以實例化一個dijit/MenuItem,添加到UI_File_DropDownMenu中。

CreateMenuItem: function () {
    var myMenuItem = new MenuItem({
        label: "打開文件",
        iconClass: "AppMenuUIMenuItemIcon FileOpenIcon"
    });
    var myThis = this;
    var myEventHander = on(myMenuItem, "click", function () {
        myThis._OnClick();
    });
    this._EventHanderArray.push(myEventHander);
    return myMenuItem;
},
//點擊命令按鈕執行的函數
_OnClick: function () {
    var myFilePathArray = ElectronDialog.showOpenDialogSync(null, {
        title: "選擇工程文件",
        properties: ["openFile"],
        filters: [
            { name: '工程文件', extensions: ['project'] }
        ]
    });
    if (myFilePathArray == null) {
        return;
    }
    var myProject = new Project();
    myProject.Open(myFilePathArray[0]);
    this._Application.SetProject(myProject);
},

把創建的MenuItem添加到菜單欄上的代碼如下。

this.UI_File_DropDownMenu.addChild(new FileNewCommand({}, this._Application).CreateMenuItem());

5、添加工具條

工具條使用dojo中定義的dijit/Toolbar,html代碼定義如下。

<div>
    <div data-dojo-type="dijit/Toolbar" data-dojo-attach-point="UI_Toolbar">
        <button data-dojo-type="dijit/form/Button" data-dojo-attach-point="UI_TopView_Button"
                data-dojo-props="iconClass:'AppToolBarUIButtonIcon TopViewIcon',showLabel:false">
            上視圖
        </button>
        <button data-dojo-type="dijit/form/Button" data-dojo-attach-point="UI_BottomView_Button"
                data-dojo-props="iconClass:'AppToolBarUIButtonIcon BottomViewIcon',showLabel:false">
            下視圖
        </button>
        <span data-dojo-type="dijit/ToolbarSeparator"></span>
        <button data-dojo-type="dijit/form/Button" data-dojo-attach-point="UI_FullExtent_Button"
                data-dojo-props="iconClass:'AppToolBarUIButtonIcon FullExtentIcon',showLabel:false">
            全圖
        </button>
    </div>
</div>

在js文件中,可以具體定義Button按鈕點擊後執行的函數,代碼如下。

//點擊全圖按鈕執行的函數
on(this.UI_FullExtent_Button, "click", function () {
    myThis._Application.Viewer.fitToScreen();
});

一些邏輯較爲複雜的工具,我們就要單獨定義了,類似於定義菜單,我們會定義一個工具按鈕。我們以新建工程爲例,js代碼如下。

define([
    "dojo/_base/declare",
    "dojo/on",
    "dijit/MenuItem",
    "dijit/form/Button"
], function (
    declare,
    on,
    MenuItem,
    Button
) {
    return declare("ProjectNewCommand", null, {
        _Application: null,
        _EventHanderArray: null,
        //構造函數
        constructor: function (args, pApplication) {
            declare.safeMixin(this, args);
            this._Application = pApplication;
            this._EventHanderArray = [];
        },
        //創建MenuItem
        CreateMenuItem: function () {
            var myMenuItem = new MenuItem({
                label: "新建工程",
                iconClass: "AppMenuUIMenuItemIcon ProjectNewIcon"
            });
            var myThis = this;
            var myEventHander = on(myMenuItem, "click", function () {
                myThis._OnClick();
            });
            this._EventHanderArray.push(myEventHander);
            return myMenuItem;
        },
        //創建Button
        CreateButton: function () {
            var myButton = new Button({
                label: "新建工程",
                iconClass: "AppToolBarUIButtonIcon ProjectNewIcon",
                showLabel: false,
            });
            var myThis = this;
            var myEventHander = on(myButton, "click", function () {
                myThis._OnClick();
            });
            this._EventHanderArray.push(myEventHander);
            return myButton;
        },
        //點擊命令按鈕執行的函數
        _OnClick: function () {
            var myThis = this;
            require(["WebRoot/CoreUI/Projects/ProjectNewDialog"],
                function (Dialog) {
                    var myDialog = new Dialog({});
                    myDialog.ShowDialog();
                    myDialog.on("ProjectCreated", function (e) {
                        myThis._Application.SetProject(e.Project);
                    });
                });
        },
        //默認銷燬函數
        destroy: function () {
            for (var i = 0; i < this._EventHanderArray.length; i++) {
                this._EventHanderArray[i].remove();
            }
            this._EventHanderArray = [];
        },
    });
});

代碼中包含了CreateMenuItem和CreateButton函數,分別返回MenuItem和Button,點擊這兩個UI都是執行_OnClick函數,也就是說,我們當前定義的ProjectNewCommand.js,實例化得到ProjectNewCommand對象後,調用CreateMenuItem函數獲取的按鈕可以添加到菜單上,調用CreateButton函數獲取的按鈕可以添加到工具條上,且點擊後,兩個按鈕的行爲一致。

把按鈕添加到工具欄上的代碼如下。

var myProjectNewCommand = new ProjectNewCommand({}, myApplication);
myAppToolBarUI.UI_Toolbar.addChild(myProjectNewCommand.CreateButton(), 0);

6、Potree Viewer初始化

系統整體佈局、菜單欄,工具條、左側工程區域以及中間顯示區域都定義好了之後,我們就要定義Viewer了。定義的時候,我們依然要參考Potree Desktop中實例化Viewer的代碼,並根據實際需求修改。

var myPotreeRenderArea = document.getElementById("potree_render_area");
var myViewerArgs = {
    noDragAndDrop: true,
};
var myViewer = new Potree.Viewer(myPotreeRenderArea, myViewerArgs);
myViewer.setEDLEnabled(true);
myViewer.setFOV(60);
myViewer.setPointBudget(3 * 1000 * 1000);
myViewer.setMinNodeSize(0);
myViewer.loadSettingsFromURL();
myViewer.setDescription("");
myViewer.setControls(myViewer.earthControls);

7、系統運行效果

執行.bat文件,運行,得到的系統主界面如下圖所示。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章