前言

智能城市是一个系统。也称为网络城市、数字化城市、信息城市。

智能城市建设是一个系统工程:首先实现的是城市管理智能化,由智能城市管理系统辅助管理城市,通过管理系统人们可以监视城市的运行,了解城市每天中发生的变化,以及及时的根据这些变化做出相应的管理;其次是包括智能交通、智能电力、智能安全等基础设施的智能化,交通是一个城市的驱动,交通的畅通加速了城市的发展,通过 Web 可视化的交通管理,可以更及时的了解交通情况,做出处理;智能城市也包括智能医疗、智能家庭、智能教育等社会智能化和智能企业、智能银行、智能商店的生产智能化,从而全面提升城市生产、管理、运行的现代化水平。

本 demo 使用 HT for Web 产品轻量化 HTML5/WebGL 建模的方案,构建了城市建筑群场景,添加了城市道路,实现了智能城市 Web 可视化,还通过动画模拟了城市的运行。

demo 地址:http://www.hightopo.com/demo/intelligent-city/entry/dest/index.html

预览图:

代码实现

加载场景

首先新建一个场景,并将场景添加到页面中。

let dm = this.dm = new ht.DataModel();
let entryG3d = this.entryG3d = new ht.graph3d.Graph3dView(dm);
entryG3d.addToDOM(); // 将场景添加到页面中

HT 的组件一般都会嵌入 BorderPane、SplitView 和 TabView 容器中使用,而最外层的 HT 组件则需要用户手工将 getView() 返回的底层 div 元素添加到页面的 DOM 元素中,这里需要注意的是,当父容器大小变化时,如果父容器是 BorderPane 和 SplitView 等这些 HT 预定义的容器组件,则 HT 的容器会自动递归调用孩子组件 invalidate 函数通知更新。但如果父容器是原生的 html 元素, 则 HT 组件无法获知需要更新,因此最外层的 HT 组件一般需要监听 window的窗口大小变化事件,调用最外层组件 invalidate 函数进行更新。

为了最外层组件加载填充满窗口的方便性,HT 的所有组件都有 addToDOM 函数,其实现逻辑如下,其中 iv 是 invalidate 的简写:

addToDOM = function(){
var self = this,
view = self.getView(), // 获取组件 div
style = view.style;
document.body.appendChild(view); // 将组件添加到文档对象中
style.left = '0';
style.right = '0';
style.top = '0';
style.bottom = '0';
window.addEventListener('resize', function () { self.iv(); }, false); // 监听窗口变化,刷新组件
}

接下来反序列化城市场景 json。

ht.Default.xhrLoad('scenes/园区/城市demo.json', (text) => {
let json = ht.Default.parse(text); // 还原 json 字符串
let scene = json.scene; // 获取 json 中设置的投影参数
entryG3d.setEye(scene.eye); // 设置视角
entryG3d.setCenter(scene.center); // 设置目标中心点
entryG3d.setFar(scene.far); // 设置远端截面位置
entryG3d.setNear(scene.near); // 设置近端截面位置
dm.deserialize(text); // 场景反序列化
this.setSkyBox(entryG3d); // 设置天空球
this.initentryG3dEvent(); // 添加场景监听事件
this.startAnimate(); // 启动城市动画
this.startCarAnimate(['car1Line'], dm.getDataByTag('car1')); // 启动消防小车1动画
this.startCarAnimate(['car2Line'], dm.getDataByTag('car2')); // 启动消防小车2动画
});

场景渲染

1. 环境光贴图:将贴图影像渲染在场景中,通过 node.s('envmap', 0.5) 来设置节点的渲染程度,主要用于地板添加物体倒影效果,也可像本 demo 一样添加星光点缀。

dataModel.setEnvmap('环境光贴图.png'); // 贴图要求宽高像素为 2^n
node.s('envmap', 0.1);

对比图:

     

右图中环境光贴图为星光,城市中心区域有了蓝色的星空色,在 demo 中旋转场景也能看到明显的星光变化。

2. 辉光:本是指低压气体中气体放电的物理现象,用在 3D 场景中将亮色突出显示,呈现一种发光的效果,可控制辉光强度,辉光显示的范围和发光阀值。

g3d.enablePostProcessing('Bloom', true); // 开启辉光
module = g3d.getPostProcessingModule('Bloom');
module.strength = 0.18; // 强度
module.threshold = 0.62; // 阈值
module.radius = 0.4; //范围
g3d.iv(); // 刷新拓扑

对比图:

     

