产品需求
需要对路口的图片进行标注,红绿灯、车道线、车道、虚拟线圈等进行标注,用于算法来对视频进行识别
采用的插件是AILablel.js
直接上代码吧
没有拆分
<template>
<div style="display: flex;width: 100%;">
<div :id="id" style="overflow: hidden;
position: relative;
height: 500px;
width: 800px;
border: 1px dashed #ccc;">
<div class="button-wrap">
<el-radio-group size="mini" v-model="mode" style="overflow: hidden;">
<el-radio-button label="PAN">拖动</el-radio-button>
<el-radio-button label="POINT">点</el-radio-button>
<el-radio-button label="LINE">线段</el-radio-button>
<el-radio-button label="POLYLINE">多线段</el-radio-button>
<el-radio-button label="RECT">矩形</el-radio-button>
<el-radio-button label="POLYGON">多边形</el-radio-button>
<el-button type="primary" size="mini" style="float: right; margin-left: 20px;" @click="info">标注规则</el-button>
</el-radio-group>
<span style="margin-left: 10px;">当前绘制 <span style="color: #4ED83A">{{ curDraw }}</span> </span>
<!-- <button class="btn btn-default" @click="getFeatures();">保存标注数据</button>-->
</div>
</div>
<div class="rightOperate">
<el-radio-group v-model="radioType">
<el-radio-button label="标定"></el-radio-button>
<el-radio-button label="算法"></el-radio-button>
</el-radio-group>
<div class="leftCon" v-show="radioType==='标定'">
<div class="title">车道标定</div>
<div style="color: #E9F3FF;font-size: 12px;margin-bottom: 10px;">车道线配置</div>
<el-button @click="draw('lane')" class="fullButton" style="border: 1px solid #4ED83A;background: #15212F;">
绘制车道线
<i class="el-icon-edit"></i>
</el-button>
<el-tag
v-for="tag in lane"
:key="tag.id"
closable
@close="delLaneLine(tag.id,'lane')"
type="info">
{{ tag.id }}
</el-tag>
<div style="color: #E9F3FF;font-size: 12px;margin: 10px 0;">车道配置</div>
<el-button @click="addLane" class="fullButton" style="border: 1px solid #37475A;background: #15212F;">
添加车道
</el-button>
<div v-for="(i,n) in lanes" style="margin-bottom: 10px;">
<!-- <span>车道</span>-->
<el-input style="width: 80px;margin: 0 6px;" v-model="i.laneName"></el-input>
<el-select style="width: 120px;" v-model="i.laneLine[0]">
<el-option
v-for="item in lane" :key="item.id" :label="item.id" :value="item.id">
</el-option>
</el-select>
<span style="margin: 0 6px;">-</span>
<el-select style="width: 120px;" v-model="i.laneLine[1]">
<el-option
v-for="item in lane" :key="item.id" :label="item.id" :value="item.id">
</el-option>
</el-select>
<i @click="delLane(n)" class="el-icon-close" style="font-size: 20px;margin-left: 16px;cursor: pointer;"></i>
</div>
<div class="title">坐标标定</div>
<div class="coordinateCalibration"
v-for="(i,n) in coordinateCalibration"
>
<div class="pointTop" style="overflow: hidden;margin-bottom: 10px;">
<el-input style="width: 200px;margin: 0 6px;" v-model="i.name"></el-input>
<i class="el-icon-location" @click="location(n)"
style="font-size: 20px;margin-left: 16px;cursor: pointer;float: right;"
></i>
</div>
<div class="pointBottom" style="overflow: hidden;">
<span>经度</span>
<el-input style="width: 100px;margin: 0 6px;" v-model="i.longitude"></el-input>
<span>纬度</span>
<el-input style="width: 100px;margin: 0 6px;" v-model="i.latitude"></el-input>
<div style="float: right;">
<span>X:{{ i.point.x }}</span>
<span>Y:{{ i.point.y }}</span>
</div>
</div>
</div>
</div>
<div class="rightCon" v-show="radioType==='算法'">
<div class="topCheckbox">
<div class="title">目标检测</div>
<el-checkbox-group v-model="objectDetection">
<el-checkbox label="1" key="1">机动车</el-checkbox>
<el-checkbox label="2" key="2">非机动车</el-checkbox>
<el-checkbox label="3" key="3">行人</el-checkbox>
<el-checkbox label="4" key="4">全部</el-checkbox>
</el-checkbox-group>
<div class="title">车辆属性</div>
<el-checkbox-group v-model="carAttribute">
<el-checkbox label="5" key="5">车辆识别</el-checkbox>
<el-checkbox label="6" key="6">车型识别</el-checkbox>
<el-checkbox label="7" key="7">车身颜色</el-checkbox>
<el-checkbox label="8" key="8">车辆品牌</el-checkbox>
</el-checkbox-group>
<div class="title">其他</div>
<el-checkbox-group v-model="other">
<el-checkbox label="9" key="9">车流统计</el-checkbox>
<el-checkbox label="10" key="10">信号灯识别</el-checkbox>
</el-checkbox-group>
</div>
<div class="drawArea">
<div class="title">
<el-checkbox true-label="1" false-label="0" v-model="interestGoalRadio"></el-checkbox>
感兴趣区域-目标检测
</div>
<el-button @click="draw('interestGoal')" class="fullButton"
style="border: 1px solid #0091FF;background: rgba(0,145,255,0.30);">
绘制区域
<i class="el-icon-edit"></i>
</el-button>
<el-tag
v-for="tag in interestGoal"
:key="tag.id"
closable
@close="delLaneLine(tag.id,'interestGoal')"
type="info">
{{ tag.id }}
</el-tag>
<div class="title">
<el-checkbox true-label="1" false-label="0" v-model="interestLightRadio"></el-checkbox>
感兴趣区域-信号灯检测
</div>
<el-button @click="draw('interestLight')" class="fullButton"
style="border: 1px solid #F94040;background: rgba(249,64,64,0.30);">
绘制区域
<i class="el-icon-edit"></i>
</el-button>
<el-tag
v-for="tag in interestLight"
:key="tag.id"
closable
@close="delLaneLine(tag.id,'interestLight')"
type="info">
{{ tag.id }}
</el-tag>
<div class="title">
<el-checkbox true-label="1" false-label="0" v-model="attributeRadio"></el-checkbox>
属性采集线
</div>
<el-button @click="draw('attribute')" class="fullButton"
style="border: 1px solid #C73AEF;background: #15212F;">
绘制采集线
<i class="el-icon-edit"></i>
</el-button>
<el-tag
v-for="tag in attribute"
:key="tag.id"
closable
@close="delLaneLine(tag.id,'attribute')"
type="info">
{{ tag.id }}
</el-tag>
<div class="title">
<el-checkbox true-label="1" false-label="0" v-model="virtualCoilRadio"></el-checkbox>
虚拟线圈
</div>
<el-button @click="draw('virtualCoilArea')" class="fullButton"
style="border: 1px solid #C4AA17;background: rgba(196,170,23,0.30);">
绘制区域
<i class="el-icon-edit"></i>
</el-button>
<el-tag
v-for="tag in virtualCoilArea"
:key="tag.id"
closable
@close="delLaneLine(tag.id,'virtualCoilArea')"
type="info">
{{ tag.id }}
</el-tag>
<el-button @click="draw('virtualCoilLine')" class="fullButton"
style="border: 1px solid #F7D200;background: #15212F;margin-left: 0;">
绘制采集线
<i class="el-icon-edit"></i>
</el-button>
<el-tag
v-for="tag in virtualCoilLine"
:key="tag.id"
closable
@close="delLaneLine(tag.id,'virtualCoilLine')"
type="info">
{{ tag.id }}
</el-tag>
</div>
<div class="switchOpen">
<div class="title">编码开关</div>
<div v-for="i in codingSwitchArr" style="display: inline-block;margin: 0 10px 10px 0;">
<span style="margin-right: 6px;color: #E9F3FF;">{{ i.label }}</span>
<el-switch
v-model="codingSwitch[i.key]"
active-color="#45B035"
inactive-color="#223142"
active-value="1"
inactive-value="0"
>
</el-switch>
</div>
</div>
</div>
</div>
<el-dialog :modal=false :close-on-click-modal=false title="配置管理" :visible.sync="configManager">
<el-table :data="configManagerData">
<el-table-column
v-for="i in configManagerColumns"
:label="i.label"
:prop="i.prop"
></el-table-column>
<el-table-column label="操作" align="center" width="230">
<template slot-scope="{ row }">
<el-button type="text" size="mini" @click="delConfig(row.id)">
删除配置
</el-button>
<el-button type="text" size="mini"
@click="readConfig(row.id)">
加载配置
</el-button>
</template>
</el-table-column>
</el-table>
<Pagination
v-show="configParams.total > 0"
:total="configParams.total"
:page.sync="configParams.page"
:limit.sync="configParams.size"
layout="prev, pager, next"
@pagination="getConfigList"
/>
<div class="configInfo">
<div class="configInfoTitle">说明:</div>
<div class="configInfoCon">
<p class="text">1.删除配置后 不可恢复!</p>
<p class="text">
2.加载配置后配置文件中,
<span class="strong">视频流、结果地址</span>
自动变更为
<span class="strong">当前更新数据</span>
其余参数请加载参数完成后再修改确认下发。
</p>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import AILabel from 'ailabel';
import Pagination from '@/components/Pagination';
import {secondSave, getConfigList, delConfig, getConfig} from '@/api/project';
let pointEnum = {
'0': '标定点①',
'1': '标定点②',
'2': '标定点③',
'3': '标定点④',
};
export default {
name: 'Ailable',
components: {Pagination},
props: {
id: {// domID
type: String,
default: 'map'
},
operateId: {
type: String | Number,
default: ''
},
imgUrl: {
type: String,
default: ''
},
videoName: {
type: String,
default: ''
},
// step: {
// type: Number,
// default: 0
// },
videoId: {
type: String | Number,
default: ''
}
},
data() {
return {
codingSwitchArr: [
{key: 'objectDetection', label: '目标检测'},
{key: 'carAttribute', label: '车辆属性'},
{key: 'regionOfInterest', label: '感兴趣区域'},
{key: 'attributeAcquisitionLine', label: '属性采集线'},
{key: 'virtualCoil', label: '虚拟线圈'},
{key: 'lane', label: '车道'},
{key: 'gps', label: 'GPS坐标'},
{key: 'all', label: '全部'},
],
drawingStyle: {},
textStyle: {
fillStyle: 'rgba(247, 210, 0, 0.3)',
strokeStyle: '#F7D200',
background: true,
globalAlpha: 1,
fontColor: '#E9F3FF'
},
mode: 'PAN',
itemName: '',
editId: '',
deleteIconId: 'delete01',
imgInfo: {
width: 1280,
height: 720
},
radioType: '标定',
curDraw: '',//当前绘制的类型
curDrawType: '',//当前绘制的类型
tags: [ //车道线tags
{name: '标签一', type: 'info'},
{name: '标签二', type: 'info'},
{name: '标签三', type: 'info'},
{name: '标签四', type: 'info'},
{name: '标签五', type: 'info'}
],
lane: [],//车道线数据
lanes: [// 车道信息
{laneName: '车道1', laneLine: ['', '']},
{laneName: '车道2', laneLine: ['', '']},
],
coordinateCalibration: [// 坐标标定
{
name: '标定点①',
point: {x: '', y: ''}, // 坐标
longitude: '', // 经度
latitude: '', // 纬度
},
{name: '标定点②', point: {x: '', y: ''}, longitude: '', latitude: ''},
{name: '标定点③', point: {x: '', y: ''}, longitude: '', latitude: ''},
{name: '标定点④', point: {x: '', y: ''}, longitude: '', latitude: ''},
],
curEditPointIdx: '',//当前编辑第几个坐标点
objectDetection: [],//目标检测
carAttribute: [],// 车辆属性
other: [],// 其他
codingSwitch: { //编码开关
objectDetection: 0, // 目标检测
carAttribute: 0, // 车辆属性
regionOfInterest: 0, // 感兴趣区域
attributeAcquisitionLine: 0, // 属性采集线
virtualCoil: 0,//虚拟线圈
lane: 0, // 车道
gps: 0, // GPS坐标
all: 0, // 全部
},
interestGoal: [],//感兴趣区域-目标检测数据
interestGoalRadio: '0',//感兴趣区域-目标检测数据-
interestLight: [],//感兴趣区域-信号灯检测数据
interestLightRadio: '0',//感兴趣区域-信号灯检测数据-
attribute: [],//属性采集线数据
attributeRadio: '0',//属性采集线数据-
virtualCoilArea: [],//虚拟线圈-区域数据
virtualCoilLine: [],//虚拟线圈-采集线数据
virtualCoilRadio: '0',//虚拟线圈-采集线数据-
editData: {},//编辑时回显的第二步的数据
configManager: false,//配置列表是否显示
configManagerData: [],//配置列表数据
configManagerColumns: [
{label: '任务名称', prop: 'projectName'},
{label: '视频流id', prop: 'videoId'},
{label: '视频流名称', prop: 'videoName'},
{label: '保存时间', prop: 'saveTime'},
],
configParams: {
total: 0,
page: 1,
size: 10
}
};
},
watch: {
mode(mode) {
this.gMap.setMode(mode);
// this.setDrawingStyle(mode);
},
curDrawType(type) {
this.setDrawingStyleByType(type);
},
configManager(val) {
if (val) {
this.configParams = {
total: 0,
page: 1,
size: 10
};
this.getConfigList();
}
}
},
methods: {
// 绘制
draw(type) {// 车道线
this.curDrawType = type;
switch (type) {
case 'lane'://车道线
this.mode = 'LINE';
this.curDraw = '车道线';
break;
case 'interestGoal': //感兴趣区域-目标检测
this.mode = 'POLYGON';
this.curDraw = '感兴趣区域-目标检测';
break;
case 'interestLight': //感兴趣区域-信号灯检测
this.mode = 'RECT';
this.curDraw = '感兴趣区域-信号灯检测';
break;
case 'attribute': //属性采集线
this.mode = 'LINE';
this.curDraw = '属性采集线';
break;
case 'virtualCoilArea': //虚拟线圈-区域
this.mode = 'POLYGON';
this.curDraw = '虚拟线圈-区域';
break;
case 'virtualCoilLine': //虚拟线圈-采集线
this.mode = 'LINE';
this.curDraw = '虚拟线圈-采集线';
break;
default:
break;
}
},
location(n) {// 标定坐标
this.curEditPointIdx = n;
this.mode = 'POINT';
this.curDrawType = 'coordinateCalibration';
this.curDraw = pointEnum[n];
},
addLane() {// 添加车道
this.lanes.push({laneName: '车道' + (this.lanes.length + 1), laneLine: ['', ''],});
},
delLane(idx) {
this.lanes.splice(idx, 1);
},
info() {
this.$alert(`
<div>
<p>1.选择模式之后进行标注</p>
<p>2.滚轮可以控制缩放</p>
<p>3.默认状态下可以随意拖动</p>
<p>4.双击标注进行编辑</p>
<p>5.ctrl+z撤销操作</p>
<p>6.编辑时可以随意调整</p>
<p>7.暂不支持修改标注的名称,如果想改名称,则删除重新添加</p>
<p style="color:#F94040;">8.绘制越早的标注,层级越高(建议把较大的标注放在最后标注)</p>
</div>
`, '标注规则', {
dangerouslyUseHTMLString: true
});
},
zoomIn() {
this.gMap.zoomIn();
},
zoomOut() {
this.gMap.zoomOut();
},
setMode(mode) {
this.mode = mode;
},
// 获取所有features
getFeatures() {
const allFeatures = this.gFirstFeatureLayer.getAllFeatures();
let arr = [];
allFeatures.forEach(item => {
let o = {
options: this.getPoints(item),
id: item.id,
props: item.props,
shape: item.shape,
type: item.type,
};
console.log(o);
arr.push(o);
});
// window.localStorage.setItem('featureItems', JSON.stringify(arr));
// this.getMarkers();
this.getText();
},
// 获取所有markers
getMarkers() {
const allMarkers = this.gMap.markerLayer.getAllMarkers();
console.log(allMarkers);
},
// 获取所有text标注
getText() {
const allText = this.gFirstTextLayer.getAllTexts();
let arr = [];
allText.forEach(item => {
let o = {
id: item.id,
props: item.props,
shape: item.textInfo,
type: item.type,
};
arr.push(o);
});
window.localStorage.setItem('textItems', JSON.stringify(arr));
},
// 初始样式
setDrawingStyle(mode) {
switch (mode) {
case 'PAN': {
break;
}
case 'MARKER': {
// 忽略
break;
}
case 'POINT': {
this.drawingStyle = {fillStyle: '#9370DB', zIndex: 99};
this.gMap.setDrawingStyle(this.drawingStyle);
break;
}
case 'CIRCLE': {
this.drawingStyle = {fillStyle: '#9370DB', strokeStyle: '#0000FF', lineWidth: 2, zIndex: 98};
this.gMap.setDrawingStyle(this.drawingStyle);
break;
}
case 'LINE': {
this.drawingStyle = {
strokeStyle: '#C73AEF',
lineJoin: 'round',
lineCap: 'round',
lineWidth: 6,
arrow: false,
};
this.gMap.setDrawingStyle(this.drawingStyle);
break;
}
case 'POLYLINE': {
this.drawingStyle = {strokeStyle: '#FF1493', lineJoin: 'round', lineCap: 'round', lineWidth: 10, zIndex: 9};
this.gMap.setDrawingStyle(this.drawingStyle);
break;
}
case 'RECT': {
this.drawingStyle = {strokeStyle: '#F94040', lineWidth: 1, zIndex: 19};
this.gMap.setDrawingStyle(this.drawingStyle);
break;
}
case 'POLYGON': {
this.drawingStyle = {
strokeStyle: '#F94040',
fillStyle: 'rgba(249,64,64,0.60)',
globalAlpha: .3,
lineWidth: 1,
fill: true,
stroke: true, zIndex: 555
};
this.gMap.setDrawingStyle(this.drawingStyle);
break;
}
case 'DRAWMASK': {
this.drawingStyle = {strokeStyle: 'rgba(255, 0, 0, .5)', fillStyle: '#00f', lineWidth: 50};
this.gMap.setDrawingStyle(this.drawingStyle);
break;
}
case 'CLEARMASK': {
this.drawingStyle = {fillStyle: '#00f', lineWidth: 30};
this.gMap.setDrawingStyle(this.drawingStyle);
break;
}
default:
break;
}
},// 初始样式
setDrawingStyleByType(type) {
switch (type) {
case 'coordinateCalibration': {
this.textStyle.fillStyle = 'rgba(78, 216, 58, 0.3)';//文字的背景
this.textStyle.strokeStyle = '#4ED83A';//文字的边框
this.drawingStyle = {fillStyle: '#4ED83A'};
this.gMap.setDrawingStyle(this.drawingStyle);
break;
}
case 'lane': {
this.textStyle.fillStyle = 'rgba(78, 216, 58, 0.3)';//文字的背景
this.textStyle.strokeStyle = '#4ED83A';//文字的边框
this.drawingStyle = {
strokeStyle: '#4ED83A',
lineJoin: 'round',
lineCap: 'round',
lineWidth: 6,
arrow: false,
};
this.gMap.setDrawingStyle(this.drawingStyle);
break;
}
case 'attribute': {
this.textStyle.fillStyle = 'rgba(199, 58, 239, 0.3)';//文字的背景
this.textStyle.strokeStyle = '#C73AEF';//文字的边框
this.drawingStyle = {
strokeStyle: '#C73AEF',
lineJoin: 'round',
lineCap: 'round',
lineWidth: 6,
arrow: false,
};
this.gMap.setDrawingStyle(this.drawingStyle);
break;
}
case 'virtualCoilLine': {
this.textStyle.fillStyle = 'rgba(196, 170, 23, 0.3)';//文字的背景
this.textStyle.strokeStyle = '#F7D200';//文字的边框
this.drawingStyle = {
strokeStyle: '#F7D200',
lineJoin: 'round',
lineCap: 'round',
lineWidth: 6,
arrow: false,
};
this.gMap.setDrawingStyle(this.drawingStyle);
break;
}
case 'interestGoal': {
this.textStyle.fillStyle = 'rgba(0, 145, 255, 0.3)';//文字的背景
this.textStyle.strokeStyle = '#0091FF';//文字的边框
this.drawingStyle = {
strokeStyle: '#0091FF',
fillStyle: 'rgba(0,145,255,0.60)',
globalAlpha: .3,
lineWidth: 1,
fill: true,
stroke: true, zIndex: 555
};
this.gMap.setDrawingStyle(this.drawingStyle);
break;
}
case 'interestLight': {
this.textStyle.fillStyle = 'rgba(249,64,64, 0.3)';//文字的背景
this.textStyle.strokeStyle = '#F94040';//文字的边框
this.drawingStyle = {
strokeStyle: '#F94040',
fillStyle: 'rgba(249,64,64,0.60)',
globalAlpha: .3,
lineWidth: 1,
fill: true,
stroke: true, zIndex: 555
};
this.gMap.setDrawingStyle(this.drawingStyle);
break;
}
case 'virtualCoilArea': {
this.textStyle.fillStyle = 'rgba(247,210,0, 0.3)';//文字的背景
this.textStyle.strokeStyle = '#F7D200';//文字的边框
this.drawingStyle = {
strokeStyle: '#F7D200',
fillStyle: 'rgba(247,210,0,0.60)',
globalAlpha: .3,
lineWidth: 1,
fill: true,
stroke: true, zIndex: 555
};
this.gMap.setDrawingStyle(this.drawingStyle);
break;
}
default:
break;
}
},
// 添加图形
addFeature(typeData, type, name, id = +new Date()) {
let that = this;
// this.setDrawingStyle(type);
this.setDrawingStyleByType(that.curDrawType);
let drawingStyle = this.drawingStyle;
let {type: dataType, data} = typeData;
let shape = {};
if (dataType === 'data') {
const scale = that.gMap.getScale();
const width = drawingStyle.lineWidth / scale;
switch (type) {
case 'LINE':
shape = {...data, width};
break;
case 'POLYLINE':
shape = {points: data, width};
break;
case 'RECT':
shape = data;
break;
case 'POLYGON':
shape = {points: data};
break;
case 'POINT':
shape = {...data, sr: 6};
break;
default:
shape = {};
break;
}
} else {
shape = data;
}
let addItem = null;
if (type === 'LINE') {
addItem = new AILabel.Feature.Line(
id, // id
// {...data, width}, // shape
shape,
{name}, // props
drawingStyle // style
);
} else if (type === 'POLYLINE') {
addItem = new AILabel.Feature.Polyline(
id, // id
// {points: data, width}, // shape
shape,
{name}, // props
drawingStyle // style
);
} else if (type === 'RECT') {
addItem = new AILabel.Feature.Rect(
id, // id
data, // shape
{name}, // props
drawingStyle // style
);
} else if (type === 'POLYGON') {
addItem = new AILabel.Feature.Polygon(
id, // id
// {points: data}, // shape
shape,
{name}, // props
drawingStyle // style
);
} else if (type === 'POINT') {
addItem = new AILabel.Feature.Point(
id, // id
// {...data, sr: 6}, // shape
shape,
{name}, // props
drawingStyle // style
);
}
addItem.delType = that.curDrawType;
that.gFirstFeatureLayer.addFeature(addItem);
},
// 画完取名
getName(mode) {
return this.$prompt(`请填写【${this.curDraw}】名字`, {
confirmButtonText: '确定',
showCancelButton: false,
inputValue: this.curDraw,
}).then(({value}) => {
return value;
}).catch(() => {
return null;
});
},
// 删除实例~~~
delLaneLine(id, delType) {
this.delItem(id, delType);
let feature = this.gFirstFeatureLayer.getFeatureById(id);
let marker = this.gMap.markerLayer.getMarkerById(this.deleteIconId);
this.del(marker, feature);
},
// 删除
delItem(id, type) {
let arr = [];
if (type === 'coordinateCalibration') {
arr = this[type].map(i => {
if (i.name === id) {
i.point = {x: '', y: ''};
}
return i;
});
} else {
arr = this[type].filter(i => i.id !== id);
}
this[type] = arr;
},
// 删除实例
del(marker, feature) {
// 首先删除当前marker
marker && this.gMap.markerLayer.removeMarkerById(marker.id);
// 删除对应text
// gFirstTextLayer.removeTextById(textId);
// 删除对应feature
feature && this.gFirstFeatureLayer.removeFeatureById(feature.id);
feature && this.gFirstTextLayer.removeTextById(feature.id);
},
// 增加删除图标
addDeleteIcon(feature) {
let gMap = this.gMap;
let that = this;
// 添加delete-icon
let points = that.getPoints(feature);
const gFirstMarker = new AILabel.Marker(
that.deleteIconId, // id
{
src: 'delete.png',
position: points[1] || points[0], // 矩形右上角
offset: {
x: -20,
y: -4
}
}, // markerInfo
{name: 'delete'} // props
);
gFirstMarker.events.on('click', marker => {
let id = feature.id;
let delType = feature.delType;
that.delItem(id, delType);
that.del(marker, feature);
});
gMap.markerLayer.addMarker(gFirstMarker);
},
// 增加标注
addText(feature, text) {
let that = this;
let name = feature.id;
text = text ? text : name;
let options = this.getPoints(feature);
const gFirstText = new AILabel.Text(
name, // id
{text: text, position: options[0], offset: {x: 0, y: 0}}, // shape
{name}, // props
that.textStyle // style
);
// gFirstText.events.on('click', text => {
// console.log(text);
// });
this.gFirstTextLayer.addText(gFirstText);
},
// 删除标注
delText(id) {
this.gFirstTextLayer.removeTextById(id);
},
// 删除 删除按钮
deIcon() {
this.gMap.markerLayer.removeAllMarkers();
},
// 编辑完要更新数据
resetItem(feature) {
let arr = [];
if (feature.delType === 'coordinateCalibration') {
arr = this[this[feature.delType]]?.map(i => {
if (i.name === feature.id) {
i.point = feature.shape;
}
return i;
});
} else {
let options = this.getPoints(feature);
arr = this[feature.delType].map(i => {
if (i.id === feature.id) {
return {
options,
id: feature.id,
props: feature.props,
shape: feature.shape,
type: feature.type
};
} else {
return i;
}
});
}
this[feature.delType] = arr;
},
// 增加事件
addEvent() {
let that = this;
let gMap = this.gMap;
gMap.events.on('drawDone', (type, data) => {
if (!that.curDraw) {
return;
}
that.getName(type).then(val => {
if (val) {
let alreadyHave = that.gFirstFeatureLayer.getFeatureById(val) ? true : false;
if (alreadyHave) {
this.$message({
type: 'info',
message: '名字重复'
});
return;
}
// 把上次画的点删除
if (this.curDrawType === 'coordinateCalibration') {
let pointId = that[that.curDrawType][that.curEditPointIdx].name;
this.delLaneLine(pointId, 'coordinateCalibration');
}
that.addFeature({type: 'data', data}, type, val, val);
// 绘制完成的实例
let feature = this.gFirstFeatureLayer.getFeatureById(val);
let options = this.getPoints(feature);
// 画完放到相应的数组
let o = {
options,
id: feature.id,
props: feature.props,
shape: feature.shape,
type: feature.type,
delType: feature.delType
};
// 标定点处理
if (this.curDrawType === 'coordinateCalibration') {
that[that.curDrawType][that.curEditPointIdx].point = feature.shape;
that[that.curDrawType][that.curEditPointIdx].name = feature.id;
} else {
that[that.curDrawType].push(o);
}
that.addText(feature);
} else {
// this.$message({
// type: 'info',
// message: '请填写名字'
// });
}
});
});
gMap.events.on('boundsChanged', data => {
return '';
});
// 双击编辑
gMap.events.on('featureSelected', feature => {
that.editId = feature.id;
// console.log('--map featureSelected--', feature);
gMap.setActiveFeature(feature);
// 增加删除按钮
that.addDeleteIcon(feature);
});
// 单机空白取消编辑
gMap.events.on('featureUnselected', (feature) => {
console.log(feature);
// 更新完要更新数据
that.resetItem(feature);
// 取消featureSelected
that.editId = '';
that.deIcon();
gMap.setActiveFeature(null);
});
// 更新完
gMap.events.on('featureUpdated', (feature, shape) => {
console.log(feature);
// 更新完要更新数据
that.resetItem(feature);
// 更新或者移动需要重新设置删除图标
that.deIcon();
that.delText(feature.id);
feature.updateShape(shape);
that.addDeleteIcon(feature);
that.addText(feature);
});
},
// 获取坐标
getPoints(feature) {
switch (feature.type) {
case 'RECT':
return feature.getPoints();
case 'LINE':
return [
feature.shape.start,
feature.shape.end,
];
case 'POLYLINE':
return feature.shape.points;
case 'POLYGON':
return feature.shape.points;
case 'POINT':
return [feature.shape];
default:
return [];
}
},
// 初始化
init() {
let that = this;
const gMap = new AILabel.Map(that.id, {
center: {x: that.imgInfo.width / 2, y: that.imgInfo.height / 2}, // 为了让图片居中
zoom: that.imgInfo.width + 10,
mode: that.mode, // 绘制线段
refreshDelayWhenZooming: true, // 缩放时是否允许刷新延时,性能更优
zoomWhenDrawing: true,
panWhenDrawing: true,
zoomWheelRatio: 5, // 控制滑轮缩放缩率[0, 10), 值越小,则缩放越快,反之越慢
withHotKeys: true // 关闭快捷键
});
that.gMap = gMap;
this.addEvent();
// 图片层添加
const gFirstImageLayer = new AILabel.Layer.Image(
'first-layer-image', // id
{
src: that.imgUrl,
width: that.imgInfo.width,
height: that.imgInfo.height,
crossOrigin: false, // 如果跨域图片,需要设置为true
position: { // 左上角相对中心点偏移量
x: 0,
y: 0
},
grid: { // 3 * 3
columns: [{color: '#9370DB'}, {color: '#FF6347'}],
rows: [{color: '#9370DB'}, {color: '#FF6347'}]
}
}, // imageInfo
{name: '图片图层'}, // props
{zIndex: 5} // style
);
// 添加到gMap对象
gMap.addLayer(gFirstImageLayer);
// 添加矢量图层
const gFirstFeatureLayer = new AILabel.Layer.Feature(
'first-layer-feature', // id
{name: '矢量图层'}, // props
{zIndex: 10} // style
);
this.gFirstFeatureLayer = gFirstFeatureLayer;
gMap.addLayer(gFirstFeatureLayer);
// 添加标注
const gFirstTextLayer = new AILabel.Layer.Text(
'first-layer-text', // id
{name: '文本图层'}, // props
{zIndex: 12, opacity: 1} // style
);
this.gFirstTextLayer = gFirstTextLayer;
this.gMap.addLayer(gFirstTextLayer);
// 历史
// this.getHistoryData();
window.onresize = function () {
gMap && gMap.resize();
};
},
delAllItem() { //清除所有实例
this.gFirstFeatureLayer.removeAllFeatures();
this.gFirstTextLayer.removeAllTexts();
this.gMap.markerLayer.removeAllMarkers();
},
// 历史数据渲染
drawHistory(data) {
data.forEach(item => {
let config = item.extraConfig;
this.curDrawType = config.delType;// 标注的类型
this.addFeature({type: 'shape', data: config.shape},
config.type, item.name, config.id);
let feature = this.gFirstFeatureLayer.getFeatureById(config.id);
this.addText(feature, item.name);
});
},
// 获取历史数据
getHistoryData() {
let historyDraw = window.localStorage.historyDraw;
if (!historyDraw) {
return;
}
historyDraw = JSON.parse(historyDraw);
this.editData = historyDraw.data.filter(i => i.videoId === this.videoId)[0];
if (!this.editData) {
return;
}
// 先清除所有实例
this.delAllItem();
let d = this.editData;
console.log('当前编辑的数据', d);
this.lane = this.reFormatData(d.laneLine);
this.drawHistory(d.laneLine);
this.lanes = d.lanes;
this.coordinateCalibration = d.coordinateCalibration;
d.coordinateCalibration.forEach(item => {
this.curDrawType = 'coordinateCalibration';// 标注的类型
this.addFeature({type: 'data', data: item.point},
'POINT', item.name, item.name);
let feature = this.gFirstFeatureLayer.getFeatureById(item.name);
this.addText(feature, item.name);
});
this.objectDetection = d.objectDetection;
this.carAttribute = d.carAttribute;
this.other = d.other;
this.codingSwitch = d.codingSwitch;
this.interestGoal = this.reFormatData(d.objectDetectionDraw.data);
this.drawHistory(d.objectDetectionDraw.data);
this.interestGoalRadio = d.objectDetectionDraw.open;
this.interestLight = this.reFormatData(d.signalLampIdentificationDraw.data);
this.drawHistory(d.signalLampIdentificationDraw.data);
this.interestLightRadio = d.signalLampIdentificationDraw.open;
this.attribute = this.reFormatData(d.attributeAcquisitionLine.data);
this.drawHistory(d.attributeAcquisitionLine.data);
this.attributeRadio = d.attributeAcquisitionLine.open;
this.virtualCoilArea = this.reFormatData(d.virtualCoil.region);
this.drawHistory(d.virtualCoil.region);
this.virtualCoilLine = this.reFormatData(d.virtualCoil.acquisitionLine);
this.drawHistory(d.virtualCoil.acquisitionLine);
this.virtualCoilRadio = d.virtualCoil.open;
},
// 保存
formatData(data) {
return this.deepCopy(data).map(i => {
return {
name: i.id,
point: i.options,
extraConfig: i,
};
});
},
reFormatData(data) {
return this.deepCopy(data).map(i => {
return i.extraConfig;
});
},
formatSubmitData() {
return {
id: this.operateId,
data: [
{
videoId: this.videoId,
videoName: this.videoName,
laneLine: this.formatData(this.lane),
lanes: this.lanes,
coordinateCalibration: this.coordinateCalibration,
objectDetection: this.objectDetection,
carAttribute: this.carAttribute,
other: this.other,
objectDetectionDraw: {
open: this.interestGoalRadio,
data: this.formatData(this.interestGoal),
},
signalLampIdentificationDraw: {
open: this.interestLightRadio,
data: this.formatData(this.interestLight),
},
attributeAcquisitionLine: {
open: this.attributeRadio,
data: this.formatData(this.attribute),
},
virtualCoil: {
open: this.virtualCoilRadio,
region: this.formatData(this.virtualCoilArea),
acquisitionLine: this.formatData(this.virtualCoilLine),
},
codingSwitch: this.codingSwitch,
}
]
};
},
save() {
// console.log('lane', this.lane);
// console.log('lanes', this.lanes);
// console.log('coordinateCalibration', this.coordinateCalibration);
// console.log('objectDetection', this.objectDetection);
// console.log('carAttribute', this.carAttribute);
// console.log('other', this.other);
// console.log('codingSwitch', this.codingSwitch);
// console.log('interestGoal', this.interestGoal);
// console.log('interestLight', this.interestLight);
// console.log('attribute', this.attribute);
// console.log('virtualCoilArea', this.virtualCoilArea);
// console.log('virtualCoilLine', this.virtualCoilLine);
// console.log(this.editData);
let o = this.formatSubmitData();
console.log(11111111111, o);
secondSave(o).then(res => {
this.$message({
type: 'success',
message: `${this.videoName}保存配置成功`
});
});
},
temporarySave() {//暂存
let o = this.formatSubmitData();
let historyDraw = window.localStorage.historyDraw;
if (!historyDraw) {
window.localStorage.setItem('historyDraw', JSON.stringify(o));
} else {
historyDraw = JSON.parse(historyDraw);
let curData = historyDraw.data.filter(i => i.videoId === o.data[0].videoId)[0];
if (curData) {
historyDraw.data = historyDraw.data.map(i => {
if (i.videoId === o.data[0].videoId) {
return o.data[0];
} else {
return i;
}
});
} else {
historyDraw.data.push(o.data[0]);
}
window.localStorage.setItem('historyDraw', JSON.stringify(historyDraw));
}
},
getConfigList() {
let {size, page} = this.configParams;
console.log(size, page);
getConfigList({size, page}).then(({data}) => {
this.configParams.total = data.total;
this.configManagerData = data.data;
});
},
delConfig(id) {//删除配置
delConfig({id}).then(res => {
this.$message.success('删除成功');
this.getConfigList();
});
},
readConfig(id) {//读取配置
getConfig({id}).then(({data}) => {
console.log('readConfig-----------------', data);
data.videoName = this.videoName;
data.videoId = this.videoId;
let historyDraw = window.localStorage.historyDraw;
if (!historyDraw) {
historyDraw = {
id: this.operateId,
data: [data]
};
} else {
historyDraw = JSON.parse(historyDraw);
// 判断是否存在暂存了
this.hasCurEdit = historyDraw.data.filter(i => i.videoId === this.videoId)[0];
if (!this.hasCurEdit) {
historyDraw.data.push(data);
} else {
historyDraw.data = historyDraw.data.map(i => {
if (i.videoId === this.videoId) {
return data;
} else {
return i;
}
});
}
console.log('readConfighistoryDraw', historyDraw);
}
window.localStorage.setItem('historyDraw', JSON.stringify(historyDraw));
this.getHistoryData();
this.configManager = false;//加载配置完成关闭弹框
});
}
},
mounted() {
this.init();
this.getHistoryData();
},
beforeDestroy() {
this.gMap && this.gMap.destroy();
// window.localStorage.removeItem('historyDraw');
},
};
</script>
<style scoped lang="scss">
.rightOperate {
width: calc(100% - 800px);
height: 500px;
overflow: auto;
padding-left: 16px;
padding-right: 6px;
::v-deep.el-radio-button__inner {
width: 64px;
height: 32px;
border-radius: 2px;
}
//标注的标题
.title {
font-size: 12px;
color: #23A6FF;
margin: 20px 0 20px 0;
}
::v-deep.fullButton {
width: 100%;
color: #B4CDE9;
margin-bottom: 10px;
}
::v-deep.el-tag.el-tag--info {
color: #CEDFF4;
background-color: #223142;
border-color: #223142;
margin-right: 6px;
}
.leftCon {
.coordinateCalibration {
margin-bottom: 16px;
}
}
}
.configInfo {
.configInfoTitle {
font-size: 14px;
color: #CEDFF4;
margin: 8px 0;
}
.configInfoCon {
.text {
font-size: 14px;
color: #9BB9DC;
margin-bottom: 6px;
.strong {
color: #E9F3FF;
}
}
}
}
.button-wrap {
padding-bottom: 10px;
position: relative;
z-index: 99;
}
</style>