前言

低碳工业园区的建设与推广是我国推进工业低碳转型的重要举措,低碳工业园区能源与碳排放管控平台是低碳工业园区建设的关键环节。如何对园区内的企业的能源量进行采集、计量、碳排放核算,如何对能源消耗和碳排放进行实时动态监测等问题,涉及多个技术领域,专业性强。其数据不仅要求准确,更要求真实可靠(即可核查、可溯源)。这是低碳工业园区“管控平台”建设的核心任务,也是当前我国工业园区建设中需要迫切解决的主要问题之一。

http://www.hightopo.com/demo/HTBuilding/index.html

这个 gif 图中显示的是一个 2D 3D 结合而成的低碳工业园区的能源监控系统,主要对各个楼宇以及园区整体的水、电等的使用量的实时监控。

代码实现

搭建场景

要创建出一个 3D 的低碳工业园区场景并不难,但是如何在同一个界面上同时显示 2D 和 3D 的场景呢?想要做出炫酷的效果,这种方式在很多情况下是非常有用的。

整个低碳工业园区的场景是搭建在 2D 上的,我们知道,HTML 给 DOM 元素设置图片只能用传统的栅格位图,但是如果怕图片被拉伸而导致图片模糊或者变形等结果,用 json 格式的矢量图片来实现是最好的,栅格位图在拉伸放大或缩小时会出现图形模糊,线条变粗出现锯齿等问题。 而矢量图片通过点、线和多边形来描述图形,因此在无限放大和缩小图片的情况下依然能保持一致的精确度。

首先我搭建了一个 2D 的场景用来放置我们的 json 矢量图,利用 ht.Default.xhrLoad 函数将 json 矢量背景图反序列化显示在 gv 上,这个 json 矢量背景图中除了作为背景的 node 还有另外两个节点,如下图,红线框起来的比较大的这个节点是用来装 3D 场景的,而右边框起来的比较小的节点是用来放置另外一个 gv 的(暂时还用不到,后期需要添加类似 form 表单的功能,所以我需要固定位置):

ht.Default.xhrLoad('displays/background.json', function(text) {
dm.deserialize(text);// 反序列化数据到数据模型 gv.addToDOM();// 将 2D 场景添加到 body 体中
});

这个 2D 场景作为背景的部分就设置完毕,接下来看看如何在 2D 场景的基础下放上 3D 场景。

2D 中添加 3D 场景

向 2D 中添加 3D 也是非常容易,问题是如何使 3D 场景根据 2D 场景缩放和平移来进行自适应变化,使 3D 场景始终保持在 2D 场景的某个固定的位置?我是通过监听 gv 的属性变化事件,监听到 zoom、translate 等属性,对 3D 场景进行自动布局的操作:

var g3dInfo = create3D('g3dNode');
gv.mp(function(e) {// 监听 gv 的属性变化事件
if (e.property === 'zoom' || e.property === 'translateX' || e.property === 'translateY' ) {
layout(g3dInfo);
}
}); function layout(info) {
var rect = info.node.getRect(),// 获取场景依赖的节点的 矩形区域
zoom = gv.getZoom(),// 获取当前 gv 的缩放值
tx = gv.tx(),// 获取当前 gv 的水平平移值
ty = gv.ty();// 获取当前 gv 的垂直平移值 // 依赖的节点的大小根据 zoom 缩放值来进行缩放
rect.x *= zoom,
rect.y *= zoom,
rect.width *= zoom,
rect.height *= zoom; var x = rect.x + tx,
y = rect.y + ty; // 设置场景自动布局
if (info.g3d) info.g3d.layout(x, y, rect.width, rect.height);
}

眼尖的同学应该已经注意到了,我没有写出 create3D 函数的声明,就展示的效果而言,这个方法只是将场景 json 图纸反序列化到 3D 场景中,并追加了一个对象 info,将 3D 场景所依赖的 node 和 3D 场景的变量传进去:

