microwave-project-unite/src/components/rtReport/index.vue

775 lines
30 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="rtReport">
<div class="realResult">
<div class="box1"><span class="sp1"></span><span class="sp2">真实性检验结果</span></div>
<div class="resContent">
<el-table
:header-cell-style="headerRowClass"
:cell-style="tableRowClassName"
style="width: 100%"
:data="tDatas"
:stripe="true"
class="table-head-transparent"
size="medium"
>
<el-table-column
v-for="(item, index) in Object.keys(tData).slice(0, midValue)"
:key="index"
:label="item | truthFunFilter"
:property="item"
align="center"
>
<template slot-scope="scope">{{ scope.row[item] }}</template>
</el-table-column>
</el-table>
<el-table
:header-cell-style="headerRowClass"
:cell-style="tableRowClassName"
style="width: 100%; margin-top: 16px"
:data="tDatas"
:stripe="true"
class="table-head-transparent"
size="medium"
>
<el-table-column
v-for="(item, index) in Object.keys(tData).slice(midValue)"
:key="index"
:label="item | truthFunFilter"
:property="item"
align="center"
>
<template slot-scope="scope">{{ scope.row[item] }}</template>
</el-table-column>
</el-table>
<!--<span v-for="(item,index) in resMap" :key="index">{{item.name | truthFunFilter}}{{item.val}}</span>-->
<div v-if="meanError.length !== 0">
<el-table
:header-cell-style="headerRowClass"
:cell-style="tableRowClassName"
style="width: 100%"
:data="meanError"
:stripe="true"
size="medium"
>
<el-table-column
prop="8_0"
label="平均相对误差相对X轴误差"
align="center"
v-if="meanError[0]['8_0'] != null"
>
</el-table-column>
<el-table-column
prop="8_1"
label="平均相对误差相对Y轴误差"
align="center"
v-if="meanError[0]['8_1'] != null"
>
</el-table-column>
<el-table-column
prop="24_0"
label="平均绝对相对误差相对X轴误差"
align="center"
v-if="meanError[0]['24_0'] != null"
>
</el-table-column>
<el-table-column
prop="24_1"
label="平均绝对相对误差相对Y轴误差"
align="center"
v-if="meanError[0]['24_1'] != null"
>
</el-table-column>
</el-table>
</div>
<!--<span>相对误差:</span>-->
<!-- <div v-if="relativeError.length !== 0 && relativeError !== []">
<el-table
:data="relativeError"
size="medium"
>
<el-table-column
prop="cre_orthoXRelativeError"
label="相对误差相对X轴误差"
align="center">
</el-table-column>
<el-table-column
prop="cre_orthoYRelativeError"
label="相对误差相对Y轴误差"
align="center">
</el-table-column>
</el-table>
</div> -->
</div>
</div>
<div class="errorGraph">
<div class="box1">
<span class="sp1"></span><span class="sp2">{{ pdSubTypeName }}误差结果图</span>
</div>
<div class="errContent">
<div id="rtReportEcharts" ref="chart"></div>
<div class="legend" v-if="pdSubType === 1">
<div class="line">
<span class="circle"></span>
<span>样本点</span>
</div>
<div class="line">
<span class="circle_2"></span>
<span>选取点</span>
</div>
</div>
<!-- <div class="erTip" v-else>
<div class="echartReg">
<span class="echartRegName"></span>
<span>{{ erExpression }}</span>
</div>
<div class="echartReg" v-show="erR2 !== null">
<span class="echartRegName"></span>
<span>{{ erR2 }}</span>
</div>
</div> -->
</div>
</div>
</div>
</template>
<script>
import { productTypeMap } from "@/lib/variateMap";
import { truthFunVal, productSubTypeVal } from "@/lib/contract";
import * as turf from "@turf/turf";
import arrow from "@/assets/images/arrow.png"
export default {
props: {
realRes: {
require: true,
},
pdSubType: {
require: true,
},
},
filters: {
truthFunFilter(truthFun) {
const truthFunMap = {
4: "平均误差",
9: "平均绝对误差",
5: "相对误差",
8: "平均相对误差",
24: "平均绝对相对误差",
6: "均方根误差",
23: "平面中误差",
7: "相关系数",
12: "误差矩阵",
14: "总体分类精度",
13: "Kappa系数",
};
return truthFunMap[truthFun];
},
},
data() {
return {
resMap: [],
myCharts: null,
relativeError: [],
meanError: [],
pdSubTypeName: "",
tData: {},
tDatas: null,
midValue: 10,
erExpression: null,
erR2: "",
};
},
created() {
this.initParam();
},
mounted() {
this.myCharts = this.$echarts.init(this.$refs.chart);
if (this.pdSubType === productSubTypeVal.ACS_ORTHOPHOTO) {
this.drawOtgEchart(this.realRes.otgVal);
let da = {};
let dae = {};
for (let i = 0; i < this.resMap.length; i++) {
const o = this.resMap[i];
if (typeof o.val === "number" || typeof o.val === "string") {
da[o.name] = o.val;
} else if (Number(o.name) === truthFunVal.ACM_MEANRELATIVEERROR) {
dae[o.name + "_0"] = o.val.X轴平均相对误差;
dae[o.name + "_1"] = o.val.Y轴平均相对误差;
} else if (Number(o.name) === truthFunVal.ACM_MEANABSOLUTERELATIVEERROR) {
dae[o.name + "_0"] = o.val.X轴平均相对绝对误差;
dae[o.name + "_1"] = o.val.Y轴平均相对绝对误差;
}
}
if (Object.keys(dae).length !== 0) {
this.meanError = [dae];
}
this.tData = da;
this.tDatas = [da];
} else {
this.drawPie(this.realRes.errorImageValue);
let da = {};
this.midValue =
this.resMap.length - 1 >= 3
? Math.ceil((this.resMap.length - 1) / 2)
: this.resMap.length;
for (let i = 0; i < this.resMap.length; i++) {
const o = this.resMap[i];
if (
Number(o.name) === truthFunVal.ACM_RELATIVEERROR ||
Number(o.name) === truthFunVal.ACM_REALVALLIST
)
continue; //跳过执行相对误差与真值列表
da[o.name] =
typeof o.val === "number" || typeof o.val === "string" ? o.val : o.val[0];
}
this.tDatas = [da];
this.tData = da;
}
},
methods: {
headerRowClass() {
return "background: #E4E9F1;text-align:center";
},
tableRowClassName({ rowIndex }) {
if ((rowIndex + 1) % 2 !== 0) {
return "background:#F5F7FA;text-align:center";
} else {
return "background:#FFFFFF;text-align:center";
}
},
getProductDes() {
let prodDes = null;
switch (this.pdSubType) {
case productSubTypeVal.ACS_DEM:
prodDes = "高程m";
break;
case productSubTypeVal.ACS_BACKSCATTERINGCOEFFICIENT:
prodDes = "后向散射系数dB";
break;
case productSubTypeVal.ACS_ATMOSPHERICDELAYCORRECTION:
prodDes = "大气延迟m";
break;
case productSubTypeVal.ACS_DEFORMATION:
prodDes = "形变量m";
break;
case productSubTypeVal.ACS_SOILMOISTURE:
prodDes = "体积含水量cm³/cm³";
break;
case productSubTypeVal.ACS_SOILSALINITYINVERSION:
prodDes = "土壤盐碱度g/kg";
break;
case productSubTypeVal.ACS_SURFACEROUGHNESS:
prodDes = "均方根高度cm";
break;
case productSubTypeVal.ACS_GROUNDVEGETATIONHEIGHT:
prodDes = "植被高度cm";
break;
case productSubTypeVal.ACS_GROUNDLEAFAREAINDEX:
prodDes = "叶面积指数LAI";
break;
}
return prodDes;
},
initParam() {
// console.log('报告信息',this.realRes)
if (this.realRes !== undefined) {
this.pdSubTypeName = productTypeMap(this.pdSubType);
const rr = this.realRes.report;
if (this.pdSubType === productSubTypeVal.ACS_ORTHOPHOTO) {
for (var i in rr) {
let rVal = rr[i];
switch (Number(i)) {
case truthFunVal.ACM_RELATIVEERROR: //相对误差
this.relativeError = rVal;
break;
case truthFunVal.ACM_MEANRELATIVEERROR: //平均相对误差
const objMap1 = {
cre_orthoXMeanRelativeError: "X轴平均相对误差",
cre_orthoYMeanRelativeError: "Y轴平均相对误差",
};
rVal = Object.fromEntries(
Object.entries(rVal).map(([k, v]) => [objMap1[k] || k, v])
);
this.resMap.push({ name: i, val: rVal });
break;
case truthFunVal.ACM_MEANABSOLUTERELATIVEERROR: //平均绝对相对误差
const objMap2 = {
cre_orthoXMeanAbsoluteRelativeError: "X轴平均相对绝对误差",
cre_orthoYMeanAbsoluteRelativeError: "Y轴平均相对绝对误差",
};
rVal = Object.fromEntries(
Object.entries(rVal).map(([k, v]) => [objMap2[k] || k, v])
);
this.resMap.push({ name: i, val: rVal });
break;
default:
this.resMap.push({ name: i, val: rVal });
}
}
} else {
for (var i in rr) {
let rVal = rr[i];
this.resMap.push({ name: i, val: rVal });
}
}
}
},
calculateR2(y, yHat) {
// y:真实值 yhat预测值
// 计算观测值的平均值
const meanY = y.reduce((acc, val) => acc + val, 0) / y.length;
// 计算回归平方和SSR
const totalSumOfSquares = yHat.map(val => Math.pow(val - meanY, 2)).reduce((acc, val) => acc + val, 0);
// 计算残差平方和SSE
const residualSum = y.map((val, index) => Math.pow(val - yHat[index], 2)).reduce((acc, val) => acc + val, 0);
// 总离差平方和SST
const residualSumOfSquares = totalSumOfSquares + residualSum;
// 计算 R2 决定系数
const rSquared = 1 - (residualSum / residualSumOfSquares);
return rSquared;
},
drawPie(errorVala) {
let data = [];
let xArr = [];
const observedValues = [];
const predictedValues = [];
for (let i in errorVala) {
const a = [errorVala[i].cre_imageValue, errorVala[i].cre_deviation];
xArr.push(errorVala[i].cre_imageValue);
data.push(a);
observedValues.push(errorVala[i].cre_deviation);
predictedValues.push(errorVala[i].cre_imageValue);
}
const x_limit = Math.abs((Math.max(...xArr) - Math.min(...xArr)) / xArr.length);
// const y_limit = Math.abs((Math.max(...observedValues) - Math.min(...observedValues)) / observedValues.length);
//多项式回归
let wcpf = data.map(a=>{
return Math.pow((a[1]-a[0]),2)
}).reduce((acc, curr) => acc + curr, 0);
let rmseValue = "RMSE = "+ Math.sqrt(wcpf/data.length);
let myRegression = this.echartRegression("polynomial", data, 1);
// 计算 R2 决定系数
this.erR2 = "r² = " + this.calculateR2(observedValues, predictedValues);
var option = {
title: {
text: this.pdSubTypeName + "误差结果图",
left: "center",
top: 16,
},
animation: false,
grid: {
right: "15%",
bottom: "25%",
height: "50%",
},
toolbox: {
show: true,
orient: "vertical",
top: 10,
feature: {
dataZoom: {
title: {
zoom: "区域缩放",
back: "缩放还原",
},
yAxisIndex: "none",
},
myTool1: {
show: true,
title: "多项式回归1次",
icon: "path://M272.5,285.5h332m-171-137v296m89-224c-85.7,61.79-199.22,144.16-199,144",
onclick: () => {
myRegression = this.echartRegression("polynomial", data, 1);
option.series[1].data = myRegression.points;
this.myCharts.setOption(option);
},
},
myTool2: {
show: true,
title: "多项式回归2次",
icon: "path://M272.5,285.5h332m-171-137v296M338,219.21c5,112.41,55.28,190.63,93,192.37,38.46,1.77,90.59-74.67,91.71-192.82",
onclick: () => {
myRegression = this.echartRegression("polynomial", data, 2);
option.series[1].data = myRegression.points;
this.myCharts.setOption(option);
},
},
myTool3: {
show: true,
title: "多项式回归3次",
icon: "path://M272.5,285.5h332m-171-137v296M590,227c-24.25,54.05-59.27,114.65-98.35,113.21-51.58-1.9-71.15-110.45-119.35-110.68-21.39-.1-51.7,21.12-88.8,120",
onclick: () => {
myRegression = this.echartRegression("polynomial", data, 3);
option.series[1].data = myRegression.points;
this.myCharts.setOption(option);
},
},
restore: {
title: "还原",
icon: "path://M2.5 2v6h6M2.66 15.57a10 10 0 1 0 .57-8.38",
},
saveAsImage: {
title: "保存为图片",
},
},
},
dataZoom: [
{
type: "slider",
xAxisIndex: 0,
filterMode: "none",
},
{
type: "slider",
yAxisIndex: 0,
filterMode: "none",
right: "60",
},
],
legend: {
top: 50,
right: 45,
// selectedMode: false,
data: [
{
name: "erExpression",
icon: "circle",
},
// {
// name: "r2",
// icon: "circle",
// itemStyle: {
// color:"#FFF"
// }
// },
{
name:"RMSE",
icon: "circle",
itemStyle: {
color:"#FFF"
}
}
],
formatter: (name) => {
if (name === "erExpression") return this.erExpression;
else if (name === "r2") return this.erR2;
else if(name ==="RMSE") return rmseValue;
},
},
xAxis: {
name: "像元值(" + this.getProductDes() + "",
nameLocation: "middle",
min: Number((Math.min(...xArr) - x_limit).toFixed(5)),
// max: Number((Math.max(...xArr) + x_limit).toFixed(5)),
nameTextStyle: {
lineHeight: 30,
height: 60,
// fontWeight: "bold",
fontSize: 13,
},
},
yAxis: {
name: "实测值(" + this.getProductDes() + "",
// min: Number((Math.min(...observedValues) - y_limit).toFixed(0)),
nameTextStyle: {
lineHeight: 30,
height: 60,
// fontWeight: "bold",
fontSize: 13,
padding: [0, 0, 20, 0],
},
nameRotate: 90, // 因为是在rightMiddle 所以需要其翻转 将其改为负值
nameLocation: "center", // y轴name处于y轴的什么位置
},
series: [
{
name: "r2",
symbolSize: 10,
type: "scatter",
data: data,
},
{
name: "RMSE",
symbolSize: 10,
type: "scatter",
// data: data,
},
{
name: "erExpression",
type: "line",
smooth: true,
showSymbol: false,
data: myRegression.points,
// itemStyle: {
// normal: {
// color: "#91cc75",
// lineStyle: {
// width: 3,
// },
// },
// },
},
],
};
option && this.myCharts.setOption(option);
},
drawOtgEchart(otgVal) {
let xl = [];
let yl = [];
let allData = [];
let allData1 = [];
let allData2 = [];
for (let i in otgVal) {
xl.push(otgVal[i].x);
yl.push(otgVal[i].y);
let ad = [];
ad.push(otgVal[i].x, otgVal[i].y);
allData.push(ad);
const j = Number(i) + 1;
if (Number.isInteger(j / 2)) {
// allData.push("-");
}
}
allData.forEach((item, index) => {
if (index % 2 === 0) {
allData1.push(item);
} else {
allData2.push(item);
}
});
// 计算两点角度
let bearingArr = [];
allData1.forEach((item, index) => {
let point1 = turf.point(item);
let point2 = turf.point(allData2[index]);
let bearing = turf.rhumbBearing(point2, point1);
bearingArr.push(bearing);
});
let xMax = Math.max(...xl),
xMin = Math.min(...xl);
let yMax = Math.max(...yl),
yMin = Math.min(...yl);
const avgxVal = (xMax - xMin) / 8;
const avgyVal = (yMax - yMin) / 8;
let option = {
grid: {
left: "14%",
},
animation: false,
xAxis: {
name: "X",
nameLocation: "middle",
nameTextStyle: {
lineHeight: 30,
height: 60,
fontWeight: "bold",
},
min: Number(xMin - avgxVal * 2).toFixed(4),
max: Number(xMax + avgxVal * 2).toFixed(4),
axisLabel: {
formatter: function (value) {
let pt = turf.point([value, 0]);
let converted = turf.toMercator(pt);
let num = converted.geometry.coordinates[0];
const p = Math.floor(Math.log(num) / Math.LN10);
const n = num * 10 ** -p;
return `${n.toFixed(4)}e${p}`;
},
},
},
yAxis: {
name: "Y",
nameTextStyle: {
lineHeight: 30,
height: 60,
fontWeight: "bold",
},
min: Number(yMin - avgyVal * 2).toFixed(4),
max: Number(yMax + avgyVal * 2).toFixed(4),
axisLabel: {
formatter: function (value) {
let pt = turf.point([0, value]);
let converted = turf.toMercator(pt);
let num = converted.geometry.coordinates[1];
const p = Math.floor(Math.log(num) / Math.LN10);
const n = num * 10 ** -p;
return `${n.toFixed(4)}e${p}`;
},
},
},
series: [
// {
// symbolSize: 8,
// data: allData,
// type: "line",
// itemStyle: {
// color: function (params) {
// if ((params.dataIndex + 1) % 3 === 2) return "#0000CD";
// },
// },
// },
{
symbol: "circle",
type: "scatter",
itemStyle: {
// 填充颜色
color: "rgba(255, 0, 0, 0)",
// 边框颜色
borderColor: "rgba(0, 0, 0, 1)",
// 边框宽度
borderWidth: 2,
// 边框圆角
borderRadius: 5,
},
symbolSize: 15,
data: allData1,
},
{
// symbol: "image://data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAAXNSR0IArs4c6QAAAjVJREFUeF7tmM1JAwEYBb90Yg1WYGeWYhd69OTVEjxagldlJeAfibtkIBOYgARk8/ZlJm/jupseKgI7VZvKTEJkH4KEJERGQFanhSRERkBWp4UkREZAVqeFJERGQFanhSRERkBWp4UkREZAVqeFJERGQFanhSRERkBWp4UkREZAVqeFJERGQFanhSRERkBWp4UkREZAVqeFJERGQFanhSRERkBWp4UkREZAVqeFJERGQFanhSRERkBWp4UkREZAVqeFJERGQFanhSRERkBWp4UkREaAq3M9M8+nxrWQUwl+vf59Zl5m5mFm7vfPy+82PRKyCdfRg3/Df9tLWQQtP69rTpWQNZTWHfPfGp6+CTp4aVuE3M7MzbpzdtQRAlsYHry0LUIeE3LWD9qPS1tCzuri8+R/hNzNzNX5e118A+ySdfEkJG8A+1KXvJ+Lr9GfvTKF3RjKhPSvE5kQpE536ghGLiQhHEskKSEIRi4kIRxLJCkhCEYuJCEcSyQpIQhGLiQhHEskKSEIRi4kIRxLJCkhCEYuJCEcSyQpIQhGLiQhHEskKSEIRi4kIRxLJCkhCEYuJCEcSyQpIQhGLiQhHEskKSEIRi4kIRxLJCkhCEYuJCEcSyQpIQhGLiQhHEskKSEIRi4kIRxLJCkhCEYuJCEcSyQpIQhGLiQhHEskKSEIRi4kIRxLJCkhCEYuJCEcSyQpIQhGLiQhHEskKSEIRi4kIRxLJCkhCEYuJCEcSyQpIQhGLuQDLNIwmfEn5ZUAAAAASUVORK5CYII=",
// symbol: "image://data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAABLCAYAAAA4TnrqAAAAv0lEQVR4nO3UsUmEQRCA0U801cASxECwAUOrMLEly7CGM7OAM/ZSCxBzA0ERLjqQQ3G5X//3YLPdYWZ2dgMAAAAAAAAAAIBd25/hDVxUR9Xzdw/OsVmn1bK6qk6qt+ppAnlN0mX1vrFeqtvqujr+Kum9CRe1HBT3sDrbsue+WlR31WpQHr9q8/Z3tR6rm8/CDibcrIdBcf/lZI3y4z9rypM12mo9OYv1JG01x2a9Vudzf14AAAAAAAAAAAD8RdUHTdhBNENzGoYAAAAASUVORK5CYII=",
symbol:"image://" + arrow,
symbolRotate: (number, params) => {
console.log(number, params);
return bearingArr[params.dataIndex];
},
// symbolOffset: ["20%", "-5%"],
type: "scatter",
symbolSize: 60,
data: allData1,
},
],
};
option && this.myCharts.setOption(option);
},
echartRegression(regressionType, data, order) {
//regressionType:回归类型'linear', 'exponential', 'logarithmic', 'polynomial'order:多项式的阶数number。对于非多项式回归可以忽略该参数。
let myRegression = this.$ecstat.regression(regressionType, data, order);
myRegression.points.sort(function (a, b) {
return a[0] - b[0];
});
this.erExpression = myRegression.expression;
return myRegression;
},
},
};
</script>
<style scoped lang="less">
.rtReport {
width: 100%;
height: 100%;
}
.realResult {
width: 100%;
height: calc(55% - 5px);
background-color: white;
margin-bottom: 5px;
}
.errorGraph {
width: 100%;
height: 45%;
background-color: white;
}
.resContent {
border: 1px black solid;
height: calc(100% - 60px);
width: calc(100% - 20px);
margin: 10px;
font-size: 20px;
overflow-y: scroll;
span {
display: block;
margin: 10px;
}
}
.errContent {
border: 1px black solid;
height: calc(100% - 60px);
width: calc(100% - 20px);
margin: 10px;
position: relative;
}
#rtReportEcharts {
width: 100%;
height: 100%;
position: relative;
}
.box1 {
height: 40px;
line-height: 40px;
border-bottom: 1px solid rgb(205, 205, 205, 0.5);
}
.sp1 {
display: inline-block;
width: 7px;
height: 26px;
background-color: #354595;
vertical-align: top;
margin-left: 20px;
margin-top: 8px;
}
.sp2 {
margin-left: 10px;
font-size: 20px;
font-weight: 700;
color: #354595;
vertical-align: top;
}
.legend {
position: absolute;
top: 20px;
right: 20px;
background-color: transparent;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
.line {
height: 20px;
line-height: 20px;
margin: 0 5px;
.circle {
display: inline-block;
width: 7px;
height: 7px;
border: 1px solid rgb(194, 53, 49);
border-radius: 50%;
margin-right: 5px;
}
.circle_2 {
display: inline-block;
width: 7px;
height: 7px;
border-radius: 50%;
margin-right: 5px;
border: 1px solid #0000cd;
}
}
}
.erTip {
position: absolute;
top: 10px;
right: 26px;
font-size: small;
text-align: end;
.echartReg {
height: 20px;
line-height: 20px;
margin: 0 5px;
.echartRegName {
display: inline-block;
width: 9px;
height: 9px;
background-color: #7a96c6;
// border: 1px solid #7A96C6;
border-radius: 50%;
margin-right: 5px;
}
}
}
</style>