MATLAB地图绘制全攻略:从数据导入到专题制图

📅 2026/7/29 5:09:46 👁️ 阅读次数 📝 编程学习
MATLAB地图绘制全攻略:从数据导入到专题制图

1. 项目概述:为什么我们需要用MATLAB画地图?

在地理信息分析、气象研究、城市规划、环境监测乃至商业选址等众多领域,地图可视化都是不可或缺的一环。你可能用过ArcGIS、QGIS这类专业GIS软件,也可能接触过Python的Basemap或Cartopy库。但如果你恰好身处一个以MATLAB为核心计算环境的团队或实验室,或者你的数据处理、模型仿真流程本身就构建在MATLAB之上,那么,直接在MATLAB环境中完成地图的绘制与叠加分析,无疑能极大提升工作效率,避免数据在不同平台间导入导出的繁琐与损耗。

“MATLAB绘制地图”这个需求,背后远不止是画几条线、填几个色块那么简单。它涉及到几个核心痛点:第一,如何获取并导入正确的地理数据?第二,如何将你的数据(比如温度场、人口密度、站点位置)准确地“贴”到地图的正确位置上?第三,如何控制地图的投影方式,使得在高纬度或大范围区域展示时不会严重失真?第四,如何让最终出图既专业美观,又符合学术出版或项目报告的要求?

我见过不少朋友,一开始试图用plot函数画个经纬度网格就当作地图,结果在展示实际地理数据时漏洞百出。也见过有人因为不熟悉地图工具箱(Mapping Toolbox)的调用逻辑,被复杂的参数设置劝退。这篇教程的目的,就是帮你系统性地打通从零到一的整个流程,不仅告诉你每一步怎么操作,更会解释清楚背后的地理信息学原理和MATLAB的实现逻辑。无论你是要画一张简单的中国省级行政区划图,还是要叠加全球海温异常数据,甚至是制作动态的台风路径图,这里面的核心方法论都是相通的。

2. 核心工具箱与数据准备:你的“武器库”和“弹药”

工欲善其事,必先利其器。在MATLAB中绘制专业地图,主要依赖两个核心:Mapping Toolbox正确格式的地理数据

2.1 认识Mapping Toolbox:不仅仅是画图

首先,确保你的MATLAB安装了Mapping Toolbox。你可以通过命令ver在命令行查看。这个工具箱提供了一整套用于处理地理数据、执行地图投影和进行地图可视化的函数。它与MATLAB基础的图形系统深度集成,这意味着你可以像操作普通plot图形一样,去设置地图的轴属性、添加颜色栏、图例,并享受MATLAB强大的交互式图形界面。

Mapping Toolbox的核心思想是“地图轴(Map Axes)”。与普通的笛卡尔坐标轴不同,地图轴理解经纬度坐标,并能根据你指定的投影方式,将球面上的点映射到二维平面上。创建地图轴的关键函数是axesmworldmapaxesm功能更强大、更底层,允许你精细控制几乎所有投影参数;worldmap则更便捷,能快速创建一个适合指定地理区域的地图。

2.2 地理数据的三大来源与处理

没有数据,再好的工具箱也是巧妇难为无米之炊。地理数据主要分为三类:矢量数据(点、线、面,如国界、海岸线、道路)、栅格数据(网格化的数值矩阵,如高程、温度、土地利用类型)和属性数据(与地理要素相关的信息,如城市名称、人口)。

1. 内置与在线数据:MATLAB Mapping Toolbox自带了一些基础数据集,例如:

  • coastlines: 全球海岸线数据(矢量线)。
  • usastateshi.shp: 美国州界数据(Shapefile格式)。
  • 通过webmapwmsread函数,可以访问一些在线的Web地图服务(WMS),获取卫星影像、地形图等栅格底图。这对于获取最新底图非常方便。

