diff --git a/src/app/(main)/health-risk-analysis/page.tsx b/src/app/(main)/health-risk-analysis/page.tsx
index ef168be..67881f7 100644
--- a/src/app/(main)/health-risk-analysis/page.tsx
+++ b/src/app/(main)/health-risk-analysis/page.tsx
@@ -4,7 +4,7 @@ import MapComponent from "@app/OlMap/MapComponent";
import Timeline from "@components/olmap/HealthRiskAnalysis/Timeline";
import MapToolbar from "@app/OlMap/Controls/Toolbar";
import { HealthRiskProvider } from "@components/olmap/HealthRiskAnalysis/HealthRiskContext";
-import HealthRiskPieChart from "@components/olmap/HealthRiskAnalysis/HealthRiskPieChart";
+import HealthRiskStatistics from "@components/olmap/HealthRiskAnalysis/HealthRiskStatistics";
import PredictDataPanel from "@components/olmap/HealthRiskAnalysis/PredictDataPanel";
import StyleLegend from "@app/OlMap/Controls/StyleLegend";
import {
@@ -24,7 +24,7 @@ export default function Home() {
HistoryPanel={PredictDataPanel}
/>
-
+
= ({
const properties = highlightFeature.getProperties();
// 计算属性字段,增加 key 字段
const pipeComputedFields = [
- { key: "flow", label: "流量", unit: "m³/s" },
+ { key: "flow", label: "流量", unit: "m³/h" },
{ key: "friction", label: "摩阻", unit: "" },
{ key: "headloss", label: "水头损失", unit: "m" },
+ { key: "headlossPerKM", label: "单位水头损失", unit: "m/km" },
{ key: "quality", label: "水质", unit: "mg/L" },
- { key: "reaction", label: "反应", unit: "1/s" },
+ { key: "reaction", label: "反应", unit: "1/d" },
{ key: "setting", label: "设置", unit: "" },
{ key: "status", label: "状态", unit: "" },
{ key: "velocity", label: "流速", unit: "m/s" },
];
const nodeComputedFields = [
- { key: "actualdemand", label: "实际需水量", unit: "m³/s" },
+ { key: "actualdemand", label: "实际需水量", unit: "m³/h" },
{ key: "head", label: "水头", unit: "m" },
{ key: "pressure", label: "压力", unit: "m" },
{ key: "quality", label: "水质", unit: "mg/L" },
diff --git a/src/app/OlMap/MapComponent.tsx b/src/app/OlMap/MapComponent.tsx
index b79dff5..1720ddb 100644
--- a/src/app/OlMap/MapComponent.tsx
+++ b/src/app/OlMap/MapComponent.tsx
@@ -293,7 +293,7 @@ const MapComponent: React.FC = ({ children }) => {
type: "point",
properties: [
// { name: "需求量", value: "demand" },
- // { name: "海拔高度", value: "elevation" },
+ { name: "高程", value: "elevation" },
{ name: "实际需求量", value: "actualdemand" },
{ name: "水头", value: "head" },
{ name: "压力", value: "pressure" },
@@ -318,6 +318,7 @@ const MapComponent: React.FC = ({ children }) => {
{ name: "流量", value: "flow" },
{ name: "摩阻系数", value: "friction" },
{ name: "水头损失", value: "headloss" },
+ { name: "单位水头损失", value: "headlossPerKM" },
{ name: "水质", value: "quality" },
{ name: "反应速率", value: "reaction" },
{ name: "设置值", value: "setting" },
diff --git a/src/components/olmap/HealthRiskAnalysis/HealthRiskPieChart.tsx b/src/components/olmap/HealthRiskAnalysis/HealthRiskStatistics.tsx
similarity index 61%
rename from src/components/olmap/HealthRiskAnalysis/HealthRiskPieChart.tsx
rename to src/components/olmap/HealthRiskAnalysis/HealthRiskStatistics.tsx
index 5806473..e02fa57 100644
--- a/src/components/olmap/HealthRiskAnalysis/HealthRiskPieChart.tsx
+++ b/src/components/olmap/HealthRiskAnalysis/HealthRiskStatistics.tsx
@@ -3,7 +3,6 @@
import React, { useMemo } from "react";
import ReactECharts from "echarts-for-react";
import {
- Paper,
Typography,
Box,
Chip,
@@ -13,11 +12,24 @@ import {
Slide,
Fade,
} from "@mui/material";
-import { ChevronLeft, ChevronRight, PieChart } from "@mui/icons-material";
+import { ChevronLeft, ChevronRight, BarChart } from "@mui/icons-material";
import { RAINBOW_COLORS, RISK_BREAKS, RISK_LABELS } from "./types";
import { useHealthRisk } from "./HealthRiskContext";
-const HealthRiskPieChart: React.FC = () => {
+const SIMPLE_LABELS = [
+ "0.0 - 0.1",
+ "0.1 - 0.2",
+ "0.2 - 0.3",
+ "0.3 - 0.4",
+ "0.4 - 0.5",
+ "0.5 - 0.6",
+ "0.6 - 0.7",
+ "0.7 - 0.8",
+ "0.8 - 0.9",
+ "0.9 - 1.0",
+];
+
+const HealthRiskStatistics: React.FC = () => {
const { predictionResults, currentYear } = useHealthRisk();
const [isExpanded, setIsExpanded] = React.useState(true);
const [hoveredYearIndex, setHoveredYearIndex] = React.useState(
@@ -27,22 +39,31 @@ const HealthRiskPieChart: React.FC = () => {
const datasetSource = useMemo(() => {
if (!predictionResults || predictionResults.length === 0) return [];
- const years = Array.from({ length: 70 }, (_, i) => 4 + i); // 4 to 73
+ // 收集所有唯一的年份
+ const allYears = new Set();
+ predictionResults.forEach((result) => {
+ if (result.survival_function.x) {
+ result.survival_function.x.forEach((year) => allYears.add(year));
+ }
+ });
+ const years = Array.from(allYears).sort((a, b) => a - b);
const header = ["Risk Level", ...years.map(String)];
- const rows = RISK_LABELS.map((label, riskIdx) => {
+ const rows = SIMPLE_LABELS.map((label, riskIdx) => {
const row: (string | number)[] = [label];
years.forEach((year) => {
let count = 0;
predictionResults.forEach((result) => {
- const { y } = result.survival_function;
- const index = year - 4;
- if (index >= 0 && index < y.length) {
- const probability = y[index];
- const lowerBound = riskIdx === 0 ? -1 : RISK_BREAKS[riskIdx - 1];
- const upperBound = RISK_BREAKS[riskIdx];
- if (probability > lowerBound && probability <= upperBound) {
- count++;
+ const { x, y } = result.survival_function;
+ if (x && x.includes(year)) {
+ const yearIndex = x.indexOf(year);
+ if (yearIndex >= 0 && yearIndex < y.length) {
+ const probability = y[yearIndex];
+ const lowerBound = riskIdx === 0 ? -1 : RISK_BREAKS[riskIdx - 1];
+ const upperBound = RISK_BREAKS[riskIdx];
+ if (probability > lowerBound && probability <= upperBound) {
+ count++;
+ }
}
}
});
@@ -58,13 +79,19 @@ const HealthRiskPieChart: React.FC = () => {
if (hoveredYearIndex !== null) {
return hoveredYearIndex + 1;
}
- // 默认显示当前时间轴年份
- return Math.max(1, Math.min(70, currentYear - 3));
- }, [hoveredYearIndex, currentYear]);
+ // 查找 currentYear 在 datasetSource header 中的索引
+ if (datasetSource.length > 0) {
+ const header = datasetSource[0] as string[];
+ const yearStr = String(currentYear);
+ const index = header.indexOf(yearStr);
+ if (index !== -1) return index;
+ }
+ return 1;
+ }, [hoveredYearIndex, currentYear, datasetSource]);
const option = {
legend: {
- top: "48%",
+ top: "middle",
left: "center",
itemWidth: 10,
itemHeight: 10,
@@ -76,53 +103,105 @@ const HealthRiskPieChart: React.FC = () => {
trigger: "axis",
showContent: true,
confine: true,
+ // formatter: function(params: any[]) {
+ // const year = params[0].axisValue;
+ // let content = `预测年份:${year}
`;
+ // params.forEach((p: any) => {
+ // content += `${p.seriesName}: ${p.value}
`;
+ // });
+ // return content;
+ // },
},
dataset: {
source: datasetSource,
},
- xAxis: {
- type: "category",
- axisLabel: {
- fontSize: 10,
+ grid: [
+ {
+ // 折线图网格
+ top: "62%",
+ bottom: "8%",
+ left: "12%",
+ right: "5%",
},
- },
- yAxis: {
- gridIndex: 0,
- axisLabel: {
- fontSize: 10,
+ {
+ // 柱状图网格
+ top: "5%",
+ bottom: "60%",
+ left: "10%",
+ right: "10%",
},
- },
- grid: {
- top: "62%",
- bottom: "8%",
- left: "12%",
- right: "5%",
- },
+ ],
+ xAxis: [
+ {
+ type: "category",
+ gridIndex: 0,
+ data:
+ datasetSource.length > 0
+ ? (datasetSource[0] as string[]).slice(1)
+ : [],
+ axisLabel: {
+ fontSize: 10,
+ },
+ },
+ {
+ type: "value",
+ gridIndex: 1,
+ name: "数量",
+ axisLabel: {
+ fontSize: 10,
+ },
+ nameTextStyle: {
+ fontSize: 10,
+ },
+ },
+ ],
+ yAxis: [
+ {
+ gridIndex: 0,
+ axisLabel: {
+ fontSize: 10,
+ },
+ },
+ {
+ type: "category",
+ gridIndex: 1,
+ data: SIMPLE_LABELS,
+ inverse: true, // 让极高风险在上方
+ axisLabel: {
+ fontSize: 10,
+ },
+ },
+ ],
series: [
...RISK_LABELS.map((_, i) => ({
+ name: RISK_LABELS[i],
type: "line",
smooth: true,
seriesLayoutBy: "row",
+ xAxisIndex: 0,
+ yAxisIndex: 0,
emphasis: { focus: "series" },
itemStyle: { color: RAINBOW_COLORS[i] },
symbol: "none",
})),
{
- type: "pie",
- id: "pie",
- radius: "35%",
- center: ["50%", "24%"],
- emphasis: {
- focus: "self",
+ type: "bar",
+ id: "bar",
+ xAxisIndex: 1,
+ yAxisIndex: 1,
+ encode: {
+ x: displayDimension,
+ y: "Risk Level",
},
label: {
- formatter: "{b}: {@[" + displayDimension + "]} ({d}%)",
+ show: true,
+ position: "right",
fontSize: 10,
},
- encode: {
- itemName: "Risk Level",
- value: displayDimension,
- tooltip: displayDimension,
+ itemStyle: {
+ color: (params: any) => {
+ return RAINBOW_COLORS[params.dataIndex];
+ },
},
},
],
@@ -158,7 +237,7 @@ const HealthRiskPieChart: React.FC = () => {
color: "text.secondary",
}}
>
-
+
暂无预测数据
@@ -178,7 +257,7 @@ const HealthRiskPieChart: React.FC = () => {
sx={{ zIndex: 1300 }}
>
-
+
{
{/* 头部 */}
-
+
管道健康风险统计
{
);
};
-export default HealthRiskPieChart;
+export default HealthRiskStatistics;
diff --git a/src/components/olmap/SCADADataPanel.tsx b/src/components/olmap/SCADADataPanel.tsx
index 1b5a0f3..b785233 100644
--- a/src/components/olmap/SCADADataPanel.tsx
+++ b/src/components/olmap/SCADADataPanel.tsx
@@ -718,7 +718,8 @@ const SCADADataPanel: React.FC = ({
{
name: `${id} (原始)`,
type: "line",
- symbol: "none",
+ showSymbol: true,
+ symbolSize: 4,
sampling: "lttb",
itemStyle: { color: colors[index % colors.length] },
data: dataset.map((item) => item[`${id}_raw`]),
@@ -726,7 +727,8 @@ const SCADADataPanel: React.FC = ({
{
name: `${id} (清洗)`,
type: "line",
- symbol: "none",
+ showSymbol: true,
+ symbolSize: 4,
sampling: "lttb",
itemStyle: { color: colors[(index + 3) % colors.length] },
data: dataset.map((item) => item[`${id}_clean`]),
@@ -734,7 +736,8 @@ const SCADADataPanel: React.FC = ({
{
name: `${id} (模拟)`,
type: "line",
- symbol: "none",
+ showSymbol: true,
+ symbolSize: 4,
sampling: "lttb",
itemStyle: { color: colors[(index + 6) % colors.length] },
data: dataset.map((item) => item[`${id}_sim`]),
@@ -744,7 +747,8 @@ const SCADADataPanel: React.FC = ({
return deviceIds.map((id, index) => ({
name: id,
type: "line",
- symbol: "none",
+ showSymbol: true,
+ symbolSize: 4,
sampling: "lttb",
itemStyle: { color: colors[index % colors.length] },
data: dataset.map((item) => item[`${id}_${selectedSource}`]),
@@ -768,7 +772,8 @@ const SCADADataPanel: React.FC = ({
: "模拟"
})`,
type: "line",
- symbol: "none",
+ showSymbol: true,
+ symbolSize: 4,
sampling: "lttb",
itemStyle: {
color: colors[(index * 3 + sIndex) % colors.length],
@@ -795,7 +800,8 @@ const SCADADataPanel: React.FC = ({
series.push({
name: id,
type: "line",
- symbol: "none",
+ showSymbol: true,
+ symbolSize: 4,
sampling: "lttb",
itemStyle: { color: colors[index % colors.length] },
data: dataset.map((item) => item[id]),