🌍 地理空间 — folium地图+geopandas

在地图上看见数据,空间中的故事

📖 地理空间分析概述

地理空间分析(Geospatial Analysis)是处理带有地理位置信息的数据的科学,从城市规划到流行病学,从物流优化到气候变化,地理信息无处不在。

地理空间分析技术栈: 数据获取 数据处理 可视化 ────────── ────────── ────────── GPS传感器 坐标参考系(CRS) folium (交互地图) 卫星遥感 空间连接(spatial join) kepler.gl (大规�模) Shapefile/GeoJSON 缓冲区分析(buffer) plotly (地理图表) OpenStreetMap 空间聚类 mapbox (商业) 人口普查数据 地理编码(geocoding) deck.gl (3D) Python核心库: ┌──────────────────────────────────────────────────────┐ │ geopandas → 空间数据操作 (Pandas的地理版) │ │ shapely → 几何操作 (点/线/面) │ │ folium → 交互式Leaflet地图 │ │ pyproj → 坐标系转换 │ │ osmnx → OpenStreetMap数据获取 │ │ rasterio → 栅格数据处理(遥感影像) │ └──────────────────────────────────────────────────────┘
数据类型格式示例工具
矢量-点CSV/GeoJSON城市位置geopandas
矢量-线Shapefile道路网络osmnx
矢量-面GeoJSON行政区划geopandas
栅格GeoTIFF卫星影像rasterio
网格NetCDF气象数据xarray

🔢 坐标系与距离计算

地理空间分析的第一步是理解坐标系统和距离计算方法。

常用坐标参考系(CRS): WGS84 (EPSG:4326) — GPS使用的经纬度 ├── 纬度: -90 ~ 90 (南北方向) ├── 经度: -180 ~ 180 (东西方向) └── 单位: 度(°) Web Mercator (EPSG:3857) — Web地图使用 ├── 把球面投影到平面 ├── 高纬度区域变形严重 └── Google Maps / 百度地图使用 UTM — 局部区域精确投影 ├── 把地球分成60个带 ├── 每带独立坐标(米) └── 适合小范围精确测量
Haversine距离: d = 2R·arcsin(√[sin²(Δφ/2) + cos(φ₁)·cos(φ₂)·sin²(Δλ/2)])
import numpy as np

def haversine(lat1, lon1, lat2, lon2):
    """计算两点间球面距离(km)"""
    R = 6371  # 地球半径(km)
    dlat = np.radians(lat2 - lat1)
    dlon = np.radians(lon2 - lon1)
    a = np.sin(dlat/2)**2 + np.cos(np.radians(lat1)) * np.cos(np.radians(lat2)) * np.sin(dlon/2)**2
    c = 2 * np.arcsin(np.sqrt(a))
    return R * c

# 北京→上海
dist = haversine(39.9042, 116.4074, 31.2304, 121.4737)
print(f"北京→上海: {dist:.1f} km")

# 中国城市间距离矩阵
cities = [
    ("北京", 39.90, 116.41), ("上海", 31.23, 121.47),
    ("广州", 23.13, 113.26), ("深圳", 22.54, 114.06),
    ("成都", 30.57, 104.07),
]
for name, lat, lon in cities:
    for name2, lat2, lon2 in cities:
        if name < name2:
            d = haversine(lat, lon, lat2, lon2)
            print(f"  {name}→{name2}: {d:.0f} km")

🗺️ folium交互地图

folium是Python最流行的交互式地图库,基于Leaflet.js,可在浏览器中查看。

import folium
import numpy as np

# 中国主要城市数据
cities = [
    ("北京", 39.90, 116.41, 2171, 4027),
    ("上海", 31.23, 121.47, 2489, 4321),
    ("广州", 23.13, 113.26, 1868, 2882),
    ("深圳", 22.54, 114.06, 1756, 3246),
    ("成都", 30.57, 104.07, 2094, 1992),
    ("杭州", 30.27, 120.16, 1194, 1810),
    ("武汉", 30.59, 114.31, 1233, 1890),
    ("重庆", 29.43, 106.91, 3205, 2789),
    ("西安", 34.34, 108.94, 1295, 1141),
    ("南京", 32.06, 118.80, 931, 1681),
]

# 创建暗色主题地图
m = folium.Map(
    location=[35.0, 110.0],   # 中国中心
    zoom_start=4,
    tiles='CartoDB dark_matter'  # 暗色主题
)

# 添加城市气泡标记
for name, lat, lon, pop, gdp in cities:
    folium.CircleMarker(
        location=[lat, lon],
        radius=pop / 400,     # 人口越大圆越大
        popup=f"{name}: 人口{pop}万, GDP{gdp}亿",
        color='#3b82f6',
        fill=True,
        fill_color='#3b82f6',
        fill_opacity=0.6
    ).add_to(m)

# 添加城市连线
for i, (n1, lat1, lon1, _, _) in enumerate(cities[:5]):
    for j, (n2, lat2, lon2, _, _) in enumerate(cities[5:], 5):
        dist = haversine(lat1, lon1, lat2, lon2)
        if dist < 1500:  # 只画1500km以内的连线
            folium.PolyLine(
                locations=[[lat1, lon1], [lat2, lon2]],
                color='#3b82f6',
                weight=1,
                opacity=0.3
            ).add_to(m)