2. 标准格式文件导入:这是最主流的方式。MATLAB可以直接读取多种GIS标准格式:

  • Shapefile (.shp): 最常用的矢量数据格式。使用shaperead函数读取,它会返回一个结构体数组,每个元素包含几何信息(X/Y坐标)和属性字段。
    S = shaperead('china_provinces.shp'); % S是一个结构体数组,每个元素代表一个省份 % S(1).X, S(1).Y 包含了第一个省份多边形的边界坐标 % S(1).NAME 可能包含省份名称属性
  • GeoTIFF (.tif): 最常用的栅格数据格式,它内部嵌入了地理坐标信息。使用readgeoraster函数读取,它能同时获取图像数据矩阵和地理参考对象。
    [A, R] = readgeoraster('dem.tif'); % A 是高程数据矩阵 % R 是地理参考对象,包含了像素大小、左上角坐标等信息
  • NetCDF (.nc) / GRIB: 常用于气候、海洋模型输出。使用ncread等函数读取。

3. 自制或转换数据:你的实验数据、模拟结果,通常是一组经纬度和对应的数值。你需要将其组织成MATLAB可以识别的格式。例如,一个包含站点信息的表格:

% 假设有三个气象站 lats = [39.9, 31.2, 23.1]; % 纬度 lons = [116.4, 121.5, 113.3]; % 经度 temp = [25.5, 28.1, 30.3]; % 温度值 station_names = {'Beijing', 'Shanghai', 'Guangzhou'};

注意:在导入Shapefile时,务必注意坐标系的统一。全球数据常用WGS84坐标系(经纬度)。如果你的数据是其他投影坐标系(如UTM),而你又用经纬度地图轴去显示,位置就会错乱。此时可能需要先用projinv函数进行坐标反变换。

3. 从零开始绘制第一张专业地图

理论说再多,不如动手画一张。我们以一个经典需求为例:绘制一张带有中国省界、主要河流,并标注几个主要城市的地图。

3.1 步骤一:创建地图轴与设置投影

我们选择使用axesm来获得完全的控制权。这里我们选择“墨卡托投影”(Mercator),它是一种等角圆柱投影,在低纬度地区形状保持较好,常用于网络地图。

figure('Position', [100, 100, 800, 600]) % 创建一个指定大小的图形窗口 ax = axesm('mercator', ... % 投影类型 'MapLatLimit', [15 55], ... % 地图的纬度显示范围 [南界, 北界] 'MapLonLimit', [70 135], ... % 地图的经度显示范围 [西界, 东界] 'Frame', 'on', ... % 显示地图框 'Grid', 'on', ... % 显示经纬网格 'MeridianLabel', 'on', ... % 标注经度刻度 'ParallelLabel', 'on', ... % 标注纬度刻度 'MLabelLocation', 20, ... % 经度标注间隔20度 'PLabelLocation', 10, ... % 纬度标注间隔10度 'MLabelParallel', 'south', ... % 经度标签放在南边 'PLabelMeridian', 'west'); % 纬度标签放在西边 title('中国区域地图')

运行这段代码,你会看到一个带有经纬网格的空白地图框,范围覆盖了中国主要区域。MapLatLimitMapLonLimit是至关重要的参数,它们决定了地图的“视野”。设置不当,你的数据可能会落在视野之外而看不到。

3.2 步骤二:添加矢量边界(海岸线、国界、省界)

现在,我们把地理要素“叠”上去。首先添加全球海岸线(内置数据):

% 加载内置海岸线数据 load coastlines % 这会加载变量 coastlat 和 coastlon % 在地图轴上绘制海岸线 plotm(coastlat, coastlon, 'k-', 'LineWidth', 0.5) % ‘k-’代表黑色实线

接下来,假设你有一个名为china_provinces.shp的Shapefile文件,包含了中国省界。

provinces = shaperead('china_provinces.shp'); % 使用geoshow函数在地图轴上显示矢量数据。geoshow是专门用于在地图轴显示地理数据的函数。 geoshow(ax, provinces, 'FaceColor', 'none', 'EdgeColor', [0.3 0.3 0.3], 'LineWidth', 0.8); % FaceColor ‘none’ 表示多边形内部不填充 % EdgeColor 设置边界线颜色

