In [ ]:
# Function to get tmp image dir. If it does not exist,
# create it
def getTmpImgDir():
from os import makedirs
from os.path import exists, join
tmp_img_dir = "tmp_img"
if (not exists(tmp_img_dir)):
makedirs(tmp_img_dir)
return tmp_img_dir
#Function that saves the layer as an image
def saveLayerAsTmpImage(layer, inname):
from os.path import join
tmp_img_dir = getTmpImgDir()
full_img_path = join(tmp_img_dir, inname)
out = open(full_img_path, 'wb')
out.write(layer.read())
out.close()
# Function to load image
def loadTmpImage(image_name):
from IPython.core.display import Image
from os.path import join
tmp_img_dir = getTmpImgDir()
filename = join(tmp_img_dir, image_name)
return Image(filename)
# Function to display image from a url
def loadRemoteImage(imgUrl):
from IPython.core.display import Image
return Image(url = imgUrl)
In [ ]:
from owslib.wms import WebMapService
from siphon.catalog import get_latest_access_url
# just need a WMS url from one TDS dataset...NCEP HRRR 2.5km forecast model
catalog = 'http://thredds.ucar.edu/thredds/catalog/grib/NCEP/HRRR/CONUS_2p5km/catalog.xml'
serverurl = get_latest_access_url(catalog, 'WMS')
wms = WebMapService( serverurl, version='1.1.1')
wms.url
In [ ]:
#This is general information, common to all datasets in a TDS server
operations =[ op.name for op in wms.operations ]
print('Available operations: ')
print(operations)
print('General information (common to all datasets):')
print(wms.identification.type)
print(wms.identification.abstract)
print(wms.identification.keywords)
print(wms.identification.version)
print(wms.identification.title)
In [ ]:
#Listing all available layers...
layers = list(wms.contents)
for l in layers:
print('Layer title: '+wms[l].title +', name:'+wms[l].name)
In [ ]:
#Values common to all GetMap requests: formats and http methods:
print(wms.getOperationByName('GetMap').formatOptions)
print(wms.getOperationByName('GetMap').methods)
#Let's choose: 'wind @ Isobaric surface' (the value in the parameter must be name of the layer)
wind = wms['wind @ Isobaric surface']
#What is its bounding box?
print(wind.boundingBox)
#available CRS
print(wind.crsOptions)
# --> NOT ALL THE AVAILABLE CRS OPTIONS ARE LISTED
# print elevations at which the wind is avaliable
elevations = [elevation.strip() for elevation in wind.elevations]
print(elevations)
# units on the elevation are not exposed. 50,000 for a height -> probably Pa
In [ ]:
img_wind = wms.getmap( layers=[wind.name], #only takes one layer
srs='EPSG:4326',
bbox=(wind.boundingBox[0],wind.boundingBox[1], wind.boundingBox[2], wind.boundingBox[3]),
size=(512, 512),
format='image/png',
elevation=elevations[0])
# Save it..
saveLayerAsTmpImage(img_wind, 'test_wind.png')
# Display the image we've just saved...
loadTmpImage('test_wind.png')
In [ ]:
times = [time.strip() for time in wind.timepositions]
print(times)
elevations = [elevation.strip() for elevation in wind.elevations]
print("")
print(elevations)
In [ ]:
img_wind = wms.getmap( layers=[wind.name],
srs='EPSG:4326',
bbox=(wind.boundingBox[0],wind.boundingBox[1], wind.boundingBox[2], wind.boundingBox[3]),
size=(600, 600),
format='image/png',
elevation=elevations[0],
time= times[-1]
)
saveLayerAsTmpImage(img_wind, 'test_wind.png')
loadTmpImage('test_wind.png')
In [ ]:
img_wind = wms.getmap( layers=[wind.name],
srs='EPSG:4326',
bbox=(wind.boundingBox[0],wind.boundingBox[1], wind.boundingBox[2], wind.boundingBox[3]),
size=(600, 600),
format='image/gif',
elevation=elevations[0],
time= f'{times[0]}/{times[4]}'
)
from IPython.core.display import Image
loadRemoteImage(img_wind.geturl())
In [ ]:
#available styles:
#print wind.styles
#Change the style of our layer
img_wind = wms.getmap( layers=[wind.name], #only takes one layer
styles=['barb/rainbow'], #one style per layer
srs='EPSG:4326',
bbox=(wind.boundingBox[0],wind.boundingBox[1], wind.boundingBox[2], wind.boundingBox[3]),
size=(600, 600),
format='image/png',
elevation=elevations[0],
time= times[0]
)
saveLayerAsTmpImage(img_wind, 'test_wind_barb.png')
loadTmpImage('test_wind_barb.png')
In [ ]:
#Reproject the bounding box to a global mercator (EPSG:3875, projection used by Google Maps, OSM...) using cartopy
import cartopy.crs as ccrs
epsg = 3857
psproj = ccrs.epsg(epsg)
xmin, ymin = psproj.transform_point(wind.boundingBox[0], wind.boundingBox[1], ccrs.Geodetic())
xmax, ymax = psproj.transform_point(wind.boundingBox[2], wind.boundingBox[3], ccrs.Geodetic())
img_wind = wms.getmap( layers=[wind.name],
srs='EPSG:%d' % epsg,
bbox=(xmin, ymin, xmax, ymax),
size=(600, 600),
format='image/png',
elevation=elevations[0],
time= times[0]
)
saveLayerAsTmpImage(img_wind, 'test_wind_3857.png')
loadTmpImage('test_wind_3857.png')
In [ ]:
temp = wms['Temperature_height_above_ground']
img_temp = wms.getmap( layers=[temp.name],
styles=['boxfill/rainbow'],
srs='EPSG:4326',
bbox=(temp.boundingBox[0],temp.boundingBox[1], temp.boundingBox[2], temp.boundingBox[3]),
size=(600, 600),
format='image/png',
elevation=2,
time= times[0]
)
saveLayerAsTmpImage(img_temp, 'test_temp.png')
loadTmpImage('test_temp.png')
ncWMS/THREDDS provides some non-standard WMS parameters that allow clients some control on the styling.
Change the scale range:
In [ ]:
img_temp = wms.getmap( layers=[temp.name],
styles=['boxfill/rainbow'],
srs='EPSG:4326',
bbox=(temp.boundingBox[0],temp.boundingBox[1], temp.boundingBox[2], temp.boundingBox[3]),
size=(600, 600),
format='image/png',
time= times[0],
colorscalerange='250,320'
)
saveLayerAsTmpImage(img_temp, 'test_temp.png')
loadTmpImage('test_temp.png')
In [ ]:
colorscalerange='290,310'
img_temp = wms.getmap( layers=[temp.name],
styles=['boxfill/rainbow'],
srs='EPSG:4326',
bbox=(temp.boundingBox[0],temp.boundingBox[1], temp.boundingBox[2], temp.boundingBox[3]),
size=(600, 600),
format='image/png',
time= times[0],
colorscalerange=colorscalerange,
abovemaxcolor='transparent',
belowmincolor='transparent'
)
saveLayerAsTmpImage(img_temp, 'test_temp.png')
loadTmpImage('test_temp.png')
In [ ]:
params ={'request': 'GetLegendGraphic',
'colorbaronly':'False', #want the text in the legend
'layer':temp.name,
'colorscalerange':colorscalerange}
legendUrl=serverurl+'?REQUEST={request:s}&COLORBARONLY={colorbaronly:s}&LAYER={layer:s}&COLORSCALERANGE={colorscalerange:s}'.format(**params)
loadRemoteImage(legendUrl)
In [ ]:
%matplotlib inline
import cartopy
import matplotlib as mpl
import matplotlib.pyplot as plt
# get the first time - Forecast Hour 0
times = [time.strip() for time in temp.timepositions]
time = times[0]
# only one elevation, so use it
elevations = [elevation.strip() for elevation in temp.elevations]
elevation = elevations[0]
# have to guess the range
color_max = 315 # K
color_min = 273 # K
colorscalerange = f'{color_min},{color_max}'
# pick a projection - going with Miller for this example
# note that with Cartopy you are NOT limited to the projections avaliable through ncWMS
plt_proj = cartopy.crs.Miller()
fig, ax = plt.subplots(figsize=(12,12), subplot_kw={'projection': plt_proj})
# Colorbar goodness
cax = fig.add_axes([0.95, 0.3, 0.02, 0.42])
norm = plt.Normalize(vmin=color_min, vmax=color_max)
cmap = plt.cm.gist_heat
cmap.set_under('None') # Colors below minimum should be transparent
cb = mpl.colorbar.ColorbarBase(cax, cmap=cmap, norm=norm, spacing='proportional', orientation='vertical')
cb.set_label('Temperature [K]')
# use bounding box info obtained from the ncWMS service to frame the image
extent = (temp.boundingBox[0], temp.boundingBox[2], temp.boundingBox[1], temp.boundingBox[3])
ax.set_extent(extent)
# ncWMS keywords (which includes the WMS keywords as well)
wms_kwargs = {'colorscalerange': colorscalerange,
'abovemaxcolor': 'transparent',
'belowmincolor': 'transparent',
'transparent': 'true',
'elevation': elevation,
'time': time}
# plot the layer using Cartopy's WMS interface
im = ax.add_wms(wms=serverurl, layers=[temp.name], wms_kwargs=wms_kwargs, cmap=cmap, zorder=2)
# Range for image data should above pixels with 0 value. This makes the 0 values
# transparent
im.norm.vmin = 1
# add coastlines, country borders and state outlines
ax.add_feature(cartopy.feature.LAND, zorder=1)
ax.add_feature(cartopy.feature.OCEAN, zorder=1)
ax.add_feature(cartopy.feature.COASTLINE, zorder=3)
ax.add_feature(cartopy.feature.BORDERS, zorder=3)
ax.add_feature(cartopy.feature.NaturalEarthFeature(
category='cultural',
name='admin_1_states_provinces_lakes',
scale='50m',
facecolor='none'), linestyle=':', zorder=3)