Getting Started¶
This document refers as a first glance on how to use lombricquiver library to produce wind streamlines at easy.
Quick start
- import the package
import xarray as xr
import lombricquiver
import pandas as pd
# Check Version
print(lombricquiver.__version__)
0.0.1
Retrieving Wind data from ERA5 dataset.¶
Wind data is retrieved through Earth Data Hub, a project linked to the Destination-Earth.
The Earth Data Hub requests a key in order to access its services. The access is granted on a free level for any user of the plataform. In case you are new, it is necessary to create a login and retrive your API-key straight on the plataform.
The API-key is located on the settings of your personal account. Please, log in on Destination Earth, then access Earth Data Hub and then go to setting, on the bottom part you are able to find your API key. After you have your API key, you can proceed with the tutorial.
Please run the code below and provide the your key
## This you open an input window to insert your API key
from lombricquiver.cacheb_path import cacheb_key
key = cacheb_key()
ds = xr.open_dataset(
f"https://edh:{key}@data.earthdatahub.destine.eu/era5/reanalysis-era5-single-levels-v0.zarr",
chunks={},
engine="zarr",
)
print(ds)
<xarray.Dataset> Size: 423TB Dimensions: (valid_time: 748752, latitude: 721, longitude: 1440) Coordinates: depthBelowLandLayer float64 8B ... entireAtmosphere float64 8B ... * latitude (latitude) float64 6kB 90.0 89.75 89.5 ... -89.75 -90.0 * longitude (longitude) float64 12kB 0.0 0.25 0.5 ... 359.5 359.8 number int64 8B ... surface float64 8B ... * valid_time (valid_time) datetime64[ns] 6MB 1940-01-01 ... 2025-... Data variables: (12/136) alnid (valid_time, latitude, longitude) float32 3TB dask.array<chunksize=(4320, 64, 64), meta=np.ndarray> alnip (valid_time, latitude, longitude) float32 3TB dask.array<chunksize=(4320, 64, 64), meta=np.ndarray> aluvd (valid_time, latitude, longitude) float32 3TB dask.array<chunksize=(4320, 64, 64), meta=np.ndarray> aluvp (valid_time, latitude, longitude) float32 3TB dask.array<chunksize=(4320, 64, 64), meta=np.ndarray> anor (valid_time, latitude, longitude) float32 3TB dask.array<chunksize=(4320, 64, 64), meta=np.ndarray> asn (valid_time, latitude, longitude) float32 3TB dask.array<chunksize=(4320, 64, 64), meta=np.ndarray> ... ... viiwn (valid_time, latitude, longitude) float32 3TB dask.array<chunksize=(4320, 64, 64), meta=np.ndarray> vilwd (valid_time, latitude, longitude) float32 3TB dask.array<chunksize=(4320, 64, 64), meta=np.ndarray> vilwe (valid_time, latitude, longitude) float32 3TB dask.array<chunksize=(4320, 64, 64), meta=np.ndarray> vilwn (valid_time, latitude, longitude) float32 3TB dask.array<chunksize=(4320, 64, 64), meta=np.ndarray> z (valid_time, latitude, longitude) float32 3TB dask.array<chunksize=(4320, 64, 64), meta=np.ndarray> zust (valid_time, latitude, longitude) float32 3TB dask.array<chunksize=(4320, 64, 64), meta=np.ndarray> Attributes: Conventions: CF-1.7 GRIB_centre: ecmf GRIB_centreDescription: European Centre for Medium-Range Weather Forecasts GRIB_edition: 1 GRIB_subCentre: 0 history: 2025-02-13T19:10 GRIB to CDM+CF via cfgrib-0.9.1... institution: European Centre for Medium-Range Weather Forecasts
Process the data¶
- Uses the class ERA5Processor to pipeline the ERA5 wind data.
The ERA5 dataset is stored as a xarray, in order to make it easier to integrate, the ERA5Processor is called and deal with all the pre-processing steps.
# 1. Initialize the processor
from lombricquiver.era5_processor import ERA5DataProcessor
processor = ERA5DataProcessor(
ds=ds,
variables=['u10', 'v10', 't2m'],
date_range=["2023-03-05", "2023-03-05"],
spatial_range={
'lat': [35.0, 71.0],
'lon': [-10.0, 40.0]
}
)
# 2. Pre-process data (slice, filter, extract variables) and load it
processor.process_data()
Example dataset - Alternative¶
In case you have not set the key within Destination Earth, you can use the example dataset to visualize some results. Altought, we encourage any user to try the API of Destination Earth due easy integration and free access.
# !! Only run this cell if you are unable to retrieve your API key from Destination Earth !!
# This is an example dataset that can be used to visualize the quiver plot
from lombricquiver.era5_processor import ERA5DataProcessor
example_dataset = xr.open_dataset("examples/dataset/era5_data.nc")
processor = ERA5DataProcessor(
ds = example_dataset,
variables=['u10', 'v10', 't2m'],
date_range=["2023-03-05", "2023-03-05"],
spatial_range={
'lat': [35.0, 71.0],
'lon': [-10.0, 40.0]
}
)
processor.loaded_dataset = example_dataset
ERA5 Pipeline¶
-Prepare data for plot
# Calculate wind speed
processor.calculate_wind_speed()
# Return the processed dataset
dataset = processor.get_processed_data()
# Create a subsampling dataset
dataset_subsampled = processor.subsample_data(2)
# Extract variables by a given timestep e.x: 10:00am
extract_var = ['u10', 'v10', 't2m','wind_speed']
dict_extract_var = processor.extract_components_by_given_timestep(extract_variables = extract_var,
timestep=10, ##10:00am
lat_long=True)
## Select a valid time to be exhibited
dataset_subsampled_10am = dataset_subsampled.isel(valid_time=10) ## 10:00 am
Create Animation¶
from lombricquiver.manim_vector_field import VectorFieldAnimation
## Create the Animator instance
vf = VectorFieldAnimation()
## All the following code are methods of the VectorFieldAnimation class
# Set the wind dataset in the class
vf.set_dataset(dataset_subsampled_10am)
# Create the background image and set under the hood
vf.create_background_image(
dict_extract_var=dict_extract_var,
var_heatmap = 'wind_speed'
)
vf.configure_streamplot(
animation_duration=5, # 5 seconds of total animation
max_anchors_per_line=100, # max number of anchors per line in the animation
flow_speed=1.5, # speed of the streamline agent in the animation
virtual_time=4, # Higher values result in longer stream lines
)
## Returns the scene class to be used in Manim
Animation_singlecolor = vf.get_scene_class()
Geographic extent: -10.00°E to 40.00°E, 35.00°N to 71.00°N Aspect ratio: 0.84 Figure dimensions: 8.37 × 7.00 inches Final image size: 2512 × 2100 pixels Image saved as:base_layer.png All good, background image path set to: base_layer.png
Render and display¶
%manim -ql -v WARNING Animation_singlecolor
Tuning the animation¶
The animation is created within the VectorFieldAnimation class, which is a animator to handle Manim's engine.
- The parameters of the animation can be tuned with configure methods.
- Title, subtitle and colorbar are optional features and are displayed with the parameter 'show'.
- The streamplot can be seen either over a choosen palette or a single color for all stream lines.
# # Create an instance of the class animator
vf = VectorFieldAnimation()
# # Set the wind dataset in the class
vf.set_dataset(dataset_subsampled_10am)
# # Create the background image and set under the hood
vf.create_background_image(
dict_extract_var=dict_extract_var,
var_heatmap = 'wind_speed'
)
## Config the animations parameters
vf.configure_streamplot(
animation_duration = 5, # 4 seconds of total animation
max_anchors_per_line=100 , # max number of anchors per line in the animation
flow_speed=1.8, # it is the speed of the streamline agent in the animation,
virtual_time=4,#Higher values therefore result in longer stream lines
)
## Config title
vf.configure_title(
show_title = True,
title = 'Vector Field - Wind Speed',
font_size= 36
)
## Config suptitle
vf.configure_subtitle(
show_subtitle = True,
font_size=16,
subtitle = pd.to_datetime(str(vf.dataset()['valid_time'].values)).strftime('%Y-%m-%d %H:%M:%S')
)
## Config colorbar
vf.configure_colorbar(
show=True,
palette = 'viridis', ## Palette - from matplotlib palettes
num_colors = 4 ,
figsize = (1.0,9)
)
## Check if the animator is ready
if vf.is_ready():
print('Everything is set')
else:
vf.get_missing_requirements()
## Create the scene class inherented from Manim Scene
Animation = vf.get_scene_class()
Geographic extent: -10.00°E to 40.00°E, 35.00°N to 71.00°N Aspect ratio: 0.84 Figure dimensions: 8.37 × 7.00 inches Final image size: 2512 × 2100 pixels Image saved as:base_layer.png All good, background image path set to: base_layer.png Everything is set
%manim -ql -v WARNING Animation
Manim Community v0.19.0
Colorbar saved as the following file_name: colorbar colorbar settings on scene 1.3155680224403927 7.000000000000001
Single Color and diferent background image.¶
- It is easy to change the background and apply other variables present at the animation.
Temperature variable now is being displayed.
## Create new background image for the animation
new_background = create_layer_base(
dict_extract_var,
var_heatmap = 't2m',
file_name = 'temp_base_layer.png'
)
## Also, let's change the time of the day, lets plot at 17:00
dict_extract_var = processor.extract_components_by_given_timestep(extract_variables = extract_var,
timestep=17, ## 10:00am,
lat_long=True)
dataset_subsampled_17 = dataset_subsampled.isel(valid_time=17) #17:00
vf.set_dataset(dataset_subsampled_17)
## Set the new background on the class animator
vf.set_background_image(new_background)
## Apply the same steps above
## Config the animations parameters
vf.configure_streamplot(
animation_duration = 4, # 4 seconds of total animation
max_anchors_per_line=75 , # max number of anchors per line in the animation
flow_speed=1.8, # it is the speed of the streamline agent in the animation,
virtual_time=4,#Higher values therefore result in longer stream lines
)
## Config title
vf.configure_title(
show_title = True,
title = 'Vector Field - Wind Speed',
font_size= 36
)
## Config suptitle
vf.configure_subtitle(
show_subtitle = True,
font_size=16,
subtitle = pd.to_datetime(str(vf.dataset()['valid_time'].values)).strftime('%Y-%m-%d %H:%M:%S')
)
## Config colorbar
vf.configure_colorbar(
show=True,
palette = 'viridis', ## Palette - from matplotlib palettes
num_colors = 4
)
## Check if the animator is ready
if vf.is_ready():
print('Everything is set')
else:
vf.get_missing_requirements()
## Create the scene class inherented from Manim Scene
Animation_dif = vf.get_scene_class()
Geographic extent: -10.00°E to 40.00°E, 35.00°N to 71.00°N Aspect ratio: 0.84 Figure dimensions: 10.77 × 9.00 inches Final image size: 2691 × 2250 pixels Image saved as:base_layer.png
<lombricquiver.manim_vector_field.VectorFieldAnimation at 0x1f8fb7d34d0>
%manim -ql -v WARNING Animation_dif
vf.dataset()
<xarray.Dataset> Size: 119kB Dimensions: (latitude: 73, longitude: 101) Coordinates: valid_time datetime64[ns] 8B 2023-03-05T10:00:00 depthBelowLandLayer float64 8B 100.0 entireAtmosphere float64 8B 0.0 number int32 4B 0 surface float64 8B 0.0 * longitude (longitude) float64 808B -10.0 -9.5 -9.0 ... 39.5 40.0 * latitude (latitude) float64 584B 71.0 70.5 70.0 ... 35.5 35.0 Data variables: u10 (latitude, longitude) float32 29kB -0.3091 ... -0.4858 v10 (latitude, longitude) float32 29kB -9.922 ... 1.663 t2m (latitude, longitude) float32 29kB ... wind_speed (latitude, longitude) float32 29kB 9.927 ... 1.733
Animation.preview_config()
{'main': {'output_file': 'wind_stream_plot', 'pixel_width': 854, 'pixel_height': 480, 'preview_frames': 30, 'transparent': False, 'quality': 'medium_quality'}, 'baseplot': {'image_height': 7}, 'title': {'show_title': True, 'title_text': 'My Visualization', 'title_font_size': 36, 'title_color': ManimColor('#FFFFFF')}, 'suptitle': {'show_suptitle': True, 'suptitle_text': '2023-03-05 10:00:00', 'suptitle_font_size': 24, 'suptitle_color': ManimColor('#FFFFFF')}, 'streamplot': {'delta_y': 0.15, 'resize_factor': 0.05, 'stroke_width': 1.5, 'flow_speed': 2.5, 'time_width': 0.3, 'animation_duration': 3, 'dt': 0.01, 'max_anchors_per_line': 100, 'color': ManimColor('#FFFFFF'), 'virtual_time': 4}}