You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

412 lines
16 KiB

import { Box, Calc, ColorChart } from "../uilts/box-drawer";
export class HeatMapDrawer{
private readonly id: string;
private canvas: any = null;
private canvasContext: any = null;
private targetCanvas: any = null;
private targetCanvasContext: any = null;
private borderWidth: number = 0;
private borderHeight: number = 0;
private readonly width: number = null;
private readonly height: number = null;
private readonly paddingLeft: number = 100;
private readonly paddingRight: number = 115;
private readonly paddingTop: number = 50;
private readonly paddingBottom: number = 100;
private colorChart: ColorChart = null;
private values: any = null;
// 垂直刻度
private readonly verticalScaleLine: number = 15;
// 水平刻度
private readonly horizontalScaleLine: number = 15;
public base64Image: string = null;
private verticalScaleIntervalLength: number = 0;
private horizontalScaleIntervalLength: number = 0;
private paddingColorLeft: number = 20;
private defaultColorWidth: number = 30;
private calc: Calc = null;
private box: Box = null;
private xAxis: CoordinateScale;
private yAxis: CoordinateScale;
private unit: string;
private name: string;
constructor(width: number, height: number, values: any, id: string, unit: string = '', name: string = '') {
if (id == null) {
throw new Error("heat map drawer id not allow null");
}
this.id = id;
this.width = width;
this.height = height;
this.values = values;
this.unit = unit;
this.name = name;
this.init();
}
public setAxis(xAxis: CoordinateScale, yAxis: CoordinateScale): void {
this.xAxis = xAxis;
this.yAxis = yAxis;
}
public setColorChart(colorChart: ColorChart) : void {
this.colorChart = colorChart;
}
public drawColorChart(): void {
let startX = this.paddingLeft + this.horizontalScaleLine + this.borderWidth + this.paddingColorLeft;
let heightInterval = 0;
let startY = this.paddingTop + this.verticalScaleLine + heightInterval;
let interval = (this.borderHeight - heightInterval) / (this.colorChart.colors.length)
// 绘制色标外边框
this.canvasContext.lineWidth="1";
this.canvasContext.strokeStyle="black";
this.canvasContext.rect(startX, startY, this.defaultColorWidth, this.borderHeight - heightInterval);
this.canvasContext.stroke();
this.canvasContext.font = "normal 20px 微软雅黑";
this.canvasContext.fillStyle = "#000000";
for(let lastLen = this.colorChart.colors.length - 1, index = lastLen; index >= 0; index --){
const targetIndex = lastLen - index;
const _y = startY + index * interval;
this.canvasContext.fillStyle = this.colorChart.colors[targetIndex];
this.canvasContext.fillRect(startX, _y, this.defaultColorWidth, interval);
this.canvasContext.fillStyle = "#000000";
if (this.colorChart.type == 'equal') {
const text = this.colorChart.values[targetIndex].label;
this.canvasContext.fillText(text, startX + this.defaultColorWidth + 2, _y + interval - 40);
continue;
}
if (index == 0){
if (this.colorChart.showStartValue){
const text = this.colorChart.values[targetIndex + 1];
this.canvasContext.fillText(text, startX + this.defaultColorWidth + 2, _y + 9);
}
const text = this.colorChart.values[targetIndex];
this.canvasContext.fillText(text, startX + this.defaultColorWidth + 2, _y + interval + 9);
}else if (index == lastLen){
if (this.colorChart.showEndValue){
const text = this.colorChart.values[targetIndex];
this.canvasContext.fillText(text, startX + this.defaultColorWidth + 2, _y + interval + 9);
}
const text = this.colorChart.values[targetIndex + 1];
this.canvasContext.fillText(text, startX + this.defaultColorWidth + 2, _y + 9);
}else{
const text = this.colorChart.values[targetIndex];
this.canvasContext.fillText(text, startX + this.defaultColorWidth + 2, _y + interval + 9);
}
}
}
public draw(): void{
this.drawCoordinate();
this.drawColorChart();
this.redraw();
this.setTargetCanvas(this.id);
}
private calcDataScale() : void{
this.verticalScaleIntervalLength = this.borderWidth / (this.values.length - 1);
this.horizontalScaleIntervalLength = this.borderHeight / (this.values[0].length - 1);
}
private redraw(): void{
this.calcDataScale();
let startX = this.paddingLeft + this.horizontalScaleLine - this.verticalScaleIntervalLength / 2;
let startY = this.paddingTop + this.verticalScaleLine - this.horizontalScaleIntervalLength / 2;
for(let dataIndex = 0, len = this.values.length, lastDataLen = len - 1; dataIndex < len; dataIndex++){
let value = this.values[dataIndex];
for(let lastInfoLen = value.length - 1, infoIndex = lastInfoLen; infoIndex >= 0; infoIndex--){
if (value[infoIndex] == null) continue;
let x = startX + dataIndex * this.verticalScaleIntervalLength;
let y = startY + (this.borderHeight - infoIndex * this.horizontalScaleIntervalLength);
let width = this.verticalScaleIntervalLength;
let height = this.horizontalScaleIntervalLength;
if (dataIndex == 0) {
x = x + this.verticalScaleIntervalLength / 2;
width = width / 2;
}
if (infoIndex == lastInfoLen){
y = y + this.horizontalScaleIntervalLength / 2;
height = height / 2;
}
if (dataIndex == lastDataLen){
width = width / 2;
}
if (infoIndex == 0){
height = height / 2;
}
let targetValue = value[infoIndex].value;
let color = this.colorChart.getColor(targetValue);
value[infoIndex].updateBorderInfo(x, y, width, height);
if (color == null) continue;
this.canvasContext.fillStyle = color;
this.canvasContext.fillRect(x, y, width, height);
}
// this.values[dataIndex].sort((x, y) => y.dataHeight - x.dataHeight);
this.values[dataIndex] = this.values[dataIndex].reverse();
}
this.createCalc(this.values);
}
private createCalc(boxes: any): void{
this.calc = new Calc(this.width, this.height, this.paddingLeft + this.verticalScaleLine, this.paddingTop + this.horizontalScaleLine,
this.paddingRight + this.horizontalScaleLine, this.paddingBottom + this.verticalScaleLine, boxes);
}
private init(): void{
// 创建画布
this.canvas = document.createElement('canvas');
this.canvas.width = this.width;
this.canvas.height = this.height;
this.canvasContext = this.canvas.getContext('2d');
this.fillBackground();
// this.resetValues();
}
private setTargetCanvas(id): void{
this.targetCanvas = document.getElementById(id);
this.targetCanvas.width = this.width;
this.targetCanvas.height = this.height;
this.targetCanvasContext = this.targetCanvas.getContext('2d');
this.copyCanvas();
this.bindMoveEvent();
}
private copyCanvas(): void{
const imgData= this.canvasContext.getImageData(0,0, this.width, this.height);
this.targetCanvasContext.putImageData(imgData, 0, 0);
}
private _onMouseMove = this.onMouseMove.bind(this);
private _onMouseOut = this.onMouseOut.bind(this)
private bindMoveEvent(): void{
this.targetCanvas.addEventListener('mousemove', this._onMouseMove);
this.targetCanvas.addEventListener('mouseout', this._onMouseOut);
}
private onMouseOut() : void{
this.copyCanvas();
}
private onMouseMove(e) : void{
let box = this.calc.calc(e.offsetX, e.offsetY);
if (box == null) {
if (this.box != null){
this.copyCanvas();
this.box = null;
}
return;
}
if (this.box != null){
if (this.box.id == box.id) return;
this.copyCanvas();
this.box = null;
}
box.show(this.targetCanvasContext);
this.box = box;
}
public close(): void{
this.values.length = 0;
if (this.targetCanvas == null) return;
this.targetCanvas.removeEventListener('mousemove', this._onMouseMove);
this.targetCanvas.removeEventListener('mouseout', this._onMouseOut);
}
private fillBackground(): void{
this.canvasContext.fillStyle = 'white';
this.canvasContext.fillRect(0,0, this.width,this.height);
}
public drawCoordinate(): void{
this.drawBorder();
this.drawVerticalScale();
this.drawHorizontalScale();
this.drawHeightText();
this.drawTemperatureText(this.unit);
this.drawResolutionName(this.name)
}
//高度单位
private drawHeightText(): void {
// 保存原有的canvas状态,供restore方法重置
this.canvasContext.save();
let name = "高度(m)";
this.canvasContext.translate(45, this.height / 2 + this.paddingTop - this.canvasContext.measureText(name).width);
this.canvasContext.rotate(Math.PI * 1.5);
this.canvasContext.font="normal 22px 微软雅黑";
this.canvasContext.fillStyle="#000000";
this.canvasContext.fillText(name, 0, 0);
this.canvasContext.restore();
// let name =['高','度','(','m',')']; // 文本内容
// let x = 70,y = 180; // 文字开始的坐标
// let letterSpacing = 10; // 设置字间距
// for(let i = 0; i < name.length; i++) {
// const str = name.slice(i, i + 1).toString();
// if (str.match(/[A-Za-z0-9-()-]/) && (y < 576)) { // 非汉字 旋转
// this.canvasContext.save();
// this.canvasContext.translate(x, y);
// this.canvasContext.rotate(Math.PI / 180 * 90);
// this.canvasContext.textBaseline = 'bottom';
// this.canvasContext.font = "normal 20px 微软雅黑";
// this.canvasContext.fillStyle = "#000000";
// this.canvasContext.fillText(str, 0, 0);
// this.canvasContext.restore();
// y += this.canvasContext.measureText(str).width + letterSpacing; // 计算文字宽度
// } else if (str.match(/[\u4E00-\u9FA5]/) && (y < 576)) {
// this.canvasContext.save();
// this.canvasContext.textBaseline = 'top';
// this.canvasContext.font = "normal 20px 微软雅黑";
// this.canvasContext.fillStyle = "#000000";
// this.canvasContext.fillText(str, x, y);
// this.canvasContext.restore();
// y += this.canvasContext.measureText(str).width + letterSpacing; // 计算文字宽度
// }
// }
}
//温度单位
private drawTemperatureText(unit): void {
this.canvasContext.font = "normal 35px 微软雅黑";
this.canvasContext.fillStyle = "#000000";
let unitX = this.width - this.paddingRight;
this.canvasContext.fillText(unit, unitX, 45);
}
private drawResolutionName(name): void {
this.canvasContext.font = "normal 35px 微软雅黑";
this.canvasContext.fillStyle = "#000000";
let nameY = this.paddingTop - 23
this.canvasContext.fillText(name, 380, nameY)
}
private drawVerticalScale(): void{
let scaleCount = this.xAxis.scales.length;
let verticalScaleIntervalLength = this.borderWidth / (scaleCount - 1);
let startX = this.paddingLeft + this.horizontalScaleLine;
let startY = this.paddingTop + this.verticalScaleLine;
this.canvasContext.font = "normal 25px 微软雅黑";
this.canvasContext.fillStyle = "#000000";
for(let index = 0, len = scaleCount, lastLen = scaleCount - 1; index < len; index++){
if ((index == 0 && !this.xAxis.showStartValue) || (index == lastLen && !this.xAxis.showEndValue)) continue;
this.setScaleStyle(true);
const _x = startX + index * verticalScaleIntervalLength;
this.canvasContext.beginPath();
this.canvasContext.moveTo(_x, startY);
this.canvasContext.lineTo(_x,startY - this.verticalScaleLine);
this.canvasContext.moveTo(_x, startY + this.borderHeight);
this.canvasContext.lineTo(_x,startY + this.verticalScaleLine + this.borderHeight);
this.canvasContext.stroke();
const currentText = this.xAxis.scales[index];
if (index == 1 || ((currentText == '00' || currentText == '0') && index > 3)){
this.setTimeDetail(_x, startY, this.values[index * 11 + 2][5].time.slice(0, 6));
}
this.canvasContext.fillText(currentText, _x - this.canvasContext.measureText(currentText).width / 2, startY + this.verticalScaleLine + this.borderHeight + 20);
}
}
private setTimeDetail(_x: number, startY: number, text: string): void{
// const text = this.values[15][0].time.slice(0, 6);
this.canvasContext.font = "normal 15px 微软雅黑";
this.canvasContext.fillText(text, _x - this.canvasContext.measureText(text).width / 2, startY + this.verticalScaleLine + this.borderHeight + 38);
this.canvasContext.font = "normal 17px 微软雅黑";
}
private drawHorizontalScale(): void{
let scaleCount = this.yAxis.scales.length;
let horizontalScaleIntervalLength = this.borderHeight / (scaleCount - 1);
let startX = this.paddingLeft + this.horizontalScaleLine;
let startY = this.paddingTop + this.verticalScaleLine;
const lastLen = scaleCount - 1;
for(let index = lastLen; index >= 0; index--) {
if ((index == 0 && !this.yAxis.showStartValue) || (index == lastLen && !this.yAxis.showEndValue)) continue;
this.setScaleStyle(true);
const _y = startY + (this.borderHeight - index * horizontalScaleIntervalLength);
this.canvasContext.beginPath();
this.canvasContext.moveTo(startX, _y);
this.canvasContext.lineTo(startX - this.horizontalScaleLine, _y);
this.canvasContext.moveTo(this.borderWidth + startX, _y);
this.canvasContext.lineTo(this.borderWidth + startX + this.horizontalScaleLine, _y);
this.canvasContext.stroke();
if (true) {
this.canvasContext.font = "normal 20px 微软雅黑";
this.canvasContext.fillStyle = "#000000";
this.canvasContext.fillText(this.yAxis.scales[index], startX - this.canvasContext.measureText(this.yAxis.scales[index]).width - this.horizontalScaleLine, _y + 10);
}
}
}
private setScaleStyle(isSpecial: boolean): void{
if (isSpecial){
this.canvasContext.lineWidth = 3;
this.canvasContext.strokeStyle = '#101010';
return;
}
this.canvasContext.lineWidth = 2;
this.canvasContext.strokeStyle = '#5e5e5e';
}
private drawBorder(): void{
const x = this.horizontalScaleLine + this.paddingLeft;
const y = this.verticalScaleLine + this.paddingTop;
const _x = this.width - this.horizontalScaleLine - this.paddingRight;
const _y = this.height - this.verticalScaleLine - this.paddingBottom;
this.canvasContext.beginPath();
this.canvasContext.lineWidth = "2";
this.canvasContext.strokeStyle = "black";
this.borderWidth = _x - x;
this.borderHeight = _y - y;
this.canvasContext.rect(x, y, this.borderWidth, this.borderHeight);
this.canvasContext.stroke();
}
}
export class CoordinateScale {
public scales: Array<Object>;
public showStartValue: boolean;
public showEndValue: boolean;
constructor(scales: Array<Object>, showStartValue: boolean = false, showEndValue: boolean = false){
this.scales = scales;
this.showEndValue = showEndValue;
this.showStartValue = showStartValue;
}
}