简介:面向需要将卫星遥感数据转换为可视化图表的MATLAB用户,这份资源以具体实例演示了从原始数据读取到地图出图的全流程。包内共3个文件:MATLAB脚本(.m)为核心,包含数据读取、格点处理、colorbar设置等绘图程序;WMV格式操作演示视频带读者逐步操作;DOC说明文档则提供卫星海洋数据处理要点与参数解释,整体体积仅1.38MB,轻量易下载。目前已有634人学习使用。通过该实例,读者可获得可直接运行的MATLAB出图模板,理解从卫星原始变量到规范海洋图的标准化流程,学习如何替换路径与变量名以适配自己的数据,并掌握图像导出与样式优化技巧。尤其适合刚接触卫星数据可视化、希望快速上手绘图程序的初学者或科研人员,是一份兼顾代码、视频与文档的紧凑型入门资料。
1. 从 sst 数据到可复现出图:卫星数据处理的完整链路
手头拿到一批卫星海洋数据,业务上最直接的需求是“把 sst(海表温度)读进 MATLAB 并出图”。但真正动手时你会发现,卡点往往不在画图那一步,而在读取层:这包数据里既有global sst.wmv这类视频演示文件,也有read 程序.m脚本和《卫星海洋.doc》说明文档,数据源很可能是 NetCDF 或 HDF 格式,也可能是从海洋卫星官网批量拉下来的逐日海温场。这里的核心思路是先搞清楚数据的存储结构,再决定用ncread还是h5read,最后通过pcolor或contourf把经纬度网格映射成可视化图像。适合刚接触卫星遥感数据、想用 MATLAB 完成“读文件—提取变量—出图—保存”这条完整链路的从业者。
2. 读取层选型:NetCDF/HDF5 内存布局与 MATLAB 读取效率
2.1 为什么卫星数据首选 NetCDF 而不是直接读二进制
卫星数据的组织方式通常不是简单的二维数组平铺,而是把维度(经度、纬度、时间)和变量属性(单位、有效值范围、填充值)一起打包进自描述文件里。如果你直接按字节读二进制,遇到数据源升级、维度顺序调整或缺失值定义变化时,整个读取程序就要重写。NetCDF 和 HDF5 的出现解决了这个问题:变量名、维度名、属性和数据本体存放在同一个结构体内,MATLAB 通过ncread或h5read可以按变量名直接取数,不需要关心文件内部的物理偏移量。
另一个现实因素是内存效率。逐日全球海温数据通常是 1440×720 的经纬度格点,单精度 float 存储时每个时间片约 4MB,但如果你用load加载未经处理的二进制文件,MATLAB 会按 double 类型读入,内存占用直接翻倍。ncread支持通过参数指定读取的起始位置和步长,只在需要时把局部数据调入工作区,这对处理多年逐日数据的场景非常关键。
2.2 ncread 参数逐项拆解:读什么、从哪读、读多少
read 程序.m里最核心的调用就是ncread。先看一段通用的读取代码:
% 打开文件 ncid = netcdf.open('sst_daily_2024.nc', 'NOWRITE'); % 查看全局属性和变量列表 info = ncinfo('sst_daily_2024.nc'); disp({info.Variables.Name}); % 按变量名读取海温数据 lon = ncread('sst_daily_2024.nc', 'lon'); lat = ncread('sst_daily_2024.nc', 'lat'); sst = ncread('sst_daily_2024.nc', 'sst', [1 1 1], [Inf Inf 1]); % 读取单独的时间变量 time = ncread('sst_daily_2024.nc', 'time'); % 关闭文件 netcdf.close(ncid);逻辑说明:ncread的第三个参数是起始下标,第四个参数是读取长度。这里[1 1 1]表示从第一个经度、第一个纬度、第一个时间片开始读;[Inf Inf 1]表示经度和纬度维度全部读取,时间维度只取第一片。这样做的好处是避免把整个三维数据一次性载入内存——如果你只需要某一天的海温场,就只读那一片。读取完成后用squeeze把单维度去掉,得到二维矩阵:
sst_2d = squeeze(sst); sst_2d(sst_2d < -999 | sst_2d > 100) = NaN;参数说明:sst_2d(sst_2d < -999 | sst_2d > 100) = NaN这一行是把卫星数据常见的填充值(如 -9999、-32767)和物理上不可能出现的海温值替换为NaN,防止后续绘图时出现异常的蓝色或黑色区域。lon和lat读取后一般是向量,需要用meshgrid扩展成与sst_2d同尺寸的网格矩阵。
2.3 数据校验:维度顺序与坐标方向
读取环节最常见的坑是维度顺序和经纬度方向。部分海洋卫星产品把维度定义为[time, lat, lon],有些则是[lat, lon, time],如果直接按习惯去索引,画出来的图会南北颠倒或东西翻转。建议在读取后先打印尺寸和坐标范围:
fprintf('size(sst) = %d x %d\n', size(sst_2d, 1), size(sst_2d, 2)); fprintf('lon range: %.2f ~ %.2f\n', min(lon), max(lon)); fprintf('lat range: %.2f ~ %.2f\n', min(lat), max(lat));如果发现纬度是从 90 到 -90 递减,而数据本身要求从 -90 到 90 递增,就用flipud(sst_2d)或fliplr调整方向。也可以直接用ncinfo查看变量的Size属性确认维度定义顺序,再决定索引方式。这一步校验做不好,后面所有出图都是错的且不易察觉——因为图像本身能显示出来,只是地理位置上颠倒了。
| 检查项 | 常见问题 | 验证方法 |
|---|---|---|
| 维度顺序 | time/lat/lon 顺序不一致 | ncinfo查看Size字段 |
| 纬度方向 | 90→-90 与 -90→90 反向 | 打印min/max并对比数据文档 |
| 填充值 | 未过滤导致图中出现异常色块 | unique(sst_2d(:))查看极值 |
| 单位换算 | 温度以 K 为单位需减 273.15 | 查看变量units属性 |
第二章到这里,读取层的选型和校验已经覆盖了。接下来进入绘图环节。
3. 绘图管线:从经纬度网格到直观海温图
3.1 pcolor / surf / contourf 怎么选
MATLAB 里绘制二维场数据有几种常见方案,适用场景不同。imagesc只接受规则网格,直接按矩阵行列显示,不经过投影,适合快速预览;pcolor按经纬度坐标绘制单元面片,支持非均匀网格,但默认会显示网格线,需要加shading interp去掉;contourf绘制等值线填充图,适合表达场的连续分布,但对数据量较大的场景渲染速度略慢。
| 绘图函数 | 适用场景 | 注意点 |
|---|---|---|
imagesc | 快速预览、不关心坐标 | 需要手动设置 XTickLabel |
pcolor | 展示原始分辨率场 | 加shading interp消除网格线 |
contourf | 等值线表达、层次分明 | 等值线间距要按业务设置 |
surf | 3D 展示地形/温度起伏 | 需要额外视角参数 |
实际处理 sst 数据时,我一般先用pcolor出原始分辨率图确认数据质量,再用contourf生成适合报告的成品图。
3.2 绘制 sst 填充图并叠加海岸线
直接用pcolor会出现一个问题:NaN值区域会绘制成空白,但如果数据中存在大量陆地掩膜(land mask),空白区域和海洋边界会混淆。常见做法是把陆地区域填成灰色,并在图上叠加海岸线矢量。下面是完整绘图流程:
% 坐标网格 [lon_grid, lat_grid] = meshgrid(lon, lat); % 创建图形窗口 figure('Color', 'w', 'Position', [100 100 1200 600]); % 海温填色 pcolor(lon_grid, lat_grid, sst_2d); shading interp; caxis([0 32]); % 海温色标范围,单位摄氏度 % 色标设置 colormap(jet); colorbar; ylabel(colorbar, 'SST (^{\circ}C)'); % 坐标轴与地图边框 xlabel('Longitude (^{\circ}E)'); ylabel('Latitude (^{\circ}N)'); axis tight; hold on; % 叠加海岸线(需要 m_map 工具箱或手动读取 coast 数据) % 这里以 m_map 为例 % m_proj('miller', 'lon', [min(lon) max(lon)], 'lat', [min(lat) max(lat)]); % m_pcolor(lon_grid, lat_grid, sst_2d); % m_gshhs('patch', [0.7 0.7 0.7]); % m_coast('color', [0 0 0]);caxis([0 32])控制色标范围,海温物理上通常不会低于 -2 摄氏度或高于 35 摄氏度,设置后图中冷暖色对比更清晰。shading interp的作用是让相邻面片颜色平滑过渡。如果使用m_map工具箱,m_gshhs('patch', [0.7 0.7 0.7])会把陆地填充为灰色,m_coast画出海岸线边界。没有m_map时,可以直接在pcolor之上用plot画经纬度边界线,但在极地投影场景下建议尽量用m_map,自带投影变换能防止高纬地区网格变形。
3.3 投影方式与出图尺寸
针对全球海温图,m_proj的投影选择直接影响视觉表达。墨卡托投影适合低纬度和中纬度海域,但高纬地区面积严重放大;miller投影对全球分布相对均衡;极地附近建议改用stereographic。分辨率方面,如果只是用于论文插图或报告,输出 300dpi 的 PNG 足够;如果要印刷,需要把print的-r参数调到 600 及以上:
print(gcf, 'sst_global_20240101.png', '-dpng', '-r300');参数说明:-r300表示 300dpi 输出,文件大小与网格分辨率成正比。如果后续还要在 GIS 软件里叠加矢量图层,建议保存为 GeoTIFF,但 MATLAB 原生不支持直接导出 GeoTIFF,需要额外写geotiffwrite或使用映射工具箱。这一步在实际项目里经常被忽略,导致后期转数据时又要重新跑一遍绘图程序。
4. 批量出图与参数调优:多日数据的时间序列处理
4.1 循环读取多日数据并批量保存
业务场景里很少只画一天的海温,往往是连续一周或一个月的逐日图拼接成动画,或者每张图对应某个时次。批量处理时,循环外层读文件、内层画图,一次性把数据加载再循环出图,比每次循环都重新读文件要快得多。下面给出一个可直接套用的框架:
% 文件列表 fileList = dir('sst_daily_*.nc'); nFiles = length(fileList); sst_all = []; for i = 1:nFiles fname = fileList(i).name; % 读取整个时间维(假设文件内只有一个时间片) sst_tmp = ncread(fname, 'sst'); lat = ncread(fname, 'lat'); lon = ncread(fname, 'lon'); % 维度顺序判断 if size(sst_tmp, 1) == length(lat) sst_tmp = sst_tmp'; end % 过滤无效值 sst_tmp(sst_tmp < -999 | sst_tmp > 100) = NaN; % 累积到三维数组 sst_all = cat(3, sst_all, sst_tmp); end % 绘图循环 [lon_grid, lat_grid] = meshgrid(lon, lat); for i = 1:nFiles figure('Visible', 'off', 'Position', [100 100 1200 600]); pcolor(lon_grid, lat_grid, squeeze(sst_all(:, :, i))); shading interp; caxis([0 32]); colormap(jet); colorbar; title(['SST Field - Day ', num2str(i)], 'FontSize', 14); saveas(gcf, ['sst_day_', num2str(i, '%03d'), '.png']); close(gcf); % 关图防止内存堆积 end这里sst_all = cat(3, sst_all, sst_tmp)是逐文件拼接时间维,squeeze去掉单个维度后传给pcolor。figure('Visible', 'off')让图形不弹出窗口,批量运行时可以显著减少界面渲染的 CPU 时间。close(gcf)必须在循环内执行,否则每生成一张图就占一份内存,跑到几十张图时 MATLAB 可能直接卡死。
4.2 色标、透明掩膜与刻度标签的精细控制
出图质量往往靠几个细节拉开差距。第一是色标范围,caxis手动指定后,不同日期的图之间才能横向对比,否则每张图自动缩放到自己的最大最小值,颜色深浅失去可比性。第二是 NaN 区域的处理,MATLAB 默认把 NaN 画成背景色,如果背景是白色而陆地区域需要单独显示,可以叠加一层掩膜:
land_mask = isnan(sst_2d); hold on; pcolor(lon_grid, lat_grid, double(land_mask)); shading flat; colormap(gca, [0.8 0.8 0.8]); % 灰色填充陆地第三是坐标轴刻度标签,尤其当经纬度不是从 0 开始时,要手动设置XTick和XTickLabel,避免出现0.5°E这种不专业的标记。这里还有一个常见误用:pcolor和imagesc的坐标轴方向不同,imagesc默认 y 轴向下,数据画出来是上下翻转的,很多人第一次用imagesc画海温图发现赤道跑到上面去了,就是这个原因。
4.3 常见错误与排查对照
| 错误类型 | 现象 | 原因分析 | 处理方案 |
|---|---|---|---|
| 维度不匹配 | pcolor 报尺寸错误 | lon/lat 长度与 sst 行列不一致 | 打印size检查后用transpose调整 |
| 全图同色 | 图像一片红或一片蓝 | 数据全为 NaN 或色标范围过大 | min/max检查数据;手动设置caxis |
| 图像反了 | 赤道在上方 | imagesc的坐标轴反向 | 用pcolor或set(gca, 'YDir', 'normal') |
| 内存不足 | 批量导入时 Out of Memory | 三维数组一次读入 | 改用循环读取或datastore分块 |
| 经纬度错位 | 海陆位置明显偏移 | 坐标维度是 [lon, lat] 但数据维度是 [lat, lon] | ncinfo查看变量名和维度定义 |
排查时第一步永远是看size,第二步是看坐标范围,第三步才是画图。很多人直接画图,一旦出问题就从头开始找,效率很低。
5. 出图效果验证与隐藏坑:用一行命令确认结果可用
最后一章落到验证和排查上。绘制完 sst 图,不要只看“有图出来”就认为任务完成,至少要验证三点:坐标零点是否在预期位置、色标是否跨了合理物理区间、数据缺失比例是否异常。下面给出几个常用的验证片段。
% 1. 验证经纬度范围和分辨率 assert(abs(lon(1) - 0) < 1 || abs(lon(1) - 0.125) < 0.1, 'lon起点异常'); assert(length(lon) == 1440, '经度网格宽度不符'); assert(length(lat) == 720, '纬度网格高度不符'); % 2. 验证无效值占比 nan_ratio = sum(isnan(sst_2d(:))) / numel(sst_2d); fprintf('NaN ratio: %.2f%%\n', nan_ratio * 100); % 3. 验证物理合理性 valid = sst_2d(~isnan(sst_2d)); assert(min(valid) > -2 && max(valid) < 35, '海温超出物理范围');assert系列在批处理脚本里特别有用:任何一个文件读取出错,程序会在这一行直接停下来,并报出具体是哪个文件出了问题,而不是画出一堆错误图之后才发现。nan_ratio超过 30% 时,大概率是陆地掩膜没有正确识别,或者是读取时维度偏移导致大部分数据落在陆地上。低于 5% 则可能数据本身没有掩膜,此时要结合《卫星海洋.doc》里的数据说明确认是否有陆地标记变量。
另一个容易忽略的坑是byte order。部分早期卫星数据文件是 big-endian 存储,而现代 PC 是 little-endian,直接读取会得到数量级离谱的数字(比如 1e30 或 -3.4e38)。用netcdf.open时可以指定'FORMAT_CLASSIC'或'FORMAT_64BIT',但更可靠的做法是读取后先打印几个角点的值,如果出现1.0e+30这类特征值,直接用typecast或swapbytes做字节序变换。这个坑很少被人提前提到,因为大多数新版 NetCDF 文件已经统一为 little-endian,但遇到老数据时依旧会踩中。
如果只是快速确认一张图的数据正确性,可以把下面这行命令放在绘图之前:
assert(isequal(size(sst_2d), [length(lat) length(lon)]), '维度顺序不是 lat x lon');这条断言的价值在于把“读数据”和“画图”解耦:程序能跑通不代表数据是对的,数据能显示不代表坐标是对的,坐标对了才到画图环节。做卫星数据处理,按这个顺序排错,浪费的时间最少。
本文还有配套的精品资源,点击获取