function create3D(tag) {
var g3d = new ht.graph3d.Graph3dView();// 3D 组件
var dataModel = g3d.dm();// 获取 3D 场景的数据容器
gv.getView().appendChild(g3d.getView());// 将 3D 场景添加到 2D 场景中
ht.Default.xhrLoad('scenes/电云维.json', function(text) {// 加载 3D 场景的 json 矢量图纸
dataModel.deserialize(text);// 反序列化数据到数据模型
}); // 停止事件的传播,阻止它被分派到其他 Document 节点
g3d.getView().addEventListener('mousedown', function(e) { e.stopPropagation()});
g3d.getView().addEventListener('mousewheel', function(e) { e.stopPropagation()});
if (isFirefox=navigator.userAgent.indexOf("Firefox") > 0) {
g3d.getView().addEventListener('DOMMouseScroll', function(e) { e.stopPropagation()});
} var info = {
g3d: g3d,
node: dm.getDataByTag(tag),
};
return info;
}

2D 和 3D 在鼠标事件上有很多相同的点,但是我们并不希望在操作 3D 场景的同时 2D 场景也跟着变化,所以上面代码中禁止了鼠标按下和滚轮的事件传播。

楼宇信息显示

低碳工业园区监控系统实现的其中一个功能:点击楼宇视线移到楼宇显示到一个比较合适的位置,并且楼宇顶部显示一个面板用来展示当前楼宇的信息。这里我直接创建了一个节点,通过设置节点的 shape3d 属性为 billboard 即可显示为一个“面片”,面板非常好用,首先它只有一个面,在 3D 场景中如果需要大量的显示数据的节点,推荐用这个 billboard 类型,非常省性能。

// 创建在建筑上面的显示面板
var billboard = new ht.Node();
billboard.setScaleX(2);// 将节点 X 轴上放大 2 倍
billboard.setScaleTall(2);// 将节点 Y 轴上放大 2 倍
billboard.s({
'shape3d': 'billboard',// 此类型为一个面片
'shape3d.image': 'symbols/nodeForm.json',// 设置面片的显示图片为矢量图片
'shape3d.autorotate': true,// 始终面向相机
'shape3d.vector.dynamic': true,// 设置矢量图形
'3d.visible': false// 不可见
});
billboard.setTag('billboard');// 设置节点的 tag 唯一属性
dataModel.add(billboard);// 将节点添加到数据容器中

通过点击不同的楼宇则将信息面板展示在当前点击的楼宇上方, 并根据不同的选中情况对 billboard 进行显隐的控制:

dataModel.sm().ms(function(e) {// 监听选中变化事件
if (e.kind === 'set' || e.kind === 'append') {// 设置选中 及 追加选中
billboard.s('3d.visible', true);
var data = dataModel.sm().ld();// 获取当前选中的最后一个节点
if (!data) return;
billboard.p3(data.getPosition().x, data.getTall() + 200, data.getPosition().y);// 设置 billboard 的位置为当前选中的节点的上方
}
else if (e.kind === 'remove') {// 选中移除
var data = dataModel.sm().ld();// 获取当前最后选中的节点
if (data) {
billboard.setPosition(data.getPosition().x, data.getPosition().y);
billboard.setElevation(data.getTall() + 200);
}
else billboard.s('3d.visible', false);
}
else if (e.kind === 'clear') billboard.s('3d.visible', false);// 清除所有的选中后设置 billboard 不可见
});

(其他例子参考)

http://www.hightopo.com/demo/large-screen-photovoltaic/

至于点击楼宇,从当前视线位置推到节点位置是通过 flyTo 函数,此函数在 6.2.2  版本是有三个参数,参数一为目标节点,参数二为是否动画,参数三为眼睛跟目标节点中心距离的计算,比如下面代码设置 0.5,表示眼睛在上述方向上动态计算距离以将目标适配到屏幕 0.5 里容纳。信息面板上方显示了当前点击的楼宇的名称,我是在设计 3D 场景的图纸时给对应的楼宇设置上 displayName 属性,当前显示则根据这个 displayName 来进行显示。

g3d.mi(function(e) {// 增加交互事件监听器
if(e.kind === 'clickData'){
g3d.flyTo(e.data, true, 0.5);// 将 eye 和 center 从当前位置“飞到”目标节点的位置 第二个参数若是1 则占满全屏。 6.2.2 版本以上有此方法
var name = e.data.getDisplayName(); // 由于 3D 中不能将模型组合到一起,所以我用追加选中的方法来解决
dataModel.each(function(node) {
if(node.getDisplayName() !== name) return;// 我将同一类型的节点的 displayName 设置相同
dataModel.sm().appendSelection(node);
})
}
});

