如何使用thingjs制作智慧粮仓可视化项目?物联网可视化项目如何制作?制作好的物联网场景该怎么去开发? 在制作项目的时候,能不能做是一个问题,怎么去做又是一个问题,交付项目的时候能否让用户满意?能否在规定时间保质保量的交付项目,而三维可视化项目的制作,相比于普通项目来说更让人头大,它要有场景模型,还得有一定的交互功能,比如摄像机视角移动或者是跟随某个物体移动,同时还得采集数据,还得使用数据采集器(传感器),收集到数据并上传到数据库中,然后3D开发人员还得获取到这个数据,并且将有需要的数据处理成可以看见的图表或者是云图等等,总体来说,一个完整需求的三维可视化开发难度还是比较大的,这里,分享一个智慧粮仓的可视化解决方案。 本次要分享的ThingJS公开的可视化解决方案是“智慧粮仓”。 如何制作这个解决方案的呢? 第一步:搭建场景; 第二步:开发场景; 第三步:数据对接; 第四步:发布项目;
场景的搭建,通过3Dmax搭建相关特殊模型,通过3dmax上传插件上传到ThingJS的campusbuilder中,通过thingjs的campusbuilder园区搭建工具制作粮仓场景;
开发场景,第一点就是控制摄像机的视角,thingjs的视角也是camera摄像机来控制的,要开发项目,首先就是学会使用camera,然后才是各种功能,智慧粮仓中有着通栏,里面含有编号、温湿度、云图、能耗、视频、人车定位这几个功能,这些功能均在thingjs在线开发左侧的官方示例。
本次分享的智慧粮仓可视化解决方案中的数据都是写死的数据,可自行使用JavaScript语法,通过thingjs官方示例中的数据对接方式,获取数据并且放入到对于图表中。以下是四种数据对接中的ajax对接方式。
/** * 说明: * 通过CORS解决跨域问题 * 并将数据挂接到物体(car01)身上,与panel面板进行数据绑定 * 当温度>25℃时 car01变红 * 教程:ThingJS教程——>数据对接——>Ajax对接 * 难度:★★☆☆☆ */ var app = new THING.App({ url: 'https://www.thingjs.com/static/models/storehouse' }); // 定时器 var timer; app.on('load', function () { var car = app.query('car01')[0]; // 物体身上创建monitorData对象 用于存储动态监控数据 car.monitorData = { '温度': '' }; updateData(car); createPanel(car); }) /** ******************* 以下为ajax数据对接 ********************/ // 服务器程序端 通过设置 Access-Control-Allow-Origin 解决跨域问题 // 更多关于 CORS "跨域资源共享"(Cross-origin resource sharing)的技术细节 请自行搜索 // 请求传入参数为 { "id": id } // 服务器返回的数据格式为 {"state":"success","data":{"id":"4967","temper":"15℃","humi":"59%","power":"20kWh"}} function updateData(obj) { // 如果网站是 https 接口则对应 https 请求 // 如果网站是 http 接口则对应 http 请求即可 $.ajax({ type: "get", url: "https://3dmmd.cn/getMonitorDataById", data: { "id": obj.id }, dataType: "json", // 返回的数据类型 json success: function (d) { console.log(d); var temper = d.data.temper; // 设置物体身上的监控数据 obj.setAttribute("monitorData/温度", temper); changeColor(obj); // 每隔3s 请求一次数据 timer = setTimeout(function () { updateData(obj) }, 3000); } }); } // 停止请求数据 function stopUpdate() { clearTimeout(timer); } function createPanel(obj) { // 界面组件 var panel = new THING.widget.Panel({ titleText: 'car01温度', hasTitle: true }); var monitorControl = panel.addBoolean({ 'isOpen': true }, "isOpen").caption("监控开关"); // 将物体的monitor对象中的数据 与 panel 进行绑定 panel.add(obj.monitorData, '温度').name('温度'); monitorControl.on('change', function (ev) { if (ev) { updateData(obj); } else { stopUpdate(); } }) } // 如果温度>25 改变颜色 function changeColor(obj) { var temper = obj.getAttribute("monitorData/温度"); var value = temper.substr(0, temper.indexOf("℃")); if (value > 25) { obj.style.color = 'rgb(255,0,0)'; } else { obj.style.color = null; } }
开发场景需要通过thingjs的app对象引入我们搭建的园区场景: //----------------------------------------------------------------------------- // 应用入口 var toolBarState = true; var startFps = false; var fpsControl = null; var app = new THING.App({ container: "div3d", skyBox: 'BlueSky', url: "models/silohouse", ak: "app_test_key", sucess: function () { app.camera.position = [98.5, 176.3, 218.5]; app.camera.target = [19.7, -47.8, -22.5]; } });
其余的就是添加页面元素以及通过URL的方式将模型加载到在线开发中,制作出对应的tab表格,完整的智慧粮仓代码如下:- document.title = 'Demo-粮仓管理';
- /**
- * 仓库封装类
- */
- function SiloHouse(obj) {
- this.name = obj.name;
- this.obj = obj;
- obj.siloHouse = this;
- this.height = obj.size[1];
- this.roof = obj.query('/gaizi')[0];
- this.roof.initPos = this.roof.position; // 保存盖子的初始位置
- this.temper = this.humi = this.power = this.store = "";
- this.info = null;
- this.heatMap = null;
- this.panel = null;
- this.ui = null;
- this.setupEvents();
- this.simulateData();
- }
- // 几个粮仓的静态变量
- SiloHouse.current = null; // 正在选中的粮仓
- SiloHouse.currentOpen = null; // 正在打开的粮仓
- SiloHouse.summeryPanel = null; // 注意统计信息只有一个面板,是静态变量
- // 选择
- SiloHouse.prototype.select = function () {
- this.obj.style.outlineColor = 0x0000FF;
- this.showSummery(true);
- }
- SiloHouse.prototype.unselect = function () {
- this.obj.style.outlineColor = null;
- this.showSummery(false);
- }
- // 屋顶
- SiloHouse.prototype.openRoof = function () {
- this.roof.initPos = this.roof.position; // 保存盖子的初始位置
- var pos = this.obj.upPosition(80);
- this.roof.moveTo({ 'position': pos, 'time': 300 });
- }
- SiloHouse.prototype.resetRoof = function () {
- var pos = this.roof.initPos;
- this.roof.moveTo({ 'position': pos, 'time': 300 });
- this.destroyHeatmap(); // 关闭房顶要确认云图删除
- }
- // 事件
- SiloHouse.prototype.setupEvents = function (obj) {
- var that = this;
- var obj = this.obj;
- // 单击
- obj.on('singleclick', function () {
- if (SiloHouse.current)
- SiloHouse.current.unselect();
- SiloHouse.current = that;
- SiloHouse.current.select();
- });
- // 双击
- obj.on('dblclick', function () {
- if (SiloHouse.currentOpen == that)
- return;
- // 取消选中的
- if (SiloHouse.current) {
- SiloHouse.current.unselect();
- SiloHouse.current = null;
- }
- // 取消上一次打开的
- if (SiloHouse.currentOpen)
- SiloHouse.currentOpen.resetRoof();
- SiloHouse.currentOpen = that;
- // 打开屋顶
- that.openRoof();
- // 摄像机跳转
- var obj = SiloHouse.currentOpen.obj;
- app.camera.flyTo({//飞到
- offset: [0, 70, -30],
- target: obj,
- time: 1000, // 耗时毫秒
- complete: function () {
- if (toolBar.data.cloud == true)
- SiloHouse.currentOpen.createHeatmap();
- }
- });
- });
- }
- // 模拟数据
- SiloHouse.prototype.simulateData = function (obj) {
- var that = this;
- this.info = {
- "基本信息": {
- "品种": Math.ceil(Math.random() * 2) == 1 ? "小麦" : "玉米",
- "库存数量": Math.ceil(Math.random() * 9000) + "",
- "报关员": Math.ceil(Math.random() * 2) == 1 ? "张三" : "李四",
- "入库时间": Math.ceil(Math.random() * 2) == 1 ? "11:24" : "19:02",
- "用电量": Math.ceil(Math.random() * 100) + "",
- "单仓核算": "无"
- },
- "粮情信息": {
- "仓房温度": Math.ceil(Math.random() * 27 + 25) + "",
- "粮食温度": Math.ceil(Math.random() * 25 + 20) + "",
- },
- "报警信息": {
- "火灾": "无",
- "虫害": "无"
- }
- };
- // 模拟间隔刷新的数据
- var simuTime = Math.ceil(1000 + Math.random() * 1000);
- setInterval(function () {
- that.temper = Math.ceil(20 + Math.random() * 10) + "℃"; // 温度
- that.humi = Math.ceil(30 + Math.random() * 10) + "%"; // 湿度
- that.power = Math.ceil(Math.random() * 20) + "kWh"; // 能耗
- }, simuTime);
- }
- // 头顶界面
- SiloHouse.prototype.createUI = function (width) {
- width = width || 120;
- // 创建widget (动态绑定数据用)
- var panel = new THING.widget.Panel({
- template: 'default',
- cornerType: 's2c5',
- width: width.toString() + "px",
- isClose: false,
- opacity: 0.8,
- media: true
- });
- this.panel = panel;
- // 创建obj ui (跟随物体用)
- var ui = app.create({
- type: 'UI',
- parent: this.obj,
- el: panel.domElement,
- offset: [0, this.height, 0],
- pivot: [0.3, 0]
- });
- this.ui = ui;
- return panel;
- }
- SiloHouse.prototype.showUI = function (uiName, boolValue) {
- if (this.panel || this.ui)
- this.hideUI();
- if (boolValue) {
- if (uiName == 'number') {
- this.createUI().add(this.obj, 'name').name('编号');
- } else if (uiName == 'temper') {
- this.createUI().add(this, uiName).name('温度');
- } else if (uiName == 'humi') {
- this.createUI().add(this, uiName).name('湿度');
- } else if (uiName == 'power') {
- this.createUI(150).add(this, uiName).name('能耗');
- }
- }
- }
- SiloHouse.prototype.hideUI = function () {
- if (this.panel) {
- this.panel.destroy();
- this.panel = null;
- }
- if (this.ui) {
- this.ui.destroy();
- this.ui = null;
- }
- }
- // 云图相关
- SiloHouse.prototype.createHeatmap = function () {
- this.heatMap = app.create({
- type: "Heatmap",
- width: this.obj.size[0],
- height: this.obj.size[2],
- minValue: 15,
- maxValue: 45,
- radius: 1.2
- });
- this.heatMap.randomData();
- this.heatMap.position = this.obj.position;
- this.heatMap.moveY(this.height + 1);
- this.heatMap.rotateX(90);
- }
- SiloHouse.prototype.destroyHeatmap = function () {
- if (!this.heatMap)
- return;
- this.heatMap.destroy();
- this.heatMap = null;
- }
- // 统计信息 (处理全局唯一一个面板)
- SiloHouse.prototype.showSummery = function (boolValue) {
- if (SiloHouse.summeryPanel) {
- SiloHouse.summeryPanel.destroy();
- SiloHouse.summeryPanel = null;
- }
- if (boolValue) {
- SiloHouse.summeryPanel = new THING.widget.Panel({
- template: 'default',
- name: this.name,
- isClose: true,
- isDrag: true,
- isRetract: true,
- hasTitle: true,
- width: "325px",
- media: true
- });
- SiloHouse.summeryPanel.setZIndex(999999);//设置ui排序
- SiloHouse.summeryPanel.addTab(this.info);
- SiloHouse.summeryPanel.setPosition({ left: 300, top: 50 });
- }
- }
- // ----------------------------------------------------------------------------
- // 摄像头封装类
- function VideoCamera(obj) {
- this.obj = obj;
- this.videoFrame = null;
- var that = this;
- this.marker = app.create({
- type: "Marker",
- offset: [0, 3.5, 0],
- size: 23,
- url: "https://www.thingjs.com/static/images/sliohouse/videocamera3.png",
- parent: obj
- });
- this.marker.visible = false;
- this.marker.on('click', function () {
- that.showVideoFrame();
- });
- }
- VideoCamera.prototype.showUI = function (boolValue) {
- this.marker.visible = boolValue;
- }
- VideoCamera.prototype.showVideoFrame = function () {
- if (this.videoFrame) {
- this.videoFrame.destroy();
- this.videoFrame = null;
- }
- this.videoFrame = new THING.widget.Panel({
- template: 'default',
- name: this.obj.name,
- isClose: true,
- isDrag: true,
- hasTitle: true,
- width: "450px",
- media: true
- });
- var ui2data = { iframe: true };
- var videoUrlList = ["https://gctxyc.liveplay.myqcloud.com/gc/ljgcdyhxgjt_1/index.m3u8?contentid=2820180516001", "http://gcdnc.v.dwion.com/gc/ljgcwglytylxs_1/index.m3u8?contentid=2820180516001", "https://gctxyc.liveplay.myqcloud.com/gc/hswlf_1/index.m3u8?contentid=2820180516001"];//大研花巷观景,万古楼遥望玉龙雪山,黄山卧云峰
- this.videoFrame.addIframe(ui2data, 'iframe').name(" ").iframeUrl('https://www.thingjs.com/demos/player/player.html?url=' + videoUrlList[parseInt((videoUrlList.length) * Math.random())]).setHeight('321px');
- this.videoFrame.setPosition({ left: app.domElement.offsetWidth - this.videoFrame.domElement.offsetWidth - 100, top: 100 });// ui位置默认在 右上角
- this.videoFrame.setZIndex(999999);
- var that = this;
- this.videoFrame.on('close', function () {
- if (that.videoFrame) {
- that.videoFrame.destroy();
- that.videoFrame = null;
- }
- });
- }
- // ----------------------------------------------------------------------------
- // 卡车封装类
- function Truck(obj) {
- this.obj = obj;
- this.info = { "车牌": "京A12345", "公司": "北京优锘科技有限公司", "状态": "出库", "仓房": "1号", "状态": "过磅" };
- }
- Truck.prototype.createUI = function (width) {
- // 创建widget (动态绑定数据用)
- var panel = new THING.widget.Panel({
- cornerType: 'polyline',
- width: "350px",
- isClose: false,
- opacity: 0.8,
- media: true
- });
- for (var key in this.info)
- panel.add(this.info, key);
- this.panel = panel;
- // 创建obj ui (跟随物体用)
- var ui = app.create({
- type: 'UI',
- parent: this.obj,
- el: panel.domElement,
- offset: [0, this.height, 0],
- pivot: [0, 1.45]
- });
- this.ui = ui;
- return panel;
- }
- Truck.prototype.showUI = function (boolValue) {
- if (this.ui || this.panel)
- this.hideUI();
- if (boolValue)
- this.createUI();
- }
- Truck.prototype.hideUI = function (width) {
- this.panel.destroy();
- this.panel = null;
- this.ui.destroy();
- this.ui = null;
- }
- //-----------------------------------------------------------------------------
- // 应用入口
- var toolBarState = true;
- var startFps = false;
- var fpsControl = null;
- var app = new THING.App({
- container: "div3d",
- skyBox: 'BlueSky',
- url: "models/silohouse",
- ak: "app_test_key",
- sucess: function () {
- app.camera.position = [98.5, 176.3, 218.5];
- app.camera.target = [19.7, -47.8, -22.5];
- }
- });
- // 加载完成
- app.on('load', function () {
- init();
- init_gui();
- });
- var siloHouseList = [];
- var videoCameraList = [];
- var truckList = [];
- function init() {
- // 摄像机
- app.camera.flyTo({
- time: 1500,
- position: [-182.16900300883736, 53.24677728392183, 72.21965470775368],
- target: [-68.1412926741533, -18.16319203074775, -23.30416731768694]
- });
- // 设置效果
- app.setPostEffect({
- enable: true,
- antialias: {
- enable: true,
- type: 'FXAA',
- },
- colorAdjustment: {
- enable: true,
- brightness: 0,
- contrast: 1.15,
- exposure: 0,
- gamma: 1,
- saturation: 1.3
- },
- screenSpaceAmbientOcclusion: {
- enable: false
- }
- });
- app.setLighting({
- ambientLight: {
- intensity: 0.4,
- color: '#FFFFFF',
- },
- mainLight: {
- shadow: true,
- intensity: 0.6,
- color: '#FFFFFF',
- alpha: 45,
- beta: 0,
- },
- secondaryLight: {
- shadow: false,
- intensity: 0,
- color: '#FFFFFF',
- alpha: 0,
- beta: 0,
- }
- });
- // 粮仓
- app.query("[物体类型=粮仓]").forEach(function (obj) {
- var siloHouse = new SiloHouse(obj);
- siloHouseList.push(siloHouse);
- });
- // 摄像头
- app.query("[物体类型=摄像头]").forEach(function (obj) {
- videoCameraList.push(new VideoCamera(obj));
- });
- // 卡车
- create_truck();
- app.query("[物体类型=卡车]").forEach(function (obj) {
- truckList.push(new Truck(obj));
- });
- // ----------------------------------------------------------------------------------
- // 单击 如果没拾取到,则取消上次选择的粮仓
- app.on('singleclick', function (event) {
- if (event.object == null || event.object.attr('物体类型') != '粮仓') {
- if (SiloHouse.current) {
- SiloHouse.current.unselect();
- SiloHouse.current = null;
- }
- }
- });
- // 双击 如果没pick到,则取消上次打开的粮仓
- app.on('dblclick', function (event) {
- if (event.object == null || event.object.attr('物体类型') != '粮仓') {
- if (SiloHouse.currentOpen) {
- SiloHouse.currentOpen.resetRoof();
- SiloHouse.currentOpen = null;
- }
- }
- });
- // 右键 则取消上次打开的粮仓
- var mouseDownPos = null;
- app.on('mousedown', function (event) {
- if (event.button == 2)
- mouseDownPos = [event.x, event.y];
- });
- app.on('click', function (event) {
- if (event.button == 2 && Math.getDistance(mouseDownPos, [event.x, event.y]) < 4) { // 小于4像素执行click事件
- if (SiloHouse.currentOpen) {
- SiloHouse.currentOpen.resetRoof();
- SiloHouse.currentOpen = null;
- }
- }
- });
- // 屏蔽鼠标右键系统菜单
- document.body.oncontextmenu = function (evt) {
- evt = evt || event;
- evt.returnValue = false;
- return false;
- };
- // 第一人称
- fpsControl = new THING.WalkControl({
- enableKeyRotate: true, walkSpeed: 0.02,turnSpeed: 0.25, gravity: 29.8, eyeHeight: 1.6,jumpSpeed: 10
- });
- }
- // ----------------------------------------------------------------------------------
- // 定位相关,演示只创建一个卡车
- var positionList = [];// 人车定位相关
- var truckInfo = { "车牌": "京A12345", "公司": "北京优锘科技有限公司", "状态": "出库", "仓房": "1号", "状态": "过磅" };
- var wayPointList = ["L109", "L110", "L104", "L103", "L102", "L108", "L109", "L118", "L119", "L112", "L111", "L117", "L118"];
- function create_truck() {
- // 生成path,从场景中物体取得位置
- var path = [];
- for (var i = 0; i < wayPointList.length; i++) {
- var pObj = app.query(wayPointList[i])[0];
- if (!pObj)
- continue;
- path.push(pObj.position);
- }
- // 创建卡车并行走路径
- truck = app.create({
- type: 'Thing',
- name: "truck",
- url: "https://www.thingjs.com/static/models/truck"
- });
- truck.movePath({
- 'orientToPath': true,
- 'orientToPathDegree': 180,
- 'path': path,
- 'speed': 20,
- 'delayTime': 500,
- 'lerp': false,
- 'loop': true
- });
- truck.attr('物体类型', '卡车');
- }
- // ----------------------------------------------------------------------------
- // 界面相关
- var toolBar = null;
- function init_gui() {//ui 初始化
- var baseURL = "https://www.thingjs.com/static/images/sliohouse/";
- toolBar = new THING.widget.Banner({ template: 'default' ,column: 'left'});
- toolBar.data = { number: false, temper: false, humi: false, power: false, store: false, video: false, cloud: false, location: false };
- var img0 = toolBar.addImageBoolean(toolBar.data, 'number').caption('仓库编号').imgUrl(baseURL + 'warehouse_code.png');
- var img1 = toolBar.addImageBoolean(toolBar.data, 'temper').caption('温度检测').imgUrl(baseURL + 'temperature.png');
- var img2 = toolBar.addImageBoolean(toolBar.data, 'humi').caption('湿度检测').imgUrl(baseURL + 'humidity.png');
- var img3 = toolBar.addImageBoolean(toolBar.data, 'power').caption('能耗统计').imgUrl(baseURL + 'statistics.png');
- var img4 = toolBar.addImageBoolean(toolBar.data, 'store').caption('粮食储量').imgUrl(baseURL + 'cereals_reserves.png');
- var img5 = toolBar.addImageBoolean(toolBar.data, 'video').caption('视频监控').imgUrl(baseURL + 'video.png');
- var img6 = toolBar.addImageBoolean(toolBar.data, 'cloud').caption('温度云图').imgUrl(baseURL + 'cloud.png');
- var img7 = toolBar.addImageBoolean(toolBar.data, 'location').caption('人车定位').imgUrl(baseURL + 'orientation.png');
- img0.on('change', function (boolValue) { onChangeImageButton('number', boolValue); });
- img1.on('change', function (boolValue) { onChangeImageButton('temper', boolValue); });
- img2.on('change', function (boolValue) { onChangeImageButton('humi', boolValue); });
- img3.on('change', function (boolValue) { onChangeImageButton('power', boolValue); });
- img4.on('change', function (boolValue) { onChangeImageButton('store', boolValue); });
- img5.on('change', function (boolValue) { onChangeImageButton('video', boolValue); });
- img6.on('change', function (boolValue) { onChangeImageButton('cloud', boolValue); });
- img7.on('change', function (boolValue) { onChangeImageButton('location', boolValue); });
- }
- // 处理工具条按钮
- function onChangeImageButton(key, boolValue) {
- // 更新界面绑定对象,其中排除 云图 和 人车定位
- if (boolValue) {
- for (var elem in toolBar.data) {
- if (elem == "cloud" || elem == "location" || elem == key)
- continue;
- toolBar.data[elem] = false;
- }
- }
- // 分类别处理
- if (key == "cloud") { // 云图
- if (!boolValue) {
- if (SiloHouse.currentOpen)
- SiloHouse.currentOpen.destroyHeatmap();
- } else {
- if (SiloHouse.currentOpen && app.camera.flying == false)
- SiloHouse.currentOpen.createHeatmap();
- }
- } else if (key == "location") { // 人车定位
- truckList.forEach(function (tr) {
- tr.showUI(boolValue);
- });
- } else if (key == "video") { // 视频监控
- videoCameraList.forEach(function (vc) {
- vc.showUI(boolValue);
- });
- } else if (key == "store") { // 储量
- siloHouseList.forEach(function (siloHouse) {
- siloHouse.hideUI();
- siloHouse.obj.visible = !boolValue;
- });
- } else { // 其他粮仓UI显示
- siloHouseList.forEach(function (siloHouse) {
- siloHouse.showUI(key, boolValue);
- });
- }
- }
- function changeFPS(start) {
- if (start) {
- app.addControl(fpsControl);
- } else {
- app.removeControl(fpsControl);
- }
- }
复制代码 |