You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Geopandas地图指定位置添加同CRS的柱状图

问题

我用欧盟统计局(Eurostat)的NUTS2021官方多边形数据(GeoJSON格式,Polygon (RG)几何类型,20M比例尺,EPSG 3857坐标系)和Geopandas制作欧洲国家地图,想要在特定位置(多数为国家质心,部分国家需多个位置)添加柱状图。这些位置坐标来自欧盟统计局NUTS2021点数据(GeoJSON格式,Points (LB)几何类型,EPSG 3857坐标系)或其他数据源(如Google公开数据)。

目前我知道用fig.add_axes(rect)添加柱状图,其中rect是[x0, y0, width, height]格式的figure坐标,但我希望用预定义的地理坐标(比如德国的NUTS点坐标(1155338.417 6634297.661),或者CSV里的经纬度(10.451526, 51.165691))来定位柱状图,而非手动计算figure坐标,该怎么实现?

已尝试方案

我试过用lon_eu_min, lon_eu_max, lat_eu_min, lat_eu_max对坐标做归一化,但没成功,因为柱状图是基于figure坐标而非地图轴坐标定位的。手动设置figure坐标可以实现,示例代码如下:

import cartopy.crs as ccrs
import geopandas as gpd
import matplotlib.pyplot as plt

# 导入并筛选国家多边形数据
nuts_path = '.\\NUTS_RG_20M_2021_3857.geojson'
gdf = gpd.read_file(nuts_path)
gdf = gdf[gdf['LEVL_CODE'] == 0]
gdf = gdf[['CNTR_CODE', 'geometry']]
gdf = gdf.rename(columns={'CNTR_CODE': 'region'})
gdf = gpd.GeoDataFrame(gdf, geometry='geometry', crs=3857)

# 导入并筛选国家点数据
nuts_points_path = '.\\NUTS_LB_2021_3857.geojson'
gdf_points = gpd.read_file(nuts_points_path)
gdf_points = gdf_points.query("LEVL_CODE == 0")
gdf_points = gdf_points[['CNTR_CODE', 'geometry']]
gdf_points = gdf_points.rename(columns={'CNTR_CODE': 'region'})

# 绘图
fig = plt.figure(figsize=(9, 10))
ax = plt.axes(projection=ccrs.epsg(3857))

# 设置地图范围
lat_eu_min = 35
lat_eu_max = 71
lon_eu_min = -12
lon_eu_max = 34
ax.set_extent([lon_eu_min, lon_eu_max, lat_eu_min, lat_eu_max])

# 绘制国家边界
gdf.plot(
    ax=ax,
    edgecolor='black',
    facecolor='lightgrey',
)

# 手动设置figure坐标定位柱状图
longitude, latitude = 0.46, 0.33

# 添加柱状图
bar_ax = fig.add_axes([longitude, latitude, 0.05, 0.05])
bar_ax.bar([1, 2, 3], [1, 2, 3], color=['C1', 'C2', 'C3'])
bar_ax.set_axis_off()

plt.tight_layout()
plt.show()

手动定位的预期效果:
预期效果

解决方法

核心思路是将地理坐标转换为figure坐标,步骤如下:

  1. 把地理坐标转换为地图轴(ax)的相对坐标(范围[0,1])
  2. 将该相对坐标直接作为fig.add_axes()的定位参数

具体实现

1. 坐标转换函数

定义函数完成地理坐标到figure坐标的转换,支持不同CRS的输入坐标:

def geo_to_figure_coords(geo_x, geo_y, ax, input_crs):
    # 将输入CRS的地理坐标转换为当前地图投影的坐标
    projected_point = ax.projection.transform_point(geo_x, geo_y, input_crs)
    # 将投影坐标转换为axes相对坐标([0,1]范围,对应figure比例)
    fig_coord = ax.transAxes.inverted().transform(ax.transData.transform(projected_point))
    return fig_coord

2. 完整示例代码

以德国的NUTS点坐标(EPSG 3857)和柏林经纬度(WGS84)为例,自动定位柱状图:

import cartopy.crs as ccrs
import geopandas as gpd
import matplotlib.pyplot as plt

# 坐标转换函数
def geo_to_figure_coords(geo_x, geo_y, ax, input_crs):
    projected_point = ax.projection.transform_point(geo_x, geo_y, input_crs)
    fig_coord = ax.transAxes.inverted().transform(ax.transData.transform(projected_point))
    return fig_coord

# 导入并处理多边形数据
nuts_path = '.\\NUTS_RG_20M_2021_3857.geojson'
gdf = gpd.read_file(nuts_path)
gdf = gdf[gdf['LEVL_CODE'] == 0][['CNTR_CODE', 'geometry']].rename(columns={'CNTR_CODE': 'region'})
gdf = gpd.GeoDataFrame(gdf, geometry='geometry', crs=3857)

# 导入并处理点数据
nuts_points_path = '.\\NUTS_LB_2021_3857.geojson'
gdf_points = gpd.read_file(nuts_points_path)
gdf_points = gdf_points.query("LEVL_CODE == 0")[['CNTR_CODE', 'geometry']].rename(columns={'CNTR_CODE': 'region'})

# 创建绘图对象
fig = plt.figure(figsize=(9, 10))
ax = plt.axes(projection=ccrs.epsg(3857))
ax.set_extent([-12, 34, 35, 71])

# 绘制国家
gdf.plot(ax=ax, edgecolor='black', facecolor='lightgrey')

# 示例1:用EPSG 3857坐标定位(德国NUTS质心)
de_x, de_y = 1155338.417, 6634297.661
fig_x, fig_y = geo_to_figure_coords(de_x, de_y, ax, ccrs.epsg(3857))
bar_ax1 = fig.add_axes([fig_x, fig_y, 0.05, 0.05])
bar_ax1.bar([1,2,3], [2,3,1], color=['#FF9999', '#99FF99', '#9999FF'])
bar_ax1.set_axis_off()

# 示例2:用WGS84经纬度定位(柏林)
berlin_lon, berlin_lat = 13.4050, 52.5200
fig_x2, fig_y2 = geo_to_figure_coords(berlin_lon, berlin_lat, ax, ccrs.PlateCarree())
bar_ax2 = fig.add_axes([fig_x2, fig_y2, 0.04, 0.04])
bar_ax2.bar([1,2], [4,2], color=['#FFCC00', '#CC00FF'])
bar_ax2.set_axis_off()

# 示例3:从NUTS点数据中提取坐标定位
de_point = gdf_points[gdf_points['region'] == 'DE'].iloc[0]
fig_x3, fig_y3 = geo_to_figure_coords(de_point.geometry.x, de_point.geometry.y, ax, ccrs.epsg(3857))
# 可在此添加第三个柱状图

plt.tight_layout()
plt.show()

关键说明

  • ax.projection.transform_point():完成不同CRS之间的坐标转换,确保输入坐标和地图投影匹配
  • ax.transData.transform():将地图数据坐标转为像素坐标
  • ax.transAxes.inverted().transform():将像素坐标转为axes相对坐标,直接对应figure的比例位置
  • 柱状图的宽高参数(如0.05)是figure尺寸的比例,可根据需求调整

内容的提问来源于stack exchange,提问作者Sacrademius

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 11:17:56