那么,只有一个 billboard,我们如何让这个 billboard 根据不同的楼宇显示不同的信息?这个时候矢量图标的优势又多了一个,通过对矢量图标中的某个部分进行数据绑定进行数据的动态变化,这边我三言两语也讲不完整,我就简单提一下如何实现,剩下的可以去官网中的数据绑定手册中查阅相关资料和具体实现。

前面给 billboard 设置了一个 shape3d.image 属性,设置的图片为 nodeForm.json,这个 json 中有四行文本显示,顶部的文本用来显示当前点击的楼宇的名称。

根据手册我们知道数据绑定的格式分为两种,一种是绑定 function 类型,另一种是绑定 string 类型,如下:

也就是说如果 HT 中没有定义我们需要的属性或者说一个矢量图上有多个相同的属性需要更改为不同的值,就可以通过 attr 来自定义属性,这里我用的就是这个方法:

"text": {
"func": "attr@buildingName",
"value": "赛普健身学院学生宿舍"
}

数据绑定完成后,我们只需要根据这个绑定数据对当前引用这个 json 矢量图标的节点的业务属性变化即可:

// 不同的楼宇上显示的内容不同
billboard.a('buildingName', name);
billboard.a('electricUsage', (Math.random()*300).toFixed(2));
billboard.a('waterUsage', (Math.random()*300).toFixed(2));
billboard.a('gasUsage', (Math.random()*300).toFixed(2));

右侧数据显示

3D 场景创建完毕,接下来如何在 3D 上面再加右边的两个数据显示面板?这里我是在前面 2D json 场景中已排布好位置的节点上添加了另外一个 2D 场景,用来显示整体场景数据。因为这个 gv 上有两个信息面板,所以我直接在 graphView 上添加了两个节点,并将节点添加到这个 graphView 的 dataModel 数据容器上,其他部分我就不再做解释了,都是基础的代码:

function createGV(tag) {
var g2d = new ht.graph.GraphView();// 2D 拓扑场景
var dataModel = g2d.dm();// 获取当前拓扑场景的数据容器
gv.getView().appendChild(g2d.getView());// 将此拓扑场景添加到底层背景图上
g2d.setInteractors([]);// 清除此组件上的交互 // 添加两个节点到拓扑场景上
var node = new ht.Node();
node.setImage('symbols/form.json');
node.setPosition(0, 0);
dataModel.add(node); var node1 = new ht.Node();
node1.setImage('symbols/form1.json');
node1.setPosition(0, dm.getDataByTag(tag).getHeight()/3);
dataModel.add(node1); g2d.fitContent(); setInterval(function() {// form表单数据动态变化
node.a('electricUse', (Math.random()*300).toFixed(2));
node.a('waterUse', (Math.random()*300).toFixed(2));
node.a('gasUse', (Math.random()*300).toFixed(2));
node.a('tempUse', (RandomNumBoth(10, 40))+'');
node.a('wetUse', (Math.floor((Math.random()*100)))+'');
}, 3000); var info = {
g2d: g2d,
node: dm.getDataByTag(tag)
}
return info;
}

