杰瑞科技汇

Python Basemap实例如何快速上手绘制地图?

Basemap 已被弃用

请务必注意:Basemap 库自 2025 年起已被官方标记为“弃用”(deprecated),其官方文档明确建议用户使用新的 Cartopy 库来替代。

为什么推荐使用 Cartopy?

  • 活跃开发:Cartopy 是目前积极维护和发展的项目。
  • 更现代:支持更多的地图投影、数据格式和 Web 服务(如 WMS, OSM)。
  • 与 Matplotlib 集成更好:API 设计更符合现代 Matplotlib 的习惯。
  • 性能更优:在某些方面性能更好。

为什么我们还要学习 Basemap?

  1. 历史代码:您可能会在很多现有的教程、书籍和旧项目中遇到 Basemap。
  2. 概念相通:Basemap 的核心概念(如投影、坐标系、添加地理特征等)与 Cartopy 高度相似,学会了 Basemap,再转向 Cartopy 会非常容易。
  3. 简单直观:对于一些基础的绘图需求,Basemap 的 API 相对简单直接。

本教程将使用 Basemap 进行讲解,但 强烈建议您在未来的新项目中学习和使用 Cartopy


准备工作:安装 Basemap

您需要安装 basemap 库,它依赖于 matplotlib, numpy 等库。

# 推荐使用 conda 安装,因为它可以更好地处理依赖关系
conda install -c conda-forge basemap
# 或者使用 pip (可能会遇到依赖问题)
pip install basemap

如果安装 basemap 时遇到困难,可以尝试安装 basemap-data-hires,它包含了高分辨率的地图数据(如海岸线、国界线),能让地图更精美。

conda install -c conda-forge basemap-data-hires

实例 1:绘制一张简单的世界地图

这是最基础的例子,我们将创建一个带有海岸线、国界线和地图投影的空白地图。

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
# 1. 创建图形和坐标轴
plt.figure(figsize=(10, 6))
# 2. 创建一个 Basemap 实例
# 这里我们使用 'mill' (Miller) 投影,这是一种世界地图的常用投影。
# llcrnrlat: 左下角纬度
# urcrnrlat: 右上角纬度
# llcrnrlon: 左下角经度
# urcrnrlon: 右上角经度
# 这四个参数定义了我们想要显示的地图区域
m = Basemap(projection='mill', llcrnrlat=-90, urcrnrlat=90,
            llcrnrlon=-180, urcrnrlon=180, resolution='c')
# 3. 绘制地图特征
# 绘制海岸线
m.drawcoastlines()
# 绘制国界线
m.drawcountries()
# 绘制地图边界
m.drawmapboundary()
# 绘制经纬度线 (parallels: 纬线, meridians: 经线)
m.drawparallels(np.arange(-90, 91, 30), labels=[1, 0, 0, 0]) # 绘制纬度线,只在左侧加标签
m.drawmeridians(np.arange(-180, 181, 60), labels=[0, 0, 0, 1]) # 绘制经度线,只在底部加标签
# 4. 添加标题"Simple World Map with Miller Projection", fontsize=15)
# 5. 显示图形
plt.show()

代码解释:

  1. Basemap(projection=...): 选择地图投影。'mill' 是 Miller 投影,适合世界地图,其他常见投影有 'merc' (墨卡托), 'robin' (罗宾森), 'npstere' (北极立体投影) 等。
  2. llcrnrlat, urcrnrlat, llcrnrlon, urcrnrlon: 这四个参数定义了地图的“可见窗口”,即地图的左下角和右上角的经纬度坐标。
  3. resolution: 设置地图的分辨率。'c' (crude, 粗糙), 'l' (low, 低), 'i' (intermediate, 中等), 'h' (high, 高), 'f' (full, 全部),分辨率越高,地图细节越多,绘制时间也越长。
  4. drawcoastlines(), drawcountries(): Basemap 提供了许多方便的函数来绘制各种地理特征。

实例 2:在地图上绘制点和线(城市和航线)

