Маска Океана или Земли из данных с использованием Картопы

Я хотел бы замаскировать площадь суши на основании данных о температуре поверхности моря по всему земному шару. Я использую Cartopy для построения графиков данных.

import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from netCDF4 import Dataset

f = Dataset('sst.mnmean.nc')
sst = f.variables['sst'][0,:,:]
lats = f.variables['lat'][:]
lons = f.variables['lon'][:]

ax = plt.axes(projection=ccrs.PlateCarree())
ax.coastlines()
plot = ax.contourf(lons, lats, sst, 60, transform=ccrs.PlateCarree())
cb = plt.colorbar(plot)
plt.show()

Приведенный выше код отображает данные следующим образом:

Я хотел бы замаскировать Землю от этого.

3 ответа

Решение

Я просмотрел картографическую документацию и наткнулся на метод add_feature. Код выглядит следующим образом:

import numpy as np
import matplotlib.pyplot as plt
import cartopy as cart
from mpl_toolkits.basemap import Basemap
from netCDF4 import Dataset

f = Dataset('sst.mnmean.nc')
sst = f.variables['sst'][0,:,:]
lats = f.variables['lat'][:]
lons = f.variables['lon'][:]

ax = plt.axes(projection=cart.crs.PlateCarree())
ax.coastlines()
ax.add_feature(cart.feature.LAND, zorder=100, edgecolor='k')
ax.set_global()
plot = ax.contourf(lons, lats, sst, 60, transform=cart.crs.PlateCarree())
cb = plt.colorbar(plot)
plt.show()

Сюжет теперь выглядит так. Чтобы замаскировать океаны, измените cart.feature.LAND в cart.feature.OCEAN

Принятое решение на самом деле не маскирует данные, график просто покрывается частями, накладываясь на карту. Хотя это хорошо работает для данной проблемы, иногда требуется фактическая маска для удаления нежелательных частей данных. Такую маску легко создать на основе растровой карты суши или океана.

С помощью приведенного ниже кода создается временная фигура, разрешение которой соответствует заданным данным. После построения карты местности получается растровое изображение карты сtostring_rgb(). Это изображение, похожее на бинарное изображение, можно затем напрямую использовать для создания маски для данных.

Преимущество этого решения заключается в том, что его можно применять к более общим задачам, таким как построение двух разных наборов данных по суше и океану соответственно. Преимущество улучшается при построении данных, подобных изображениям, поскольку прозрачность может использоваться для достижения гладких краев с учетом цветового градиента растеризованной маски. Это можно легко сделать с помощьюPIL.Image.fromarray(mask)с последующимconvert('L')и, наконец, применениеputalpha(mask)на заданном изображении.

      import matplotlib.pyplot as plt
import numpy as np
import cartopy
import netCDF4

# load data
data = netCDF4.Dataset('sst.mnmean.nc')
sst  = data.variables['sst'][0,:,:]
lats = data.variables['lat'][:]
lons = data.variables['lon'][:]

# prepare temporary plot and create mask from rasterized map
proj = {'projection': cartopy.crs.PlateCarree()}
fig, ax = plt.subplots(figsize=(len(lons)/100, len(lats)/100), dpi=100, subplot_kw=proj)
fig.subplots_adjust(left=0.0, bottom=0.0, right=1.0, top=1.0)
ax.set_frame_on(False)

ax.add_feature(cartopy.feature.LAND, facecolor='black')
fig.canvas.draw()
mask = fig.canvas.tostring_rgb()
ncols, nrows = fig.canvas.get_width_height()
plt.close(fig)

mask = np.frombuffer(mask, dtype=np.uint8).reshape(nrows, ncols, 3)
mask = mask.mean(axis=2)
sst  = np.where(mask>0, sst, np.nan)

# create actual plot
fig, ax = plt.subplots(subplot_kw=proj)
ax.contourf(lons, lats, sst, 60, transform=cartopy.crs.PlateCarree(central_longitude=180))
ax.coastlines()
plt.show()

Для маскировки земельного участка было бы проще использовать базовую карту.

from mpl_toolkits.basemap import Basemap
map = Basemap(projection='mill',lon_0=180) # create projection
....                                       # whatever processing needed
map.fillcontinents(color='coral')          # mask land mass

Смотрите пример базовой карты здесь

Другие вопросы по тегам