SAEON / somisana

SOMISANA-related tooling
MIT License
6 stars 2 forks source link

Add download workflow for server generated assets #23

Open zachsa opened 2 years ago

zachsa commented 2 years ago

It would be nice to be able to download python (and other language) generated assets using the CROCO output. For example giffs. To do this the website needs to implement some UI controls for requesting these assets (and generating them), as well as a mechanism for running scripts that may take too long for the duration of a single request (email notifications, or simple file management for logged in users, etc)

zachsa commented 2 years ago

Here is the Python script (from @mattcarr03) to create the example output:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Feb 25 18:07:52 2022

@author: matthew
"""
#! /usr/bin/env python
#
# a script to animate croco surface currents with parcels particles
#

from matplotlib import pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
from netCDF4 import Dataset
import cartopy
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from datetime import timedelta, date, datetime
import sys
#sys.path.insert(1, '/home/osboxes/2001_DEA/models/parcels/croco_test')
#import croco_tools_visualisation as croco
import xarray as xr
from shapely.geometry import Point, Polygon
import netCDF4 as nc

# %%

def v2rho_3d(var_v):
     [T,M,Lp]=var_v.shape
     var_rho=np.zeros((T,M+1,Lp))
     var_rho[:,1:M-1,:]=0.5*np.squeeze([var_v[:,0:M-2,:]+var_v[:,1:M-1,:]])
     var_rho[:,0,:]=var_rho[:,1,:]
     var_rho[:,M,:]=var_rho[:,M-1,:]
     return var_rho

def u2rho_3d(var_u):
     [T,Mp,L]=var_u.shape
     var_rho=np.zeros((T,Mp,L+1))
     var_rho[:,:,1:L-1]=0.5*np.squeeze([var_u[:,:,0:L-2]+var_u[:,:,1:L-1]])
     var_rho[:,:,0]=var_rho[:,:,1]
     var_rho[:,:,L]=var_rho[:,:,L-1]
     return var_rho

def remove_child(lon_parent,lat_parent,data_parent,lon_child,lat_child):
    # assign missing values for parent data inside child domain
    # only works for regular grids

    # get the lon lat coordinates along the boundary of the model (actually 1 grid cell in to ensure some overlap between parent and child grids)
    lon_bry=lon_child[:,1].tolist()+lon_child[-2,:].tolist()+lon_child[-2:1:-1,-2].tolist()+lon_child[1,-2:1:-1].tolist()
    lat_bry=lat_child[:,1].tolist()+lat_child[-2,:].tolist()+lat_child[-2:1:-1,-2].tolist()+lat_child[1,-2:1:-1].tolist()
    #lon_bry=lon_child[:,0].tolist()+lon_child[-1,:].tolist()+lon_child[-1:0:-1,-1].tolist()+lon_child[0,-1:0:-1].tolist()
    #lat_bry=lat_child[:,0].tolist()+lat_child[-1,:].tolist()+lat_child[-1:0:-1,-2].tolist()+lat_child[0,-1:0:-1].tolist()

    bry = [(i,j) for i,j in zip(lon_bry,lat_bry)]
    poly = Polygon(bry)
    ny,nx=np.shape(lat_parent)
    for j in range(ny):
        for i in range(nx):
            p = Point(lon_parent[j,i], lat_parent[j,i])
            if p.within(poly):
                data_parent[j,i]=np.nan

    return data_parent    

# %% define file names

path = '/home/matthew/confs/False_bay_forecast/croco/forecast/'
#child_path = '/home/matthew/confs/False_bay_forecast/croco/scratch/'
child_path = '/home/matthew/Downloads/'

# define file names
grdfile='grd.nc'
#avgchildfile='avg_20220225.nc.1'
avgchildfile = 'false-bay-forecast-20221101.nc'

#Loading reference dataset to find surface level index
ds_child = xr.open_dataset(child_path+avgchildfile) 

# %% Loading variables from dataset 

#Child 
lon_child = ds_child.lon_rho.values 
lat_child = ds_child.lat_rho.values 

U_child = ds_child.u[:,19,:,:].values  
V_child = ds_child.v[:,19,:,:].values 

U_child = u2rho_3d(U_child)
V_child = v2rho_3d(V_child)

# %% Loading temperature 

SST_child = ds_child.temperature[:,39,:,:].values 

SST_child[np.where(SST_child==0)]=np.nan

# %% Loading time

# get the time
nc_id_avg = Dataset(child_path+avgchildfile, 'r')
time = nc_id_avg.variables['time'][:]
# time origin (needs to be hard coded here as it is not written to the croco output file)
date_ref=datetime(2000,1,1,0,0,0)
#generate a list of dates
dates=[]
for t in time:
    date_now=date_ref + timedelta(seconds=np.float64(t))
    dates.append(date_now)

# %% UV animation figure 

skip = 2

# # setup figure object
fig = plt.figure(figsize=(12,8))
#ax = fig.add_axes([0, 0, 1, 1], projection=ccrs.PlateCarree())
ax = plt.axes(projection=ccrs.PlateCarree())
#ax.set_extent([25.55,26.55,-34.1,-33.65])
ax.add_feature(cfeature.LAND, zorder=0, edgecolor='black')
gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True,
                  linewidth=1, color='gray', alpha=0.5, linestyle=':')
gl.left_labels = False
gl.top_labels = False
# add the vectors
U = U_child[0,:,:]
V = V_child[0,:,:]
qf=ax.quiver(lon_child[::skip,::skip], lat_child[::skip,::skip], U_child[0,::skip,::skip], V_child[0,::skip,::skip], scale=8, transform=ccrs.PlateCarree())
fig.tight_layout()
qk=ax.quiverkey(qf,0.7, 0.7, -0.5, '0.5 m s$^{-1}$', coordinates = 'figure', transform=ccrs.PlateCarree())
# show the time stamp
time_lon=18.6; time_lat=-34.00
tx=ax.text(time_lon, time_lat, str(dates[0]),
         horizontalalignment='left',
         transform=ccrs.PlateCarree())

# animation function
def animate(i):
    #cont.remove()
    Uchd = U_child[i,::skip,::skip]
    Vchd = V_child[i,::skip,::skip]
    qf.set_UVC(Uchd, Vchd) 
    tx.set_text(str(dates[i]))

anim = FuncAnimation(
    fig, animate, interval=200, frames=range(0,240,1)) #len(time)-48

anim.save('Forecast_UV_child_test_processed.gif', writer='imagemagick')

# %% Temperature animations 
levels = np.linspace(10,22,20,endpoint=True)

skip = 2

# # setup figure object
fig = plt.figure(figsize=(12,8))
#ax = fig.add_axes([0, 0, 1, 1], projection=ccrs.PlateCarree())
ax = plt.axes(projection=ccrs.PlateCarree())
#plt.contourf(lon_child, lat_child, SST_child[0,:,:])
ax.set_extent([np.nanmin(lon_child),np.nanmax(lon_child),np.nanmin(lat_child),np.max(lat_child)])
ax.add_feature(cfeature.LAND, zorder=0, edgecolor='black')
gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True,
                  linewidth=1, color='gray', alpha=0.5, linestyle=':')
gl.left_labels = False
gl.top_labels = False
# add the vectors
SST = SST_child[0,:,:]
cont = ax.contourf(lon_child, lat_child, SST,cmap='RdYlBu_r'); 
cont1 = ax.contour(lon_child, lat_child, SST,levels,colors='k'); 
ax.contourf(lon_child, lat_child, SST_child[0,:,:],cmap='RdYlBu_r')
cont3 = ax.contour(lon_child, lat_child, SST_child[0,:,:],levels,colors='k')
fig.tight_layout()
# show the time stamp
time_lon=18.6; time_lat=-34.00
tx=ax.text(time_lon, time_lat, str(dates[0]),
         horizontalalignment='left',
         transform=ccrs.PlateCarree())

# animation function
def animate(i):
    global cont
    for c in cont.collections:
        c.remove()  # removes only the contours, leaves the rest intact
        #cont.remove()
    SST = SST_child[i,:,:]
    cont = ax.contourf(lon_child, lat_child, SST,levels,cmap='RdYlBu_r');
    global cont1
    for c in cont1.collections:
        c.remove()  # removes only the contours, leaves the rest intact
        #cont.remove()
    cont1 = ax.contour(lon_child, lat_child, SST,levels,colors='k')

    global cont3
    for c in cont3.collections:
        c.remove()  # removes only the contours, leaves the rest intact
        #cont.remove()
    cont3 = ax.contour(lon_child, lat_child, SST_child[i,:,:],levels,colors='k')
    ax.contourf(lon_child, lat_child, SST_child[i,:,:],levels,cmap='RdYlBu_r')
    tx.set_text(str(dates[i]))

    return cont

anim = FuncAnimation(
    fig, animate, interval=200, frames=range(0,240,1)) #len(time)-48

anim.save('Forecast_test_processed.gif', writer='imagemagick')