3. 景深:对场景中心周围的清晰程度的控制,将周围虚化,美化画面,突出主体,增强透视,可控制景深阀值(周围模糊范围程度)。

g3d.enablePostProcessing('Dof', true); // 开启景深
module = g3d.getPostProcessingModule('Dof');
module.aperture = 0.18; // 景深阀值
module.image= '景深贴图.png'; // 景深贴图
g3d.iv(); // 刷新拓扑

对比图:

     

可以看到右图中卫星区域和最左侧变得模糊。

4. 天空球:将场景模型放置在一个大的球体中,球体内部进行贴图,来模拟天空。

node = new ht.Node()
node.s({
'shape3d':'sphere', // 球体
'shape3d.image': 'earth' // 贴图路径
});
node.s3(10000, 10000, 10000);
g3d.dm().add(node);
g3d.setSkyBox(node); // 设置天空球

动画实现

加载后的城市场景如下图所示:

我们可以看到建筑物群有各自的数据展示面板,围绕着中心大楼有一个光圈列车模型,是模拟的城市中心列车,还有右边一个城市卫星,现在我们来让数据面板数据变化,让列车、卫星开始动起来。

动画的实现是用调度函数来实现的(调度手册),我们先了解一下调度函数的用法:

HT 中调度进行的流程是,先通过 dataModel 添加调度任务,dataModel 会在调度任务指定的时间间隔到达时, 遍历 dataModel 所有图元回调调度任务的 action 函数,可在该函数中对传入的 data 图元做相应的属性修改以达到动画效果。

dataModel.addScheduleTask(task) 添加调度任务,其中 task 为 json 对象,可指定如下属性:

  • interval:间隔毫秒数,默认值为10
  • enabled:是否启用开关,默认为 true
  • action:间隔动作函数,该函数必须设置

dataModel.removeScheduleTask(task) 删除调度任务,其中 task 为以前添加过的调度任务对象。

动画实现代码如下:

startAnimate() {
let dr = Math.PI / 180 * 2, PI2 = Math.PI * 2;
let dm = this.dm;
let rotateOval = dm.getDataByTag('rotateOval'); // 获取城市列车模型
let logo = dm.getDataByTag('logo'); // 获取城市卫星模型

   // 设置动画参数 
let roatateTask = this.roatateTask = {
interval: 100,
action: function(data){
if(data === rotateOval || data === logo){
data.setRotation((data.getRotation() - dr) % PI2);
}
}
};
dm.addScheduleTask(roatateTask); // 将动画加入到调度任务中

   // 获取建筑物数据面板
let dglsd = dm.getDataByTag('dglsd'); // 戴谷岭隧道
let xlsd = dm.getDataByTag('xlsd'); // 杏林隧道
let xmg = dm.getDataByTag('xmg'); // 厦门港
let jcglzx = dm.getDataByTag('jcglzx'); // 机场管理中心    // 模拟建筑物数据面板的动态展示
let valueChangeTask = this.valueChangeTask = {
interval: 1000,
action: function(data){
if(data === dglsd){
data.a('carHour', util.randomNumBetween(3000, 6000));
}
if(data === xlsd){
data.a('carHour', util.randomNumBetween(3000, 6000));
}
if(data === xmg){
data.a('ttl', util.randomNumBetween(3, 10));
data.a('zy', util.randomNumBetween(0, 100));
}
if(data === jcglzx){
data.a('sskll', util.randomNumBetween(500, 2000));
}
}
};
dm.addScheduleTask(valueChangeTask); // 将数据变化加入到调度任务中
}

实现的动画效果如下图:

demo 还模拟了消防车赶往火灾发生地,动画如下:

消防车的行驶用到了 ht.Default.startAnim,我们先来了解一下:

ht.Default.startAnim({
frames: 12, // 动画帧数
interval: 10, // 动画帧间隔毫秒数
easing: function(t){ return t * t; }, // 动画缓动函数,默认采用 ht.Default.animEasing
finishFunc: function(){ console.log('Done!') }, // 动画结束后调用的函数。
action: function(v, t){ // action函数必须提供,实现动画过程中的属性变化。
node.setPosition( // 此例子展示将节点`node`从位置`p1`动画到位置`p2`。
p1.x + (p2.x - p1.x) * v,
p1.y + (p2.y - p1.y) * v
);
}
});

以上为 Frame-Based 方式动画, 这种方式用户通过指定 frames 动画帧数,以及 interval 动画帧间隔参数控制动画效果。

ht.Default.startAnim({
duration: 500, // 动画周期毫秒数,默认采用`ht.Default.animDuration`
action: function(v, t){
...
}
});

