From 9c72e61f7b681701114dd33a70471d955750aa65 Mon Sep 17 00:00:00 2001 From: tsutterley Date: Fri, 27 Sep 2024 05:22:25 -0700 Subject: [PATCH] docs: add notebook with a cotidal chart for #344 --- doc/source/user_guide/Examples.rst | 8 + notebooks/Check Tide Map.ipynb | 2 - notebooks/Plot Antarctic Cotidal Charts.ipynb | 309 ++++++++++++++++++ notebooks/Plot Antarctic Tidal Currents.ipynb | 2 - notebooks/Plot Antarctic Tide Range.ipynb | 3 +- notebooks/Plot Arctic Ocean Map.ipynb | 2 +- notebooks/Plot Ross Ice Shelf Map.ipynb | 3 +- notebooks/Plot Tide Forecasts.ipynb | 2 - notebooks/Solve Synthetic Tides.ipynb | 2 - pyTMD/tools.py | 27 +- test/test_download_and_read.py | 2 +- 11 files changed, 330 insertions(+), 32 deletions(-) create mode 100644 notebooks/Plot Antarctic Cotidal Charts.ipynb diff --git a/doc/source/user_guide/Examples.rst b/doc/source/user_guide/Examples.rst index 7bd1235f..cb194389 100644 --- a/doc/source/user_guide/Examples.rst +++ b/doc/source/user_guide/Examples.rst @@ -14,6 +14,8 @@ The available examples include: Plots a weekly forecast of tidal displacements at a given location - Harmonic Constants Solver |github solve| |nbviewer solve| Solves for the amplitude and phase of harmonic constituents at a given location +- Antarctic Cotidal Chart Mapper |github cotidal| |nbviewer cotidal| + Plot the cotidal charts for selected constituents around Antarctica - Antarctic Tidal Amplitudes Mapper |github range| |nbviewer range| Plot the total combined tidal amplitude for all constituents around Antarctica - Arctic Ocean Tide Mapper |github arctic| |nbviewer arctic| @@ -43,6 +45,12 @@ The available examples include: .. |nbviewer solve| image:: https://raw.githubusercontent.com/jupyter/design/master/logos/Badges/nbviewer_badge.svg :target: https://nbviewer.jupyter.org/github/tsutterley/pyTMD/blob/main/notebooks/Solve\ Synthetic\ Tides.ipynb +.. |github cotidal| image:: https://img.shields.io/badge/GitHub-view-6f42c1?style=flat&logo=Github + :target: https://github.com/tsutterley/pyTMD/blob/main/notebooks/Plot\ Antarctic\ Cotidal\ Charts.ipynb + +.. |nbviewer cotidal| image:: https://raw.githubusercontent.com/jupyter/design/master/logos/Badges/nbviewer_badge.svg + :target: https://nbviewer.jupyter.org/github/tsutterley/pyTMD/blob/main/notebooks/Plot\ Antarctic\ Cotidal\ Charts.ipynb + .. |github currents| image:: https://img.shields.io/badge/GitHub-view-6f42c1?style=flat&logo=Github :target: https://github.com/tsutterley/pyTMD/blob/main/notebooks/Plot\ Antarctic\ Tidal\ Currents.ipynb diff --git a/notebooks/Check Tide Map.ipynb b/notebooks/Check Tide Map.ipynb index a2b3141e..24c42f9f 100644 --- a/notebooks/Check Tide Map.ipynb +++ b/notebooks/Check Tide Map.ipynb @@ -84,7 +84,6 @@ "TMDwidgets.VBox([\n", " TMDwidgets.directory,\n", " TMDwidgets.model,\n", - " TMDwidgets.atlas,\n", " TMDwidgets.compress\n", "])" ] @@ -111,7 +110,6 @@ "source": [ "# get model parameters\n", "model = pyTMD.io.model(TMDwidgets.directory.value,\n", - " format=TMDwidgets.atlas.value,\n", " compressed=TMDwidgets.compress.value\n", " ).elevation(TMDwidgets.model.value)\n", "\n", diff --git a/notebooks/Plot Antarctic Cotidal Charts.ipynb b/notebooks/Plot Antarctic Cotidal Charts.ipynb new file mode 100644 index 00000000..c3e31279 --- /dev/null +++ b/notebooks/Plot Antarctic Cotidal Charts.ipynb @@ -0,0 +1,309 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot Antarctic Cotidal Charts\n", + "=============================\n", + "\n", + "Demonstrates plotting cotidal charts around Antarctica\n", + "\n", + "OTIS format tidal solutions provided by Oregon State University and ESR \n", + "- http://volkov.oce.orst.edu/tides/region.html \n", + "- https://www.esr.org/research/polar-tide-models/list-of-polar-tide-models/\n", + "- ftp://ftp.esr.org/pub/datasets/tmd/ \n", + "\n", + "Global Tide Model (GOT) solutions provided by Richard Ray at GSFC \n", + "\n", + "Finite Element Solution (FES) provided by AVISO \n", + "- https://www.aviso.altimetry.fr/en/data/products/auxiliary-products/global-tide-fes.html\n", + "\n", + "#### Python Dependencies\n", + " - [numpy: Scientific Computing Tools For Python](https://www.numpy.org) \n", + " - [scipy: Scientific Tools for Python](https://www.scipy.org/) \n", + " - [pyproj: Python interface to PROJ library](https://pypi.org/project/pyproj/) \n", + " - [netCDF4: Python interface to the netCDF C library](https://unidata.github.io/netcdf4-python/) \n", + " - [matplotlib: Python 2D plotting library](http://matplotlib.org/) \n", + " - [cartopy: Python package designed for geospatial data processing](https://scitools.org.uk/cartopy/docs/latest/) \n", + "\n", + "#### Program Dependencies\n", + "\n", + "- `arguments.py`: load the nodal corrections for tidal constituents \n", + "- `astro.py`: computes the basic astronomical mean longitudes \n", + "- `crs.py`: Coordinate Reference System (CRS) routines \n", + "- `io.model.py`: retrieves tide model parameters for named tide models\n", + "- `io.OTIS.py`: extract tidal harmonic constants from OTIS tide models \n", + "- `io.ATLAS.py`: extract tidal harmonic constants from ATLAS netcdf models \n", + "- `io.GOT.py`: extract tidal harmonic constants from GOT tide models \n", + "- `io.FES.py`: extract tidal harmonic constants from FES tide models \n", + "- `predict.py`: predict tidal values using harmonic constants \n", + "- `time.py`: utilities for calculating time operations\n", + "- `utilities.py`: download and management utilities for files\n", + "\n", + "This notebook uses Jupyter widgets to set parameters for calculating the cotidal maps. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Load modules" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import pyproj\n", + "import numpy as np\n", + "import matplotlib\n", + "matplotlib.rcParams['axes.linewidth'] = 2.0\n", + "import matplotlib.pyplot as plt\n", + "import cartopy.crs as ccrs\n", + "\n", + "# import tide programs\n", + "import pyTMD.io\n", + "import pyTMD.predict\n", + "import pyTMD.tools\n", + "import pyTMD.utilities\n", + "\n", + "# autoreload\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Set parameters for program\n", + "\n", + "- Model directory \n", + "- Tide model " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# available model list\n", + "model_list = sorted(pyTMD.io.model.ocean_elevation())\n", + "# display widgets for setting directory and model\n", + "TMDwidgets = pyTMD.tools.widgets()\n", + "TMDwidgets.model.options = model_list\n", + "TMDwidgets.model.value = 'CATS2008'\n", + "TMDwidgets.VBox([\n", + " TMDwidgets.directory,\n", + " TMDwidgets.model,\n", + " TMDwidgets.compress,\n", + "])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Setup tide model parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# get model parameters\n", + "model = pyTMD.io.model(TMDwidgets.directory.value,\n", + " compressed=TMDwidgets.compress.value\n", + " ).elevation(TMDwidgets.model.value)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Setup coordinates for calculating tidal currents" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# create an image around Antarctica\n", + "xlimits = [-560.*5e3,560.*5e3]\n", + "ylimits = [-560.*5e3,560.*5e3]\n", + "spacing = [20e3,-20e3]\n", + "# x and y coordinates\n", + "x = np.arange(xlimits[0],xlimits[1]+spacing[0],spacing[0])\n", + "y = np.arange(ylimits[1],ylimits[0]+spacing[1],spacing[1])\n", + "xgrid,ygrid = np.meshgrid(x,y)\n", + "# x and y dimensions\n", + "nx = int((xlimits[1]-xlimits[0])/spacing[0])+1\n", + "ny = int((ylimits[0]-ylimits[1])/spacing[1])+1\n", + "# convert image coordinates from polar stereographic to latitude/longitude\n", + "crs1 = pyproj.CRS.from_epsg(3031)\n", + "crs2 = pyproj.CRS.from_epsg(4326)\n", + "transformer = pyproj.Transformer.from_crs(crs1, crs2, always_xy=True)\n", + "lon,lat = transformer.transform(xgrid.flatten(), ygrid.flatten())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Calculate tide map" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# read tidal constants and interpolate to grid points\n", + "if model.format in ('OTIS','ATLAS-compact','TMD3'):\n", + " amp,ph,D,c = pyTMD.io.OTIS.extract_constants(lon, lat, model.grid_file,\n", + " model.model_file, model.projection, type=model.type, crop=True,\n", + " method='spline', grid=model.file_format)\n", + "elif (model.format == 'ATLAS-netcdf'):\n", + " amp,ph,D,c = pyTMD.io.ATLAS.extract_constants(lon, lat, model.grid_file,\n", + " model.model_file, type=model.type, crop=True, method='spline',\n", + " scale=model.scale, compressed=model.compressed)\n", + "elif model.format in ('GOT-ascii', 'GOT-netcdf'):\n", + " amp,ph,c = pyTMD.io.GOT.extract_constants(lon, lat, model.model_file,\n", + " grid=model.file_format, crop=True, method='spline', scale=model.scale,\n", + " compressed=model.compressed)\n", + "elif (model.format == 'FES-netcdf'):\n", + " amp,ph = pyTMD.io.FES.extract_constants(lon, lat, model.model_file,\n", + " type=model.type, version=model.version, crop=True,\n", + " method='spline', scale=model.scale, compressed=model.compressed)\n", + " c = model.constituents" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Select constituent for cotidal chart" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "TMDwidgets.constituents.options = c\n", + "TMDwidgets.constituents.value = c[0]\n", + "display(TMDwidgets.constituents)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Create cotidal chart" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# extract amplitude and phase for constituent\n", + "i = TMDwidgets.constituents.index\n", + "cons = TMDwidgets.constituents.value\n", + "amplitude = amp[:,i].reshape(ny,nx)\n", + "phase = ph[:,i].reshape(ny,nx)\n", + "\n", + "# plot Antarctic cotidal charts\n", + "projection = ccrs.Stereographic(central_longitude=0.0,\n", + " central_latitude=-90,true_scale_latitude=-71.0)\n", + "fig, ax = plt.subplots(num=1, figsize=(9,8),\n", + " subplot_kw=dict(projection=projection))\n", + "# plot tide amplitude\n", + "extent = (xlimits[0],xlimits[1],ylimits[0],ylimits[1])\n", + "im = ax.imshow(amplitude, interpolation='nearest',\n", + " vmin=0, vmax=amplitude.max(), transform=projection,\n", + " extent=extent, origin='upper')\n", + "# plot tide phase\n", + "contour_levels = np.arange(0, 360, 30)\n", + "contour = ax.contour(xgrid, ygrid, phase,\n", + " contour_levels, colors='0.2', linestyles='solid',\n", + " transform=projection)\n", + "# add high resolution cartopy coastlines\n", + "ax.coastlines('10m')\n", + "\n", + "# Add colorbar and adjust size\n", + "# pad = distance from main plot axis\n", + "# extend = add extension triangles to upper and lower bounds\n", + "# options: neither, both, min, max\n", + "# shrink = percent size of colorbar\n", + "# aspect = lengthXwidth aspect of colorbar\n", + "cbar = plt.colorbar(im, ax=ax, pad=0.025, extend='max',\n", + " extendfrac=0.0375, shrink=0.85, aspect=22.5, drawedges=False)\n", + "# rasterized colorbar to remove lines\n", + "cbar.solids.set_rasterized(True)\n", + "# Add label to the colorbar\n", + "cbar.ax.set_ylabel(f'{model.name} {cons} Tide Chart', labelpad=10, fontsize=13)\n", + "cbar.ax.set_title('cm', fontsize=13, va='bottom')\n", + "# ticks lines all the way across\n", + "cbar.ax.tick_params(which='both', width=1, length=20,\n", + " labelsize=13, direction='in')\n", + "\n", + "# set x and y limits\n", + "ax.set_xlim(xlimits)\n", + "ax.set_ylim(ylimits)\n", + "\n", + "# stronger linewidth on frame\n", + "ax.spines['geo'].set_linewidth(2.0)\n", + "ax.spines['geo'].set_capstyle('projecting')\n", + "# adjust subplot within figure\n", + "fig.subplots_adjust(left=0.02,right=0.98,bottom=0.05,top=0.98)\n", + "\n", + "# show the plot\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.10.4 64-bit", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.14" + }, + "vscode": { + "interpreter": { + "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/notebooks/Plot Antarctic Tidal Currents.ipynb b/notebooks/Plot Antarctic Tidal Currents.ipynb index 38e31ba9..74a9b3ed 100644 --- a/notebooks/Plot Antarctic Tidal Currents.ipynb +++ b/notebooks/Plot Antarctic Tidal Currents.ipynb @@ -105,7 +105,6 @@ "TMDwidgets.VBox([\n", " TMDwidgets.directory,\n", " TMDwidgets.model,\n", - " TMDwidgets.atlas,\n", " TMDwidgets.compress,\n", " TMDwidgets.datepick\n", "])" @@ -126,7 +125,6 @@ "source": [ "# get model parameters\n", "model = pyTMD.io.model(TMDwidgets.directory.value,\n", - " format=TMDwidgets.atlas.value,\n", " compressed=TMDwidgets.compress.value\n", " ).current(TMDwidgets.model.value)" ] diff --git a/notebooks/Plot Antarctic Tide Range.ipynb b/notebooks/Plot Antarctic Tide Range.ipynb index 16db2917..1573b42c 100644 --- a/notebooks/Plot Antarctic Tide Range.ipynb +++ b/notebooks/Plot Antarctic Tide Range.ipynb @@ -34,6 +34,7 @@ "- `io.model.py`: retrieves tide model parameters for named tide models \n", "- `io.OTIS.py`: extract tidal harmonic constants from OTIS tide models \n", "- `io.ATLAS.py`: extract tidal harmonic constants from ATLAS netcdf models \n", + "- `io.GOT.py`: extract tidal harmonic constants from GOT tide models \n", "- `io.FES.py`: extract tidal harmonic constants from FES tide models \n", "\n", "This notebook uses Jupyter widgets to set parameters for calculating the tidal maps. " @@ -94,7 +95,6 @@ "TMDwidgets.VBox([\n", " TMDwidgets.directory,\n", " TMDwidgets.model,\n", - " TMDwidgets.atlas,\n", " TMDwidgets.compress\n", "])" ] @@ -114,7 +114,6 @@ "source": [ "# get model parameters\n", "model = pyTMD.io.model(TMDwidgets.directory.value,\n", - " format=TMDwidgets.atlas.value,\n", " compressed=TMDwidgets.compress.value\n", " ).elevation(TMDwidgets.model.value)" ] diff --git a/notebooks/Plot Arctic Ocean Map.ipynb b/notebooks/Plot Arctic Ocean Map.ipynb index fb1d29f3..13abd338 100644 --- a/notebooks/Plot Arctic Ocean Map.ipynb +++ b/notebooks/Plot Arctic Ocean Map.ipynb @@ -37,6 +37,7 @@ "- `io.OTIS.py`: extract tidal harmonic constants from OTIS tide models \n", "- `io.ATLAS.py`: extract tidal harmonic constants from ATLAS netcdf models \n", "- `io.FES.py`: extract tidal harmonic constants from FES tide models \n", + "- `io.GOT.py`: extract tidal harmonic constants from GOT tide models \n", "- `predict.py`: predict tidal values using harmonic constants \n", "- `time.py`: utilities for calculating time operations\n", "- `utilities.py`: download and management utilities for files\n", @@ -126,7 +127,6 @@ "source": [ "# get model parameters\n", "model = pyTMD.io.model(TMDwidgets.directory.value,\n", - " format=TMDwidgets.atlas.value,\n", " compressed=TMDwidgets.compress.value\n", " ).elevation(TMDwidgets.model.value)" ] diff --git a/notebooks/Plot Ross Ice Shelf Map.ipynb b/notebooks/Plot Ross Ice Shelf Map.ipynb index cd646473..4c10c99e 100644 --- a/notebooks/Plot Ross Ice Shelf Map.ipynb +++ b/notebooks/Plot Ross Ice Shelf Map.ipynb @@ -36,6 +36,7 @@ "- `io.model.py`: retrieves tide model parameters for named tide models\n", "- `io.OTIS.py`: extract tidal harmonic constants from OTIS tide models \n", "- `io.ATLAS.py`: extract tidal harmonic constants from ATLAS netcdf models \n", + "- `io.GOT.py`: extract tidal harmonic constants from GOT tide models \n", "- `io.FES.py`: extract tidal harmonic constants from FES tide models \n", "- `predict.py`: predict tidal values using harmonic constants \n", "- `time.py`: utilities for calculating time operations\n", @@ -107,7 +108,6 @@ "TMDwidgets.VBox([\n", " TMDwidgets.directory,\n", " TMDwidgets.model,\n", - " TMDwidgets.atlas,\n", " TMDwidgets.compress,\n", " TMDwidgets.datepick\n", "])" @@ -128,7 +128,6 @@ "source": [ "# get model parameters\n", "model = pyTMD.io.model(TMDwidgets.directory.value,\n", - " format=TMDwidgets.atlas.value,\n", " compressed=TMDwidgets.compress.value\n", " ).elevation(TMDwidgets.model.value)" ] diff --git a/notebooks/Plot Tide Forecasts.ipynb b/notebooks/Plot Tide Forecasts.ipynb index c8bbd3e9..cb9d46e0 100644 --- a/notebooks/Plot Tide Forecasts.ipynb +++ b/notebooks/Plot Tide Forecasts.ipynb @@ -94,7 +94,6 @@ "TMDwidgets.VBox([\n", " TMDwidgets.directory,\n", " TMDwidgets.model,\n", - " TMDwidgets.atlas,\n", " TMDwidgets.compress,\n", " TMDwidgets.datepick\n", "])" @@ -124,7 +123,6 @@ "\n", "# get model parameters\n", "model = pyTMD.io.model(TMDwidgets.directory.value,\n", - " format=TMDwidgets.atlas.value,\n", " compressed=TMDwidgets.compress.value\n", " ).elevation(TMDwidgets.model.value)\n", "\n", diff --git a/notebooks/Solve Synthetic Tides.ipynb b/notebooks/Solve Synthetic Tides.ipynb index 66ae8fd8..ea888415 100644 --- a/notebooks/Solve Synthetic Tides.ipynb +++ b/notebooks/Solve Synthetic Tides.ipynb @@ -86,7 +86,6 @@ "TMDwidgets.VBox([\n", " TMDwidgets.directory,\n", " TMDwidgets.model,\n", - " TMDwidgets.atlas,\n", " TMDwidgets.compress,\n", "])" ] @@ -130,7 +129,6 @@ "source": [ "# get model parameters\n", "model = pyTMD.io.model(TMDwidgets.directory.value,\n", - " format=TMDwidgets.atlas.value,\n", " compressed=TMDwidgets.compress.value\n", " ).elevation(TMDwidgets.model.value)\n", "\n", diff --git a/pyTMD/tools.py b/pyTMD/tools.py index 5b7d0caf..9616eb68 100644 --- a/pyTMD/tools.py +++ b/pyTMD/tools.py @@ -1,7 +1,7 @@ #!/usr/bin/env python u""" tools.py -Written by Tyler Sutterley (07/2024) +Written by Tyler Sutterley (09/2024) Jupyter notebook, user interface and plotting tools PYTHON DEPENDENCIES: @@ -17,6 +17,8 @@ https://github.com/matplotlib/matplotlib UPDATE HISTORY: + Updated 09/2024: removed widget for ATLAS following database update + added widget for setting constituent to plot in a cotidal chart Updated 07/2024: renamed format for netcdf to ATLAS-netcdf Updated 04/2024: use wrapper to importlib for optional dependencies Updated 12/2023: pass through VBox and HBox @@ -83,16 +85,15 @@ def __init__(self, **kwargs): style=self.style, ) - # dropdown menu for setting ATLAS format model - atlas_list = ['OTIS','ATLAS-netcdf'] - self.atlas = ipywidgets.Dropdown( - options=atlas_list, - value='ATLAS-netcdf', - description='ATLAS:', + # dropdown menu for setting model constituents + constituents_list = ['q1','o1','p1','k1','n2','m2','s2','k2','s1'] + self.constituents = ipywidgets.Dropdown( + options=constituents_list, + value='m2', + description='Constituents:', disabled=False, style=self.style, ) - self.atlas.layout.display = 'none' # checkbox for setting if tide files are compressed self.compress = ipywidgets.Checkbox( @@ -110,16 +111,6 @@ def __init__(self, **kwargs): style=self.style, ) - # watch widgets for changes - self.model.observe(self.set_atlas) - - # function for setting available map layers - def set_atlas(self, sender): - """function for updating ATLAS widget visibility - """ - if (self.model.value in pyTMD.io.model.ATLAS()): - self.atlas.layout.display = 'inline-flex' - # define projections for ipyleaflet tiles projections = dict( # Alaska Polar Stereographic (WGS84) diff --git a/test/test_download_and_read.py b/test/test_download_and_read.py index c7a54b6c..29dee7b1 100644 --- a/test/test_download_and_read.py +++ b/test/test_download_and_read.py @@ -592,7 +592,7 @@ def test_solve(self, SOLVER): LAT,LON = (-76.0, -40.0) amp,ph,D = pyTMD.io.OTIS.interpolate_constants( np.atleast_1d(LON), np.atleast_1d(LAT), - constituents, model.projection, type=model.type, + constituents, type=model.type, method='spline', extrapolate=True) # calculate complex form of constituent oscillation