Как визуализировать океанологические данные на карте в MATLAB
Сегодня мы рассмотрим несколько готовых рецептов для визуализации океанологических данных. Эта заметка именно с рецептами и не претендует на полноценное руководство, которое для Матлаба существует и достаточно объёмное (около 1000 страниц). Скачать его сегодня (в ноябре 2018 года) можно по ссылке.
В прошлой заметке мы с вами открыли netCDF файл в MATLAB, теперь настало время визуализировать данные на карте. Если в прошлый раз мы выдумали гипотетическое название файла, то сегодня откроем самые настоящие данные. Для примера скачаем файл с температурой поверхности океана (SST) с сайта https://oceancolor.gsfc.nasa.gov/
В нашем файле содержатся среднемесячные значения температуры поверхности воды за сентябрь 2018 года, если хотите точно такой же, то вот ссылка.
Теперь настало время выудить из этого файла данные о температуре:
sst = ncread('A20182442018273.L3m_MO_SST_sst_9km.nc','sst');
Полученную переменную sst легко визуализировать простыми средствами (как вы бы визуализировали любую другую матрицу), например через функцию imagesc:
imagesc(sst)
Если вас смущает, что матрица «лежит на боку», то можете переориентировать её, написав в командной строке:
sst = permute(sst, [2 1]);
(но это только если смущает, нас не смущает и мы не будем этого делать).
Да, на полученной картинке мы видим распределение температуры и очертания континентов, но картой такую визуализацию матрицы пока что совсем нельзя назвать. Что ж, идём дальше. А дальше нам понадобятся данные о широтах и долготах для нашей координатной сетки, поэтому «вынимаем» из файла и их тоже:
lat = ncread('A20182442018273.L3m_MO_SST_sst_9km.nc','lat');
lon = ncread('A20182442018273.L3m_MO_SST_sst_9km.nc','lon');
Теперь посмотрим на все наши переменные (либо взглянув в окно Workspace, либо, набрав в командной строке команду whos):
whos
Name Size Bytes Class
lat 2160x1 17280 double
lon 4320x1 34560 double
sst 4320x2160 74649600 double
Итого, у нас есть двумерная матрица sst и два одномерных вектора lat и lon. Однако, для построения карт нам нужно будет сделать из lat и lon двумерные матрицы такого же размера, что и sst. Дело в том, что функции, при помощи которых мы будем строить карты, требуют, чтобы все три матрицы были одинакового размера Т.е. для каждой ячейки матрицы sst должны существовать такие же ячейки в матрицах lat и lon, содержащие координаты нужной нам ячейки. Всё это можно сделать лёгким движением руки при помощи функции ndgrid:
[lon,lat]=ndgrid(lon,lat);
Обратите внимание, что, если вы переворачивали матрицу функцией permute (см. выше), то вам нужно будет поменять в коде lat и lon местами, вот так:
[lat,lon]=ndgrid(lat,lon);
Теперь начинаем строить саму карту и прежде всего нам нужна «подложка» с какой-нибудь картографической проекцией. На самом деле MATLAB позволяет делать это разными способами, мы используем пожалуй, самый интуитивно понятный:
worldmap('World')
В качестве картографической проекции для карты мира по умолчанию выбирается проекция Робинсона (‘robinson’), но это, конечно, можно будет поменять.
Что ж, подложка у нас есть, осталось нанести на неё наши данные. Сделаем мы это при помощи функции geoshow, в которой в качестве аргументов укажем наши три двумерные матрицы lat, lon и sst:
geoshow(lat,lon,sst)
Такая карта, состоящая только из белого и чёрного цветов нас вряд ли устроит. Дело в том, что у функции geoshow аргумент ‘displaytype’, отвечающий за тип построения, установлен по умолчанию на ‘image’. Этот тип нам подходит, пожалуй, меньше всего, поэтому мы поменяем его на ‘texturemap’, которые наиболее похож на функцию imagesc. Обратите внимание, что MATLAB поймёт, даже если мы вместо ‘texturemap’ напишем ‘texture’:
geoshow(lat,lon,sst,'displaytype','texture')
Можно сказать, что дело сделано, наши данные оказались на карте и, резюмируя, приведём здесь весь код, который нам для этого понадобился (включая открытие nc-файла)
sst = ncread('A20182442018273.L3m_MO_SST_sst_9km.nc','sst');
lat = ncread('A20182442018273.L3m_MO_SST_sst_9km.nc','lat');
lon = ncread('A20182442018273.L3m_MO_SST_sst_9km.nc','lon');
[lon,lat]=ndgrid(lon,lat);
worldmap('World')
geoshow(lat,lon,sst,'displaytype','texture')
Задача выполнена, однако очень формально и обычно этого не достаточно. Теперь давайте пофантазируем, какие ещё видоизменения полученной карты нам могут пригодится в работе. Прежде всего следует отметить, что океанологи чаще работают с отдельными районами, а не со всем Мировым океаном. Ограничить район достаточно просто, для этого функции worldmap нужно задать в качестве аргументов два вектора с пределами по широте и пределами по долготе.
worldmap([40 80],[-40 20])
geoshow(lat,lon,sst,'displaytype','texture')
Обратите внимание, что проекция поменялась, теперь она равнопромежуточную коническую (‘eqdconic’).
Глядя на такую карту, сложно понять, где очертания континентов и островов, а где пропуски в данных, так как и то и другое обозначается у нас в данных при помощи NaN. Нанести очертания берега можно разными способами и мы ещё рассмотрим их в дальнейших заметках. Сейчас же мы используем встроенный в MATLAB shp-файл с береговой линией, нас он вполне устроит:
worldmap([40 80],[-40 20])
geoshow(lat,lon,sst,'displaytype','texture')
geoshow('landareas.shp')
Теперь я сделаю ещё несколько видоизменений на свой вкус, прокомментировав сделанное прямо в коде:
worldmap([40 80],[-40 20])
geoshow(lat,lon,sst,'displaytype','texture')
%установим границы цветовой шкалы от -2 до 25 градусов:
caxis([-2 25])
%изменим цвет континентов на серый, указав три значения RGB:
geoshow('landareas.shp','FaceColor',[0.5 0.5 0.5])
%изменим цветовую карту на jet:
colormap jet
% добавим сбоку шкалу с цветами:
colorbar
Полученную карту можно раскритиковать за то, что невозможно отличить значения с низкой температурой от NaN (пропусков в данных). Самый простой способ избежать этого — выбрать другой тип отображения данных на карте (заменим ‘texturemap’ на ‘surface’):
worldmap([40 80],[-40 20])
geoshow(lat,lon,sst,'displaytype','surface')
caxis([-2 25])
geoshow('landareas.shp','FaceColor',[0.5 0.5 0.5])
colormap jet
colorbar
На данных картинках ‘texture’ не отличим от ‘surface’, но это потому что матрицы у нас слишком большие, если бы сетка будет не такой густой, то ‘texture’ выдаст нам изображение, состоящее из прямоугольников, а ‘surface’ создаст сглаженное изображение:
Всего в geoshow существуюет пять типов отображения: ‘surface’, ‘contour’, ‘mesh’, ‘texturemap’, ‘image’. С тремя из них, мы уже познакомились. Что касается двух оставшихся, то, прежде чем их попробовать, давайте условимся, что для построения мы будем брать не все данные, а только каждое десятое значение. Сделаем мы это из-за того, что наши матрицы достаточно большого размера, поэтому в случае с ‘mesh’ мы без масштабирования просто не разглядим результат, а в случае с ‘contour’ процесс построения будет долгим.
Что ж, давайте попробуем. Параметр ‘mesh’ представит наши данные на карте в виде сетки:
worldmap([40 80],[-40 20])
geoshow(lat(1:10:end,1:10:end),lon(1:10:end,1:10:end),...
sst(1:10:end,1:10:end),'displaytype','mesh')
caxis([-2 25])
geoshow('landareas.shp','FaceColor',[0.5 0.5 0.5])
colormap jet
colorbar
Для построения с параметром ‘contour’ сразу обозначим шаг прорисовки изолиний (‘LevelStep’,1) иначе по умолчанию MATLAB построит слишком мало изолиний:
worldmap([40 80],[-40 20])
geoshow(lat(1:10:end, 1:10:end),lon(1:10:end, 1:10:end),...
sst(1:10:end, 1:10:end),'displaytype','contour','LevelStep',1)
caxis([-2 25])
geoshow('landareas.shp','FaceColor',[0.5 0.5 0.5])
colormap jet
colorbar
По умолчанию изолинии имеют цвет, это слегка непривычно. Сделать их чёрными можно, присвоив параметру ‘LineColor’ значение ‘black’. Дополнительно заполним цветом промежутки между изолиниями (‘Fill’,’on’):
worldmap([40 80],[-40 20])
geoshow(lat(1:10:end,1:10:end),lon(1:10:end,1:10:end),...
sst(1:10:end,1:10:end),'displaytype','contour','Fill','on',...
'LineColor','black','LevelStep',1)
caxis([-2 25])
geoshow('landareas.shp','FaceColor',[0.5 0.5 0.5])
colormap jet
colorbar
Ну и пожалуй последнее на сегодня видоизменение, которое внесём в нашу карту — поменяем проекцию, например, на проекцию Меркатора. Конечно, выбор для океанолога не случаен, это проекция является одной из самых популярных, так как используется в навигационных картах и хороша для отображения данных, связанных с направлением (направления ветра, направления течений). Тонкая настройка карты выполняется при помощи функции setm. Необходимо помнить, что задавать проекцию следует в самом начале, сразу после worldmap:
worldmap([40 80],[-40 20])
setm(gca,'MapProjection','mercator')
geoshow(lat,lon,sst,'displaytype','surface')
caxis([-2 25])
geoshow('landareas.shp','FaceColor',[0.5 0.5 0.5])
colormap jet
colorbar
Пожалуй, на сегодня хватит. Следите за обновлениями, впереди ещё работа с более детальной береговой чертой, нанесение точек на карту, построение карты батиметрии и много другое.
Чтобы не пропустить новые материалы, подпишитесь на канал в Telegram: https://t.me/koldunovaleksey