<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>居民幸福感导向建成环境智能优化系统</title>
<!-- Leaflet & ECharts -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<style>
:root {
--panel-bg: rgba(6, 26, 50, 0.3);
--accent-blue: #00f2ff;
--border-color: rgba(0, 242, 255, 0.15);
--text-main: #cbd5e1;
}
body, html {
margin: 0; padding: 0; width: 100vw; height: 100vh;
background: #020b1a;
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
overflow: hidden;
color: var(--text-main);
}
#map {
height: 100%; width: 100%; z-index: 1;
background: #050d18 !important;
}
.leaflet-tile-pane {
filter: invert(100%) hue-rotate(180deg) brightness(0.7) contrast(1.2) grayscale(0.5);
}
.grid-overlay {
position: fixed;
top: 0; left: 0; width: 100%; height: 100%;
background-image: radial-gradient(rgba(0, 242, 255, 0.05) 0.5px, transparent 0.5px);
background-size: 60px 60px;
pointer-events: none;
z-index: 5;
opacity: 0.1;
}
.glass-panel {
background: var(--panel-bg);
border: 1px solid var(--border-color);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
display: flex;
flex-direction: column;
padding: 10px;
overflow: hidden;
border-radius: 6px;
}
.panel-title {
color: #fff; font-size: 11px; font-weight: bold;
display: flex; align-items: center; margin-bottom: 8px;
flex-shrink: 0; letter-spacing: 0.5px;
text-shadow: 0 0 10px rgba(0, 242, 255, 0.5);
}
.panel-title::before {
content: ""; width: 3px; height: 11px; background: var(--accent-blue);
margin-right: 6px; box-shadow: 0 0 8px var(--accent-blue);
}
.header {
position: absolute; top: 0; left: 0; width: 100%; height: 50px;
background: linear-gradient(180deg, rgba(2,11,26,0.6) 0%, rgba(2,11,26,0.1) 100%);
backdrop-filter: blur(4px);
z-index: 100; display: flex; justify-content: space-between; align-items: center;
padding: 0 25px; border-bottom: 1px solid rgba(0, 242, 255, 0.1);
}
.side-container {
position: absolute; top: 65px; bottom: 15px;
display: flex; flex-direction: column; gap: 12px;
z-index: 20; transition: all 0.4s ease;
}
.home-width { width: 230px; }
.detail-width { width: 32vw; min-width: 480px; }
.left-side { left: 15px; }
.right-side { right: 15px; }
.chart-wrapper { flex: 1; width: 100%; min-height: 0; position: relative; }
.horizontal-scroll-box { flex: 1; width: 100%; overflow-x: auto; overflow-y: hidden; cursor: grab; }
.scroll-content { height: 100%; min-width: 450px; }
.scroll-content-wide { height: 100%; min-width: 800px; }
.data-table { width: 100%; font-size: 10px; border-collapse: collapse; }
.data-table th {
color: var(--accent-blue); text-align: left; padding: 6px 4px;
border-bottom: 1px solid rgba(0, 242, 255, 0.2);
position: sticky; top: 0; background: rgba(8, 29, 58, 0.4); z-index: 1;
}
.data-table td { padding: 6px 4px; border-bottom: 1px solid rgba(255, 255, 255, 0.05); }
.scroll-custom::-webkit-scrollbar { height: 3px; width: 3px; }
.scroll-custom::-webkit-scrollbar-thumb { background: rgba(0, 242, 255, 0.3); border-radius: 10px; }
.nav-item { cursor: pointer; transition: all 0.3s; padding: 4px 0; position: relative; color: rgba(255,255,255,0.7); }
.nav-item:hover { color: #fff; }
.nav-item.active { color: var(--accent-blue); font-weight: bold; }
.nav-item.active::after { content: ""; position: absolute; bottom: 0; left: 0; width: 100%; height: 2px; background: var(--accent-blue); box-shadow: 0 0 10px var(--accent-blue); }
.hidden-panel { display: none !important; }
.custom-select {
background: rgba(8, 29, 58, 0.3); border: 1px solid rgba(0, 242, 255, 0.3);
color: #fff; font-size: 10px; padding: 2px 4px; border-radius: 2px;
outline: none; width: 100%; cursor: pointer;
}
.analysis-text-container { font-size: 10px; line-height: 1.6; color: #cbd5e1; padding-right: 8px; }
.keyword-highlight { color: var(--accent-blue); font-weight: bold; text-shadow: 0 0 5px rgba(0, 242, 255, 0.3); }
.important-term { color: #facc15; font-weight: bold; }
.text-section-title { color: #fff; font-weight: bold; margin-top: 8px; margin-bottom: 4px; display: block; border-bottom: 1px solid rgba(0, 242, 255, 0.1); padding-bottom: 2px; }
/* Leaflet Popup 样式优化 */
.leaflet-popup-content-wrapper {
background: rgba(255, 255, 255, 0.95) !important;
color: #1a1a1a !important;
border-radius: 6px !important;
box-shadow: 0 4px 15px rgba(0,0,0,0.3) !important;
border: 1px solid rgba(0, 242, 255, 0.5);
}
.leaflet-popup-tip { background: rgba(255, 255, 255, 0.95) !important; }
.embedded-analysis-img {
width: 100%; height: 100%;
object-fit: contain;
border: none;
background: rgba(0, 0, 0, 0.2);
}
</style>
</head>
<body>
<div class="grid-overlay"></div>
<div id="map"></div>
<header class="header">
<div>
<h1 class="text-base font-bold italic text-white tracking-tight">居民幸福感导向建成环境智能优化系统</h1>
<p class="text-[7px] tracking-[0.2em] text-cyan-400 uppercase">Intelligent Built Environment Optimization System</p>
</div>
<nav class="flex space-x-8 text-[11px]">
<span id="nav-home" class="nav-item active" onclick="switchTab('home')">首页</span>
<span id="nav-detail" class="nav-item" onclick="switchTab('detail')">指标详情</span>
</nav>
</header>
<!-- [首页视图] -->
<div id="home-left" class="side-container left-side home-width">
<div class="glass-panel flex-[1.1]">
<div class="panel-title">主导情绪数目统计</div>
<div id="pieChart" class="chart-wrapper"></div>
</div>
<div class="glass-panel flex-[1.1]">
<div class="panel-title">主导情绪波动统计</div>
<div class="horizontal-scroll-box scroll-custom">
<div class="scroll-content">
<div id="lineChart" class="w-full h-full"></div>
</div>
</div>
</div>
<div class="glass-panel flex-[1]">
<div class="panel-title">各街道幸福感指数</div>
<div class="horizontal-scroll-box scroll-custom">
<div class="scroll-content-wide">
<div id="barChart" class="w-full h-full"></div>
</div>
</div>
</div>
</div>
<div id="home-right" class="side-container right-side home-width">
<div class="glass-panel" style="height: 150px; flex-shrink: 0;">
<div class="panel-title">幸福感总体评价</div>
<div class="flex justify-around items-center h-full">
<div class="text-center">
<div id="gauge1" style="width:90px; height:90px;"></div>
<div class="text-[8px] -mt-2 text-cyan-400 font-bold">综合指数</div>
</div>
<div class="text-center">
<div id="gauge2" style="width:90px; height:90px;"></div>
<div class="text-[8px] -mt-2 text-cyan-400 font-bold">环境匹配</div>
</div>
</div>
</div>
<div class="glass-panel flex-1">
<div class="panel-title">各街道幸福感排名</div>
<div class="flex-1 overflow-y-auto scroll-custom">
<table class="data-table">
<thead>
<tr><th>街道</th><th>数值</th><th>趋势</th></tr>
</thead>
<tbody id="rankTable"></tbody>
</table>
</div>
</div>
</div>
<!-- [指标详情页] -->
<div id="detail-left" class="side-container left-side home-width hidden-panel">
<div class="glass-panel" style="padding: 10px;">
<div class="panel-title">关键建成环境指标探索</div>
<div class="flex flex-col gap-3 mt-1">
<div>
<label class="text-[9px] text-cyan-400/80 mb-1 block">一级指标</label>
<select id="mainCategory" class="custom-select" onchange="updateSubCategory()">
<option value="1">1.道路交通指标</option>
<option value="2" selected>2.建筑宜居指标</option>
<option value="3">3.社会经济指标</option>
<option value="4">4.功能复合指标</option>
</select>
</div>
<div>
<label class="text-[9px] text-cyan-400/80 mb-1 block">二级指标</label>
<select id="subCategory" class="custom-select" onchange="checkVisibility()">
</select>
</div>
</div>
</div>
</div>
<div id="detail-right" class="side-container right-side detail-width hidden-panel">
<div id="detailContentContainer" class="flex flex-col gap-3 h-full hidden-panel">
<div class="flex gap-3 h-[38%]">
<div class="glass-panel flex-1">
<div class="panel-title">容积率指标汇总表</div>
<div id="detailRadar" class="chart-wrapper"></div>
</div>
<div class="glass-panel flex-[1.6]">
<div class="panel-title">智能规划建议</div>
<div class="chart-wrapper overflow-y-auto scroll-custom analysis-text-container pr-1">
<div class="mb-2">
<span class="text-section-title">关键词——<span class="keyword-highlight">最优容积率区间:0.8 - 1.5</span></span>
</div>
<div class="text-justify">
<span class="text-section-title">分析过程——</span>
<p class="mb-2">观察散点图,高幸福感(紫色点)主要分布在幸福感评分 <span class="important-term">65-80</span> 的区域,对应的容积率集中在 <span class="important-term">0.5-1.5</span> 之间,其中 <span class="keyword-highlight">0.8-1.5</span> 最为密集。</p>
<p class="mb-2">当容积率低于0.5时,虽然也有高幸福感点,但这些样本在南京建成区中极其稀少(多位于远郊),不具备普遍性。当容积率超过1.5时,高幸福感点开始明显减少,超过2.0后几乎消失。</p>
<p class="mb-2">南京主城区现状容积率普遍在1.2-2.5之间。容积率低于0.8意味着别墅、低密度洋房,这类产品仅占市场供应量的5%以下,且单价极高,不具备普遍参考价值。</p>
<p class="mb-2">因此,具有建设指导意义的区间应该是 <span class="important-term">0.8-1.5</span> ——这个区间对应的是 <span class="important-term">6-11 层的小高层住宅</span>,既能保证居住舒适度(一梯两户、南北通透、楼间距充裕),又具备现实可操作性。</p>
<p>超过1.5则进入高层住宅范畴,人口密度上升、电梯等待时间增加、公共空间被压缩,幸福感开始下降。所以,数据与现实的交集告诉我们:在南京建成区,容积率控制在 <span class="keyword-highlight">1.0-1.5</span> 之间的小高层社区,是幸福感与可操作性的最佳平衡点。</p>
</div>
</div>
</div>
</div>
<div class="glass-panel flex-[2] p-0 overflow-hidden">
<div class="panel-title px-3 pt-3">幸福感与容积率关系分析</div>
<div class="chart-wrapper flex items-center justify-center overflow-hidden">
<img id="analysisImg" src="https://i.imgs.ovh/2026/04/08/ZHuRKh.jpeg" class="embedded-analysis-img" alt="分析图">
</div>
</div>
</div>
<div id="detailPlaceholder" class="glass-panel flex-1">
<div class="empty-state flex flex-col items-center justify-center h-full text-cyan-400/30 text-xs">
<span>请在左侧选择具体指标以查看详细分析</span>
</div>
</div>
</div>
<script>
const geojsonData = {
"type": "FeatureCollection",
"name": "南京幸福感大样本数据",
"features": [
{ "type": "Feature", "properties": { "幸福感": 92.16, "街道": "宁海路街道" }, "geometry": { "type": "Point", "coordinates": [ 118.905248, 32.109082 ] } },
{ "type": "Feature", "properties": { "幸福感": 92.56, "街道": "华侨路街道" }, "geometry": { "type": "Point", "coordinates": [ 118.790343, 32.156244 ] } },
{ "type": "Feature", "properties": { "幸福感": 87.21, "街道": "湖南路街道" }, "geometry": { "type": "Point", "coordinates": [ 118.835926, 32.168242 ] } },
{ "type": "Feature", "properties": { "幸福感": 92.16, "街道": "中央门街道" }, "geometry": { "type": "Point", "coordinates": [ 118.750558, 32.017352 ] } },
{ "type": "Feature", "properties": { "幸福感": 94.19, "街道": "挹江门街道" }, "geometry": { "type": "Point", "coordinates": [ 118.70892, 31.977134 ] } },
{ "type": "Feature", "properties": { "幸福感": 98.14, "街道": "江东街道" }, "geometry": { "type": "Point", "coordinates": [ 118.719673, 32.011841 ] } },
{ "type": "Feature", "properties": { "幸福感": 77.67, "街道": "莫愁湖街道" }, "geometry": { "type": "Point", "coordinates": [ 118.737599, 32.037651 ] } },
{ "type": "Feature", "properties": { "幸福感": 85.58, "街道": "建邺街道" }, "geometry": { "type": "Point", "coordinates": [ 118.653587, 31.97263 ] } },
{ "type": "Feature", "properties": { "幸福感": 65.42, "街道": "栖霞街道" }, "geometry": { "type": "Point", "coordinates": [ 118.8982, 32.1321 ] } },
{ "type": "Feature", "properties": { "幸福感": 82.11, "街道": "新街口街道" }, "geometry": { "type": "Point", "coordinates": [ 118.7831, 32.0425 ] } },
{ "type": "Feature", "properties": { "幸福感": 74.88, "街道": "迈皋桥街道" }, "geometry": { "type": "Point", "coordinates": [ 118.8122, 32.1054 ] } },
{ "type": "Feature", "properties": { "幸福感": 91.32, "街道": "燕子矶街道" }, "geometry": { "type": "Point", "coordinates": [ 118.8251, 32.1481 ] } },
{ "type": "Feature", "properties": { "幸福感": 79.25, "街道": "后宰门街道" }, "geometry": { "type": "Point", "coordinates": [ 118.8093, 32.0461 ] } },
{ "type": "Feature", "properties": { "幸福感": 88.64, "街道": "朝天宫街道" }, "geometry": { "type": "Point", "coordinates": [ 118.7752, 32.0354 ] } },
{ "type": "Feature", "properties": { "幸福感": 71.55, "街道": "红山街道" }, "geometry": { "type": "Point", "coordinates": [ 118.8105, 32.0894 ] } },
{ "type": "Feature", "properties": { "幸福感": 83.47, "街道": "夫子庙街道" }, "geometry": { "type": "Point", "coordinates": [ 118.7901, 32.0182 ] } },
{ "type": "Feature", "properties": { "幸福感": 95.80, "街道": "双闸街道" }, "geometry": { "type": "Point", "coordinates": [ 118.6952, 31.9785 ] } },
{ "type": "Feature", "properties": { "幸福感": 68.91, "街道": "玄武门街道" }, "geometry": { "type": "Point", "coordinates": [ 118.7952, 32.0652 ] } },
{ "type": "Feature", "properties": { "幸福感": 76.33, "街道": "五老村街道" }, "geometry": { "type": "Point", "coordinates": [ 118.7981, 32.0381 ] } },
{ "type": "Feature", "properties": { "幸福感": 84.19, "街道": "龙江街道" }, "geometry": { "type": "Point", "coordinates": [ 118.7352, 32.0582 ] } },
{ "type": "Feature", "properties": { "幸福感": 90.15, "街道": "仙林街道" }, "geometry": { "type": "Point", "coordinates": [ 118.917, 32.102 ] } },
{ "type": "Feature", "properties": { "幸福感": 62.45, "街道": "西善桥街道" }, "geometry": { "type": "Point", "coordinates": [ 118.718, 31.954 ] } },
{ "type": "Feature", "properties": { "幸福感": 78.90, "街道": "赛虹桥街道" }, "geometry": { "type": "Point", "coordinates": [ 118.761, 32.002 ] } },
{ "type": "Feature", "properties": { "幸福感": 85.32, "街道": "梅山街道" }, "geometry": { "type": "Point", "coordinates": [ 118.635, 31.902 ] } },
{ "type": "Feature", "properties": { "幸福感": 94.67, "街道": "顶山街道" }, "geometry": { "type": "Point", "coordinates": [ 118.692, 32.084 ] } },
{ "type": "Feature", "properties": { "幸福感": 88.12, "街道": "泰山街道" }, "geometry": { "type": "Point", "coordinates": [ 118.721, 32.135 ] } },
{ "type": "Feature", "properties": { "幸福感": 71.44, "街道": "雨花街道" }, "geometry": { "type": "Point", "coordinates": [ 118.775, 31.985 ] } },
{ "type": "Feature", "properties": { "幸福感": 69.21, "街道": "铁心桥街道" }, "geometry": { "type": "Point", "coordinates": [ 118.785, 31.962 ] } },
{ "type": "Feature", "properties": { "幸福感": 96.45, "街道": "兴隆街道" }, "geometry": { "type": "Point", "coordinates": [ 118.725, 32.001 ] } },
{ "type": "Feature", "properties": { "幸福感": 82.56, "街道": "沙洲街道" }, "geometry": { "type": "Point", "coordinates": [ 118.712, 31.975 ] } },
{ "type": "Feature", "properties": { "幸福感": 89.92, "街道": "南苑街道" }, "geometry": { "type": "Point", "coordinates": [ 118.742, 32.022 ] } },
{ "type": "Feature", "properties": { "幸福感": 76.81, "街道": "莫愁湖街道" }, "geometry": { "type": "Point", "coordinates": [ 118.735, 32.038 ] } },
{ "type": "Feature", "properties": { "幸福感": 91.21, "街道": "江东路" }, "geometry": { "type": "Point", "coordinates": [ 118.724, 32.031 ] } },
{ "type": "Feature", "properties": { "幸福感": 84.55, "街道": "汉中门" }, "geometry": { "type": "Point", "coordinates": [ 118.765, 32.042 ] } },
{ "type": "Feature", "properties": { "幸福感": 68.32, "街道": "瑞金路" }, "geometry": { "type": "Point", "coordinates": [ 118.812, 32.032 ] } },
{ "type": "Feature", "properties": { "幸福感": 72.89, "街道": "光华路" }, "geometry": { "type": "Point", "coordinates": [ 118.834, 32.018 ] } },
{ "type": "Feature", "properties": { "幸福感": 93.11, "街道": "大行宫" }, "geometry": { "type": "Point", "coordinates": [ 118.795, 32.042 ] } },
{ "type": "Feature", "properties": { "幸福感": 79.45, "街道": "月牙湖" }, "geometry": { "type": "Point", "coordinates": [ 118.831, 32.035 ] } },
{ "type": "Feature", "properties": { "幸福感": 86.72, "街道": "紫金山" }, "geometry": { "type": "Point", "coordinates": [ 118.855, 32.062 ] } },
{ "type": "Feature", "properties": { "幸福感": 90.54, "街道": "锁金村" }, "geometry": { "type": "Point", "coordinates": [ 118.815, 32.078 ] } }
]
};
let map;
const charts = [];
let pointLayer;
let currentTab = 'home';
function getColor(v) {
return v > 90 ? '#d946ef' : v > 80 ? '#3b82f6' : v > 70 ? '#06b6d4' : v > 60 ? '#22c55e' : '#eab308';
}
function initMap() {
map = L.map('map', {
center: [32.05, 118.78],
zoom: 12, zoomControl: false, attributionControl: false,
renderer: L.canvas({ tolerance: 5 })
});
L.tileLayer('https://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', {
subdomains: ['1', '2', '3', '4']
}).addTo(map);
pointLayer = L.geoJSON(geojsonData, {
pointToLayer: function (feature, latlng) {
const val = feature.properties.幸福感;
return L.circleMarker(latlng, {
radius: 6, fillColor: getColor(val), color: "#ffffff",
weight: 1.5, opacity: 0.8, fillOpacity: 0.9, interactive: true
});
},
onEachFeature: function (feature, layer) {
layer.on('mouseover', function(e) {
if (currentTab !== 'home') return;
const val = feature.properties.幸福感;
const street = feature.properties.街道;
const color = getColor(val);
const content = `
<div style="min-width:140px; padding: 5px;">
<div style="font-size: 13px; font-weight: bold; color: #1a1a1a; margin-bottom: 4px; border-bottom: 1px solid #eee; padding-bottom: 2px;">${street}</div>
<div style="font-size: 10px; color: #666; margin-bottom: 4px;">居民幸福感指数</div>
<div style="font-size: 26px; font-weight: 900; color: ${color}; line-height: 1;">${val}<span style="font-size: 14px; margin-left: 2px;">%</span></div>
</div>
`;
L.popup({ closeButton: false, autoPan: false, offset: [0, -8] })
.setLatLng(e.latlng).setContent(content).openOn(map);
this.setStyle({ radius: 9, weight: 3, opacity: 1 });
});
layer.on('mouseout', function() {
map.closePopup();
this.setStyle({ radius: 6, weight: 1.5, opacity: 0.8 });
});
}
}).addTo(map);
if (geojsonData.features.length > 0) {
map.fitBounds(pointLayer.getBounds(), { padding: [80, 80] });
}
}
function initHomeCharts() {
const commonAxis = {
axisLine:{lineStyle:{color:'rgba(0,242,255,0.1)'}},
axisLabel:{color:'#94a3b8', fontSize:7},
splitLine:{show:false}
};
// 模块一:主导情绪数目统计 - 修复点击显示不完整问题
const pie = echarts.init(document.getElementById('pieChart'));
pie.setOption({
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(8, 29, 58, 0.95)',
borderColor: 'rgba(0, 242, 255, 0.4)',
borderWidth: 1,
textStyle: { color: '#fff', fontSize: 10 },
// 自定义显示格式,确保包含:主导情绪、数量、占比
formatter: function(params) {
return `<div style="padding:4px">
<b style="color:${params.color}; font-size:11px">${params.name}</b><br/>
数量:${params.value}<br/>
占比:${params.percent}%
</div>`;
}
},
legend: {
orient: 'vertical', right: '2%', top: 'center',
itemWidth: 8, itemHeight: 8,
textStyle: { color: '#cbd5e1', fontSize: 9 }
},
color: ['#00f2ff', '#0095ff', '#32c5ff', '#1e40af', '#fbbf24', '#475569'],
series: [{
type: 'pie', radius: ['45%', '70%'], center: ['35%', '50%'],
avoidLabelOverlap: true,
label: { show: false },
data: [
{ value: 160983, name: '快乐' },
{ value: 25793, name: '伤心' },
{ value: 19499, name: '惊讶' },
{ value: 13021, name: '厌恶' },
{ value: 9380, name: '愤怒' },
{ value: 2424, name: '恐惧' }
]
}]
});
charts.push(pie);
// 模块二:主导情绪波动统计
const line = echarts.init(document.getElementById('lineChart'));
const pseudoNames = Array.from({length: 15}, (_, i) => `${i+1}街道`);
line.setOption({
grid: { top: 10, bottom: 20, left: 25, right: 5 },
xAxis: { ...commonAxis, type: 'category', data: pseudoNames },
yAxis: commonAxis,
series: [{ type: 'line', smooth: true, data: pseudoNames.map(() => Math.floor(Math.random()*50 + 100)), color: '#00f2ff', areaStyle: { color: 'rgba(0,242,255,0.04)' } }]
});
charts.push(line);
// 模块三:各街道幸福感指数 - 左右滑动折线图保持同步
const bar = echarts.init(document.getElementById('barChart'));
const streetDataForLine = geojsonData.features.map(f => ({
name: f.properties.街道,
val: f.properties.幸福感
}));
bar.setOption({
grid: { top: 15, bottom: 25, left: 30, right: 15 },
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(8, 29, 58, 0.9)',
textStyle: {color: '#fff', fontSize: 10}
},
xAxis: {
...commonAxis,
type: 'category',
data: streetDataForLine.map(d => d.name),
axisLabel: { ...commonAxis.axisLabel, interval: 0, rotate: 35 }
},
yAxis: { ...commonAxis, min: 50 },
series: [{
type: 'line',
data: streetDataForLine.map(d => d.val),
smooth: true,
symbol: 'circle',
symbolSize: 4,
itemStyle: { color: '#00f2ff' },
lineStyle: { width: 2, shadowBlur: 10, shadowColor: 'rgba(0,242,255,0.5)' },
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(0,242,255,0.3)' },
{ offset: 1, color: 'rgba(0,242,255,0)' }
])
}
}]
});
charts.push(bar);
const createGauge = (id, val, color) => {
const chart = echarts.init(document.getElementById(id));
chart.setOption({
series: [{
type: 'gauge', radius: '100%', startAngle: 210, endAngle: -30,
progress: { show: true, width: 3, itemStyle: { color: color } },
detail: { formatter: '{value}%', color: '#fff', fontSize: 10, offsetCenter: [0, '30%'] },
data: [{ value: val }],
axisLine: { lineStyle: { width: 2, color: [[1, 'rgba(255,255,255,0.05)']] } },
axisTick: { show: false }, splitLine: { show: false }, axisLabel: { show: false }, pointer: { show: false }
}]
});
charts.push(chart);
};
createGauge('gauge1', 84, '#00f2ff');
createGauge('gauge2', 68, '#3b82f6');
}
const subData = {
"1": ["1.1 公交站点密度", "1.2 主干路网密度", "1.3 次干路网密度", "1.4 支路网密度", "1.5 交叉口密度", "1.6 地铁交通可达性"],
"2": ["2.1 生活圈容积率", "2.2 生活圈建筑密度"],
"3": ["3.1 人口密度", "3.2 平均房价", "3.3 人均GDP", "3.4 失业率"],
"4": ["4.1 混合度指数", "4.2 POI多样性", "4.3 产城融合度"]
};
function updateSubCategory() {
const mainVal = document.getElementById('mainCategory').value;
const subSelect = document.getElementById('subCategory');
subSelect.innerHTML = "";
(subData[mainVal] || []).forEach((item, index) => {
const opt = document.createElement('option');
opt.value = mainVal + "." + (index + 1);
opt.textContent = item;
subSelect.appendChild(opt);
});
checkVisibility();
}
function checkVisibility() {
const mainVal = document.getElementById('mainCategory').value;
const subVal = document.getElementById('subCategory').value;
const content = document.getElementById('detailContentContainer');
const placeholder = document.getElementById('detailPlaceholder');
if (mainVal === "2" && subVal === "2.1") {
content.classList.remove('hidden-panel');
placeholder.classList.add('hidden-panel');
setTimeout(() => charts.forEach(c => c.resize()), 50);
} else {
content.classList.add('hidden-panel');
placeholder.classList.remove('hidden-panel');
}
}
function initDetailCharts() {
const radar = echarts.init(document.getElementById('detailRadar'));
radar.setOption({
radar: {
indicator: [{name:'容积率',max:100},{name:'绿化',max:100},{name:'公服',max:100},{name:'交通',max:100},{name:'建筑',max:100}],
name: { textStyle: { color: '#94a3b8', fontSize: 8 } },
splitArea: { show: false }, splitLine: { lineStyle: { color: 'rgba(0,242,255,0.05)' } }
},
series: [{ type:'radar', data:[{value:[85,70,80,65,75]}], itemStyle:{color:'#00f2ff'}, areaStyle:{color:'rgba(0,242,255,0.05)'}}]
});
charts.push(radar);
}
function populateData() {
const rankTable = document.getElementById('rankTable');
const streetsFromGeo = geojsonData.features.map(f => ({
name: f.properties.街道 || "未名街道",
val: f.properties.幸福感 || 0
}));
const sortedStreets = streetsFromGeo.sort((a, b) => b.val - a.val);
rankTable.innerHTML = "";
sortedStreets.forEach((s) => {
rankTable.innerHTML += `
<tr>
<td class="text-white/60 py-2">${s.name}</td>
<td class="text-cyan-400 font-mono">${s.val.toFixed(2)}</td>
<td class="${s.val > 80 ? 'text-green-400' : 'text-yellow-400'} text-[8px]">${s.val > 85 ? '▲' : '▬'}</td>
</tr>
`;
});
}
function switchTab(tab) {
currentTab = tab;
document.querySelectorAll('.nav-item').forEach(el => el.classList.remove('active'));
document.getElementById(`nav-${tab}`).classList.add('active');
map.closePopup();
const isHome = (tab === 'home');
document.getElementById('home-left').classList.toggle('hidden-panel', !isHome);
document.getElementById('home-right').classList.toggle('hidden-panel', !isHome);
document.getElementById('detail-left').classList.toggle('hidden-panel', isHome);
document.getElementById('detail-right').classList.toggle('hidden-panel', isHome);
if (isHome) {
if (!map.hasLayer(pointLayer)) map.addLayer(pointLayer);
} else {
if (map.hasLayer(pointLayer)) map.removeLayer(pointLayer);
checkVisibility();
}
setTimeout(() => charts.forEach(c => c.resize()), 200);
}
window.onload = function() {
initMap();
initHomeCharts();
initDetailCharts();
populateData();
updateSubCategory();
};
window.onresize = () => charts.forEach(c => c.resize());
</script>
</body>
</html>