古代工业化的推动在极大减速现代化过程的同时也带来的相应的安全隐患,在传统的监控畛域,个别都是基于 Web 前端技术来实现 2D 可视化监控,本文采纳 ThingJS 来结构轻量化的 3D 可视化场景,该 3D 场景展现了一个现代化商场的数字孪生可视化场景,包含人员的实时地位、电子围栏的范畴、现场的平安状况等等,帮忙直观的理解以后人员的平安情况。
电子围栏 又称周界防盗报警零碎,监控防区工作状态,实景中的电子围栏零碎用于农业、畜牧业,以及监狱、军事设施等平安敏感地区。ThingJS 平台上,电子围栏指的是一个区域,应用 PolygonRegion 属性。创立物体对象或模型并开启挪动性能,即可开始检测指标点是否进入电子围栏区域,判断 true 或 false 显示告警反馈。
本篇文章通过对数字孪生可视化场景的搭建和模型的加载,人物实时定位代码的实现、电子围栏和轨迹图的实现进行论述,理解如何通过应用 ThingJS 实现一个简略的 3D 电子围栏可视化。
// 增加电子围栏
new THING.widget.Button('增加电子围栏', function() {
// 形成多边形的点(取世界坐标系下的坐标)points = [[81, 0.5, 63],
[81, 0.5, 52],
[72, 0.5, 52],
[72, 0.5, 63]
];
if (polygonMarker) {return;}
// 创立电子围栏(区域)polygonMarker = app.create({
type: 'PolygonRegion',
points: points, // 传入世界坐标系下点坐标
style: {
regionOpacity: .6,
regionColor: '#3CF9DF', // 区域色彩
lineColor: '#3CF9DF' // 线框色彩
}
});
// 设置永远在最上层显示
polygonMarker.style.alwaysOnTop = false;
})
当人物或物体对象登程警报时,有 2 种形式揭示留神,一是踏足的禁区围栏色彩产生扭转;二是展现面板显示报警信息,可视化监控指标点的挪动范畴。
残缺代码如下:
// 增加图片标注
new THING.widget.Button('增加图片标注', function() {var coord = [83, 0.5, 61];
if (marker1) {return;}
// 创立指标点(marker)
marker1 = app.create({
type: "Marker",
id: "marker1",
url: "/guide/examples/images/navigation/user.png",
position: coord,
size: 1
})
})
var point = [[81, 63],
[81, 52],
[72, 52],
[72, 63]
];
// 挪动图片标注
new THING.widget.Button('挪动图片标注', function() {var markerEndPoint = [68, 0.5, 55];
if (marker1 != null) {var moveState = marker1.getAttribute('moveState');
if (moveState == 'complete') {marker1.off('update', null, '监控图片标注');
return;
}
// 指标点挪动
marker1.moveTo({
position: markerEndPoint, // 挪动到起点地位
time: 2 * 1000,
orientToPath: true, // 沿门路方向
complete: function(ev) {marker1.off('update', null, '监控图片标注');
$('.warninfo1').css('display', 'none');
$('.warninfo2').css('display', 'block');
$('.warninfo3').css('display', 'none');
marker1.setAttribute('moveState', 'complete');
}
})
}
if (points != null) {
// 监控图片标注是否进入电子围栏区域
if (marker1 != null) {marker1.on('update', function() {if (polygonMarker != null) {var intoPolygonMarker = isInPolygon([marker1.position[0], marker1.position[2]], point);
if (intoPolygonMarker) {
polygonMarker.regionColor = '#a94442';
polygonMarker.lineColor = '#a94442'
$('.warninfo1').css('display', 'block');
$('.warninfo2').css('display', 'none');
$('.warninfo3').css('display', 'none');
} else {
polygonMarker.regionColor = '#3CF9DF';
polygonMarker.lineColor = '#3CF9DF'
$('.warninfo1').css('display', 'none');
$('.warninfo2').css('display', 'none');
$('.warninfo3').css('display', 'block');
}
}
}, '监控图片标注')
}
}
})
// 增加模型标注
new THING.widget.Button('增加模型标注', function() {// 创立指标点(Obj)
people = app.query('#worker')[0];
people.position = [83, 0.1, 56];
people.visible = true;
people.scale = [1.5, 1.5, 1.5];
})
// 挪动模型标注
new THING.widget.Button('挪动模型标注', function() {var objEndPoint = [70, 0.1, 60];
if (people != null) {var moveState = people.getAttribute('moveState');
if (moveState == 'complete') {people.off('update', null, '监控图片标注');
return;
}
// 播放模型动画
people.playAnimation({
name: '走',
speed: 1,
loopType: THING.LoopType.Repeat,
});
// 模型挪动
people.moveTo({
position: objEndPoint, // 挪动到起点地位
orientToPath: true, // 沿门路方向
time: 8 * 1000,
complete: function(ev) {people.stopAnimation('走');
people.off('update', null, '监控模型标注');
$('.warninfo1').css('display', 'none');
$('.warninfo2').css('display', 'block');
$('.warninfo3').css('display', 'none');
people.setAttribute('moveState', 'complete');
}
})
}
if (points != null) {
// 监控模型标注是否进入电子围栏区域
if (people != null) {people.on('update', function() {if (polygonMarker != null) {var intoPolygonMarker = isInPolygon([people.position[0], people.position[2]], point);
if (intoPolygonMarker) {
polygonMarker.regionColor = '#a94442';
polygonMarker.lineColor = '#a94442'
$('.warninfo1').css('display', 'block');
$('.warninfo2').css('display', 'none');
$('.warninfo3').css('display', 'none');
} else {
polygonMarker.regionColor = '#3CF9DF';
polygonMarker.lineColor = '#3CF9DF'
$('.warninfo1').css('display', 'none');
$('.warninfo2').css('display', 'none');
$('.warninfo3').css('display', 'block');
}
}
}, '监控模型标注')
}
}
})
// 重置
new THING.widget.Button('重置', function() {if (polygonMarker) {polygonMarker.destroy();
polygonMarker = null;
}
if (marker1) {marker1.destroy();
marker1 = null;
}
if (people) {
people.visible = false;
people.setAttribute('moveState', null);
}
$('.warninfo1').css('display', 'none');
$('.warninfo1').css('display', 'none');
$('.warninfo1').css('display', 'block');
})
createTip(); // 创立提醒面板});
/**
* 创立提醒面板
*/
function createTip() {
var html =
`<div class="fencing" style="width:200px;position: absolute;top: 50px;left: 50%;transform: translateX(-50%);z-index: 999;">
<div class="alert alert-danger warninfo1" role="alert" style="padding: 15px;margin-bottom: 20px;color: #a94442;background-color: #f2dede;border-color: #ebccd1;border-radius: 4px;display:none;"> 指标已进入围栏 </div>
<div class="alert alert-info warninfo2" role="alert" style="padding: 15px;margin-bottom: 20px;color: #31708f;background-color: #d9edf7;border-color: #bce8f1;border-radius: 4px;display:none;"> 达到目的地 </div>
<div class="alert alert-warning warninfo3" role="alert" style="padding: 15px;margin-bottom: 20px;color: #8a6d3b;background-color: #fcf8e3;border-color: #faebcc;border-radius: 4px;"> 指标未进入围栏 </div>
<div onclick="fenClose()" style="cursor: pointer;position: absolute;top: -7px;right: -8px;width: 16px;height: 16px;border-radius: 50%;background-color: #777777;border: 3px solid #ffffff;">
<div style="position: absolute;width: 10px;height: 2px;background-color: #fff;transform: rotate(45deg);top: 7px;left: 3px;"></div>
<div style="position: absolute;width: 10px;height: 2px;background-color: #fff;transform: rotate(-45deg);top: 7px;left: 3px;"></div>
</div>
</div>`;
$('#div2d').append($(html));
}
/**
* 敞开提醒面板
*/
function fenClose() {$(".fencing").hide();}
/**
* 检测指标点是否进入电子围栏区域
* @param {Array} checkPoint - 校验坐标
* @param {Array} polygonPoints - 造成电子围栏的坐标
* @returns {Boolean} true 或 false
* @description 此办法仅判断处于同一个立体的指标点是否在区域内(只判断坐标 x 和 z 值),* 不思考两者以后离地高度(坐标的 y 值)*/
function isInPolygon(checkPoint, polygonPoints) {
var counter = 0;
var i;
var xinters;
var p1, p2;
var pointCount = polygonPoints.length;
p1 = polygonPoints[0];
for (i = 1; i <= pointCount; i++) {p2 = polygonPoints[i % pointCount];
if (checkPoint[0] > Math.min(p1[0], p2[0]) && checkPoint[0] <= Math.max(p1[0], p2[0])) {if (checkPoint[1] <= Math.max(p1[1], p2[1])) {if (p1[0] != p2[0]) {xinters = (checkPoint[0] - p1[0]) * (p2[1] - p1[1]) / (p2[0] - p1[0]) + p1[1];
if (p1[1] == p2[1] || checkPoint[1] <= xinters) {counter++;}
}
}
}
p1 = p2;
}
if (counter % 2 == 0) {return false;} else {return true;}
}
—————————————————
数字孪生可视化:https://www.thingjs.com/