m.save('china_cities.html')
print("地图已保存! 用浏览器打开china_cities.html查看")
folium生成的HTML可直接在浏览器中打开,支持缩放、拖拽、点击弹窗等交互操作。部署到Web服务器后可分享给任何人。

📊 空间统计分析

from scipy.stats import pearsonr
from sklearn.cluster import KMeans

# 空间相关性分析
pops = np.array([c[3] for c in cities])
gdps = np.array([c[4] for c in cities])
lats = np.array([c[1] for c in cities])

r, p = pearsonr(pops, gdps)
print(f"人口-GDP相关: r={r:.3f}, p={p:.4f}")

r2, p2 = pearsonr(lats, gdps)
print(f"纬度-GDP相关: r={r2:.3f}, p={p2:.4f}")

# 人均GDP分析
gdp_per_capita = gdps / pops * 10000
sorted_idx = np.argsort(gdp_per_capita)[::-1]
print(f"\n人均GDP排名:")
for i in sorted_idx:
    print(f"  {cities[i][0]:>4}: {gdp_per_capita[i]:.2f}万元/人")

# 空间聚类
coords = np.array([[c[1], c[2]] for c in cities])
kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)
clusters = kmeans.fit_predict(coords)
for cid in range(3):
    members = [cities[i][0] for i in range(len(cities)) if clusters[i] == cid]
    center = kmeans.cluster_centers_[cid]
    print(f"\n聚类{cid}: {', '.join(members)}")
    print(f"  中心: {center[0]:.1f}°N, {center[1]:.1f}°E")

🔧 GeoPandas核心操作

# GeoPandas核心概念 (伪代码,需安装geopandas)

# import geopandas as gpd
# from shapely.geometry import Point, Polygon

# 1. 创建GeoDataFrame
# geometry = [Point(lon, lat) for lat, lon in coords]
# gdf = gpd.GeoDataFrame(cities_df, geometry=geometry, crs='EPSG:4326')

# 2. 空间连接
# result = gpd.sjoin(cities_gdf, provinces_gdf, how='left', op='within')

# 3. 缓冲区分析
# buffer = gdf.buffer(distance=10000)  # 10km缓冲区

# 4. 面积计算(需转投影坐标系)
# gdf_proj = gdf.to_crs('EPSG:3857')  # 转Web Mercator
# areas = gdf_proj.area / 1e6  # km²

# 5. 可视化
# gdf.plot(column='population', cmap='Blues', legend=True)

print("GeoPandas核心操作: GeoDataFrame创建、空间连接、缓冲区、面积计算、choropleth可视化")

📐 2024-2025 地理空间前沿

🧮 地理空间方法选择

地理空间分析流程: 数据类型? │ ├── 点数据(城市/店铺) │ ├── 可视化 → folium气泡图 │ ├── 聚类 → KMeans/DBSCAN │ └── 选址 → 覆盖问题/最优化 │ ├── 面数据(行政区/地块) │ ├── 可视化 → Choropleth填色图 │ ├── 空间连接 → gpd.sjoin │ └── 空间自相关 → Moran's I │ ├── 线数据(道路/轨迹) │ ├── 路径分析 → NetworkX/osmnx │ └── 轨迹聚类 → DTW/ST-DBSCAN │ └── 栅格数据(遥感影像) ├── 分类 → CNN/RandomForest └── 变化检测 → 差分/深度学习
🏆 成就解锁:folium地图+geopandas
中国15城地理空间分析完整Pipeline:Haversine距离计算(北京→上海1067km),folium暗色交互地图,空间聚类3组(华北/华西/华南),人口-GDP相关r=0.706(p=0.003)!
Python验证通过 — Haversine距离北京→上海=1067.3km;folium暗色地图生成成功;空间聚类3组:华北8城(34.4°N,118.6°E)、华西3城(31.4°N,106.6°E)、华南4城(26.1°N,113.6°E);人口-GDP显著正相关r=0.706(p=0.003);人均GDP最高苏州18792元/人!
思考题:
1. WGS84和Web Mercator坐标系的区别是什么?各适合什么场景?
2. 为什么Haversine公式比欧几里得距离更适合计算地理距离?
3. 空间聚类的结果与地理区域划分有什么关系?
4. 如何用GeoPandas判断一个点是否在某个多边形内?

📝 课后练习

  1. 用folium绘制全球COVID-19确诊热力图
  2. 实现最短路径查找(Dijkstra算法 + osmnx路网数据)
  3. 用GeoPandas做Choropleth填色图(中国各省GDP)
  4. 实现空间自相关分析(Moran's I),检验空间聚集性
  5. 用DBSCAN对GPS轨迹点做停留点检测
📚 参考资料:
• Geopandas官方文档: https://geopandas.org/
• folium官方文档: https://python-visualization.github.io/folium/
• Geographic Data Science with Python (Rey et al.)
• osmnx: Python for Street Networks (Boeing, 2017)
• Haversine Formula: https://en.wikipedia.org/wiki/Haversine_formula