这个例子将展示如何将经纬度坐标转换为地图上的像素坐标,并绘制点和线。

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
# 创建图形
plt.figure(figsize=(12, 8))
# 创建一个以经度0度、纬度0点为中心的墨卡托投影地图
m = Basemap(projection='merc', llcrnrlat=-60, urcrnrlat=80,
            llcrnrlon=-150, urcrnrlon=180, resolution='i')
# 绘制地图特征
m.drawcoastlines()
m.drawcountries()
m.drawmapboundary(fill_color='lightblue') # 填充地图边界颜色为浅蓝
m.fillcontinents(color='coral', lake_color='lightblue') # 填充大陆颜色为珊瑚色,湖泊为浅蓝
m.drawparallels(np.arange(-90, 91, 30), labels=[1, 0, 0, 0])
m.drawmeridians(np.arange(-180, 181, 60), labels=[0, 0, 0, 1])
# 定义一些城市 (经度, 纬度)
cities = {
    'New York': (-74.0060, 40.7128),
    'London': (-0.1276, 51.5072),
    'Tokyo': (139.6503, 35.6762),
    'Sydney': (151.2093, -33.8688),
    'Los Angeles': (-118.2437, 34.0522)
}
# 定义航线 (起点, 终点)
routes = [
    ('New York', 'London'),
    ('London', 'Tokyo'),
    ('Tokyo', 'Sydney'),
    ('Los Angeles', 'New York')
]
# --- 关键步骤:将经纬度坐标转换为地图坐标 ---
# Basemap 的 `x, y = m(lon, lat)` 方法可以完成这个转换
city_coords = {name: m(lon, lat) for name, (lon, lat) in cities.items()}
# 绘制航线
for start, end in routes:
    x1, y1 = city_coords[start]
    x2, y2 = city_coords[end]
    m.plot([x1, x2], [y1, y2], color='blue', linewidth=1, alpha=0.6)
# 绘制城市点
for name, (x, y) in city_coords.items():
    m.plot(x, y, 'o', color='red', markersize=8)
    # 添加城市名称标签
    # axtext 是 Basemap 提供的,可以更好地处理标签位置
    m.text(x, y, '  ' + name, fontsize=9, color='black')
"Major Cities and Flight Routes", fontsize=15)
plt.show()

核心知识点:

  • 坐标转换: x, y = m(lon, lat) 是 Basemap 中最重要的操作之一,它将地理坐标(经度、纬度)转换为 Matplotlib 绘图所使用的笛卡尔坐标(x, y)。所有要在地图上绘制的自定义数据,都必须经过这个转换。
  • 填充颜色: fillcontinentsdrawmapboundary 可以让地图更具视觉吸引力。

实例 3:在地图上绘制数据(温度数据)

这个例子将展示如何将一个二维的数值数据(如温度)绘制在地图上,形成类似“热力图”的效果,我们将使用 pcolormesh 函数。

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
# 1. 准备数据
# 创建一个经纬度网格
lon = np.linspace(-180, 180, 360)
lat = np.linspace(-90, 90, 181)
lon_grid, lat_grid = np.meshgrid(lon, lat)
# 生成一些模拟的温度数据 (一个简单的正弦函数来模拟温度变化)
# 这里温度只随纬度变化,越靠近赤道越热
temp_data = 30 * np.cos(np.radians(lat_grid)) - 10
# 2. 创建地图
plt.figure(figsize=(14, 10))
m = Basemap(projection='robin', lon_0=0, resolution='c') # 罗宾森投影,中心经度为0
# 3. 绘制地图底图
m.drawcoastlines(linewidth=0.5)
m.drawcountries(linewidth=0.5)
m.drawmapboundary(fill_color='white')
# 4. 绘制温度数据
# 关键:将数据网格的经纬度坐标也转换为地图坐标
x, y = m(lon_grid, lat_grid)
# 使用 pcolormesh 绘制伪彩色图
# cmap: 颜色映射,这里用 'jet' 从蓝到红
# shading: 'auto' 让 matplotlib 自动处理阴影
cs = m.pcolormesh(x, y, temp_data, shading='auto', cmap='jet')
# 5. 添加颜色条
cbar = plt.colorbar(cs, orientation='horizontal', pad=0.05, shrink=0.8)
cbar.set_label('Temperature (°C)', fontsize=12)
# 6. 添加标题"Global Temperature Simulation", fontsize=16)
# 7. 显示图形
plt.show()

