This notebook will provide you guidance how to explore and plot ECMWF open dataset to produce the map from the ECMWF open charts web product.
The original product can be found on this link: https://charts.ecmwf.int/products/medium-rain-detailed
Some ECMWF real-time products are governed by the ECMWF Standard Licence Agreement and are not free or open. Access to these products depends on your use case. To find out which licence applies to you, please visit: Use cases and licence conditions page.
To find out how to obtain the access to the ECMWF forecast data please visit our Access page.
This product takes in input 4 parameters :
In this example, we will use:
First we need to install them in the current Jupyter kernel:
#!pip install ecmwf-api-client metview metview-python
import metview as mv
from ecmwfapi import *
import requests
filename = 'medium-rain-detailed.grib'
filename
'medium-rain-detailed.grib'
If you already have the access to the ECMWF archived forecast data, you can use the next cell to download data from the MARS archive:
server = ECMWFService("mars")
server.execute(
{
"class": "od",
"date": "0",
"expver": "1",
"levtype": "sfc",
"param": "151/228/143/142",
"step": "18/24",
"stream": "oper",
"time": "00",
"type": "fc",
"grid": "0.25/0.25"
},
filename)
Now we can use Metview's read() function to read the file.
data = mv.read(filename)
The describe() function will give us the overview of the dataset.
data.describe()
parameter | typeOfLevel | level | date | time | step | paramId | class | stream | type | experimentVersionNumber |
---|---|---|---|---|---|---|---|---|---|---|
cp | surface | 0 | 20250227 | 0 | 18,24 | 143 | od | oper | fc | 0001 |
lsp | surface | 0 | 20250227 | 0 | 18,24 | 142 | od | oper | fc | 0001 |
msl | surface | 0 | 20250227 | 0 | 18,24 | 151 | od | oper | fc | 0001 |
tp | surface | 0 | 20250227 | 0 | 18,24 | 228 | od | oper | fc | 0001 |
And an overview of one parameter, where we can see more information, such as units or type of level.
data.describe('tp')
shortName | tp |
---|---|
name | Total precipitation |
paramId | 228 |
units | m |
typeOfLevel | surface |
level | 0 |
date | 20250227 |
time | 0 |
step | 18,24 |
class | od |
stream | oper |
type | fc |
experimentVersionNumber | 0001 |
We can use ls() function to list all the fields in the file we downloaded.
data.ls()
centre | shortName | typeOfLevel | level | dataDate | dataTime | stepRange | dataType | gridType | |
---|---|---|---|---|---|---|---|---|---|
Message | |||||||||
0 | ecmf | msl | surface | 0 | 20250227 | 0 | 18 | fc | regular_ll |
1 | ecmf | tp | surface | 0 | 20250227 | 0 | 18 | fc | regular_ll |
2 | ecmf | cp | surface | 0 | 20250227 | 0 | 18 | fc | regular_ll |
3 | ecmf | lsp | surface | 0 | 20250227 | 0 | 18 | fc | regular_ll |
4 | ecmf | msl | surface | 0 | 20250227 | 0 | 24 | fc | regular_ll |
5 | ecmf | tp | surface | 0 | 20250227 | 0 | 24 | fc | regular_ll |
6 | ecmf | cp | surface | 0 | 20250227 | 0 | 24 | fc | regular_ll |
7 | ecmf | lsp | surface | 0 | 20250227 | 0 | 24 | fc | regular_ll |
We can see that we have both parameters at steps 18 and 24.
If we want to plot the total precipitation between steps 18 and 24 UTC, we need the data on both timesteps because total precipitation is parameter that is accumulated from the start of the forecast period. This means that step 18 has all accumulated precipitation from start of the forecast (00UTC) until 18 UTC, step 24 has accumulation from 00 to 24 UTC and so on.
If we want the precipitation from 18 to 24 we need to deaccumulate it, by simply substracting.
We will use the select() function to filter the total precipitation at different steps.
tp_12 = data.select(shortName='tp', step= 18)
tp_24 = data.select(shortName='tp', step= 24)
tp = (tp_24 - tp_12)
tp.ls()
centre | shortName | typeOfLevel | level | dataDate | dataTime | stepRange | dataType | gridType | |
---|---|---|---|---|---|---|---|---|---|
Message | |||||||||
0 | ecmf | tp | surface | 0 | 20250227 | 0 | 24 | fc | regular_ll |
cp_12 = data.select(shortName='cp', step= 18)
cp_24 = data.select(shortName='cp', step= 24)
cp = (cp_24 - cp_12)
cp.ls()
centre | shortName | typeOfLevel | level | dataDate | dataTime | stepRange | dataType | gridType | |
---|---|---|---|---|---|---|---|---|---|
Message | |||||||||
0 | ecmf | cp | surface | 0 | 20250227 | 0 | 24 | fc | regular_ll |
lsp_12 = data.select(shortName='lsp', step= 18)
lsp_24 = data.select(shortName='lsp', step= 24)
lsp = (lsp_24 - lsp_12)
lsp.ls()
centre | shortName | typeOfLevel | level | dataDate | dataTime | stepRange | dataType | gridType | |
---|---|---|---|---|---|---|---|---|---|
Message | |||||||||
0 | ecmf | lsp | surface | 0 | 20250227 | 0 | 24 | fc | regular_ll |
msl = data.select(shortName= 'msl', step = 24)
msl.describe()
parameter | typeOfLevel | level | date | time | step | paramId | class | stream | type | experimentVersionNumber |
---|---|---|---|---|---|---|---|---|---|---|
msl | surface | 0 | 20250227 | 0 | 24 | 151 | od | oper | fc | 0001 |
Mean sea level pressure data has unites Pa, but we want to plot it in hPa, therefore we need to convert it.
msl /= 100
We also need to convert total precipitation from metres to milimetres by multiplying with 1000.
tp = tp * 1000
cp = cp * 1000
lsp = lsp * 1000
And finally, we can plot the data on the map.
First we plot total precipitation and Mean sea level pressure.
# define coastlines
coast = mv.mcoast(
map_coastline_colour="charcoal",
map_coastline_resolution="medium",
map_coastline_land_shade="on",
map_coastline_land_shade_colour="cream",
map_coastline_sea_shade="off",
map_boundaries="on",
map_boundaries_colour= "charcoal",
map_boundaries_thickness = 1,
map_disputed_boundaries = "off",
map_grid_colour="tan",
map_label_height=0.35,
)
# define view
view = mv.geoview(
area_mode="name",
area_name="europe",
coastlines=coast
)
#define styles
tp_shade = mv.mcont(legend= "on",
contour_automatics_settings = "style_name",
contour_style_name = "sh_blured_f05t300lst")
msl_shade = mv.mcont(legend= "off",
contour_automatics_settings = "style_name",
contour_style_name = "ct_blk_i5_t2")
title = mv.mtext(
text_lines = ["Total Precipitation during last 6 hours (total / large scale / convective)",
"START TIME: <grib_info key='base-date' format='%a %d %B %Y %H' where='shortName=msl' /> ",
"VALID TIME: <grib_info key='valid-date' format='%a %d %B %Y %H' where='shortName=msl' />, STEP: <grib_info key='step' where='shortName=msl' />"],
text_font_size=0.4,
text_colour = 'charcoal')
ecmwf_text = mv.mtext(
text_lines = ["© European Centre for Medium-Range Weather Forecasts (ECMWF)",
"Source: www.ecmwf.int Licence: CC-BY-4.0 and ECMWF Terms of Use",
"https://apps.ecmwf.int/datasets/licences/general/"],
text_justification = 'center',
text_font_size = 0.3,
text_mode = "positional",
text_box_x_position = 6.,
text_box_y_position = -0.2,
text_box_x_length = 8,
text_box_y_length = 2,
text_colour = 'charcoal')
# generate plot
mv.setoutput('jupyter', plot_widget=False)
mv.plot(view, tp, tp_shade, msl, msl_shade, title, ecmwf_text)
Next we plot convective precipitation.
# define coastlines
coast = mv.mcoast(
map_coastline_colour="charcoal",
map_coastline_resolution="medium",
map_coastline_land_shade="on",
map_coastline_land_shade_colour="cream",
map_coastline_sea_shade="off",
map_boundaries="on",
map_boundaries_colour= "charcoal",
map_boundaries_thickness = 1,
map_disputed_boundaries = "off",
map_grid_colour="tan",
map_label_height=0.35,
)
# define view
view = mv.geoview(
area_mode="name",
area_name="europe",
coastlines=coast
)
#define styles
tp_shade = mv.mcont(legend= "on",
contour_automatics_settings = "style_name",
contour_style_name = "sh_blured_f05t300lst")
msl_shade = mv.mcont(legend= "off",
contour_automatics_settings = "style_name",
contour_style_name = "ct_blk_i5_t2")
title = mv.mtext(
text_lines = ["Convective Precipitation during last 6 hours",
"START TIME: <grib_info key='base-date' format='%a %d %B %Y %H' where='shortName=msl' /> ",
"VALID TIME: <grib_info key='valid-date' format='%a %d %B %Y %H' where='shortName=msl' />, STEP: <grib_info key='step' where='shortName=msl' />"],
text_font_size=0.4,
text_colour = 'charcoal')
ecmwf_text = mv.mtext(
text_lines = ["© European Centre for Medium-Range Weather Forecasts (ECMWF)",
"Source: www.ecmwf.int Licence: CC-BY-4.0 and ECMWF Terms of Use",
"https://apps.ecmwf.int/datasets/licences/general/"],
text_justification = 'center',
text_font_size = 0.3,
text_mode = "positional",
text_box_x_position = 6.,
text_box_y_position = -0.2,
text_box_x_length = 8,
text_box_y_length = 2,
text_colour = 'charcoal')
# generate plot
mv.setoutput('jupyter', plot_widget=False)
mv.plot(view, cp, tp_shade, msl, msl_shade, title, ecmwf_text)
In the end we plot the Large scale precipitation.
# define coastlines
coast = mv.mcoast(
map_coastline_colour="charcoal",
map_coastline_resolution="medium",
map_coastline_land_shade="on",
map_coastline_land_shade_colour="cream",
map_coastline_sea_shade="off",
map_boundaries="on",
map_boundaries_colour= "charcoal",
map_boundaries_thickness = 1,
map_disputed_boundaries = "off",
map_grid_colour="tan",
map_label_height=0.35,
)
# define view
view = mv.geoview(
area_mode="name",
area_name="europe",
coastlines=coast
)
#define styles
tp_shade = mv.mcont(legend= "on",
contour_automatics_settings = "style_name",
contour_style_name = "sh_blured_f05t300lst")
msl_shade = mv.mcont(legend= "off",
contour_automatics_settings = "style_name",
contour_style_name = "ct_blk_i5_t2")
title = mv.mtext(
text_lines = ["Large-scale Precipitation during last 6 hours",
"START TIME: <grib_info key='base-date' format='%a %d %B %Y %H' where='shortName=msl' /> ",
"VALID TIME: <grib_info key='valid-date' format='%a %d %B %Y %H' where='shortName=msl' />, STEP: <grib_info key='step' where='shortName=msl' />"],
text_font_size=0.4,
text_colour = 'charcoal')
ecmwf_text = mv.mtext(
text_lines = ["© European Centre for Medium-Range Weather Forecasts (ECMWF)",
"Source: www.ecmwf.int Licence: CC-BY-4.0 and ECMWF Terms of Use",
"https://apps.ecmwf.int/datasets/licences/general/"],
text_justification = 'center',
text_font_size = 0.3,
text_mode = "positional",
text_box_x_position = 6.,
text_box_y_position = -0.2,
text_box_x_length = 8,
text_box_y_length = 2,
text_colour = 'charcoal')
# generate plot
mv.setoutput('jupyter', plot_widget=False)
mv.plot(view, lsp, tp_shade, msl, msl_shade, title, ecmwf_text)
To generate the png files you can run the following three cells.
png = mv.png_output(
output_name = "medium-rain-detailed-tp", # specify relative or full path
output_title = "medium-rain-detailed-tp", # title used by a viewer
output_width = 1000, # set width in pixels
)
mv.setoutput(png)
mv.plot(view, tp, tp_shade, msl, msl_shade, title, ecmwf_text)
png = mv.png_output(
output_name = "medium-rain-detailed-cp", # specify relative or full path
output_title = "medium-rain-detailed-cp", # title used by a viewer
output_width = 1000, # set width in pixels
)
mv.setoutput(png)
mv.plot(view, cp, tp_shade, msl, msl_shade, title, ecmwf_text)
png = mv.png_output(
output_name = "medium-rain-detailed-lsp", # specify relative or full path
output_title = "medium-rain-detailed-lsp", # title used by a viewer
output_width = 1000, # set width in pixels
)
mv.setoutput(png)
mv.plot(view, lsp, tp_shade, msl, msl_shade, title, ecmwf_text)
Note that plot produced using open data dataset will slightly differ from one from Open Charts. This is due to different resolution of the data.
The data used hereis on 0.25x0.25 resolution, while high resolution data is 0.1x0.1 grid.