用同样的方法,你可以添加河流、湖泊等任何矢量图层。关键在于使用geoshow而非普通的plot,因为geoshow能自动处理数据与地图轴投影的匹配。

3.3 步骤三:添加点数据与标注

现在标注北京、上海、广州三个城市。

city_lat = [39.9, 31.2, 23.1]; city_lon = [116.4, 121.5, 113.3]; city_name = {'北京', '上海', '广州'}; % 在地图轴上绘制散点 scatterm(city_lat, city_lon, 80, 'r', 'filled', '^') % 红色、填充、三角形标记,大小80 % 添加文本标注 textm(city_lat, city_lon, city_name, ... 'HorizontalAlignment', 'left', ... % 文本水平对齐方式(相对于坐标点) 'VerticalAlignment', 'bottom', ... % 文本垂直对齐方式 'FontSize', 10, ... 'Color', 'b', ... 'FontWeight', 'bold');

scattermtextmscattertext的地图轴版本,它们接受经纬度作为坐标输入。

3.4 步骤四:美化与输出

一张专业的地图离不开细节的美化。

% 设置网格线样式 setm(ax, 'GLineStyle', ':', 'GLineWidth', 0.5, 'GColor', [0.7 0.7 0.7]) % 设置字体 set(gca, 'FontName', 'Arial', 'FontSize', 11) % 添加指北针和比例尺(Mapping Toolbox 提供) northarrow('latitude', 50, 'longitude', 130, 'scaleratio', 0.05) % 在指定经纬度位置添加指北针 scaleruler on % 在左下角添加比例尺 % 调整图形输出质量 set(gcf, 'Color', 'w'); % 设置图形背景为白色 exportgraphics(gcf, 'china_map.png', 'Resolution', 300) % 以300DPI导出PNG图片

至此,一张要素完整、样式规范的中国区域地图就生成了。这个过程体现了地图绘制的基本工作流:创建地图轴 -> 添加底层矢量要素 -> 叠加业务数据 -> 美化与输出

4. 进阶技巧:处理栅格数据与专题制图

很多时候,我们的数据不是简单的点线面,而是一张覆盖整个区域的“图片”,比如数字高程模型(DEM)、卫星反演的海表温度、模拟的降水场。这就是栅格数据。

4.1 加载与显示栅格数据(以DEM为例)

假设我们有一个GeoTIFF格式的DEM文件dem_china.tif

[A, R] = readgeoraster('dem_china.tif'); % A是高程值矩阵,R是关联的地理参考对象

直接使用geoshow显示栅格数据:

figure; ax2 = axesm('mercator', 'MapLatLimit', R.LatitudeLimits, 'MapLonLimit', R.LongitudeLimits); % 使用geoshow显示栅格,并指定颜色映射 h = geoshow(ax2, A, R, 'DisplayType', 'texturemap'); colormap(ax2, 'terrain') % 使用‘terrain’配色方案,适合高程 colorbar('southoutside') % 在图形下方添加颜色栏 title('中国区域数字高程模型 (DEM)')

‘texturemap’显示模式会将数据矩阵像贴图一样渲染在地图表面。这里R对象至关重要,它告诉geoshow矩阵A中每一个像素所对应的真实地理位置。

4.2 创建分级设色图(Choropleth Map)

这是另一种常见的专题地图,用不同的颜色填充行政区划(如各省),颜色深浅代表某个统计指标(如GDP、人口密度)的大小。

假设我们有一个结构体数组provinces(来自Shapefile),并且我们有一个对应的数值数组gdp_data(长度与省份数相同),代表各省的GDP。

