From ef15856979e6f735f5660af18e5763d8d29fbb4c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=88=98=E6=AC=A3=E7=84=B6?=
<12817866+yueranzhishang@user.noreply.gitee.com>
Date: Wed, 11 Jun 2025 17:29:26 +0000
Subject: [PATCH] =?UTF-8?q?add=20neo/static/js/underWater.js.=20=E6=B0=B4?=
=?UTF-8?q?=E4=B8=8B=E7=B3=BB=E7=BB=9F=E6=B7=BB=E5=8A=A0js=E6=96=87?=
=?UTF-8?q?=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: 刘欣然 <12817866+yueranzhishang@user.noreply.gitee.com>
---
neo/static/js/underWater.js | 650 ++++++++++++++++++++++++++++++++++++
1 file changed, 650 insertions(+)
create mode 100644 neo/static/js/underWater.js
diff --git a/neo/static/js/underWater.js b/neo/static/js/underWater.js
new file mode 100644
index 0000000..f20a43c
--- /dev/null
+++ b/neo/static/js/underWater.js
@@ -0,0 +1,650 @@
+// 鱼类介绍信息-文字+图片
+const speciesInfoData = {
+ "Bream": {
+ description: "鲤鱼(Bream)是一种常见的淡水鱼,体型较大,背部呈青灰色,腹部银白色。它们通常生活在湖泊和河流的底部,以水生植物和小型无脊椎动物为食。鲤鱼是欧洲重要的食用鱼之一。",
+ imageUrl: "../static/images/Bream.jpg"
+ },
+ "Roach": {
+ description: "拟鲤(Roach)是一种小型淡水鱼,广泛分布于欧洲和亚洲。它们身体侧扁,呈银灰色,背鳍和尾鳍略带红色。拟鲤通常成群活动,以水生昆虫和植物为食。",
+ imageUrl: "../static/images/Roach.jpg"
+ },
+ "Whitefish": {
+ description: "白鱼(Whitefish)是一类冷水性鱼类的统称,主要生活在北半球的淡水湖泊中。它们体型修长,银白色,是重要的商业捕捞鱼类。白鱼肉质细嫩,味道鲜美。",
+ imageUrl: "../static/images/Whitefish.jpg"
+ },
+ "Parkki": {
+ description: "派克鱼(Parkki)是一种生活在北欧淡水中的鱼类,体型中等,背部呈橄榄绿色,腹部银白色。它们是肉食性鱼类,以小鱼和水生昆虫为食。",
+ imageUrl: "../static/images/Parkki.jpg"
+ },
+ "Perch": {
+ description: "鲈鱼(Perch)是一种受欢迎的淡水鱼,身体呈黄绿色,有5-9条垂直的黑色条纹。它们是凶猛的捕食者,以小鱼和甲壳类为食。鲈鱼是垂钓者的热门目标。",
+ imageUrl: "../static/images/Whitefish.jpg"
+ },
+ "Pike": {
+ description: "梭子鱼(Pike)是一种大型淡水掠食鱼,身体细长,口部大而尖。它们潜伏在水草中伏击猎物,包括鱼类、青蛙甚至小型水鸟。梭子鱼是运动钓鱼的热门目标。",
+ imageUrl: "../static/images/Whitefish.jpg"
+ },
+ "Smelt": {
+ description: "胡瓜鱼(Smelt)是一种小型鱼类,生活在北半球的淡水和咸水中。它们身体细长,半透明,通常成群活动。胡瓜鱼是许多大型鱼类和鸟类的重要食物来源。",
+ imageUrl: "../static/images/Whitefish.jpg"
+ }
+};
+
+// 计算箱线图的各值d3
+function calculateBoxplotData(data) {
+ const sorted = [...data].sort((a, b) => a - b);
+ return [
+ d3.min(sorted), // min
+ d3.quantile(sorted, 0.25), // Q1
+ d3.quantile(sorted, 0.5), // median
+ d3.quantile(sorted, 0.75), // Q3
+ d3.max(sorted) // max
+ ];
+}
+
+// 计算整体平均重量
+function calculateOverallAvg(fishData) {
+ const validWeights = fishData.weights.filter(w => !isNaN(w));
+ if (validWeights.length === 0) return 0;
+ const sum = validWeights.reduce((a, b) => a + b, 0);
+ return Math.round(sum / validWeights.length);
+}
+
+// 加载csv文件
+async function loadCSVData() {
+ try {
+ const response = await fetch('/static/Fish.csv');
+ if (!response.ok) throw new Error("Failed to load CSV");
+ const csvText = await response.text();
+ return parseCSV(csvText);
+ } catch (error) {
+ console.error('Error loading CSV:', error);
+ return null;
+ }
+}
+
+function parseCSV(csvText) {
+ const lines = csvText.split('\n');
+ const result = {
+ species: [],
+ weights: [],
+ lengths: [],
+ heights: [],
+ widths: [],
+ records: [],
+ speciesData: {}
+ };
+
+ // 跳过表头行(如果有)
+ const startLine = lines[0].includes('Species') ? 1 : 0;
+
+ for (let i = startLine; i < lines.length; i++) {
+ if (!lines[i].trim()) continue;
+
+ // 处理CSV行,考虑到可能有逗号在引号中
+ const row = lines[i].split(',').map(item => item.trim());
+ const species = row[0];
+ const weight = parseFloat(row[1]);
+ const length1 = parseFloat(row[2]);
+ const length2 = parseFloat(row[3]);
+ const length3 = parseFloat(row[4]);
+ const height = parseFloat(row[5]);
+ const width = parseFloat(row[6]);
+
+ // 添加到总数据
+ result.species.push(species);
+ result.weights.push(weight);
+ result.lengths.push(length3); // 使用Length3作为主要长度
+ result.heights.push(height);
+ result.widths.push(width);
+
+ // 按物种分组数据
+ if (!result.speciesData[species]) {
+ result.speciesData[species] = {
+ weights: [],
+ lengths: [],
+ heights: [],
+ widths: []
+ };
+ }
+
+ result.speciesData[species].weights.push(weight);
+ result.speciesData[species].lengths.push(length3);
+ result.speciesData[species].heights.push(height);
+ result.speciesData[species].widths.push(width);
+
+ // 添加完整记录
+ result.records.push({
+ species,
+ weight,
+ length1,
+ length2,
+ length3,
+ height,
+ width
+ });
+ }
+
+ // 计算每个物种的统计数据
+ const speciesStats = {};
+ Object.keys(result.speciesData).forEach(species => {
+ const data = result.speciesData[species];
+
+ speciesStats[species] = {
+ count: data.weights.length,
+ avgWeight: calculateAverage(data.weights),
+ minWeight: Math.min(...data.weights),
+ maxWeight: Math.max(...data.weights),
+ avgLength: calculateAverage(data.lengths),
+ minLength: Math.min(...data.lengths),
+ maxLength: Math.max(...data.lengths),
+ avgHeight: calculateAverage(data.heights),
+ minHeight: Math.min(...data.heights),
+ maxHeight: Math.max(...data.heights),
+ avgWidth: calculateAverage(data.widths),
+ minWidth: Math.min(...data.widths),
+ maxWidth: Math.max(...data.widths)
+ };
+ });
+
+ // 计算唯一物种列表
+ result.uniqueSpecies = [...new Set(result.species)];
+ result.totalRecords = result.records.length;
+ result.speciesStats = speciesStats;
+
+ return result;
+}
+
+// 计算平均数
+function calculateAverage(values) {
+ const validValues = values.filter(v => !isNaN(v));
+ if (validValues.length === 0) return 0;
+ const sum = validValues.reduce((a, b) => a + b, 0);
+ return sum / validValues.length;
+}
+
+// 初始化图表
+async function initCharts() {
+ const fishData = await loadCSVData();
+ if (!fishData) {
+ alert('无法加载数据,请检查数据文件');
+ return;
+ }
+
+ // 在 initCharts 函数末尾添加:
+ window.fishData = fishData;
+
+ // 更新统计卡片
+ document.getElementById('species-count').textContent = fishData.uniqueSpecies.length;
+ document.getElementById('total-records').textContent = fishData.totalRecords;
+ document.getElementById('avg-weight').textContent = calculateOverallAvg(fishData) + 'g';
+
+ // 1. 鱼类分布饼图 - 按物种计数
+ const speciesCounts = {};
+ fishData.species.forEach(species => {
+ speciesCounts[species] = (speciesCounts[species] || 0) + 1;
+ });
+
+ const distChart = echarts.init(document.getElementById('species-distribution'));
+ const distOption = {
+ tooltip: {
+ trigger: 'item',
+ formatter: '{a}
{b}: {c} ({d}%)'
+ },
+ legend: {
+ orient: 'vertical',
+ right: 10,
+ top: 'center',
+ data: fishData.uniqueSpecies
+ },
+ series: [
+ {
+ name: '鱼类分布',
+ type: 'pie',
+ radius: ['40%', '70%'],
+ avoidLabelOverlap: false,
+ itemStyle: {
+ color: function(params) {
+ const colorList = ['#7b51db', '#5a8dee', '#00c9db', '#00d4a0', '#ffc107', '#ff6b6b', '#a162e8'];
+ return colorList[params.dataIndex % colorList.length];
+ },
+ borderRadius: 10,
+ borderColor: '#fff',
+ borderWidth: 2
+ },
+ label: {
+ show: false,
+ position: 'center'
+ },
+ emphasis: {
+ label: {
+ show: true,
+ fontSize: '18',
+ fontWeight: 'bold'
+ }
+ },
+ labelLine: {
+ show: false
+ },
+ data: fishData.uniqueSpecies.map(species => ({
+ value: speciesCounts[species],
+ name: species
+ }))
+ }
+ ]
+ };
+ distChart.setOption(distOption);
+
+ // 2. 重量长度分布箱线图
+ const weightChart = echarts.init(document.getElementById('weight-distribution'));
+ const weightOption = {
+ tooltip: {
+ trigger: 'item',
+ axisPointer: {
+ type: 'shadow'
+ },
+ formatter: function(params) {
+ const data = params.data;
+ return [
+ '鱼类: ' + params.name + '