以上为 Time-Based 方式动画,该方式用户只需要指定 duration 的动画周期的毫秒数即可,HT 将在指定的时间周期内完成动画。

由于 js 语言无法精确控制 interval 时间间隔, 采用 Frame-Based 不能精确控制动画时间周期,即使相同的 frames 和 interval 参数在不同的环境,可能会出现动画周期差异较大的问题, 因此 HT 默认采用 Time-based 的方式,如果不设置 duration 和 frames 参数,则 duration 参数将被系统自动设置为 ht.Default.animDuration 值。

消防车行驶实现代码如下:

startCarAnimate(polylineTagArr, airNode) { // 传入消防车行驶路线 tag(唯一标签)组和消防车模型节点
let dm = this.dm;
let entryG3d = this.entryG3d;
let curIndex = 0;
let polyline = dm.getDataByTag(polylineTagArr[curIndex]); // 获取消防路线 ht.PolyLine(http://www.hightopo.com/guide/guide/core/shape/ht-shape-guide.html#ref_different
let prePos = airNode.p3(); // 获取节点初始位置
let preRotate = airNode.r3(); // 获取节点初始旋转角度
let lineLength = entryG3d.getLineLength(polyline); // 获取管道长度
let params = { // 设置消防车行驶动画参数
duration: 10000,
easing: function(t){
return t * t;
},
action: function(v, t){
let offset = entryG3d.getLineOffset(polyline, lineLength * v), // 获取管道指定比例的偏移信息
point = offset.point,
px = point.x,
py = point.y,
pz = point.z,
tangent = offset.tangent,
tx = tangent.x,
ty = tangent.y,
tz = tangent.z;
airNode.p3(px, py, pz); // 移动消防车到下一位置
airNode.lookAt([px + tx, py + ty, pz + tz], 'front'); // 消防车沿着管道方向转向
},
finishFunc: function(){
if(curIndex < polylineTagArr.length - 1) { // 进入下一段路线
curIndex++;
}
else {
curIndex = 0; // 返回第一段路线
}
if(curIndex === 0) { // 消防车返回原点
airNode.p3(prePos);
airNode.r3(preRotate);
}
polyline = dm.getDataByTag(polylineTagArr[curIndex]);
lineLength = entryG3d.getLineLength(polyline);
this.carAni = ht.Default.startAnim(params); // 执行下一段行驶动画
}
};
this.carAni = ht.Default.startAnim(params);
}

消防车的行驶可总结为先获取行驶道路的管线长度信息,计算一定比例的管线偏移点,移动消防车位置到计算的偏移点,调整消防车模型的朝向,计算下一偏移点进行位移,直到到达当前管线尾,然后获取下一管线的长度信息,继续进行偏移。

代码中提到的三个方法:getLineLength、getLineOffset、lookAt,我们来了解一下。

  1. g3d.getLineLength(edgeOrPolyLine)

  根据参数 edgeOrPolyLine 方法命名我们就可以知道此方法是获取连线或管道的长度。

  2. g3d.getLineOffset(edgeOrPolyLine, offset)

    • edgeOrPolyLine  连线或者管道
    • offset 偏移百分比

  此方法获取连线或者管道的偏移信息,返回 { point, tangent },其中 point 是 3D 坐标,tangent 是 point 点沿当前线的切线向量。

  3. node.lookAt(point, direction)

    • point 3D 坐标点
    • direction 节点某一面,值可为 ‘front | back | left  | right | top | bottom’

  将节点的某一面朝向空间某坐标点。

消防车行驶转向示意图:

  1. 起始位置;

  2. 通过 setPosition3d(point.x, point.y, point.z) 到达下一位置,红线为 tangent 切线向量;

  3. 通过 node.lookAt([point.x + tangent.x, point.y + tangent.y, point.z + tangent.z], 'front'); 将车头摆向切线方向;

  4.5. 重复以上步骤。

  其中 [point.x + tangent.x, point.y + tangent.y, point.z + tangent.z] 即为目标点 target 的坐标。

  

总结

智能城市系统还包括智能楼宇的管理:

http://www.hightopo.com/demo/ht-smart-building/

地铁是一个城市的核心交通工具,地铁站的管理也必不可少:

http://www.hightopo.com/demo/ht-subway/