% 假设 provinces 已从 shaperead 加载 % 假设 gdp_data 是一个数值向量 % 1. 对GDP数据进行分等级 num_classes = 5; % 分为5级 [~, edges] = histcounts(gdp_data, num_classes); % 获取分级边界 % 2. 为每个省份确定其所属的等级 province_class = discretize(gdp_data, edges); % 3. 定义一个颜色映射(例如,从浅黄到深红) cmap = [linspace(1, 0.9, num_classes)', linspace(1, 0.2, num_classes)', linspace(0.8, 0.1, num_classes)']; % 4. 绘制地图,按等级着色 figure; ax3 = axesm('mercator', 'MapLatLimit', [15 55], 'MapLonLimit', [70 135]); hold(ax3, 'on') for i = 1:length(provinces) face_color = cmap(province_class(i), :); % 根据等级获取颜色 geoshow(ax3, provinces(i), 'FaceColor', face_color, 'EdgeColor', 'k', 'LineWidth', 0.5); end colormap(ax3, cmap) caxis([1, num_classes]) % 设置颜色轴范围 colorbar('Ticks', 1:num_classes, 'TickLabels', compose('Level %d', 1:num_classes)) title('中国各省GDP分级设色图')

这个例子展示了如何将属性数据与几何数据结合,生成具有统计意义的专题地图。关键在于discretize函数对连续数据进行离散化分级,然后为每个几何要素(省份多边形)分配对应的颜色。

4.3 叠加多层数据:矢量与栅格的融合

在实际分析中,我们经常需要将多种数据叠加在一张图上。例如,在DEM地形图上叠加省界和城市点。

figure; ax4 = axesm('mercator', 'MapLatLimit', R.LatitudeLimits, 'MapLonLimit', R.LongitudeLimits); % 先显示栅格底图(DEM) geoshow(ax4, A, R, 'DisplayType', 'texturemap'); colormap(ax4, 'gray') % 先用灰度显示地形 hold(ax4, 'on') % 再叠加省界矢量(半透明,突出显示) geoshow(ax4, provinces, 'FaceColor', 'none', 'EdgeColor', 'cyan', 'LineWidth', 1.5, 'FaceAlpha', 0); % 最后叠加城市点 scatterm(city_lat, city_lon, 100, 'r', 'filled') % 添加独立的颜色栏用于地形(如果需要) % 注意:当叠加多个使用不同颜色映射的图层时,管理颜色栏会复杂一些,通常只为最主要的图层添加。

这里的关键是使用hold on来保持当前地图轴,以便在其上添加新的图形对象。叠加顺序很重要,通常先画底图(栅格或大范围面),再画上层细节(线、点)。

5. 常见问题、调试技巧与性能优化

即使按照教程操作,你也可能会遇到各种“坑”。这里分享一些我踩过之后总结的经验。

5.1 数据位置错乱或显示空白

这是最常见的问题,根本原因几乎都是坐标系不匹配

  • 症状:数据点跑到非洲、大西洋,或者根本看不见。
  • 排查
    1. 检查数据坐标值:用dispwhos命令查看你的经纬度数据。经度范围通常是[-180, 180]或[0, 360],纬度范围是[-90, 90]。如果你的数据是[500000, 3000000]这种大数字,那很可能是投影坐标(如米制单位)。
    2. 检查地图轴范围:确认MapLatLimitMapLonLimit是否包含了你的数据范围。可以暂时把它们设得宽一些(如全球范围[-90 90],[-180 180])看看数据是否出现。
    3. 检查投影:某些投影对数据范围有特殊要求。例如,墨卡托投影在极点附近会无限放大,通常不用于显示包含极点的区域。如果你的数据包含南北纬80度以上地区,考虑使用‘eqdconic’(等距圆锥投影)或‘pcarree’(等距圆柱投影,即简单的经纬度网格)。
  • 解决:如果数据是投影坐标,你需要使用projinv函数将其转换为经纬度坐标,前提是你知道原始投影的PROJ字符串或EPSG代码。例如,如果数据是UTM 50N带(EPSG:32650):
    proj = projcrs(32650); % 创建投影坐标系对象 [lat, lon] = projinv(proj, x_utm, y_utm); % 反投影得到经纬度

5.2 图形渲染慢或卡死

处理高分辨率栅格数据(如30米全球DEM)或复杂的矢量数据(如精细的海岸线)时,MATLAB图形渲染可能会非常慢。

  • 优化策略
    1. 数据重采样:对于预览性质的绘图,不必使用最高分辨率数据。可以使用imresize函数对栅格数据进行降采样。
      scale_factor = 0.2; % 缩小到原尺寸的20% A_small = imresize(A, scale_factor, 'bilinear'); % 注意:需要同步调整地理参考对象R,通常像素大小会变为原来的1/scale_factor倍 R_small = R; R_small.RasterSize = size(A_small); R_small.CellExtentInWorldX = R.CellExtentInWorldX / scale_factor; R_small.CellExtentInWorldY = R.CellExtentInWorldY / scale_factor;
    2. 简化矢量数据:过于复杂的多边形边界(如拥有数万个节点的海岸线)会严重拖慢速度。可以使用reducem函数进行道格拉斯-普克算法简化。
      [lat_simple, lon_simple] = reducem(coastlat, coastlon, 0.01); % 0.01是容差,值越大简化越厉害,细节丢失越多
    3. 分块绘制:对于超大型区域,可以考虑分块读取和绘制,或者只绘制当前感兴趣的区域(通过设置MapLatLimitMapLonLimit)。

5.3 颜色映射与标注不匹配

在专题图中,颜色栏的标注是“等级1-5”,但你想显示实际的数值范围(如“0-100亿”)。

  • 解决:颜色栏的TicksTickLabels属性可以自定义。在上面的分级设色图例子中,我们可以这样修改:
    % 假设 edges 是之前histcounts得到的分级边界 [e1, e2, e3, e4, e5, e6] tick_positions = 1:num_classes; % 颜色条上的刻度位置(1到5) tick_labels = cell(1, num_classes); for i = 1:num_classes tick_labels{i} = sprintf('%.1f-%.1f', edges(i), edges(i+1)); % 格式化为范围字符串 end colorbar('Ticks', tick_positions, 'TickLabels', tick_labels)
    更直接的方法是,不使用离散化的等级,而将连续值直接映射到颜色。这时可以使用scattermgeoshow(对点数据)的‘CData’参数,并为颜色栏设置对应的数值范围。
    % 对点数据绘制,颜色代表连续的GDP值 scatterm(province_centroids_lat, province_centroids_lon, 200, gdp_data, 'filled') colormap('parula') colorbar % colorbar会自动根据gdp_data的范围进行标注

5.4 导出图片分辨率或尺寸不满意

使用exportgraphicsprint函数导出时,发现图片模糊或尺寸不对。

  • 经验
    • exportgraphics是R2020a后推荐的方式,‘Resolution’参数设置DPI(每英寸点数),300是出版常用标准。
    • 图形窗口的尺寸(通过figure('Position', [x, y, width, height])设置)决定了导出图片的“画布”大小。如果你想导出宽度为15厘米的图片,可以这样计算:
      width_cm = 15; height_cm = 10; % 假设高宽比 dpi = 300; width_pixel = round(width_cm / 2.54 * dpi); % 厘米转英寸再乘DPI height_pixel = round(height_cm / 2.54 * dpi); figure('Position', [100, 100, width_pixel, height_pixel], 'Units', 'pixels'); % ... 绘制地图 ... exportgraphics(gcf, 'output.png', 'Resolution', dpi);
    • 对于需要透明背景的图片(如用于叠加在PPT或报告上),可以使用exportgraphics(gcf, 'output.png', 'Resolution', 300, 'BackgroundColor', 'none')

地图绘制是一个将数据、投影、视觉设计结合的过程。MATLAB的Mapping Toolbox提供了强大而灵活的工具链,但掌握它需要理解一些基本的地理信息概念。最好的学习方式就是找一个你感兴趣的小项目(比如绘制你家乡的地形图,或者可视化某次台风过程的降雨数据),从数据准备开始,一步步实现,遇到问题就查阅文档或搜索社区。当你成功产出第一张让自己满意的专业地图时,你会发现这套工具能为你科研或工作中的空间数据分析带来极大的便利。