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

Matplotlib绘制3D散点图z=0平面图像时无法消除全部空白

问题描述

需要在3D散点图的z=0平面上绘制Cartopy地图作为基底。已通过2D图生成了范围匹配的地图,但将其转为numpy数组贴到3D平面时,地图范围正确但侧边存在空白,无法完全覆盖z=0平面。

原因分析
  • 2D画布的宽高比与地理范围(经纬度跨度)的纵横比不匹配,导致生成的地图图像本身存在留白
  • 2D图的布局设置未彻底消除所有边距
  • 3D表面绘制时的纹理映射未完全对齐地理范围
解决方案

1. 匹配画布与地理范围的纵横比

计算经纬度范围的宽高比,以此设置2D画布的尺寸,确保地图完全填充画布,无额外留白。

2. 彻底清除2D图的边距与边框

强化布局设置,确保2D图的轴完全填充画布,无任何边距。

3. 优化3D表面的纹理绘制

确保plot_surface使用的网格与地图图像的像素尺寸严格对应,避免纹理拉伸或错位。

修改后的完整代码
import matplotlib.pyplot as plt
import numpy as np
import cartopy.crs as ccrs
import cartopy.io.img_tiles as cimgt
import os
import pandas as pd

# 假设data_ts、cmin、cmax、output_path、flight已提前定义
# Create a new figure
fig = plt.figure(figsize=(12, 8), layout='tight')
ax = fig.add_subplot(111, projection='3d', computed_zorder=False)

# Define the map boundaries
min_lat, max_lat = np.round(data_ts['Lat'].min() - 0.1, 5), np.round(data_ts['Lat'].max() + 0.1, 5)
min_lon, max_lon = np.round(data_ts['Lon'].min() - 0.1, 5), np.round(data_ts['Lon'].max() + 0.1, 5)
print(min_lat, max_lat, min_lon, max_lon)

# Set up the map tiles
tiles = cimgt.GoogleTiles()
tiler = tiles.crs

# 匹配画布与地理范围的纵横比
lon_span = max_lon - min_lon
lat_span = max_lat - min_lat
aspect_ratio = lon_span / lat_span

fig_height = 10
fig_width = fig_height * aspect_ratio
fig_2d = plt.figure(figsize=(fig_width, fig_height), frameon=False)

ax_2d = fig_2d.add_subplot(111, projection=tiler)
ax_2d.set_axis_off()

# 彻底清除边距
plt.subplots_adjust(top=1, bottom=0, right=1, left=0, hspace=0, wspace=0)
ax_2d.margins(0)

ax_2d.set_extent([min_lon, max_lon, min_lat, max_lat], crs=ccrs.PlateCarree())
ax_2d.add_image(tiles, 12)  # Zoom level

# 无边距保存图像
fig_2d.savefig(os.path.join(output_path,f"{flight}.2d3d.tif"), format='tif',dpi=300, bbox_inches='tight', pad_inches=0)

# Extract the map image from the 2D plot
fig_2d.canvas.draw()
map_img = np.array(fig_2d.canvas.renderer.buffer_rgba())
map_img = np.flipud(map_img)

# Convert the map image to a texture
x = np.linspace(min_lon, max_lon, map_img.shape[1])
y = np.linspace(min_lat, max_lat, map_img.shape[0])
print(y.min(), y.max(), x.min(),x.max())
x, y = np.meshgrid(x, y)
z = np.zeros_like(x)

# Plot the texture on the z=0 plane
ax.plot_surface(x, y, z, rstride=1, cstride=1, facecolors=map_img / 255, zorder=1)

# Plot the 3D scatter plot
sc = ax.scatter(data_ts['Lon'], data_ts['Lat'], data_ts['Alt'], c=data_ts['Alt'], vmin=cmin, vmax=cmax, cmap='turbo', marker='o', alpha=0.5, zorder=2)
ax.scatter([min_lon, min_lon, max_lon, max_lon], [min_lat, max_lat, min_lat, max_lat])
ax.set_xlim(min_lon, max_lon)
ax.set_ylim(min_lat, max_lat)
ax.set_zlim(0, data_ts['Alt'].max())

# Add color bar
cb = plt.colorbar(sc, ax=ax, shrink=0.5, aspect=5)            
cb.set_label('Altitude (m)')

# Label axes
ax.set_xlabel('Longitude')
ax.set_ylabel('Latitude')
ax.set_zlabel('Altitude (m)')

# Set the view angle for better visualization
ax.view_init(elev=30, azim=-60)

plt.show()
关键修改说明
  • 计算地理范围的纵横比,设置2D画布尺寸与之匹配,避免画布比例不当导致的留白
  • 移除plt.tight_layout(),改用bbox_inches='tight', pad_inches=0保存图像,确保无额外边距
  • 简化3D表面绘制,只调用一次plot_surface,避免重复绘制可能的层级问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 20:42:14