以上,整个低碳工业园区监控系统的实现全部结束,有问题的或者建议都可以给我留言,或者直接访问官网(http://hightopo.com/)查阅对应的资料。

基于 H5与webGL 的低碳工业园区监控系统的更多相关文章

  1. 基于 HTML5 WebGL 的低碳工业园区监控系统

    前言 低碳工业园区的建设与推广是我国推进工业低碳转型的重要举措,低碳工业园区能源与碳排放管控平台是低碳工业园区建设的关键环节.如何对园区内的企业的能源量进行采集.计量.碳排放核算,如何对能源消耗和碳排 ...

  2. 基于 HTML5 的 WebGL 3D 智能楼宇监控系统

    前言 智能监控的领域已经涉及到了各大领域,工控.电信.电力.轨道交通.航天航空等等,为了减少人员的消耗,监控系统必不可少.之前我写过一篇 2D 的智能地铁监控系统广受好评,突然觉得,既然 2D 的这么 ...

  3. 基于 HTML5 的 WebGL 自定义 3D 摄像头监控模型

    前言 随着视频监控联网系统的不断普及和发展, 网络摄像机更多的应用于监控系统中,尤其是高清时代的来临,更加快了网络摄像机的发展和应用. 在监控摄像机数量的不断庞大的同时,在监控系统中面临着严峻的现状问 ...

  4. 基于 HTML5 和 WebGL 的地铁站 3D 可视化系统

    前言 工业互联网,物联网,可视化等名词在我们现在信息化的大背景下已经是耳熟能详,日常生活的交通,出行,吃穿等可能都可以用信息化的方式来为我们表达,在传统的可视化监控领域,一般都是基于 Web SCAD ...

  5. 基于 H5 和 webGL 的 3d 智慧城市

    前言 中共中央.国务院在今年12月印发了<长江三角洲区域一体化发展规划纲要>(下文简称<纲要>),并发出通知,要求各地区各部门结合实际认真贯彻落实. <纲要>强调, ...

  6. 基于H5与webGL的 3d 电子围栏展示

    前言 现代工业化的推进在极大加速现代化进程的同时也带来的相应的安全隐患,在传统的可视化监控领域,一般都是基于 Web SCADA 的前端技术来实现 2D 可视化监控,本系统采用 Hightopo 的 ...

  7. 基于 H5与WebGL 的科幻风机 3D 展示

    前言 许多世纪以来,风力机同水力机械一样,作为动力源替代人力.畜力,对生产力的发展发挥过重要作用.近代机电动力的广泛应用以及二十世纪50年代中东油田的发现,使风机发电机的发展缓慢下来. 70年代初期, ...

  8. 基于canvas+ajax+zui.js的蓄电池监控系统

    今天抽空做的,仪表盘用canvas做的,数据采用ajax刷新,左边菜单用zui.js做的

  9. 使用WebGL 自定义 3D 摄像头监控模型

    前言 随着视频监控联网系统的不断普及和发展, 网络摄像机更多的应用于监控系统中,尤其是高清时代的来临,更加快了网络摄像机的发展和应用. 在监控摄像机数量的不断庞大的同时,在监控系统中面临着严峻的现状问 ...

随机推荐

  1. Linux学习_菜鸟教程_3

    我是在UBANTO上运行Linux的,开机启动时按下shift或者Esc都不能进入到grub,没有百度到可靠的教程. 暂时先这样吧.免得我把系统搞坏了,先学点实用的知识~~ Next Chapter

  2. 【转】基于ArcGIS for javascript api 轨迹回放

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/stri ...

  3. Java字符串(String类)

    定义方法: 1.String demo = "test"; 2.String demo = new String(); 3.String demo = new String(&qu ...

  4. Lua表(table)的个人总结

    1.表的简介和构造 table是个很强大且神奇的东西,又可以作为数组和字典,又可以当作对象,设置module.它是由数组和哈希表结合的实现的.他的key可以是除nil以外任意类型的值,key为整数时, ...

  5. Golang最强大的访问控制框架casbin全解析

    Golang最强大的访问控制框架casbin全解析 Casbin是一个强大的.高效的开源访问控制框架,其权限管理机制支持多种访问控制模型.目前这个框架的生态已经发展的越来越好了.提供了各种语言的类库, ...

  6. FJUT-1370 记录一次解题过程

    题目在福工院的1370 首先看题目,好家伙,全英文 那么大致的题意就是.有几个城市同在一条线上(相当于在x轴上),max i是第i个城市到其他所有城市的距离中的最大值,min i也就是所有中最小的. ...

  7. 加深对于 MVC、MVP、MVVM 的概念理解

    目录 MVC 对 MVC 的误解及缘由 MVP MVVM MVC MVC - 维基百科,自由的百科全书 MVC 是软件工程的一种软件架构模式,它不是具体的技术,而是一种代码分层的理念,主要体现了职责分 ...

  8. Kafka日志压缩剖析

    1.概述 最近有些同学在学习Kafka时,问到Kafka的日志压缩(Log Compaction)问题,对于Kafka的日志压缩有些疑惑,今天笔者就为大家来剖析一下Kafka的日志压缩的相关内容. 2 ...

  9. 质数的判定 Miller_Rabin

    ----------- 10^18 #include <bits/stdc++.h> #define min(a,b) ((a)<(b)?(a):(b)) #define max(a ...

  10. dp - 求符合题意的序列的个数

    The sequence of integers a1,a2,…,ak is called a good array if a1=k−1 and a1>0. For example, the s ...