import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { Component, Input, OnInit } from '@angular/core'; import { NzMessageService } from 'ng-zorro-antd/message'; import { NzModalService } from 'ng-zorro-antd/modal'; @Component({ selector: 'app-image-label-anxin', templateUrl: './image-label-anxin.component.html', styleUrls: ['./image-label-anxin.component.scss'], }) export class ImageLabelAnxinComponent implements OnInit { constructor( private http: HttpClient, private message: NzMessageService, private modal: NzModalService ) {} @Input() cameraId: any; //传递id camerasData: any; //摄像头Data imgItem: any; //图片 URL canvasWidth: number = 0; canvasHeight: number = 0; copyCanvas: any; //拷贝 canvas底图 selectedBtn: string; //按钮选中 //返回上一步路由 goback() { history.go(-1); } ngOnInit(): void {} //获取 摄像头图片/标注点位 getImgMarkData() { console.log('获取照片', new Date().getTime()); return new Promise((resolve, reject) => { this.http.get(`/api/Cameras/${this.cameraId}`).subscribe((data: any) => { this.camerasData = data; // this.camerasData.dimensionedPointsAnxin = null this.markType = data.type; const httpOptions = { responseType: 'blob' as 'json', params: { cameraId: this.cameraId }, }; let date = new Date().getTime(); this.http.get(`/api/Cameras/Images?v=${date}`, httpOptions).subscribe({ next: (data) => { resolve(data); }, error: (err) => { reject('error'); }, }); }); }); } anewgetImg() { let params = { cameraId: this.cameraId, provider: 1, }; this.http .put('/api/Cameras/Commands/CaptureImages', '', { params: params }) .subscribe({ next: (value: Object) => { this.message.create( 'success', '向边缘设备发送请求成功,请过一段时间手动刷新页面!' ); }, error: (error: HttpErrorResponse) => {}, complete: () => {}, }); } ngAfterContentInit(): void { this.getImgMarkData() .then((res: any) => { this.imgItem = window.URL.createObjectURL(res); window.setTimeout(() => { this.initBackgroundImg(); }, 0); }) .catch((err) => { this.message.create('error', '获取图片失败!'); window.setTimeout(() => { this.initBackgroundImg(); }, 0); }); } //初始化背景图 canvas; ctx; initBackgroundImg() { this.canvas = document.getElementById('canvas') as any; //取消鼠标右键事件 this.canvas.oncontextmenu = () => { return false; }; // 检测canvas支持性 if (this.canvas.getContext) { this.ctx = this.canvas.getContext('2d'); // 返回一个对象,该对象提供了用在画布上绘图的方法和属性 } else { document.write('你的浏览器不支持canvas,请升级你的浏览器!'); return; } // 图片加载完后,将其显示在canvas中 var img = new Image(); img.src = this.imgItem ? this.imgItem : '../../../assets/images/noImg.png'; console.log('img', img); img.onload = () => { console.log('原始宽度', img.width); console.log('原始高度', img.height); if (img.width > 1920) { img.height = img.height * (1920 / img.width); img.width = 1920; } this.canvasWidth = img.width; this.canvasHeight = img.height; console.log('显示宽度', this.canvasWidth); console.log('显示高度', this.canvasHeight); // return; window.setTimeout(() => { // 加载图片 this.ctx.drawImage(img, 0, 0, this.canvasWidth, this.canvasHeight); this.copyCanvas = this.ctx.getImageData( 0, 0, this.canvasWidth, this.canvasHeight ); //初始化标绘图形 this.initMark(this.canvas, this.ctx); //监听canvas事件 this.initCanvasEvent(this.canvas); }, 0); }; } //初始化标绘图形 DrawPolygoning = ''; initMark(canvas, context) { // return if (!this.camerasData.dimensionedPointsAnxin) { return; } else { this.camerasData.dimensionedPointsAnxin = JSON.parse( this.camerasData.dimensionedPointsAnxin ); } console.log('原始标点数据', this.camerasData.dimensionedPointsAnxin); // for (const key in this.camerasData.dimensionedPointsAnxin.rawData) { // const element = this.camerasData.dimensionedPointsAnxin.rawData[key]; // console.log(key, element); // if (element.length !== 0) { // for (let index = 0; index < element.length; index++) { // const item = element[index]; // console.log(666, item); // let obj = this.PolygonData[key]; // this.DrawPolygoning = key; // this.DrawPolygon( // item.x, // item.y, // canvas, // context, // obj.Points, // obj.Circles, // obj.Allpoints, // obj.IsDragging, // obj.IsInOut, // obj.Color // ); // } // } // this.DrawPolygoning = ''; // } for ( let key = 0; key < this.camerasData.dimensionedPointsAnxin.yamlData.length; key++ ) { const element = this.camerasData.dimensionedPointsAnxin.yamlData[key]; let name = enum_area[element.type]; let arr = this.handleArr(element.points); for (let index = 0; index < arr.length; index++) { const item = arr[index]; let obj = this.PolygonData[name]; this.DrawPolygoning = name; this.DrawPolygon( item.x, item.y, canvas, context, obj.Points, obj.Circles, obj.Allpoints, obj.IsDragging, obj.IsInOut, obj.Color ); } this.DrawPolygoning = ''; } } handleArr(arr, n = 2) { const res = []; for (let i = 0; i < arr.length - 1; i += 2) { let index = Math.floor(i / n); const obj = { x: Number((arr[i] * this.canvasWidth).toFixed(0)), y: Number((arr[i + 1] * this.canvasHeight).toFixed(0)), }; res[index] = obj; } return res; } markType: number = 0; //0=进出口,1=加油区,2=卸油区,3=便利店, //记录鼠标点击位置 downx = 0; downy = 0; //初始化 canvas画布 监听事件 context; initCanvasEvent(canvas) { var context = canvas.getContext('2d'); this.context = context; canvas.onmousedown = (e) => { if (!this.selectedBtn) { this.message.create('warning', '请先选择要绘制的区域!'); return; } //鼠标按下事件 var clickX = e.pageX - canvas.offsetLeft; var clickY = e.pageY - canvas.offsetTop; this.downx = clickX; this.downy = clickY; let obj; obj = this.PolygonData[this.selectedBtn]; this.DrawPolygon( clickX, clickY, canvas, context, obj.Points, obj.Circles, obj.Allpoints, obj.IsDragging, obj.IsInOut, obj.Color ); }; canvas.onmouseup = (e) => { //鼠标松开事件 canvas.onmousemove = (ev) => { //鼠标移动事件 return false; }; }; } PolygonData = { 进出口: { //进出口多边形 Points: [], //线段的点的集合 Circles: [], //可拖动圆圈的点的集合 Allpoints: [], //整体移动点位 IsDragging: false, //是否可拖拽 IsInOut: false, //是否在绘制区域内 Color: 'red', }, 收银区: { //收银区多边形 Points: [], //线段的点的集合 Circles: [], //可拖动圆圈的点的集合 Allpoints: [], //整体移动点位 IsDragging: false, //是否可拖拽 IsInOut: false, //是否在绘制区域内 Color: 'yellow', }, 加油区: { //加油区多边形 Points: [], //线段的点的集合 Circles: [], //可拖动圆圈的点的集合 Allpoints: [], //整体移动点位 IsDragging: false, //是否可拖拽 IsInOut: false, //是否在绘制区域内 Color: 'green', }, 卸油区: { Points: [], //线段的点的集合 Circles: [], //可拖动圆圈的点的集合 Allpoints: [], //整体移动点位 IsDragging: false, //是否可拖拽 IsInOut: false, //是否在绘制区域内 Color: 'black', }, }; //绘制多边形的方法 DrawPolygon( clickX, clickY, canvas, context, Points, Circles, Allpoints, IsDragging, IsInOut, Color ) { if (this.isInt(clickX, clickY, Points)) { IsInOut = true; return; } else { IsInOut = false; } let index; //判断当前点击点是否在已经绘制的圆圈上,如果是执行相关操作,并return,不进入画线的代码 for (var i = 0; i < Circles.length; i++) { let circle = Circles[i]; //使用勾股定理计算这个点与圆心之间的距离 var distanceFromCenter = Math.sqrt( Math.pow(circle.x - clickX, 2) + Math.pow(circle.y - clickY, 2) ); // 如果是其他的点,则设置可以拖动 if (distanceFromCenter <= circle.radius) { // 清除之前选择的圆圈 index = i; IsDragging = true; //停止搜索 return; } } //如果点击新的位置,则进入下面的代码,绘制点 context.clearRect(0, 0, canvas.width, canvas.height); this.copyCanvas ? context.putImageData(this.copyCanvas, 0, 0) : null; //重绘除了自己的其他区域 for (const key in this.PolygonData) { const item = this.PolygonData[key]; if (key !== this.selectedBtn && key !== this.DrawPolygoning) { this.redrawPolygon( item.Points, item.Allpoints, item.Circles, context, item.Color ); } } //遍历数组画圆 var circle = { x: clickX, y: clickY, radius: 5, color: Color, isSelected: false, //拖拽点的标记 }; Circles.push(circle); Allpoints = JSON.parse(JSON.stringify(Circles)); Circles[0].color = Color; for (var i = 0; i < Circles.length; i++) { let circle = Circles[i]; // 绘制圆圈 context.globalAlpha = 0.85; context.beginPath(); context.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2); context.fillStyle = circle.color; context.strokeStyle = Color; context.fill(); context.stroke(); } // 画线 var point = { x: clickX, y: clickY, }; Points.push(point); context.beginPath(); context.lineWidth = 3; //从起始点开始绘制 context.moveTo(Points[0].x, Points[0].y); for (var i = 0; i < Points.length; i++) { context.lineTo(Points[i].x, Points[i].y); } context.closePath(); context.strokeStyle = Color; context.stroke(); } //判断点位是否在图形区域内 isInt(x, y, points) { if (!points[2]) { return; } var pt = { x: x, y: y, }; return this.PointInPoly(pt, points); } //射线法判断点位 PointInPoly(pt, poly) { for (var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) ((poly[i].y <= pt.y && pt.y < poly[j].y) || (poly[j].y <= pt.y && pt.y < poly[i].y)) && pt.x < ((poly[j].x - poly[i].x) * (pt.y - poly[i].y)) / (poly[j].y - poly[i].y) + poly[i].x && (c = !c); return c; } //根据已有数据重绘多边形 redrawPolygon(data, points, circles, context, color) { data.forEach((element) => { //遍历数组画圆 points = JSON.parse(JSON.stringify(circles)); circles[0].color = color; for (var i = 0; i < circles.length; i++) { let circle = circles[i]; // 绘制圆圈 context.globalAlpha = 0.85; context.beginPath(); context.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2); context.fillStyle = circle.color; context.strokeStyle = color; context.fill(); context.stroke(); } // 画线 context.beginPath(); context.lineWidth = 3; //从起始点开始绘制 context.moveTo(data[0].x, data[0].y); for (var i = 0; i < data.length; i++) { context.lineTo(data[i].x, data[i].y); } context.closePath(); context.strokeStyle = color; context.stroke(); }); } //清空画布 clearCanvas() { let canvas = document.getElementById('canvas') as any; let context = canvas.getContext('2d'); for (const key in this.PolygonData) { const element = this.PolygonData[key]; element.Points = []; element.Circles = []; element.Allpoints = []; element.IsDragging = false; element.IsInOut = false; } context.clearRect(0, 0, canvas.width, canvas.height); this.copyCanvas ? context.putImageData(this.copyCanvas, 0, 0) : null; // console.log(this.PolygonData) } //清除某个标绘 clearCanvasItem(e, type) { e.stopPropagation(); this.modal.confirm({ nzTitle: '确认要清除此标绘吗?', nzOkText: '确定', nzOkType: 'primary', nzOkDanger: true, nzOnOk: () => { this.context.clearRect(0, 0, this.canvasWidth, this.canvasHeight); this.copyCanvas ? this.context.putImageData(this.copyCanvas, 0, 0) : null; this.PolygonData[type].Points = []; this.PolygonData[type].Circles = []; this.PolygonData[type].Allpoints = []; this.redrawAll(); }, nzCancelText: '取消', }); } redrawAll() { //重绘除了自己的其他区域 for (const key in this.PolygonData) { const item = this.PolygonData[key]; this.redrawPolygon( item.Points, item.Allpoints, item.Circles, this.context, item.Color ); } } //保存 save() { console.log(this.camerasData.dimensionedPointsAnxin); console.log('标点数据', this.PolygonData); for (const key in this.PolygonData) { const element = this.PolygonData[key]; if (element.Points.length !== 0 && element.Points.length <= 2) { this.message.create('info', '标绘图形必须为封闭图形!'); return; } } if (!this.camerasData.dimensionedPointsAnxin) { this.camerasData.dimensionedPointsAnxin = {}; this.camerasData.dimensionedPointsAnxin.rawData = { 进出口: [], 收银区: [], 加油区: [], 卸油区: [], }; this.camerasData.dimensionedPointsAnxin.yamlData = []; } let yamlData = []; for (const key in this.PolygonData) { const item = this.PolygonData[key]; this.camerasData.dimensionedPointsAnxin.rawData[key] = item.Points; if (item.Points.length !== 0) { let points = []; for (const key in item.Points) { const v = item.Points[key]; points.push(Number((v.x / this.canvasWidth).toFixed(4))); points.push(Number((v.y / this.canvasHeight).toFixed(4))); } let obj = { type: enum_area[key], points: points, }; yamlData.push(obj); } } this.camerasData.dimensionedPointsAnxin.yamlData = yamlData; let body = { dimensionedPointsAnxin: JSON.stringify( this.camerasData.dimensionedPointsAnxin ), }; console.log('标点结果', this.camerasData.dimensionedPointsAnxin); // return; this.http .put(`/api/Cameras/${this.camerasData.id}/DimensionedPoints`, body) .subscribe((data) => { this.message.create('success', '保存成功!'); const isFullScreen = document.fullscreenElement; if (document.exitFullscreen && isFullScreen) { //关闭全屏 document.exitFullscreen(); } this.modal.closeAll(); }); } //更换底图 changeImg(e) { this.clearCanvas(); let file = e.target.files[0] || null; //获取上传的文件 let fileUrl = URL.createObjectURL(file); console.log(fileUrl); var img = new Image(); img.src = fileUrl; img.onload = () => { this.canvasWidth = img.width; this.canvasHeight = img.height; window.setTimeout(() => { // 加载图片 this.ctx.drawImage(img, 0, 0, this.canvasWidth, this.canvasHeight); this.copyCanvas = this.ctx.getImageData( 0, 0, this.canvasWidth, this.canvasHeight ); }, 0); }; } } enum enum_area { '加油区' = 2, '卸油区' = 3, '收银区' = 1, '进出口' = 0, }