地理特征类可视化图像总结
目录
一、蜂窝热力地图
(一)特点
(二)应用场景
(三)代码实现及变体
(四)注意事项
(五)构成与视觉通道
(六)适用数据
二、变形地图
(一)特点
(二)应用场景
(三)代码实现及变体
(四)注意事项
(五)构成与视觉通道
(六)适用数据
三、关联地图
(一)特点
(二)应用场景
(三)代码实现及变体
(四)注意事项
(五)构成与视觉通道
(六)适用数据
四、气泡地图
(一)特点
(二)应用场景
(三)代码实现及变体
编辑
编辑 (四)注意事项
(五)构成与视觉通道
(六)适用数据
五、总结
总结地理特征类相关的可视化图像,包括:蜂窝热力地图、变形地图、关联地图、气泡地图等。总结内容包括但不限于图表的特点、应用场景、使用python工具实现过程及结果、绘制图表时有哪些注意事项以及其变体等:
一、蜂窝热力地图
(一)特点
1. 核心特征
蜂窝结构:将地理区域划分为规则或不规则的六边形网格(蜂窝单元),相比传统网格(如正方形),六边形具有更均匀的邻接性和视觉稳定性。
热力编码:通过颜色深浅、透明度或数值标签映射数据大小,直观展示地理空间上的密度、强度或分布特征。
空间聚合:对离散点数据(如人口分布)进行区域聚合,降低数据复杂度,突出宏观趋势。
2. 优势
视觉平衡:六边形网格减少正方形网格的 “直角效应”,更符合人类视觉对自然形态的感知。
数据降噪:通过聚合过滤噪声点,适用于展示大尺度空间数据的分布规律。
灵活性:可调整网格大小、颜色方案和透明度,适配不同数据密度和分析目标。
3. 局限性
空间失真:规则蜂窝网格可能无法完全贴合复杂地理边界(如海岸线、行政区)。
细节丢失:聚合过程可能掩盖局部异常值,需结合原始数据或更高分辨率分析。
(二)应用场景
城市规划与管理:分析人口密度、交通流量、商业设施分布(如餐饮、商场 POI),辅助选址或资源配置。
环境与生态研究:展示野生动物栖息地密度、污染物扩散范围、植被覆盖度等空间特征。
公共安全与应急响应:可视化犯罪事件、灾害风险(如地震、洪水)的空间聚集性,辅助应急预案制定。
商业分析:评估商圈客流量、用户活跃度(如外卖订单分布),优化营销策略。
交通物流:分析共享单车停放热点、物流配送密度,优化路线规划。
(三)代码实现及变体
1.蜂窝热力图
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors
from matplotlib import cm
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC", "sans-serif"]
# 设置随机种子以保证可重复性
np.random.seed(42)
# 生成随机数据
n = 10000
x = np.random.normal(0, 1, n)
y = np.random.normal(0, 1, n)
# 创建图形和坐标轴
fig, ax = plt.subplots(figsize=(10, 8), dpi=100)
# 创建蜂窝热力图
hexbin = ax.hexbin(x, y, gridsize=50, cmap='viridis',edgecolors='none', mincnt=1,norm=colors.LogNorm() if n > 1000 else None)
# 添加颜色条
cb = fig.colorbar(hexbin, ax=ax, label='计数')
cb.outline.set_edgecolor('none')
# 自定义样式
ax.set_facecolor('#f0f0f0') # 设置背景色
ax.grid(True, linestyle='--', linewidth=0.5, alpha=0.7, color='white')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
# 添加标题和标签
ax.set_title('蜂窝热力图示例', pad=20, fontsize=14, fontweight='bold')
ax.set_xlabel('X轴', labelpad=10)
ax.set_ylabel('Y轴', labelpad=10)
# 调整布局
plt.tight_layout()
plt.show()
2.3D蜂窝热力图
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC", "sans-serif"]
# 模拟数据(某区域房价分布)
np.random.seed(42)
n = 500 # 数据点数量
x = np.random.uniform(0, 10, n) # X坐标
y = np.random.uniform(0, 10, n) # Y坐标
values = 50000 + 2000 * x + 3000 * y + np.random.normal(0, 5000, n) # 模拟房价
# 创建六边形网格
grid_size = 1.0
x_grid = np.arange(0, 10, grid_size)
y_grid = np.arange(0, 10, grid_size)
# 数据聚合到网格
grid_values = []
for i in range(len(x_grid) - 1):for j in range(len(y_grid) - 1):mask = (x > x_grid[i]) & (x < x_grid[i + 1]) & \(y > y_grid[j]) & (y < y_grid[j + 1])cell_values = values[mask]if len(cell_values) > 0:grid_values.append({'x': (x_grid[i] + x_grid[i + 1]) / 2,'y': (y_grid[j] + y_grid[j + 1]) / 2,'value': np.mean(cell_values)})
# 提取网格坐标和数值
x_grid_points = [point['x'] for point in grid_values]
y_grid_points = [point['y'] for point in grid_values]
z_values = [point['value'] for point in grid_values]
# 创建3D图形
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111, projection='3d')
# 绘制3D蜂窝热力图(使用柱状图近似)
for x, y, z in zip(x_grid_points, y_grid_points, z_values):# 创建六边形底面hex_x = []hex_y = []for k in range(6):angle = 2 * np.pi * k / 6hex_x.append(x + 0.45 * np.cos(angle)) # 0.45为六边形半径hex_y.append(y + 0.45 * np.sin(angle))# 绘制六边形柱体ax.bar3d(x, y, 0, 0.9, 0.9, z, shade=True, color=cm.viridis(z / max(z_values)), alpha=0.8)
# 设置坐标轴和标签
ax.set_xlabel('X坐标', fontsize=12)
ax.set_ylabel('Y坐标', fontsize=12)
ax.set_zlabel('房价 (元/平方米)', fontsize=12)
plt.title('某区域房价分布3D蜂窝热力图', fontsize=14)
# 添加颜色条
m = cm.ScalarMappable(cmap=cm.viridis)
m.set_array(z_values)
cbar = plt.colorbar(m, ax=ax, pad=0.1)
cbar.set_label('房价 (元/平方米)', rotation=270, labelpad=20)
plt.tight_layout()
plt.show()
3.分类蜂窝图
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import geopandas as gpd
from shapely.geometry import Point
import contextily as ctx
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC", "sans-serif"]
# 生成模拟分类数据 - 上海不同类型商业设施
np.random.seed(456)
lat = np.random.normal(31.2304, 0.1, 4000)
lon = np.random.normal(121.4737, 0.1, 4000)
categories = np.random.choice(['商场', '餐饮', '写字楼', '酒店'], 4000, p=[0.3, 0.4, 0.2, 0.1])
data = pd.DataFrame({'latitude': lat, 'longitude': lon, 'category': categories})
# 转换为GeoDataFrame
geometry = [Point(xy) for xy in zip(data['longitude'], data['latitude'])]
gdf = gpd.GeoDataFrame(data, geometry=geometry, crs="EPSG:4326")
gdf = gdf.to_crs(epsg=3857)
# 为每个类别创建子图
fig, axes = plt.subplots(2, 2, figsize=(15, 15))
axes = axes.flatten()
categories = gdf['category'].unique()
# 定义备选地图源列表
map_sources = [ctx.providers.OpenStreetMap.Mapnik,ctx.providers.CartoDB.Positron,ctx.providers.Stamen.TonerLite,ctx.providers.Esri.WorldStreetMap
]
for ax, category in zip(axes, categories):subset = gdf[gdf['category'] == category]hexbin = ax.hexbin(x=subset.geometry.x,y=subset.geometry.y,gridsize=35,cmap='Blues',edgecolors='none',alpha=0.8)# 尝试多个地图源,直到成功加载一个for source in map_sources:try:ctx.add_basemap(ax, source=source)break # 如果成功加载,跳出循环except:continue # 如果失败,尝试下一个地图源ax.set_title(f'{category}分布', fontsize=12)ax.set_axis_off()plt.colorbar(hexbin, ax=ax, label='数量')
fig.suptitle('上海市商业设施分类蜂窝热力图', fontsize=16, y=0.95)
plt.tight_layout()
# 添加数据来源标注
plt.figtext(0.5, 0.01, "数据来源:模拟数据", ha="center", fontsize=10)
plt.show()
4.混合蜂窝图
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import geopandas as gpd
from shapely.geometry import Point, LineString
import contextily as ctx
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC", "sans-serif"]
# 生成模拟数据 - 成都道路和POI点
np.random.seed(987)
# POI点数据
poi_lat = np.random.normal(30.5728, 0.1, 2000)
poi_lon = np.random.normal(104.0668, 0.1, 2000)
poi_data = pd.DataFrame({'latitude': poi_lat, 'longitude': poi_lon})
# 模拟几条主要道路
roads = []
for i in range(5):road_lon = np.linspace(103.9, 104.2, 50)road_lat = np.linspace(30.5, 30.65, 50) + np.random.normal(0, 0.01, 50)roads.append(LineString(zip(road_lon, road_lat)))
# 创建GeoDataFrame
poi_geometry = [Point(xy) for xy in zip(poi_data['longitude'], poi_data['latitude'])]
poi_gdf = gpd.GeoDataFrame(poi_data, geometry=poi_geometry, crs="EPSG:4326")
road_gdf = gpd.GeoDataFrame(geometry=roads, crs="EPSG:4326")
# 转换为Web墨卡托
poi_gdf = poi_gdf.to_crs(epsg=3857)
road_gdf = road_gdf.to_crs(epsg=3857)
# 创建混合图
fig, ax = plt.subplots(figsize=(12, 10))
# 蜂窝热力图
hexbin = ax.hexbin(x=poi_gdf.geometry.x,y=poi_gdf.geometry.y,gridsize=40,cmap='plasma',edgecolors='none',alpha=0.7,mincnt=1
)
# 添加道路
road_gdf.plot(ax=ax, color='white', linewidth=1.5, alpha=0.7)
# 尝试多种地图源
map_sources = [ctx.providers.OpenStreetMap.Mapnik, # 默认OpenStreetMapctx.providers.CartoDB.Positron, # 简洁的浅色地图ctx.providers.Esri.WorldStreetMap, # Esri街道地图ctx.providers.Stamen.TonerLite # 最后尝试Stamen
]
for source in map_sources:try:ctx.add_basemap(ax, source=source)break # 如果成功加载则跳出循环except:continue # 尝试下一个地图源
# 添加颜色条
cbar = plt.colorbar(hexbin, ax=ax)
cbar.set_label('POI点密度')
# 添加标题和数据来源
ax.set_title('成都市POI分布与主要道路混合蜂窝图', fontsize=16)
plt.figtext(0.5, 0.01, "数据来源:模拟数据 | 地图源:OpenStreetMap", ha="center", fontsize=10)
ax.set_axis_off()
plt.tight_layout()
plt.show()
(四)注意事项
数据预处理:确保经纬度坐标格式正确(WGS84 坐标系优先),剔除异常值(如明显超出研究区域的点)。聚合方式需匹配业务逻辑:计数(Count)适用于频次分析,均值(Mean)适用于强度分析。
地图投影与坐标系:使用Basemap
或PyProj
处理投影转换,避免跨区域数据拼接时的变形(如全球数据需用等面积投影)。
视觉通道优化:颜色方案需符合数据性质:连续型数据用渐变色(如viridis
),分类型数据用对比色。避免颜色过度饱和导致信息过载,建议搭配透明度显示底层地理特征。
计算性能:大数据量(如百万级点)需使用空间索引或分布式计算框架(如 Dask)优化聚合效率。
(五)构成与视觉通道
构成要素 | 视觉通道 | 作用 |
---|---|---|
蜂窝网格 | 形状(六边形)、大小 | 划分空间单元,控制数据聚合粒度 |
数据值 | 颜色(色相、饱和度、明度) | 编码数值大小,引导视觉注意力 |
地理底图 | 线条(海岸线、道路)、填充色 | 提供空间参考,增强地图可读性 |
标签与注释 | 文本、箭头 | 标注关键区域或数据极值点 |
颜色条 | 连续色带 | 映射颜色与数值的对应关系 |
(六)适用数据
1.空间类型
点数据(Point):如 POI 坐标、事故发生地、用户签到位置。
面数据(Polygon):如行政区边界、商圈范围(需先转换为网格中心点)。
2.数据尺度
定量数据:数值型数据(如人口密度、PM2.5 浓度)。
定性数据:需先转换为数值编码(如不同等级的风险用 1-5 分表示)。
3.数据规模
中小规模数据(<10 万点):直接使用常规 Python 库处理。
大规模数据:需结合空间数据库(如 PostGIS)或大数据框架预处理。
二、变形地图
变形地图是通过扭曲地理空间(如面积、形状)来可视化非地理属性(如人口、GDP)的地图类型。核心思想是将地理实体的面积或形状与目标数据关联,而非真实地理位置,从而突出数据分布特征。
(一)特点
1.核心特征
空间扭曲:面积变形中,区域面积与数据值成比例(如人口越多的国家面积越大)。形状变形中,保持区域拓扑关系,但扭曲边界以适应数据(如连续色调变形地图)。
数据主导:地理真实性让步于数据可视化,强调数据分布而非地理准确性。
对比鲜明:通过变形直观展示数据差异,适合发现空间数据的异常值或模式。
2. 优势:突破传统地图限制,直观呈现数据与地理的非线性关系。适合展示单一变量的全局分布(如人口密度、选举结果)。
3. 局限性:地理参照性减弱,可能导致空间认知偏差。复杂变形算法可能掩盖局部细节。
(二)应用场景
人口与经济:可视化全球人口分布、地区 GDP 总量(如世界银行数据)。
社会与政治:展示选举结果、失业率、教育水平分布。
环境与健康:呈现疾病传播密度、碳排放强度(如 COVID-19 病例分布)。
市场分析:显示消费者行为数据、产品销售区域分布(如电商订单热力图)。
(三)代码实现及变体
数据来源:Naturalearthdata.com网站。https://www.naturalearthdata.com/https://www.naturalearthdata.com/
1.变形地图
import geopandas as gpd
import matplotlib.pyplot as plt# 加载下载的Shapefile文件
world = gpd.read_file(r"G:\数据可视化技术\地理特征类可视化图像总结\ne_110m_admin_0_countries\ne_110m_admin_0_countries.shp")# 打印所有列名
print(world.columns)# 查看数据框的前几行,检查列名是否合适
print(world.head())# 假设列名是 'POP_EST'(根据打印结果,选择合适的列)
# 使用找到的列名进行可视化
fig, ax = plt.subplots(figsize=(12, 8))
world.plot(column='POP_EST', cmap='coolwarm', linewidth=0.8, ax=ax, edgecolor='0.8', legend=True)
plt.title("Cartogram of the World (Population-based)", fontsize=16)
plt.show()
2.非连续变形地图
import geopandas as gpd
import matplotlib.pyplot as plt
import numpy as np
from shapely.affinity import scale# 加载下载的Shapefile文件
world = gpd.read_file(r"G:\数据可视化技术\地理特征类可视化图像总结\ne_110m_admin_0_countries\ne_110m_admin_0_countries.shp")# 打印所有列名确认数据
print("可用列名:", list(world.columns)) # 转换为list更易读# 尝试找出人口列
possible_pop_columns = ['POP_EST', 'POPEST', 'POPULATION', 'POP', 'POP2020', 'POP2023']
pop_col = Nonefor col in possible_pop_columns:if col in world.columns:pop_col = colbreakif pop_col is None:# 如果没有找到标准人口列,显示前几行数据帮助识别print("\n前五行数据:")print(world.head())raise ValueError("无法自动识别人口列,请检查上述列名并手动指定")print(f"\n使用的人口列: {pop_col}")
print(f"示例人口数据:\n{world[[pop_col, 'NAME']].head()}")# 1. 创建基础地图(原始大小)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 10))# 原始地图
world.plot(column=pop_col, cmap='coolwarm', linewidth=0.8,ax=ax1, edgecolor='0.8', legend=True)
ax1.set_title(f"Original World Map ({pop_col})", fontsize=14)# 2. 创建非连续变形地图
# 计算缩放因子(基于人口)
min_pop = world[pop_col].min()
max_pop = world[pop_col].max()
world['scale_factor'] = 0.5 + 1.5 * (world[pop_col] - min_pop) / (max_pop - min_pop) # 缩放范围0.5-2.0# 创建缩放后的几何图形(保持中心点不变)
world_scaled = world.copy()
centroids = world.geometry.centroid
# 对每个国家进行独立缩放
scaled_geoms = []
for idx, row in world.iterrows():geom = row['geometry']sf = row['scale_factor']centroid = centroids[idx]scaled_geom = scale(geom, xfact=sf, yfact=sf, origin=centroid)scaled_geoms.append(scaled_geom)world_scaled['geometry'] = scaled_geoms# 绘制非连续变形地图
world_scaled.plot(column=pop_col, cmap='coolwarm', linewidth=0.8,ax=ax2, edgecolor='0.8', legend=True)
ax2.set_title(f"Non-Contiguous Cartogram ({pop_col})", fontsize=14)# 添加国家标签(可选,只显示人口大国)
large_countries = world.nlargest(10, pop_col)
for idx, row in large_countries.iterrows():ax2.annotate(text=row['NAME'], xy=(row['geometry'].centroid.x, row['geometry'].centroid.y),ha='center', fontsize=8, color='black')plt.tight_layout()
plt.show()
(四)注意事项
1.数据准确性:变形前需验证数据可靠性(如人口统计的时效性)。避免对小区域过度变形导致视觉失真。
2.地理编码:确保地理数据包含正确的坐标系,避免投影错误。
3.用户认知:需明确标注 “非真实地理比例”,避免误导读者。
(五)构成与视觉通道
构成要素 | 视觉通道 | 作用 |
---|---|---|
地理区域 | 面积、形状 | 编码数据值(面积∝数据大小) |
数据值 | 颜色(明度、饱和度) | 辅助编码数据(如颜色深浅表示密度) |
时间(变体) | 动画帧、透明度 | 展示数据时序变化 |
标签与图例 | 文本、比例尺 | 标注数据单位、变形规则 |
(六)适用数据
1.空间类型
面数据(Polygon):如国家、省、市边界数据。
点数据需先聚合为面数据(如通过泰森多边形)。
2.数据尺度
定量数据:数值型变量(人口、GDP、温度)。
定性数据:需转换为数值编码(如风险等级 1-5 分)。
3.数据规模
中小规模数据(<1000 个区域):直接使用 PySAL 等库。
大规模数据:需优化算法或使用分布式计算(如 Dask)。
三、关联地图
(一)特点
关联地图(Connection Map)是一种展示地理实体间关系或流动的数据可视化形式,主要特点包括:
核心思想:通过地理空间关联展示变量与地理单元的关系,强调空间分布与属性数据的映射。
关键特征:基于行政区划或自然地理边界(如国家、州、县)。使用颜色、面积、长度等视觉通道编码属性数据(如人口、GDP、气温)。可展示单一变量分布或多变量对比(如变体中的变形地图)。
优势:直观呈现数据的空间聚集性、异常值或梯度变化。
局限:可能受地理边界限制,忽略空间连续性(如插值数据)。
(二)应用场景
社会经济领域:展示人口密度、收入水平、选举结果的空间分布。
环境科学:呈现气温、降水、污染指标的地理差异。
商业分析:可视化市场覆盖率、用户分布、物流网络。
公共卫生:追踪疾病传播、医疗资源分布。
(三)代码实现及变体
1.关联地图
import geopandas as gpd
import matplotlib.pyplot as plt
from matplotlib import font_manager# 加载Shapefile文件
world = gpd.read_file(r"G:\数据可视化技术\地理特征类可视化图像总结\ne_110m_admin_0_countries\ne_110m_admin_0_countries.shp")# 查看所有列名,确认合适的列名
print(world.columns)# 查看数据框的前几行,确认合适的列名
print(world.head())# 绘制一个基于 'SOVEREIGNT' 列的地图
fig, ax = plt.subplots(figsize=(12, 8))# 绘制颜色,使用 'SOVEREIGNT' 列,并且启用图例
world.plot(column='SOVEREIGNT',cmap='Set3',linewidth=0.8,ax=ax,edgecolor='0.8',legend=True,legend_kwds={'title': 'Countries', 'loc': 'center left','bbox_to_anchor': (1, 0.5)})# 设置图例为2列(使用ncol参数)
legend = ax.get_legend()
legend.set_bbox_to_anchor((1.05, 0.5)) # 图例位置,避免与地图重叠
legend.set_title("Countries") # 设置图例标题# 设置图例字体大小
for label in legend.get_texts():label.set_fontsize(10)# 设置图例列数为2
legend.set_ncols(2)# 添加标题
plt.title("Map of Countries", fontsize=16)# 调整图表右边距以避免图例被裁剪
plt.subplots_adjust(right=0.85) # 增加右边距# 显示图像
plt.show()
2.弧线图
import geopandas as gpd
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Arc
from shapely.geometry import LineString
from matplotlib.path import Path # 添加这一行
from matplotlib.patches import PathPatch # 添加这一行# 1. 加载数据
world = gpd.read_file(r"G:\数据可视化技术\地理特征类可视化图像总结\ne_110m_admin_0_countries\ne_110m_admin_0_countries.shp")# 2. 准备数据(示例:随机生成国家间关系)
np.random.seed(42)
countries = world['SOVEREIGNT'].unique()
n_countries = min(20, len(countries)) # 选择前20个国家避免过于拥挤
selected_countries = np.random.choice(countries, n_countries, replace=False)# 创建模拟关系数据
relations = []
for i in range(len(selected_countries)):for j in range(i + 1, len(selected_countries)):if np.random.rand() > 0.7: # 70%概率不创建连接value = np.random.randint(1, 10)relations.append({'source': selected_countries[i],'target': selected_countries[j],'value': value})# 3. 创建弧线图
fig, ax = plt.subplots(figsize=(15, 15))# 计算国家位置(圆形布局)
angles = np.linspace(0, 2 * np.pi, n_countries, endpoint=False)
radius = 10
x = radius * np.cos(angles)
y = radius * np.sin(angles)country_pos = dict(zip(selected_countries, zip(x, y)))# 绘制国家节点
for country, (xi, yi) in country_pos.items():ax.plot(xi, yi, 'o', markersize=10, color='steelblue')ax.text(xi * 1.1, yi * 1.1, country, ha='center', va='center', fontsize=8)
# 绘制弧线
max_value = max(r['value'] for r in relations)
for relation in relations:source = country_pos[relation['source']]target = country_pos[relation['target']]# 计算控制点(使弧线弯曲)control_x = (source[0] + target[0]) / 2 + (target[1] - source[1]) * 0.3control_y = (source[1] + target[1]) / 2 + (source[0] - target[0]) * 0.3# 绘制贝塞尔曲线verts = [source, (control_x, control_y), target]codes = [1, 4, 4] # MOVETO, CURVE4, CURVE4path = Path(verts, codes)patch = PathPatch(path,facecolor='none',edgecolor=plt.cm.plasma(relation['value'] / max_value),lw=relation['value'],alpha=0.6)ax.add_patch(patch)# 设置图形属性
ax.set_xlim(-radius * 1.5, radius * 1.5)
ax.set_ylim(-radius * 1.5, radius * 1.5)
ax.set_aspect('equal')
ax.axis('off')
plt.title('Country Connections Chord Diagram', fontsize=16)# 添加颜色条
sm = plt.cm.ScalarMappable(cmap='plasma',norm=plt.Normalize(vmin=1, vmax=max_value))
sm._A = []
cbar = plt.colorbar(sm, ax=ax, shrink=0.5)
cbar.set_label('Connection Strength')plt.tight_layout()
plt.show()
图片展示了不同国家之间的连接强度,通过颜色和线条的粗细来表示不同的连接强度。右侧的颜色条显示了连接强度的范围,从1到9。每个节点代表一个国家,而连接它们的弧线则表示它们之间的关系或交互强度。
(四)注意事项
1.数据准备:确保地理坐标系统一致;处理缺失或异常连接数据;对流量/数值进行标准化处理。
2.可视化设计:避免线条过度重叠交叉;使用合适颜色区分不同流向;添加图例说明视觉编码规则。
3.性能考虑:大数据集需简化或聚合;考虑使用WebGL加速的交互式可视化。
4.解读指导:提供交互工具提示;添加比例尺和方向指示;考虑动画展示时间序列变化。
(五)构成与视觉通道
构成要素 | 视觉通道 | 示例编码 |
---|---|---|
地理边界 | 轮廓线颜色、粗细 | 黑色细边(默认) |
属性数据 | 颜色(单变量) | 人口密度:浅色→深色渐变 |
面积 / 形状 | 缩放比例(变形地图) | 人口总量→州面积放大 |
多变量对比 | 分层绘制或分面图 | 叠加气温与降水等值线 |
(六)适用数据
1.空间数据
矢量数据(.shp
, GeoJSON):行政区划边界、点 / 线要素。
栅格数据(如遥感影像):需转换为矢量多边形或网格。
2.属性数据
连续型(如人口、温度):适合颜色渐变或面积缩放。
离散型(如类别、等级):适合分类颜色或符号标记。
四、气泡地图
(一)特点
1.核心特征:通过地图上的气泡(圆形符号)展示数据,气泡的大小(半径 / 面积)映射数值变量,位置对应地理坐标(经纬度)。
2.视觉优势:直观呈现空间分布与数值大小的关联(如人口、GDP、犯罪率等)。可叠加多维度数据(如颜色映射另一变量,形成复合气泡图)。
3.局限性
-
气泡重叠可能导致信息遮挡,需配合交互(如缩放、悬停提示)或优化布局。
-
面积感知误差:人眼对面积的判断不如长度精准,需注意数据与半径的数学映射。
(二)应用场景
城市规划:展示城市各区域的人口密度、商业设施数量。
环境科学:映射空气质量指数(AQI)、污染物浓度的空间分布。
商业分析:标注门店分布及销售额、用户活跃度热点。
公共卫生:可视化疾病病例数、疫苗接种率的地理差异。
(三)代码实现及变体
1.气泡地图
import pandas as pd
import plotly.express as px# 示例数据:城市、经纬度、人口数
data = {'City': ['Beijing', 'Shanghai', 'Guangzhou', 'Shenzhen', 'Chengdu'],'Latitude': [39.9042, 31.2304, 23.1291, 22.5431, 30.5728],'Longitude': [116.4074, 121.4737, 113.2644, 114.0579, 104.0668],'Population': [21540000, 24240000, 15000000, 13000000, 16000000]
}df = pd.DataFrame(data)# 创建气泡地图
fig = px.scatter_geo(df,lat='Latitude',lon='Longitude',text='City',size='Population',size_max=60,color='Population',color_continuous_scale='Viridis',projection='natural earth',title='中国主要城市人口气泡地图')# 展示图表
fig.show()
2.带边界的分级气泡地图
import pandas as pd
import plotly.express as px# 示例数据:城市、经纬度、人口数、GDP和城市等级
data = {'City': ['Beijing', 'Shanghai', 'Guangzhou', 'Shenzhen', 'Chengdu'],'Latitude': [39.9042, 31.2304, 23.1291, 22.5431, 30.5728],'Longitude': [116.4074, 121.4737, 113.2644, 114.0579, 104.0668],'Population': [21540000, 24240000, 15000000, 13000000, 16000000],'GDP': [450, 550, 350, 400, 300], # 单位:十亿美元'Tier': ['Tier 1', 'Tier 1', 'Tier 1', 'Tier 1', 'Tier 2'] # 城市等级
}df = pd.DataFrame(data)# 定义城市等级颜色映射
tier_colors = {'Tier 1': 'rgb(231, 41, 138)', # 玫红色'Tier 2': 'rgb(102, 166, 30)', # 绿色'Tier 3': 'rgb(255, 153, 0)' # 橙色
}# 创建分级气泡地图
fig = px.scatter_geo(df,lat='Latitude',lon='Longitude',text='City',size='Population',size_max=60,color='Tier', # 按城市等级着色color_discrete_map=tier_colors, # 使用自定义颜色hover_name='City',hover_data={'Population': ':,.0f','GDP': ':,.0f','Latitude': False,'Longitude': False},projection='natural earth',title='中国主要城市分级气泡地图(带边界)',scope='asia')# 添加详细边界和地理特征
fig.update_geos(resolution=50,showcountries=True, # 显示国家边界countrycolor="Black",countrywidth=1.5,showsubunits=True, # 显示次级行政单位(省/州)subunitcolor="Grey",subunitwidth=0.5,showcoastlines=True, # 显示海岸线coastlinecolor="Blue",coastlinewidth=1.5,showland=True,landcolor="rgb(245, 245, 240)",showocean=True,oceancolor="rgb(212, 230, 255)",showlakes=True,lakecolor="rgb(150, 200, 255)",showrivers=True,rivercolor="rgb(150, 200, 255)"
)# 自定义布局
fig.update_layout(legend_title_text='城市等级',legend=dict(x=0.02,y=0.98,bgcolor='rgba(255, 255, 255, 0.7)',bordercolor='Black',borderwidth=1),margin=dict(l=0, r=0, t=40, b=0),geo=dict(landcolor='rgb(245, 245, 240)',subunitwidth=0.5,countrywidth=1.5,coastlinewidth=1.5,showframe=True, # 显示地图边框framecolor="Black",framewidth=1)
)# 调整气泡样式
fig.update_traces(marker=dict(line=dict(width=1, # 气泡边框宽度color='DarkSlateGrey' # 气泡边框颜色),opacity=0.8 # 气泡透明度),textposition='top center' # 城市名称显示位置
)# 展示图表
fig.show()
3.交互式气泡地图
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go# 示例数据
data = {'City': ['Beijing', 'Shanghai', 'Guangzhou', 'Shenzhen', 'Chengdu', 'Chongqing', 'Tianjin', 'Wuhan', 'Xi\'an', 'Hangzhou'],'Latitude': [39.9042, 31.2304, 23.1291, 22.5431, 30.5728, 29.5630, 39.0841, 30.5928, 34.3416, 30.2741],'Longitude': [116.4074, 121.4737, 113.2644, 114.0579, 104.0668, 106.5516, 117.3610, 114.3052, 108.9398, 120.1551],'Population': [21540000, 24240000, 15000000, 13000000, 16000000, 31000000, 13860000, 11210000, 12330000, 10360000],'GDP': [450, 550, 350, 400, 300, 380, 280, 250, 200, 280], # 十亿美元'Tier': ['Tier 1', 'Tier 1', 'Tier 1', 'Tier 1', 'Tier 2', 'Tier 2', 'Tier 2', 'Tier 2', 'Tier 2', 'Tier 2']
}df = pd.DataFrame(data)# 定义颜色映射
tier_colors = {'Tier 1': '#E7298A', # 玫红色'Tier 2': '#66A61E', # 绿色'Tier 3': '#E6AB02' # 黄色
}# 创建基础气泡地图
fig = px.scatter_geo(df,lat='Latitude',lon='Longitude',size='Population',size_max=40,color='Tier',color_discrete_map=tier_colors,hover_name='City',hover_data={'Population': ':,.0f','GDP': ':,.0f','Latitude': False,'Longitude': False,'Tier': True},projection='natural earth',title='<b>中国主要城市交互式气泡地图</b>',scope='asia',template='plotly_white')
# 增强地图边界和地理特征
fig.update_geos(resolution=110,showcountries=True,countrycolor="black",countrywidth=1,showsubunits=True,subunitcolor="grey",subunitwidth=0.5,showcoastlines=True,coastlinecolor="#4292c6",coastlinewidth=1.5,showland=True,landcolor="#f7f7f7",showocean=True,oceancolor="#deebf7",showlakes=True,lakecolor="#deebf7",showrivers=True,rivercolor="#deebf7"
)# 添加下拉菜单和滑块
fig.update_layout(# 下拉菜单选择显示的数据维度updatemenus=[{"buttons": [{"args": [{"size": [df['Population']/1000000], "title": "按人口显示"}],"label": "人口","method": "update"},{"args": [{"size": [df['GDP']/5], "title": "按GDP显示"}],"label": "GDP","method": "update"}],"direction": "down","showactive": True,"x": 0.1,"xanchor": "left","y": 1.1,"yanchor": "top"}],# 滑块控制气泡大小范围sliders=[{"active": 0,"currentvalue": {"prefix": "气泡大小比例: "},"steps": [{"args": [{"size_max": 20}], "label": "小", "method": "update"},{"args": [{"size_max": 40}], "label": "中", "method": "update"},{"args": [{"size_max": 60}], "label": "大", "method": "update"}],"x": 0.1,"len": 0.8}],# 其他布局设置legend_title_text='城市等级',legend=dict(x=0.02,y=0.98,bgcolor='rgba(255,255,255,0.8)',bordercolor='black',borderwidth=1),margin=dict(l=0, r=0, t=80, b=0),hoverlabel=dict(bgcolor="white",font_size=12,font_family="Arial")
)# 添加点击交互功能
fig.update_traces(marker=dict(line=dict(width=0.8, color='black'),opacity=0.8),hovertemplate=("<b>%{hovertext}</b><br><br>" +"人口: %{customdata[0]:,.0f}<br>" +"GDP: %{customdata[1]:,.0f} 十亿美元<br>" +"等级: %{customdata[2]}<extra></extra>"),# 点击事件 - 实际应用中可连接回调函数customdata=df[['Population', 'GDP', 'Tier']]
)# 添加中国边界高亮
fig.add_trace(go.Scattergeo(lon = [110, 120, 120, 110, 110],lat = [20, 20, 40, 40, 20],mode = 'lines',line = dict(width=2, color='red'),opacity=0.5,showlegend=False,hoverinfo='skip')
)
# 显示图表
fig.show()
(四)注意事项
数据预处理:确保地理数据包含坐标信息,或通过经纬度字段创建点几何对象。处理缺失值,避免无效气泡。
气泡大小映射:采用平方根缩放,避免数值差异过大导致气泡超出画布范围。避免使用面积直接映射。
重叠问题:使用透明度(alpha
)或交互工具(如 Plotly 的动态缩放)减少遮挡。按数据优先级分层绘制(如先画小气泡,再画大气泡)。
底图选择:简单底图(如国家轮廓)避免干扰气泡视觉焦点。
(五)构成与视觉通道
构成要素 | 视觉通道 | 作用 |
---|---|---|
地理底图 | 轮廓线、颜色、透明度 | 提供空间参考框架 |
气泡位置 | 经纬度坐标 | 定位数据点的地理空间 |
气泡大小 | 半径 / 面积 | 映射数值型变量(如 GDP、人口) |
气泡颜色 | 色调、饱和度、亮度 | 映射分类变量或另一数值变量(如类别、人口密度) |
气泡透明度 | 透明度(alpha 值) | 减少重叠遮挡,突出层次 |
标签与图例 | 文本、颜色条、比例尺 | 辅助解读数据含义 |
(六)适用数据
1.空间数据:点数据(如城市坐标、POI 点位)。面数据(如区域边界,需提取中心点作为气泡位置)。
2.属性数据:
- 数值型(必须):用于气泡大小映射(如销售额、数量)。
- 分类 / 连续型(可选):用于颜色映射(如类别标签、温度)。
3.数据格式:推荐使用含几何字段的GeoDataFrame
(Geopandas 格式),或 CSV 文件配合经纬度列。
五、总结
可视化类型 | 数据强调维度 | 适合数据规模 | 地理精度要求 | 多维表达能力 | 实现复杂度 |
---|---|---|---|---|---|
蜂窝热力图 | 密度分布 | 大(>1000点) | 中 | 中 | 中 |
变形地图 | 区域数值比较 | 中小(<100区域) | 低 | 低 | 高 |
关联地图 | 空间关系 | 中小(<500连接) | 高 | 高 | 中高 |
气泡地图 | 点数据比较 | 中小(<500点) | 高 | 高 | 低 |