Визуализация скалярного поля в PyNGL
Задача: визуализировать концентрацию льда
?нструменты: Python с модулем PyNGL
Продолжаем тему визуализации при помощи питоновского модуля PyNGL. Подробно о том что это такое и зачем оно нам можно посмотреть в
?сходные данные находятся в netCDF файле, который можно скачать отсюда.
Начнём работать сразу со скрипта который что то нам нарисует:
import Ngl
import Nio
import numpy
file = Nio.open_file("NCEP_MR109_sico_1980-1999_mean_09.nc")
concentration_nio = file.variables["SICOMO"]
lon = file.variables["lon"]
lat = file.variables["lat"]
concentration = concentration_nio[0,0,:,:]
lon = lon[:]
lat = lat[:]
#
# Open a workstation.
#
rlist = Ngl.Resources()
rlist.wkColorMap = 'posneg_1'
wks_type = "ps"
wks = Ngl.open_wks(wks_type,"contours",rlist)
resources = Ngl.Resources()
resources.sfXArray = lon[:]
resources.sfYArray = lat[:]
map = Ngl.contour_map(wks,concentration[:,:],resources)
Назовём его contours.py. Происходит тут следующее — мы открываем файл NCEP_MR109_sico_1980-1999_mean_09.nc, вытягиваем из него переменные
- SICOMO — концентрация льда
- lon — долгота
- lat — широта
Напомню что посмотреть на переменные находящиеся в открытом файле можно набрав в ipython:
Далее переменные преобразуются в NymPy матрицы. А вот после этого идёт интересное, мы создаём рабочую станцию:
- rlist = Ngl.Resources() — создаём переменную в которой будут жить ресурсы относящиеся к рабочей станции
- rlist.wkColorMap = ‘posneg_1’ — выбираем цветовую палитру
- wks_type = «ps» — выбираем выходной формат картинки
- wks = Ngl.open_wks(wks_type,»contours»,rlist) — создаём рабочую станцию.
Цветовую палитру (color map) можно либо выбрать из
Дальше:
- resources = Ngl.Resources() создаём переменную в которой будут все ресурсы относящиеся к карте
- resources.sfXArray = lon[:] сообщаем где у нас содержатся долготы
- resources.sfYArray = lat[:] то же с широтами
Запустив скрипт получим такое вот чудо-юдо:
Что то похожее на изолинии в районе полюсов наблюдается (там где должен быть лёд) но разобрать ничего, конечно, нельзя. Понятно что дефолтные значения нас не устраивают, будем менять. Все последующие куски кода нужно вставлять в ваш скрипт перед командой на отрисовку:
map = Ngl.contour_map(wks,concentration[:,:],resources).
? всё что мы будем делать это изменять эту самую resources.
Давайте сконцентрируемся на Арктике, для этого добавим в скрипт:
resources.mpDataBaseVersion = "MediumRes"
resources.mpLimitMode = "LatLon"
resources.mpMinLonF = 0
resources.mpMaxLonF = 360
resources.mpMinLatF = 60
resources.mpMaxLatF = 90
В принципе названия ресурсов и их значения говорят сами за себя. Мы выбираем стереографическую проекцию из
Получаем:
Как это не грустно, но уже этот зародыш карты лучше чем многое из того что можно встретить даже в топовых научных журналах :).
Хоть это и не по международным правилам, но давайте повернём нашу карту так чтобы Россия была внизу, так многим привычнее, и разрешим заливку.
resources.mpFillOn = True
Мы задали центральную долготу и сказали что хотим залить нашу карту.
Получаем:
Тут нужно сказать о заливке. Большинство областей на карте принадлежит к так называемым группам. Основные — Океан, Суша, Воды суши. Контролируется она ресурсом mpFillColors которому передаются цвета заливки различных групп. Первые четыре это:
resources.mpFillColors = [Не принадлежащие ни какой группе, Океан, Суша, Вода окружённая землёй]
Мы ресурс не задавали, поэтому для нашей карты были использованы дефолтные значения:
resources.mpFillColors = [16, 10, 8, 10]
Но допустим что у нас есть собственное мнение по поводу цвета заливки, а кое что мы вообще заливать не хотим. Добавляем:
resources.mpFillColors = [0,-1,igray,-1]
resources.cnLineDrawOrder = "Predraw"
Мы хотим залить землю серым цветом, а океан и воду суши оставить прозрачными. Но серого у нас в палитре нет. Это не беда, поскольку существует функция Ngl.new_color которая добавляет в конец существующей палиты ещё один цвет. Цвет определяется в RGB но значения не от 0 до 255 а от 0 до 1. Не очень понятно почему так, но как уж есть. Во второй строчке используем ресурс mpFillColors, в результате заливка:
- Области не принадлежащие никакой группе = 0 = цвет бэкграунда (у нас белый)
- Океан = -1 = прозрачность
- Суша = igray = серый заданный нами
- Воды суши = -1 = прозрачность
С таким же успехом вместо igray можно написать 21 (в нашей палитре 20 цветов и мы добавили ещё один — серый).
Также последней строчкой мы заставляем изолинии рисоваться вначале, чтобы потом всякие каки, возникающие у берегов, спокойно перекрыть сушей 🙂
Получаем:
Давайте теперь раскрасим всё это дело:
resources.cnFillDrawOrder = "Predraw"
resources.cnLineLabelsOn = False
Разрешаем заливку контуров, говорим что контуры заливаются первыми и заодно выключаем не нужные теперь подписи на изолиниях.
Получаем:
В принципе уже живенько ). Белая полоска связана с тем что, насколько я понимаю, тут сходятся края матрицы. Бороться с этим можно, но я пока не знаю как 🙂
Давайте теперь чуть поиграем с цветами:
resources.nglSpreadColorEnd = -2
resources.cnLevelSelectionMode = "ExplicitLevels" # Define own levels.
resources.cnLevels = numpy.arange(0.,1.,0.1)
Начнём рисовать с цвета номер 8, а закончим вторым с конца, поскольку первый с конца это добавленный нами серый. Последние две строчки показывают как задавать свои собственные уровни, но у нас они адекватно определились, так что для нас в этом смысле ничего не поменяется.
Получаем:
Ну и, наконец, давайте займёмся легендой, которая сейчас уж больно огромная:
resources.lbTitleFontHeightF = 0.018
resources.lbLabelFontHeightF = 0.012
resources.lbTitleOffsetF = -0.27
resources.lbBoxMinorExtentF = 0.15
resources.pmLabelBarOrthogonalPosF = -0.01
resources.lbOrientation = "Horizontal"
lbTitleString отвечает за заголовок легенды, и мы вместо того чтобы явно задать строчку, используем информацию которая содержится в исходном файле. У переменной concentration_nio, которая связана с SICOMO есть атрибут __dict__. Он содержит такой словарь:
Комбинируя long_name и units мы получаем название нашей легенды.
Остальные параметры интуитивно понятны. lbOrientation делает легенду горизонтальной.
Ну и напоследок вспомним что у нас модельные данные, а значит всё на сетке:
resources.cnLinesOn = False
Включаем другой метод заполнения и выключаем изолинии.
Теперь избавимся от дурацкой белой линии посередине. Для этого надо изменить код тут:
resources.sfYArray = Ngl.add_cyclic(lat[:])
и в самом конце:
Функция Ngl.add_cyclic дополняет наш массив ещё одной колонкой, то есть ещё одним рядом «долготы». Огромное спасибо Маше за наводку на эту функцию!
Получаем:
По моему очень живенько 🙂
Полностью скрипт:
import Ngl
import Nio
import numpy
file = Nio.open_file("NCEP_MR109_sico_1980-1999_mean_09.nc")
concentration_nio = file.variables["SICOMO"]
lon = file.variables["lon"]
lat = file.variables["lat"]
concentration = concentration_nio[0,0,:,:]
lon = lon[:]
lat = lat[:]
#
# Open a workstation.
#
rlist = Ngl.Resources()
rlist.wkColorMap = 'posneg_1'
wks_type = "ps"
wks = Ngl.open_wks(wks_type,"contours",rlist)
resources = Ngl.Resources()
resources.sfXArray = Ngl.add_cyclic(lon[:])
resources.sfYArray = Ngl.add_cyclic(lat[:])
resources.mpProjection = "Stereographic"
resources.mpDataBaseVersion = "MediumRes"
resources.mpLimitMode = "LatLon"
resources.mpMinLonF = 0
resources.mpMaxLonF = 360
resources.mpMinLatF = 60
resources.mpMaxLatF = 90
resources.mpCenterLonF = 100.
resources.mpFillOn = True
igray = Ngl.new_color(wks,0.7,0.7,0.7)
resources.mpFillColors = [0,-1,igray,-1]
resources.cnLineDrawOrder = "Predraw"
resources.cnFillOn = True
resources.cnFillDrawOrder = "Predraw"
resources.cnLineLabelsOn = False
resources.nglSpreadColorStart = 8
resources.nglSpreadColorEnd = -2
resources.cnLevelSelectionMode = "ExplicitLevels" # Define own levels.
resources.cnLevels = numpy.arange(0.,1.,0.1)
resources.lbTitleString = concentration_nio.__dict__['long_name']+', '+concentration_nio.__dict__['units']
resources.lbTitleFontHeightF = 0.018
resources.lbLabelFontHeightF = 0.012
resources.lbTitleOffsetF = -0.27
resources.lbBoxMinorExtentF = 0.15
resources.pmLabelBarOrthogonalPosF = -0.01
resources.lbOrientation = "Horizontal"
#resources.cnFillMode = "RasterFill"
#resources.cnLinesOn = False
map = Ngl.contour_map(wks,Ngl.add_cyclic(concentration[:,:]),resources)
tut rasskazano kak mozhno beluy polosochku ubrat’
http://www.pyngl.ucar.edu/Functions/Ngl.add_cyclic.shtml
2masha
Спасибо, исправил пост, теперь финальная версия без дырки )
Like!! I blog frequently and I really thank you for your content. The article has truly peaked my interest.
Cephalexin Drug Class Cialis Tadalafil Online Reliable Cialis Canadian Generic Levitra
Injection Amoxicillin Dosage buy cialis uk Amoxicillin Reaction In Infants cialis professional Buy Kamagra Jelly
Very nice post. I just stumbled upon your weblog and wished to say that I have truly enjoyed browsing your blog posts. In any case I’ll be subscribing to your rss feed and I hope you write again very soon!|