現代工業化的推進在極大加速現代化進程的同時也帶來的相應的安全隱患,在傳統的監控領域,一般都是基於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.506064.com/zh-hk/n/209940.html
微信掃一掃
支付寶掃一掃