核心知识点:

  • 数据网格: 对于区域性的数据,我们通常需要一个二维的经度网格 (lon_grid) 和一个二维的纬度网格 (lat_grid),它们共同构成一个覆盖整个区域的网格。
  • pcolormesh: 这是 Matplotlib 中用于绘制非结构化网格数据的函数,Basemap 对它进行了封装,使其可以直接使用转换后的地图坐标 (x, y) 和原始数据 (temp_data) 来绘制。
  • 颜色条: plt.colorbar() 可以自动根据 pcolormesh 的数据生成一个颜色条,用于表示数值与颜色的对应关系。

从 Basemap 迁移到 Cartopy (强烈推荐)

如果您已经理解了 Basemap 的核心思想,学习 Cartopy 会非常快,下面我们将上面 实例 3 用 Cartopy 重写,您可以直观地感受二者的异同。

import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs # Cartopy 的坐标参考系统
import cartopy.feature as cfeature # Cartopy 的地理特征
# 1. 准备数据 (与 Basemap 完全相同)
lon = np.linspace(-180, 180, 360)
lat = np.linspace(-90, 90, 181)
lon_grid, lat_grid = np.meshgrid(lon, lat)
temp_data = 30 * np.cos(np.radians(lat_grid)) - 10
# 2. 创建图形和坐标轴
# Cartopy 的核心是使用 cartopy.crs 作为投影
plt.figure(figsize=(14, 10))
ax = plt.axes(projection=ccrs.Robinson(central_longitude=0)) # 罗宾森投影,中心经度为0
# 3. 添加地图特征
# Cartopy 使用 add_feature 方法,并且需要指定特征
ax.add_feature(cfeature.COASTLINE, linewidth=0.5)
ax.add_feature(cfeature.BORDERS, linewidth=0.5)
ax.add_feature(cfeature.OCEAN, color='lightblue') # 添加海洋
ax.add_feature(cfeature.LAND, color='coral') # 添加陆地
ax.gridlines(draw_labels=True) # 绘制并自动添加经纬度标签
# 4. 绘制温度数据
# Cartopy 的 pcolormesh 直接接受原始经纬度和数据
# transform 参数告诉 cartopy 数据本身的坐标系统是什么
cs = ax.pcolormesh(lon_grid, lat_grid, temp_data,
                   transform=ccrs.PlateCarree(), # 数据的坐标系统是 Plate Carree (经纬度)
                   cmap='jet', shading='auto')
# 5. 添加颜色条和标题
cbar = plt.colorbar(cs, orientation='horizontal', pad=0.05, shrink=0.8)
cbar.set_label('Temperature (°C)', fontsize=12)
ax.set_title("Global Temperature Simulation (with Cartopy)", fontsize=16)
plt.show()

Basemap vs. Cartopy 对比:

特性 Basemap Cartopy
坐标转换 x, y = m(lon, lat) 在绘图前手动转换 transform=ccrs.PlateCarree() 在绘图函数中指定,自动处理
添加地理特征 m.drawcoastlines(), m.fillcontinents() ax.add_feature(cfeature.COASTLINE), ax.add_feature(cfeature.LAND)
投影设置 Basemap() 构造函数中 plt.axes(projection=ccrs.xxx())
经纬度标签 m.drawparallels(), m.drawmeridians() ax.gridlines(draw_labels=True) (非常方便)
状态 已弃用 推荐使用,活跃开发

希望这些实例能帮助您理解 Basemap 的使用,并顺利过渡到更现代的 Cartopy!

分享:
扫描分享到社交APP
上一篇
下一篇