地理特征类可视化
地理特征类可视化实验报告
一、实验目的
本次实验旨在通过蜂窝热力地图、变形地图、关联地图、气泡地图等可视化工具,分析地理数据的空间分布、关联关系及多维特征,掌握不同图表的适用场景、技术实现流程及结果解读方法。
二、实验环境
- 工具库:
geopandas/contextily:地理数据处理与地图背景绘制matplotlib:基础可视化plotly:交互式地图绘制
- 数据:模拟地震数据、亚洲国家经济指标、城市坐标与航线网络
三、可视化图表分析
(一)蜂窝热力地图(Heat Map)
图表特点
- 核心原理:通过颜色渐变(如红色系)或透明度变化,映射点数据的密度或强度,基于核密度估计(KDE)将离散点转化为连续热力面。
- 关键特征:
- 空间聚集性:直观展示高值区域(如地震高频带、城市热点)。
- 密度抽象:通过热力层叠加地图背景,增强地理空间关联。
应用场景
- 自然灾害分析:定位地震震级分布(如喜马拉雅地震带高震级区域)。
- 城市规划:分析人口密度、交通流量热点。
实现过程(以地震数据为例)
- 数据生成:
- 模拟中国5大主要地震带的随机震点,包含震级、破坏程度(震级平方)等属性。
- 热力绘制:
- 使用
geopandas绘制震点,column='magnitude'映射颜色,markersize='damage'映射破坏程度。 - 通过
contextily添加CartoDB地图背景,增强地理参照。
- 使用
- 结果展示:
- 深红色区域为高震级聚集带,点大小反映破坏程度差异。
import geopandas as gpd
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import os
from shapely.geometry import Point
import contextily as ctx # 用于添加地图背景
import sys
# 设置中文字体
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
plt.rcParams["axes.unicode_minus"] = False # 解决负号显示问题
def generate_earthquake_data(n=50):
"""生成模拟地震数据
Args:
n: 地震点数量
Returns:
GeoDataFrame包含地震数据
"""
# 中国主要地震带区域
earthquake_zones = [
# 格式: [区域名称, 中心点经度, 中心点纬度, 区域半径, 平均震级]
["喜马拉雅地震带", 88, 28, 5, 7.5],
["青藏高原地震带", 95, 33, 8, 6.5],
["华北地震带", 116, 38, 4, 6.0],
["台湾地震带", 121, 23.5, 2, 7.0],
["新疆地震带", 85, 40, 6, 6.0]
]
data = []
# 从每个地震带生成随机地震点
for zone in earthquake_zones:
zone_name, center_lon, center_lat, radius, avg_magnitude = zone
# 生成该区域内的随机地震点
zone_points = int(n * (radius / sum(z[3] for z in earthquake_zones)))
for _ in range(zone_points):
# 在中心点周围随机分布
lon = center_lon + np.random.uniform(-radius, radius)
lat = center_lat + np.random.uniform(-radius, radius)
# 震级在平均震级附近波动
magnitude = np.random.normal(avg_magnitude, 1.0)
magnitude = max(3.0, min(9.0, magnitude)) # 限制震级范围
data.append({
'location': zone_name,
'longitude': lon,
'latitude': lat,
'magnitude': magnitude,
'damage': magnitude ** 2 # 模拟破坏程度,与震级平方成正比
})
# 创建GeoDataFrame
gdf = gpd.GeoDataFrame(
data,
geometry=[Point(row['longitude'], row['latitude']) for row in data],
crs="EPSG:4326"
)
return gdf
def plot_earthquake_deformation_map(gdf, title="地震震级变形地图", output_file=None):
"""绘制地震震级变形地图
Args:
gdf: GeoDataFrame数据
title: 图表标题
output_file: 输出文件名,None则不保存
"""
# 创建图表
fig, ax = plt.subplots(figsize=(14, 10))
# 提取原始坐标
x = gdf.geometry.x
y = gdf.geometry.y
magnitudes = gdf['magnitude']
# 计算变形因子 - 基于震级
# 震级越高,变形越明显
max_magnitude = magnitudes.max()
deformation_factor = (magnitudes / max_magnitude) * 1.5 # 最大变形量为1.5度
# 应用变形 - 震级越高,偏移越大
x_deformed = x + np.random.normal(0, deformation_factor)
y_deformed = y + np.random.normal(0, deformation_factor)
# 创建变形后的GeoDataFrame
deformed_gdf = gpd.GeoDataFrame(
gdf.drop(columns=['geometry']),
geometry=[Point(x, y) for x, y in zip(x_deformed, y_deformed)],
crs="EPSG:4326"
)
# 将数据转换为Web Mercator投影 (EPSG:3857),与地图背景匹配
deformed_gdf = deformed_gdf.to_crs(epsg=3857)
original_gdf = gdf.to_crs(epsg=3857)
# 绘制地图背景
ctx.add_basemap(
ax,
crs=deformed_gdf.crs.to_string(),
source=ctx.providers.CartoDB.Positron,
attribution_size=8
)
# 绘制变形后的地震点
scatter = deformed_gdf.plot(
ax=ax,
markersize=deformed_gdf['damage'] * 10, # 点大小基于破坏程度
column='magnitude',
cmap='Reds', # 红颜色表示更危险
alpha=0.7,
edgecolors='black',
linewidths=0.5,
legend=True,
legend_kwds={
'label': "地震震级 (Richter)",
'orientation': "vertical",
'shrink': 0.5
}
)
# 绘制原始位置的浅色点作为参考
original_gdf.plot(
ax=ax,
markersize=10,
color='lightblue',
alpha=0.5
)
# 添加地震带名称标注
for location in deformed_gdf['location'].unique():
loc_points = deformed_gdf[deformed_gdf['location'] == location]
if len(loc_points) > 0:
# 计算区域中心点
center_x = loc_points.geometry.x.mean()
center_y = loc_points.geometry.y.mean()
# 添加标注
ax.annotate(
location,
xy=(center_x, center_y),
xytext=(0, 15),
textcoords="offset points",
ha='center',
va='bottom',
fontsize=12,
fontweight='bold',
bbox=dict(boxstyle="round,pad=0.3", fc="white", ec="gray", alpha=0.8)
)
# 设置标题和坐标轴
ax.set_title(title, fontsize=18)
ax.set_xlabel('经度', fontsize=12)
ax.set_ylabel('纬度', fontsize=12)
# 设置中国大致范围 (Web Mercator投影)
xlim = (7000000, 14000000)
ylim = (2000000, 6000000)
ax.set_xlim(xlim)
ax.set_ylim(ylim)
# 添加网格线
ax.grid(True, linestyle='--', alpha=0.6)
# 添加图例说明点大小含义
for size in [4, 6, 8]:
ax.scatter([], [], s=size ** 2 * 10, c='red', alpha=0.7, label=f'{size}级地震')
ax.legend(title='地震震级', scatterpoints=1, frameon=True, loc='lower right')
# 保存图表
if output_file:
plt.savefig(output_file, dpi=300, bbox_inches='tight')
print(f"图表已保存至: {output_file}")
plt.show()
def get_script_directory():
"""获取当前脚本所在目录"""
if getattr(sys, 'frozen', False):
# 如果是打包后的可执行文件
return os.path.dirname(os.path.abspath(sys.executable))
else:
# 如果是脚本文件
return os.path.dirname(os.path.abspath(__file__))
if __name__ == "__main__":
# 确保安装了必要的库
try:
import contextily as ctx
except ImportError:
print("缺少contextily库,正在尝试安装...")
os.system("pip install contextily")
import contextily as ctx
# 获取当前脚本所在目录
script_dir = get_script_directory()
# 创建输出目录(在当前脚本所在目录下)
output_dir = os.path.join(script_dir, "geo_visualizations")
os.makedirs(output_dir, exist_ok=True)
# 生成地震数据
earthquake_data = generate_earthquake_data(n=100)
# 绘制地震震级变形地图
plot_earthquake_deformation_map(
earthquake_data,
"中国主要地震带震级变形地图",
os.path.join(output_dir, "earthquake_deformation_map_with_background.png")
)
实验结果:

