应用场景:
场景1:在场(厂)内作业中,叉车作为特种设备,必须按要求在场(厂)内行驶,未获得监管部门允许,不得私自驶入场(厂)外道路。
此时,需要智能终端设备能对厂车的行驶区域做监管预警,防止车辆未按规定区域行驶,这就用到了电子围栏
当设备位置超出/驶出电子围栏区域时,设备触发报警并上报平台预警。
场景2:监所里看管犯人,如果犯人离开了管控区域,则发生报警
废话不多说,先上个效果图
ElectronicFence.ts
/**
* @description 电子围栏功能
*/
/**
* 实现思路
* 1、通过鼠标拾取点位,创建自定义形状的电子围栏
* 2、移除电子围栏
* 3、监测电子围栏(通过update方式,刷新移动物体,拿到电子围栏形成的点位x,z坐标,移动物体的x,z坐标。通过算法监测物体位置是否在围栏位置范围内)
*/
// 绘制电子围栏的工具
THING.Utils.dynamicLoad(['/vendor/areaTools/index.js'])
class ElectronicFence {
private _app: any
constructor() {
this._app = THING.App.current
}
/**
* @description 鼠标拾取点位,创建自定义形状的电子围栏
*/
drawElectronicFence() {
areaTools.init()
}
/**
* @description 结束绘制电子围栏
*/
endDrawElectronicFence() {
// 将数组存储在localStore
const points = areaTools.getCoordinatesArray()
localStorage.setItem('electronicFencePoint', JSON.stringify(points))
// 销毁拾取的平面
areaTools.destroyArea()
}
/**
* @description 创建电子围栏
*/
createElectronicFence(
name: string,
points: any[],
regionColor: string,
lineColor: string
) {
const area = this._app.create({
type: 'PolygonRegion',
name: `${name}_electronicFence`,
points: points, // 传入世界坐标系下点坐标
style: {
regionColor: regionColor, // 区域颜色
lineColor: lineColor, // 边框颜色
regionOpacity: 0.7, // 不透明度 (默认是 0.5 半透明)
},
})
return area
}
/**
* @description 移除电子围栏
*/
removeElectronicFence() {
const fences = this._app.query(/electronicFence/)
if (fences.length) fences.destroyAll()
}
/**
* @description 检测目标点是否进入电子围栏区域
* @param {Array} checkPoint - 校验坐标
* @param {Array} polygonPoints - 形成电子围栏的坐标
* @returns {Boolean} true 或 false
* 此方法仅判断处于同一个平面的目标点是否在区域内(只判断坐标x和z值),
* 不考虑两者当前离地高度(坐标的y值)
*/
monitorElectronicFence(checkPoint: any[], polygonPoints: any[]) {
let counter = 0
let p1, p2
const pointCount = polygonPoints.length
p1 = polygonPoints[0]
for (let i = 1; i <= pointCount; i += 1) {
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]) {
const 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
}
}
}
export default ElectronicFence
Demo.vue
<!--
* @Author: 袁宇宙中有朵云
* @Description: 电子围栏Demo
-->
<template>
<div class="demo">
<button @click="drawFence">绘制电子围栏</button>
<button @click="endDrawFence">结束绘制电子围栏</button>
<button @click="createFence">创建电子围栏</button>
<button @click="removeFence">移除电子围栏</button>
<button @click="monitorFence">监测电子围栏</button>
<div class="tips" v-show="tipMessage">{{ tipMessage }}</div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import ElectronicFence from '@/controllers/business/ElectronicFence'
// 电子围栏对象
let areaPolygonObj: any = {}
const tipMessage = ref('')
onMounted(() => {
window.electronicFence = new ElectronicFence()
})
onBeforeUnmount(() => {
window.electronicFence = null
removeFence()
})
function drawFence() {
window.electronicFence.drawElectronicFence()
}
function endDrawFence() {
window.electronicFence.endDrawElectronicFence()
}
function createFence() {
// 获取localStorage中的围栏信息
const points: any = localStorage.getItem('electronicFencePoint')
areaPolygonObj = window.electronicFence.createElectronicFence('电子围栏1号', JSON.parse(points), '#3CF9DF', '#3CF9DF')
}
function removeFence() {
window.electronicFence.removeElectronicFence()
const box = app.query('box')[0]
if (box) box.destroy()
tipMessage.value = ''
}
// 模拟人物在电子围栏中移动的路线
function monitorFence() {
const paths = [
[-322.83731293191397, -0.00004130086550686862, 291.00748323559094],
[-306.4490531432021, -0.00004114802510697679, 286.5421925683819],
[-304.3588156887912, -0.00004144668099810567, 295.2675387077349],
[-290.86054423027514, -0.00004128456881435927, 290.53136922421515],
[-298.2861463575858, -0.000041718462059761596, 303.2077260104783],
[-319.55418467413523, -0.000041723725334022466, 303.361494722941],
[-328.35732290653607, -0.00004182404337797763, 306.292324785822]
]
// 创建一个白盒子
const box = app.create({
type: 'Box',
name: 'box',
position: paths[0],
})
box.movePath({
path: paths,
orientToPath: true,
time: 10 * 1000,
complete: () => {
console.log('移动完成')
},
update: () => {
const checkPoint = [box.position[0], box.position[2]]
const points: any = localStorage.getItem('electronicFencePoint')
const pointsArr: any[] = []
JSON.parse(points).forEach((p: any) => {
pointsArr.push([p[0], p[2]])
})
const isIn = window.electronicFence.monitorElectronicFence(checkPoint, pointsArr)
if (isIn) {
areaPolygonObj.style.regionColor = '#3CF9DF'
tipMessage.value = ''
} else {
areaPolygonObj.style.regionColor = '#a94442'
tipMessage.value = '当前位置不在电子围栏内'
}
}
})
}
</script>
<style lang="less" scoped>
.demo {
position: absolute;
top: 10px;
width: 100%;
height: 100%;
pointer-events: none;
button {
pointer-events: all;
}
.tips {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: auto;
height: 40px;
background: rgba(255, 255, 255, 0.5);
color: red;
text-align: center;
line-height: 20px;
}
}
</style>
以上功能的实现,里面涉及了一个绘制电子围栏区域的小工具,代码未呈现出来。以上只是提供了一个大概实现思路,需要根据具体业务需求进行使用。
文章中使用的API,可通过ThingJS的API文档查看学习https://docs.thingjs.com/cn/apidocs/