基于 HTML5 WebGL 智能城市的模拟运行的更多相关文章

  1. 基于 HTML5 WebGL 的智慧城市(一)

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

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

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

  3. 基于 HTML5 WebGL 的计量站三维可视化监控系统 Web 组态工控应用

    得益于 HTML5 WebGL 技术的成熟,从技术上对工控管理的可视化,数据可视化变得简单易行!完成对工控设备的管理效率,资源管理,风险管理等的大幅度提高,同时也对国家工业4.0计划作出有力响应! 如 ...

  4. 基于 HTML5 WebGL 的 3D 风机 Web 组态工业互联网应用

    基于 HTML5 WebGL 的 3D 风机 Web 组态工业互联网应用 前言 在目前大数据时代背景之下,数据可视化的需求也变得越来越庞大,在数据可视化的背景之下,通过智能机器间的链接并最终将人机链接 ...

  5. 基于 HTML5 + WebGL 实现 3D 可视化地铁系统

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

  6. 基于 HTML5 + WebGL 的地铁 3D 可视化系统

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

  7. 基于 HTML5 WebGL 与 WebVR 3D 虚实现实的可视化培训系统

    前言 2019 年 VR, AR, XR, 5G, 工业互联网等名词频繁出现在我们的视野中,信息的分享与虚实的结合已经成为大势所趋,5G 是新一代信息通信技术升级的重要方向,工业互联网是制造业转型升级 ...

  8. 基于 HTML5 WebGL + WebVR 的 3D 虚实现实可视化培训系统

    前言 2019 年 VR, AR, XR, 5G, 工业互联网等名词频繁出现在我们的视野中,信息的分享与虚实的结合已经成为大势所趋,5G 是新一代信息通信技术升级的重要方向,工业互联网是制造业转型升级 ...

  9. 基于 HTML5 WebGL + WebVR 的 3D 虚实现实可视化系统

    前言 2019 年 VR, AR, XR, 5G, 工业互联网等名词频繁出现在我们的视野中,信息的分享与虚实的结合已经成为大势所趋,5G 是新一代信息通信技术升级的重要方向,工业互联网是制造业转型升级 ...

随机推荐

  1. 理解JMeter聚合报告(Aggregate Report)

    Aggregate Report 是 JMeter 常用的一个 Listener,中文被翻译为“聚合报告”.今天再次有同行问到这个报告中的各项数据表示什么意思,顺便在这里公布一下,以备大家查阅. 如果 ...

  2. 控制HTML页面内容不能选中的方法

    方法有二 一: css 方法 user-seletct: none;-webkit-user-seletct: none;-moz-user-seletct: none;-ms-user-seletc ...

  3. CAGradientLayer实现图片渐变透明效果

    CAGradientLayer实现图片渐变透明效果 要实现的效果如下: 源码: // // RootViewController.m // CAGradientLayer // // Copyrigh ...

  4. 延期年金(deferred annuity)

    含义:推迟m个时期后才开始付款的年金. 延期年金现值为 二.永续年金(Perpetuity) 永续年金:无限期支付下去的年金. 为期末付永续年金(perpetuity-immediate)的现值 表示 ...

  5. oracle中RAW数据类型

    近日在研究v$latch视图时,发现一个从未见过的数据类型.v$latch 中ADDR属性的数据类型为RAW(4|8)  同时也发现v$process中的ADDR属性的数据类型也为RAW(4|8).于 ...

  6. December 10th 2016 Week 50th Saturday

    Storms make trees take deeper roots. 风暴使树木深深扎根. Sometimes, you may feel frustrated for failing to wi ...

  7. 把所有的小图标一起做成雪碧图吧 请用gulp-css-spriter.

    用gulp-css-spriter很简单. 第一步: 在某个文件夹用shitf+鼠标右键 第二步: npm install gulp-css-spriter https://www.npmjs.com ...

  8. EDM邮件营销激活不活跃客户群的五大策略

    有很多朋友给U-Mail马工大倒苦水:我邮件群发做了大量工作,可是有一些潜在消费者却始终无动于衷,你要说他没什么用吧,可是明明显示他有打开过,你把这个地址排除出去又有点可惜了,你要说他不可或缺,可是他 ...

  9. U-Mail:邮件营销如何大量获取并筛选有效地址

    工欲善其事必先利其器,在所有的邮件推广中,最基础的工作就是收集到足够多的有效邮箱地址,俗话说韩信将兵多多益善,邮箱地址越多,也就意味着潜在的转化为消费者的数量众多,但是如果大量滥竽充数的地址被搜罗进来 ...

  10. 理解活在Iphone中的那些App (四)

    App生存环境之宿主环境 终于开始说一些技术性的话题了,从这里开始的一些技术细节的东西,以前我也没有太刻意的注意过.为了写这个也是刚刚看了一点资料,如果有纰漏,恳请指出. 一个App生存的宿主环境主要 ...