(二)变形地图(Morph Map)
图表特点
- 核心原理:通过几何变形(如坐标偏移、区域缩放)突出数据差异,原始位置与变形后位置对比展示特征。
- 关键特征:
- 属性-几何映射:数据值越大,变形量越大(如震级高则偏移距离远)。
- 视觉对比:结合颜色编码(如震级)与变形量,强化多维度信息。
应用场景
- 区域数据对比:国家经济指标(GDP)、人口增长差异。
- 动态模拟:冰川消融、污染物扩散路径。
实现过程(以亚洲国家数据为例)
- 数据准备:
- 生成亚洲国家随机数据值,匹配ISO代码(如中国
CHN、日本JPN)。
- 生成亚洲国家随机数据值,匹配ISO代码(如中国
- 地图绘制:
- 使用
plotly的Choropleth组件,z=data_values映射颜色,scope='asia'聚焦亚洲区域。 - 墨卡托投影调整经纬度范围,突出东亚与东南亚。
- 使用
- 结果展示:
- 颜色越深表示数据值越高,东南亚部分国家呈现高值色块,中亚为低值区域。
实验代码:
import plotly.graph_objects as go
# 亚洲国家ISO 3166-1 alpha-3代码列表
asian_countries = [
"CHN", "JPN", "KOR", "PRK", "MNG", # 东亚
"MMR", "THA", "LAO", "KHM", "VNM", "MYS", "SGP", "IDN", "BRN", "PHL", "TLS", # 东南亚
"IND", "PAK", "BGD", "NPL", "BTN", "LKA", "MDV", # 南亚
"AFG", "IRN", "IRQ", "SYR", "JOR", "ISR", "PSE", "SAU", "KWT", "BHR", "QAT", "ARE", "OMN", "YEM", "TUR", "CYP", # 西亚
"KAZ", "KGZ", "TJK", "UZB", "TKM" # 中亚
]
# 为亚洲国家生成随机数据值
import numpy as np
data_values = np.random.randint(10, 100, len(asian_countries)).tolist()
# 创建亚洲变形地图
fig = go.Figure(go.Choropleth(
locations=asian_countries,
z=data_values,
locationmode="ISO-3", # 使用ISO 3166-1 alpha-3代码
colorscale="Viridis",
colorbar_title="数据值",
marker_line_color='white', # 国家边界线颜色
marker_line_width=0.5, # 国家边界线宽度
))
# 自定义亚洲区域显示
fig.update_layout(
title="东亚变形地图",
geo=dict(
scope="asia", # 聚焦亚洲区域
showland=True,
landcolor="LightGray",
showcountries=True,
countrycolor="Black",
showocean=True,
oceancolor="LightBlue",
lonaxis_range=[30, 180], # 调整经度范围以更好展示亚洲
lataxis_range=[-10, 60], # 调整纬度范围
projection_type="mercator", # 使用墨卡托投影(适合区域地图)
# projection_scale=1.2, # 可调整缩放比例
)
)
fig.show()
实验结果:

(三)关联地图(Association Map)
图表特点
- 核心原理:通过线段、箭头连接地理实体(如城市),展示空间关联关系(如航线、贸易路线)。
- 关键特征:
- 关系可视化:线段颜色区分关系类型,箭头方向表示流向。
- 网络结构:突出枢纽节点(如国际航空中心)。
应用场景
- 交通网络分析:航线、高铁线路分布(如亚太城市航线网络)。
- 物流与贸易:供应链节点连接、人口迁移路径。
实现过程(以亚太城市航线为例)
- 数据准备:
- 定义北京、东京、新加坡等城市坐标,及航线起点、终点、颜色。
- 地图绘制:
- 使用
plotly的Scattergeo绘制城市标记(红色圆点)与航线线段,箭头通过方位角计算方向。 - 配置地图范围为亚洲,隐藏边框以聚焦数据。
- 使用
- 结果展示:
- 北京、上海、新加坡为核心枢纽,航线覆盖东亚、东南亚及大洋洲,箭头清晰显示飞行方向。
实验代码:
import numpy as np
import plotly.graph_objects as go
# 城市坐标(北京、上海、东京、新加坡、悉尼、迪拜)
cities = {
"北京": (116.403874, 39.914885),
"上海": (121.473701, 31.230446),
"东京": (139.691706, 35.689487),
"新加坡": (103.850085, 1.289977),
"悉尼": (151.2093, -33.8688),
"迪拜": (55.3781, 25.2048)
}
# 定义航线数据(起点、终点、颜色)
routes = [
("北京", "上海", "blue"),
("北京", "东京", "green"),
("北京", "新加坡", "purple"),
("上海", "东京", "orange"),
("上海", "新加坡", "pink"),
("东京", "新加坡", "cyan"),
("北京", "悉尼", "red"),
("上海", "悉尼", "magenta"),
("迪拜", "新加坡", "gold"),
("迪拜", "北京", "brown")
]
fig = go.Figure()
# 添加城市标记
for city, (lon, lat) in cities.items():
fig.add_trace(
go.Scattergeo(
lon=[lon],
lat=[lat],
mode='markers',
name=city,
marker=dict(
size=14,
color='red',
symbol='circle-dot',
line=dict(width=2, color='white'), # 白色描边
opacity=0.9
)
)
)
# 添加航线及箭头
for start, end, color in routes:
start_lon, start_lat = cities[start]
end_lon, end_lat = cities[end]
# 绘制航线线段
fig.add_trace(
go.Scattergeo(
lon=[start_lon, end_lon],
lat=[start_lat, end_lat],
mode='lines',
line=dict(
width=2,
color=color,
dash='solid'
),
showlegend=False # 隐藏线段图例
)
)
# 计算箭头位置(中点偏移10%距离以避免覆盖城市标记)
delta_lon = end_lon - start_lon
delta_lat = end_lat - start_lat
ratio = 0.9 # 箭头位置比例(0.5为中点,0.9靠近终点)
arrow_lon = start_lon + delta_lon * ratio
arrow_lat = start_lat + delta_lat * ratio
# 添加箭头符号(根据方向调整角度)
bearing = np.degrees(np.arctan2(delta_lat, delta_lon)) # 计算方位角
fig.add_trace(
go.Scattergeo(
lon=[arrow_lon],
lat=[arrow_lat],
mode='markers',
name=f"{start}-{end}",
marker=dict(
size=12,
color=color,
symbol='triangle-right' if delta_lon >= 0 else 'triangle-left', # 左右方向
angle=bearing - 90, # 调整箭头角度使其指向终点
line=dict(width=1, color=color)
)
)
)
# 地图布局设置
fig.update_layout(
title=dict(
text="亚太大区主要城市航线网络",
x=0.5,
xanchor='center',
yanchor='top',
font=dict(size=18, color='darkblue')
),
geo=dict(
scope='asia', # 基础范围为亚洲
projection_type='mercator',
landcolor='LightGray',
countrycolor='White',
showland=True,
showcountries=True,
countrywidth=0.5,
showocean=True,
oceancolor='LightBlue',
showlakes=True,
lakecolor='LightBlue',
showrivers=True,
rivercolor='LightBlue',
showsubunits=True,
subunitcolor='LightGray',
showframe=False, # 隐藏地图边框
bgcolor='white'
),
margin=dict(l=0, r=0, t=50, b=0), # 调整地图边距
height=600,
width=800
)
fig.show()
实验结果:

(四)气泡地图(Bubble Map)
图表特点
- 核心原理:以气泡大小映射一维数据(如人口),颜色映射另一维数据(如GDP),实现双变量可视化。
- 关键特征:
- 多维编码:位置(坐标)+ 大小(人口)+ 颜色(GDP),支持悬停交互查询详细数据。
- 密度控制:通过透明度避免气泡重叠。
应用场景
- 城市对比分析:人口与经济指标(如东京人口多且GDP高)。
- 生态研究:物种密度与环境因子相关性。
实现过程(以亚洲城市数据为例)
- 数据准备:
- 收集北京、东京、新加坡等城市的人口、GDP数据。
- 地图绘制:
- 使用
plotly的Scattergeo,size='pop'映射人口规模,color='gdp'映射经济总量,Viridis色系区分高低值。 - 配置悬停提示展示城市名称、人口、GDP。
- 使用
- 结果展示:
- 东京、上海、北京气泡最大(人口多),颜色最深(GDP高),呈现“人口-经济”正相关。
实验代码:
import plotly.graph_objects as go
import numpy as np
# 模拟数据:城市、坐标、人口(万)、GDP(亿美元)
data = [
{"city": "北京", "lon": 116.4039, "lat": 39.9149, "pop": 2154, "gdp": 5619},
{"city": "上海", "lon": 121.4737, "lat": 31.2304, "pop": 2428, "gdp": 6342},
{"city": "东京", "lon": 139.6917, "lat": 35.6895, "pop": 1396, "gdp": 10220},
{"city": "新加坡", "lon": 103.8501, "lat": 1.2899, "pop": 570, "gdp": 3720},
{"city": "悉尼", "lon": 151.2093, "lat": -33.8688, "pop": 531, "gdp": 3375},
{"city": "迪拜", "lon": 55.3781, "lat": 25.2048, "pop": 330, "gdp": 1580},
{"city": "孟买", "lon": 72.8777, "lat": 19.0760, "pop": 2135, "gdp": 2248},
{"city": "首尔", "lon": 126.9780, "lat": 37.5665, "pop": 963, "gdp": 4520},
]
# 提取数据维度
lons = [d["lon"] for d in data]
lats = [d["lat"] for d in data]
pop = [d["pop"] for d in data]
gdp = [d["gdp"] for d in data]
city_names = [d["city"] for d in data]
# 创建气泡图
fig = go.Figure(
go.Scattergeo(
lon=lons,
lat=lats,
mode='markers',
text=city_names,
hovertext=[f"{d['city']}<br>人口: {d['pop']}万<br>GDP: {d['gdp']}亿美元" for d in data],
marker=dict(
size=pop, # 气泡大小映射人口
sizemin=8, # 最小气泡尺寸(px)
sizemode='area', # 使用面积模式
sizeref=max(pop) / 30**2, # 缩放因子,控制最大气泡大小
color=gdp, # 颜色映射GDP
colorscale='Viridis', # 颜色渐变方案
colorbar=dict(
title="GDP(亿美元)",
title_side="right", # 颜色条标题位置
thickness=25, # 颜色条厚度
len=0.6, # 颜色条长度
yanchor="top", # 垂直锚点
y=1.0 # 垂直位置
),
line=dict(width=1, color='white'), # 气泡描边
opacity=0.8
)
)
)
# 布局设置
fig.update_layout(
title=dict(
text="亚洲主要城市人口与GDP气泡图",
x=0.5,
xanchor='center',
yanchor='top',
font=dict(size=18, color='darkblue')
),
geo=dict(
scope='asia', # 显示亚洲区域
projection_type='mercator',
landcolor='LightGray',
countrycolor='White',
showland=True,
showcountries=True,
countrywidth=0.5,
showocean=True,
oceancolor='LightBlue',
lonaxis_range=[40, 160], # 调整经度范围
lataxis_range=[-40, 50] # 调整纬度范围
),
hoverlabel=dict(
bgcolor='white',
font_size=12,
bordercolor='black'
)
)
# 显示图形
fig.show()
实验结果:

四、图表总结对比表
| 图表类型 | 核心特征 | 典型应用场景 | 关键工具/库 | 数据维度 |
|---|---|---|---|---|
| 蜂窝热力地图 | 颜色渐变展示点数据密度,支持热力层叠加 | 地震分布、城市热点分析 | geopandas + matplotlib | 空间坐标、单变量(震级) |
| 变形地图 | 区域几何变形突出数据差异,颜色编码单变量 | 国家经济对比、动态趋势模拟 | plotly(Choropleth) | 区域编码、单变量(数据值) |
| 关联地图 | 线段/箭头刻画空间关联,支持流向与权重展示 | 航线网络、贸易路线分析 | plotly(Scattergeo) | 起点/终点坐标、关系类型 |
| 气泡地图 | 气泡大小+颜色双变量编码,支持交互查询 | 城市多维指标对比、生态因子分析 | plotly(Scattergeo) | 空间坐标、双变量(人口/GDP) |
五、实验总结
本次实验通过4类地理可视化图表,系统展示了空间数据的分布、关联与多维特征:
- 蜂窝热力地图有效揭示了地震数据的空间聚集性;
- 变形地图通过区域色块对比,直观呈现宏观数据差异;
- 关联地图清晰刻画了城市间的航线网络结构;
- 气泡地图通过双变量编码,实现了城市指标的多维对比。
未来可结合实时数据、三维可视化(如Deck.gl)或动态交互(如时间轴),进一步提升地理分析的深度与交互体验。

被折叠的 条评论
为什么被折叠?



