可视化人口密度
根据美国人口普查数据创建交互式可视化;
<!DOCTYPE html>
<html lang="en">
<head>
<title>可视化人口密度</title>
<meta property="og:description" content="根据美国人口普查数据创建交互式可视化" />
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel='stylesheet' href='https://unpkg.com/maplibre-gl@5.5.0/dist/maplibre-gl.css' />
<script src='https://unpkg.com/maplibre-gl@5.5.0/dist/maplibre-gl.js'></script>
<style>
body { margin: 0; padding: 0; }
html, body, #map { height: 100%; }
.map-overlay {
position: absolute;
bottom: 0;
right: 0;
background: rgba(255, 255, 255, 0.8);
margin-right: 20px;
font-family: Arial, sans-serif;
overflow: auto;
border-radius: 3px;
max-width: 300px;
padding: 10px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
line-height: 1.5;
}
.map-overlay h2 {
margin: 0 0 10px;
font-size: 16px;
border-bottom: 1px solid #ddd;
padding-bottom: 5px;
}
#legend {
padding: 10px;
margin-bottom: 20px;
}
.legend-key {
display: inline-block;
border-radius: 20%;
width: 10px;
height: 10px;
margin-right: 5px;
}
#controls {
top: 10px;
right: 10px;
max-width: 300px;
background: rgba(255, 255, 255, 0.8);
position: absolute;
padding: 10px;
border-radius: 3px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
#controls label {
display: block;
margin-bottom: 5px;
}
#data-selector {
width: 100%;
padding: 5px;
margin-bottom: 10px;
}
.info-box {
position: absolute;
margin: 0;
padding: 5px;
background: white;
border: 1px solid #ddd;
font-family: Arial, sans-serif;
font-size: 12px;
color: #222;
white-space: nowrap;
z-index: 1000;
pointer-events: none;
border-radius: 3px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
display: none;
}
</style>
</head>
<body>
<div id="map"></div>
<div id="controls">
<h2>美国县级人口统计数据</h2>
<label for="data-selector">选择数据显示:</label>
<select id="data-selector">
<option value="density">人口密度 (人/平方公里)</option>
<option value="population">总人口</option>
<option value="change">2010-2020年人口变化率 (%)</option>
</select>
</div>
<div id="info-box" class="info-box"></div>
<div id="legend" class="map-overlay">
<h2 id="legend-title">人口密度 (人/平方公里)</h2>
<div id="legend-items">
<!-- 图例项由JavaScript动态生成 -->
</div>
</div>
<script>
const map = new maplibregl.Map({
container: 'map',
style: 'https://api.maptiler.com/maps/basic/style.json?key=get_your_own_OpIi9ZULNHzrESv6T2vL',
center: [-95.7129, 37.0902],
zoom: 3
});
map.addControl(new maplibregl.NavigationControl());
// 颜色比例尺定义
const colorScales = {
density: [
[0, '#FFEDA0'], // 低密度 - 浅黄色
[10, '#FED976'],
[50, '#FEB24C'],
[100, '#FD8D3C'],
[500, '#FC4E2A'],
[1000, '#E31A1C'],
[5000, '#BD0026'],
[10000, '#800026'] // 高密度 - 深红色
],
population: [
[0, '#edf8fb'], // 低人口 - 浅蓝色
[10000, '#b2e2e2'],
[50000, '#66c2a4'],
[100000, '#2ca25f'],
[500000, '#006d2c'],
[1000000, '#00441b'] // 高人口 - 深绿色
],
change: [
[-20, '#d7191c'], // 人口减少 - 红色
[-10, '#fdae61'],
[0, '#ffffbf'], // 无变化 - 黄色
[10, '#a6d96a'],
[20, '#1a9641'] // 人口增长 - 绿色
]
};
// 图例标题映射
const legendTitles = {
density: '人口密度 (人/平方公里)',
population: '总人口',
change: '2010-2020年人口变化率 (%)'
};
// 当前选择的数据类型
let currentData = 'density';
// 更新图例函数
function updateLegend(dataType) {
// 更新标题
document.getElementById('legend-title').textContent = legendTitles[dataType];
// 清除现有图例项
const legendItems = document.getElementById('legend-items');
legendItems.innerHTML = '';
// 为当前数据类型创建新图例项
colorScales[dataType].forEach((item, i) => {
const value = item[0];
const color = item[1];
const nextValue = i < colorScales[dataType].length - 1
? colorScales[dataType][i + 1][0]
: '以上';
const item_div = document.createElement('div');
item_div.innerHTML = `<span class="legend-key" style="background-color: ${color}"></span>
${value}${i < colorScales[dataType].length - 1 ? ` - ${nextValue}` : '+'}`;
legendItems.appendChild(item_div);
});
}
// 数据选择器变更事件监听
document.getElementById('data-selector').addEventListener('change', (e) => {
currentData = e.target.value;
// 更新图层填充颜色
if (map.getLayer('counties')) {
map.setPaintProperty('counties', 'fill-color', [
'interpolate',
['linear'],
['get', currentData],
...colorScales[currentData].flat()
]);
}
// 更新图例
updateLegend(currentData);
});
// 用于信息弹窗显示的元素
const infoBox = document.getElementById('info-box');
map.on('load', () => {
// 加载美国县级地理数据
map.addSource('counties', {
'type': 'geojson',
'data': 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_10m_admin_2_counties_lakes.geojson'
});
// 添加用于显示县级数据的填充图层
map.addLayer({
'id': 'counties',
'type': 'fill',
'source': 'counties',
'paint': {
'fill-color': [
'interpolate',
['linear'],
['get', currentData],
...colorScales[currentData].flat()
],
'fill-opacity': 0.75,
'fill-outline-color': '#FFFFFF'
}
});
// 生成默认图例
updateLegend(currentData);
// 模拟数据 - 在实际应用中,这将来自真实的API或数据源
// 这里我们为GeoJSON中的每个县随机生成值
const counties = map.getSource('counties')._data.features;
counties.forEach(county => {
if (!county.properties) county.properties = {};
// 模拟人口密度数据 (1-10000人/平方公里)
county.properties.density = Math.floor(Math.random() * 10000);
// 模拟总人口数据 (100-3000000人)
county.properties.population = Math.floor(Math.random() * 3000000) + 100;
// 模拟人口变化率 (-20%到+20%)
county.properties.change = Math.floor(Math.random() * 40) - 20;
// 县名 - 实际应用中应从数据获取
if (!county.properties.name) {
county.properties.name = `县 ${Math.floor(Math.random() * 1000)}`;
}
});
// 更新源数据
map.getSource('counties').setData({
type: 'FeatureCollection',
features: counties
});
// 鼠标悬停时显示县级信息
map.on('mousemove', 'counties', (e) => {
if (e.features.length > 0) {
map.getCanvas().style.cursor = 'pointer';
const feature = e.features[0];
const props = feature.properties;
// 显示信息框
infoBox.style.display = 'block';
infoBox.style.left = e.point.x + 10 + 'px';
infoBox.style.top = e.point.y + 10 + 'px';
// 更新信息框内容
infoBox.innerHTML = `
<strong>${props.name || '未知县'}</strong><br/>
人口密度: ${props.density.toLocaleString()} 人/平方公里<br/>
总人口: ${props.population.toLocaleString()}<br/>
人口变化率: ${props.change}%
`;
}
});
// 鼠标离开要素时隐藏信息框
map.on('mouseleave', 'counties', () => {
map.getCanvas().style.cursor = '';
infoBox.style.display = 'none';
});
});
</script>
</body>
</html>