- 积分
- 3799
- 贡献
-
- 精华
- 在线时间
- 小时
- 注册时间
- 2015-7-6
- 最后登录
- 1970-1-1
|
登录后查看更多精彩内容~
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
本帖最后由 Lighting 于 2020-5-17 16:40 编辑
写在最前:
最近重新整理了一下白化的程序,把支持m_map工具箱的功能完善了一下。内容都发在 https://i-lightning.cn/2020/05/matlab_mask/ 这里了,代码也都开源了。此外还加入了一个简单的函数m_mapshow,支持添加任意shapefile文件到地图中。
==================================================
感谢那些对程序的完善提出宝贵意见的朋友。
@kc121 对程序的进一步修改提出了宝贵的意见。在此表示由衷的感谢!
==================================================
最近看到python板块提供了不少对地图进行完美白化的方法。
比如@平流层的萝卜 python完美白化
@非对称 利用scipy和sympy扩大三角网格形成的轮廓
也看到了MATLAB版对白化功能的讨论,也给出了一些方法。
比如 @斥鷃 提供的 matlab中地图边界与掩膜的实现
@raul928 提供的 也提供一个去地图外边界的方法
上述两种方法都能很好的对地图进行白化。
本来想将@非对称 的方法用matlab实现的,大部分代码已经写好,但有一点不是太明白就暂时放置在一旁了。然后在无意之间发现了mapshow函数利用 facecolor属性可以设置区域内白化,经过一番探索,便有了下面的白化方法。
1) 如果需要白化某一区域内的话,只需要使用mapshow函数即可,以shapefile文件为例
- z = peaks(1000);
- lon = [60 150];
- lat = [10 60];
- [LON,LAT] = meshgrid(linspace(lon(1),lon(2),1000), linspace(lat(1), lat(2),1000));
- c = contourf(LON,LAT,z,'linestyle', 'none');
- mapshow('E:\MATLAB\shp\bou2_4p.shp','displaytype','polygon', 'facecolor','w')
复制代码
mapshow 的 displaytype 属性默认为 'line',这里要设置为'polygon'。
2) 白化区域外部分
- function maskMap(shapefile, masktype, varargin)
- % 对所绘制图形进行白化
- % 输入参数:
- % shapefile : shapefile文件。 字符串型或元胞型
- % 为元胞数组时可通过指定多个省份的shp文件进行白化。
- % 比如要白化江苏省,江西省,黑龙江省。使用元胞数组白化多个省份,需要手动添加中国边界地图
- % shapefile = {'path\江苏省.shp', 'path\江西省.shp', 'path\黑龙江省.shp'}
- % 其中path为shp文件所在路径
- % masktype : 逻辑值。
- % true : 对区域外进行白化
- % 可选参数:
- % longitudes : 图形的经度范围,二元素向量。 数值型。
- % latitudes : 图形的纬度范围,二元素向量。 数值型。
- % multiprovinces : 元胞数组。
- % 用于指定要白化的多个区域。使用此参数需要shapefile为字符型且要包含省份名称
- % 此处建议大家使用 bou2_4p.shp 进行白化
- % 比如要白化江苏省,江西省,黑龙江省,则
- % multipro = {'江苏省','江西省','黑龙江省'}
- % reversemask : 指定要白化的是 multiprovinces 元胞中指定的区域还是
- % 元胞中指定的区域以外的部分。 逻辑值。
- % true : 表示白化元胞中指定的区域。 默认值。
- % false : 表示白化元胞中指定区域以外的部分。
- % false : 对区域内进行白化
- % 共享可选参数值对:
- % facecolor : 用于指定白化区域的颜色。 默认为白色。 字符型或数值型。
- % 取值为 'none'时,不起作用。数值型时为RGB三元组。
- % edgecolor : 指定的边界线的颜色。 默认为黑色。 字符型或数值型。同上。
- % linewidth : 边界线宽度。 默认为1. 数值型。
- %%
- p = inputParser;
- validShape = @(x) ischar(x) || iscell(x);
- validLLFcn = @(x) isvector(x) && length(x) == 2;
- validFaceFcn = @(x) (ischar(x) && ~strcmp(x, 'none')) || (isvector(x) && length(x) == 3);
- validEdgeFcn = @(x) ischar(x) || (isvector(x) && length(x) == 3);
- defaultLon = []; defaultLat = []; defaultProv = {}; defaultReve = true;
- defaultFace = 'w'; defaultLine = 'k'; defaultWidth = 1;
- addRequired(p, 'shapefile', validShape)
- addRequired(p, 'masktype', @islogical);
- addParameter(p, 'longitudes', defaultLon, validLLFcn);
- addParameter(p, 'latitudes', defaultLat, validLLFcn);
- addParameter(p, 'multiprovinces', defaultProv, @iscell);
- addParameter(p, 'reversemask', defaultReve, @islogical);
- addParameter(p, 'facecolor', defaultFace, validFaceFcn)
- addParameter(p, 'edgecolor', defaultLine, validEdgeFcn)
- addParameter(p, 'linewidth', defaultWidth, @isnumeric)
- parse(p, shapefile, masktype, varargin{:});
- if p.Results.masktype
- longitudes = p.Results.longitudes;
- latitudes = p.Results.latitudes;
- if isempty(longitudes) || isempty(latitudes)
- error('需指定经纬度范围!')
- end
- loninc = longitudes(1):longitudes(end);
- latinc = latitudes(1):latitudes(end);
- lone = ones(1,length(latinc)-2)*longitudes(end);
- lons = ones(1,length(latinc)-1)*longitudes(1);
- lats = ones(1,length(loninc)-1)*latitudes(1);
- late = ones(1,length(loninc)-2)*latitudes(end);
- LON = [loninc lone loninc(end:-1:1) lons NaN];
- LAT = [lats latinc late latinc(end:-1:1) NaN];
- [LON,LAT] = poly2cw(LON,LAT);
-
- if ischar(p.Results.shapefile)
- s = shaperead(p.Results.shapefile);
- if isfield(s,'NAME')
- allprovs = {s.NAME}';
- allprovnum = length(allprovs);
- maskindex = zeros(allprovnum,1);
- elseif isfield(s,'NAME99')
- allprovs = {s.NAME99}';
- allprovnum = length(allprovs);
- maskindex = zeros(allprovnum,1);
- end
- long = [s.X]; lati = [s.Y];
- % 获取指定的多个省份的边界点
- if ~isempty(p.Results.multiprovinces)
- provinces = p.Results.multiprovinces;
- pronum = length(p.Results.multiprovinces);
- for ii = 1:pronum
- sindex = strfind(allprovs, provinces{ii});
- if any(cell2mat(sindex))
- for jj = 1: allprovnum
- if isempty(sindex{jj}) ; sindex{jj} = 0; end;
- end
- maskindex = maskindex | cell2mat(sindex);
- else
- error('所选省份 %s 不在指定的shp文件中或省份名错误!',provinces{ii})
- end
- end
- if p.Results.reversemask
- maskindex = ~maskindex; % 指定省份以外的省份在shapefile中的索引
- end
- LON = [LON s(maskindex).X];
- LAT = [LAT s(maskindex).Y];
- end
- elseif iscell(p.Results.shapefile)
- s = shaperead(p.Results.shapefile{1});
- long = s.X; lati = s.Y;
- for jj = 2:length(p.Results.shapefile)
- s = shaperead(p.Results.shapefile{jj});
- [long, lati] = polybool('union',long,lati, s.X, s.Y);
- end
- end
-
- [xc, yc] = polybool('xor', LON,LAT , long, lati);
- [f, v] = poly2fv(xc, yc);
- %此处edgecolor必须为none,否则facecolor属性值将失效
- patch('Faces', f, 'Vertices', v, 'FaceColor', p.Results.facecolor, 'edgecolor', 'none');
- mapshow(long ,lati, 'color', p.Results.edgecolor, 'linewidth', p.Results.linewidth);
- set(gca, 'xlim', p.Results.longitudes,'ylim', p.Results.latitudes)
- else
- if ischar(p.Results.shapefile)
- mapshow(p.Results.shapefile,'displaytype','polygon','facecolor',p.Results.facecolor,'edgecolor',p.Results.edgecolor,'linewidth',p.Results.linewidth)
- else
- error('仅支持单文件白化!')
- end
- end
- end
复制代码
maskMap函数提供了一个逻辑值选项用于选择区域内白化还是区域外白化。如果是区域内白化,只需要提供shapefile和masktype两个参数即可,如果选择区域外白化,除上述两个参数之外,还需要提供‘longitudes’ 和 'latitudes' 两个参数,调用方式和matlab内置函数调用方式类似。
参考@平流层的萝卜 的python完美白化的帖子,也支持了白化几个省份的部分。
无论是白化区域内还是白化区域外,均可以通过设置 facecolor, edgecolor, linewidth等属性值设置 白化区域的颜色,边界线颜色,线宽等。鉴于白化时通常只需要改变这几个属性,目前仅支持这几个属性。
下图是放大之后的,可以看出已经 完美白化了,没有任何锯齿存在!
直接调用上述函数即可。使用精确到县级的shp文件。示例代码如下:
- z = peaks(1000);
- lon = [60 135];
- lat = [10 60];
- [LON,LAT] = meshgrid(linspace(lon(1),lon(2),1000), linspace(lat(1), lat(2),1000));
- c = contourf(LON,LAT,z,'linestyle', 'none');
- maskMap('E:\MATLAB\shp\BOUNT_poly.shp',true,'lon',lon,'lat',lat,'linewidth',0.5, 'edgecolor','b')
复制代码 可以看出,使用县级文件进行白化也毫无问题!
=================================================
2016.9.8 解决调用 maskMap 函数白化区域内出错的问题。程序已经更改,复制替换源程序即可。
2016.9.9 解决白化时设置edgecolor 属性值为非'none'值时出现一条直线的问题。
2016.9.14 解决设置等值线线型后无法白化区域外等值线的问题(2016a版本可行,2014a版本不可行。应该是语法有更新导致);同时通过更改白化方法解决了设置线宽过大时或颜色不为黑色时,坐标轴会出现粗线或多颜色相间的问题。
2016.9.23 释放使用RGB三元组设置facecolor 和 edgecolor 属性值。
把重新编辑后的内容以PDF文档的形式上传,其中包括了对函数的详细解释,以及最新更新(当然最新的程序也已更新在 2)中)。白化算法有更新,但是文档内程序未更新,不过用法是相同的。
注意:如果使用过程中出现以下错误提示,说明你使用的MATLAB版本较低,请更换到更高版本,建议更新到2015版之后。
- Undefined function 'addParameter' for input arguments of type 'inputParser'.
复制代码
==== 插播更新利用m_map工具箱生成海岸线数据进行白化 ====
最近看了一下m_map工具箱中关于生成海岸线的部分,发现可以自定义常用的绘图区,结合上面的白化方法,便想着利用m_map工具箱和 gshhs 高精度地形数据制作用于白化的数据。
废话不多说,直接上程序。
- lon = [60 150]; lat = [10 60];
- FILNAME = 'private/gshhs_i.b';
- h = figure;
- m_proj('mercator', 'longitudes', lon, 'latitudes', lat)
- m_grid('box','on')
- % mu_coast一般情况下是不可调用的,可把private文件夹下的函数复制到另一个文件夹下
- % 把该文件夹加入到搜索路径中
- mu_coast('i',FILNAME,'tag','m_gshhs_i');
- m_gshhs_i('save','test') % 保存 ncst , area ,k 数据到 test.mat 文件中
- set(h , 'visible','off') % 不显示图形。如果想显示图形到窗口可注释此语句
复制代码
上面这部分用于生成绘图海岸线数据。
下一步就是调用函数生成用于白化的海岸线数据。
- function coast = creatCoast(longitudes,latitudes, ncst)
- % 生成用于掩膜的经纬度数组
- % 输入参数:
- % longitudes : 经度起止坐标. 二元素向量.
- % latitudes : 纬度起止坐标. 二元素向量.
- % ncst : 由m_gshhs_* 函数生成的高精度经纬度坐标信息
- % N X 2 数组。 其中第一列为经度信息
- % 第二列为纬度信息
- % 输出参数:
- % coast 为结构体变量. 包含了用于白化的经纬度信息
- % coast.long 存储的是经度信息
- % coast.lat 存储的是纬度信息
- %%
- validateattributes(longitudes,{'double'}, {'vector','numel',2})
- validateattributes(latitudes,{'double'}, {'vector', 'numel',2})
- validateattributes(ncst,{'double'}, {'ncols',2})
- loninc = longitudes(1):longitudes(end);
- latinc = latitudes(1):latitudes(end);
- lone = ones(1,length(latinc)-2)*longitudes(end);
- lons = ones(1,length(latinc)-1)*longitudes(1);
- lats = ones(1,length(loninc)-1)*latitudes(1);
- late = ones(1,length(loninc)-2)*latitudes(end);
- LON.index = [loninc lone loninc(end:-1:1) lons NaN];
- LAT.index = [lats latinc late latinc(end:-1:1) NaN];
- LON.index = [LON.index ncst(:,1)'];
- LAT.index = [LAT.index ncst(:,2)'];
- coast.long = LON.index;
- coast.lat = LAT.index;
- end
复制代码
注意: 从头到尾所使用的经纬度的起止范围要是相同的,不要频繁更改经纬度起止范围。
- z = peaks(1000);
- lon = [60 150];
- lat = [10 60];
- [LON,LAT] = meshgrid(linspace(lon(1),lon(2),1000), linspace(lat(1), lat(2),1000));
- figure
- contourf(LON,LAT,z,'linestyle' , 'none')
- load test.mat
- coast = creatCoast(lon,lat,ncst);
- % displaytype 要设置为 'polygon'
- mapshow(coast.long,coast.lat, 'Displaytype', 'polygon', 'facecolor', 'w', 'edgecolor', 'k', 'linewidth', 1);
复制代码
执行后会发现有一点问题!竟然凭空的多出一块出来!应该是自定义海岸线时,多了一部分,但是究竟是怎么回事暂时不清楚。
不用担心,解决这个问题很简单!只要执行以下语句限制坐标轴范围就行了!
- set(gca, 'xlim', lon, 'ylim', lat)
复制代码 除直接限制坐标轴范围外,还可以进行数据筛选,把超出范围部分找出,然后去除即可。
以上部分仅供参考。
========= 提供一种 MATLAB Central 中提到的一种白化方法 ==============
首先附上链接: ocean mask
文中提到的白化方法是利用MATLAB自带的coast.mat文件进行白化,主要是反转全球海岸线经纬度向量的顶点方向进行白化。尝试着反转中国的shp数据和gshhs数据进行白化,但是均以失败告终。如果大家有兴趣可以尝试一下。成功了的话欢迎科普
测试代码:
- coast = load('coast');
- z = peaks(1000);
- lon = [60 150];
- lat = [10 60];
- figure('Color','w')
- axesm('mercator','MapLatLimit',lat,'MapLonLimit', lon)
- axis off; framem on; gridm on; mlabel on; plabel on;
- setm(gca,'MLabelLocation',60)
- R=makerefmat('RasterSize',size(z),'Lonlim', lon,'Latlim',lat);
- contourfm(z,R,'linestyle','none')
- geoshow(flipud(coast.lat),flipud(coast.long),'DisplayType','polygon','facecolor','w')
复制代码
maskMap.pdf
(780.95 KB, 下载次数: 477)
|
评分
-
查看全部评分
|