diff --git a/docs/user_guide/defaults.rst b/docs/user_guide/defaults.rst index fc5e2a8..fbf7b0e 100644 --- a/docs/user_guide/defaults.rst +++ b/docs/user_guide/defaults.rst @@ -33,7 +33,7 @@ The default is return_as = "xarray" [nearesttime] - within = "1H" + within = "1h" return_as = "xarray" The ``[default]`` section are global settings used by each download method. These can be overwritten for each method. For instance, *s3_refresh* is set to false for ``[timerange]`` because it's unlikely you will need to refresh the file listing. Also, ``[latest]`` and ``[nearesttime]`` are by default returned as an xarray object instead of a list of files downloaded. diff --git a/docs/user_guide/notebooks/DEMO_download_goes_single_point_timerange.ipynb b/docs/user_guide/notebooks/DEMO_download_goes_single_point_timerange.ipynb new file mode 100644 index 0000000..c37a019 --- /dev/null +++ b/docs/user_guide/notebooks/DEMO_download_goes_single_point_timerange.ipynb @@ -0,0 +1,1958 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Isaac Barlet** \n", + "*June 13, 2024*\n", + "\n", + "# Download GOES Data: Single Point Timerange\n", + "For all options, refer to the GOES-2-go Reference Guide: [goes2go.data.goes_single_point_timerange](https://blaylockbk.github.io/goes2go/_build/html/reference_guide/index.html#goes2go.data.goes_single_point_timerange)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "c:\\Users\\isaac\\Documents\\GitHub\\goes2go\\docs\\user_guide\\notebooks\\../../..\\goes2go\\data.py:665: FutureWarning: 'H' is deprecated and will be removed in a future version. Please use 'h' instead of 'H'.\n", + " within=pd.to_timedelta(config[\"nearesttime\"].get(\"within\", \"1h\")),\n", + "c:\\Users\\isaac\\Documents\\GitHub\\goes2go\\docs\\user_guide\\notebooks\\../../..\\goes2go\\NEW.py:188: FutureWarning: 'H' is deprecated and will be removed in a future version. Please use 'h' instead of 'H'.\n", + " within=pd.to_timedelta(config[\"nearesttime\"].get(\"within\", \"1h\")),\n" + ] + } + ], + "source": [ + "import sys\n", + "sys.path.append(\"../../../\")\n", + "from goes2go.data import goes_single_point_timerange\n", + "\n", + "from datetime import datetime\n", + "import pandas as pd" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "### Example 1: \n", + "Download an ABI-L2-TWP file from GOES-East for an hour period. Data is returned as a file list. This behaves the same as the normal timerange method" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991001172_e20240991003544_c20240991005435.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991006172_e20240991008544_c20240991010555.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991011172_e20240991013544_c20240991016039.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991016172_e20240991018544_c20240991021029.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991026172_e20240991028545_c20240991030485.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991021172_e20240991023544_c20240991025496.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991031172_e20240991033544_c20240991035361.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991036172_e20240991038544_c20240991040451.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991041172_e20240991043544_c20240991045431.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991056172_e20240991058544_c20240991100530.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991046172_e20240991048545_c20240991050462.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991051172_e20240991053545_c20240991055420.nc\n", + "📦 Finished downloading [12] files to [C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC].\n" + ] + } + ], + "source": [ + "## Latitudes and Longitudes may be specified as floats in degrees or radians\n", + "\n", + "## Specifiy Latitude and Lognitude in degrees\n", + "latitude = 38.897957\n", + "longitude = -77.036560\n", + "\n", + "## Dates may be specified as datetime, pandas datetimes, or string dates\n", + "## that pandas can interpret.\n", + "\n", + "## Specify start/end time with datetime object\n", + "#start = datetime(2024, 4, 8, 10, 0)\n", + "#end = datetime(2024, 4, 8, 11, 0)\n", + "\n", + "## Specify start/end time as a panda-parsable string\n", + "start = '2024-04-08 10:00'\n", + "end = '2024-04-08 11:00'\n", + "\n", + "g = goes_single_point_timerange(latitude, longitude,\n", + " start, end,\n", + " satellite='goes16',\n", + " product='ABI-L2-TPW',\n", + " return_as='filelist',\n", + " decimal_coordinates=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
fileproduct_modesatellitestartendcreationproductmode_bandsmodeband
0noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-...ABI-L2-TPWC-M6G162024-04-08 10:01:17.2002024-04-08 10:03:54.4002024-04-08 10:05:43.500ABI-L2-TPWCM66None
1noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-...ABI-L2-TPWC-M6G162024-04-08 10:06:17.2002024-04-08 10:08:54.4002024-04-08 10:10:55.500ABI-L2-TPWCM66None
2noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-...ABI-L2-TPWC-M6G162024-04-08 10:11:17.2002024-04-08 10:13:54.4002024-04-08 10:16:03.900ABI-L2-TPWCM66None
3noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-...ABI-L2-TPWC-M6G162024-04-08 10:16:17.2002024-04-08 10:18:54.4002024-04-08 10:21:02.900ABI-L2-TPWCM66None
4noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-...ABI-L2-TPWC-M6G162024-04-08 10:21:17.2002024-04-08 10:23:54.4002024-04-08 10:25:49.600ABI-L2-TPWCM66None
5noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-...ABI-L2-TPWC-M6G162024-04-08 10:26:17.2002024-04-08 10:28:54.5002024-04-08 10:30:48.500ABI-L2-TPWCM66None
6noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-...ABI-L2-TPWC-M6G162024-04-08 10:31:17.2002024-04-08 10:33:54.4002024-04-08 10:35:36.100ABI-L2-TPWCM66None
7noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-...ABI-L2-TPWC-M6G162024-04-08 10:36:17.2002024-04-08 10:38:54.4002024-04-08 10:40:45.100ABI-L2-TPWCM66None
8noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-...ABI-L2-TPWC-M6G162024-04-08 10:41:17.2002024-04-08 10:43:54.4002024-04-08 10:45:43.100ABI-L2-TPWCM66None
9noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-...ABI-L2-TPWC-M6G162024-04-08 10:46:17.2002024-04-08 10:48:54.5002024-04-08 10:50:46.200ABI-L2-TPWCM66None
10noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-...ABI-L2-TPWC-M6G162024-04-08 10:51:17.2002024-04-08 10:53:54.5002024-04-08 10:55:42.000ABI-L2-TPWCM66None
11noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-...ABI-L2-TPWC-M6G162024-04-08 10:56:17.2002024-04-08 10:58:54.4002024-04-08 11:00:53.000ABI-L2-TPWCM66None
\n", + "
" + ], + "text/plain": [ + " file product_mode \\\n", + "0 noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-... ABI-L2-TPWC-M6 \n", + "1 noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-... ABI-L2-TPWC-M6 \n", + "2 noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-... ABI-L2-TPWC-M6 \n", + "3 noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-... ABI-L2-TPWC-M6 \n", + "4 noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-... ABI-L2-TPWC-M6 \n", + "5 noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-... ABI-L2-TPWC-M6 \n", + "6 noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-... ABI-L2-TPWC-M6 \n", + "7 noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-... ABI-L2-TPWC-M6 \n", + "8 noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-... ABI-L2-TPWC-M6 \n", + "9 noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-... ABI-L2-TPWC-M6 \n", + "10 noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-... ABI-L2-TPWC-M6 \n", + "11 noaa-goes16/ABI-L2-TPWC/2024/099/10/OR_ABI-L2-... ABI-L2-TPWC-M6 \n", + "\n", + " satellite start end \\\n", + "0 G16 2024-04-08 10:01:17.200 2024-04-08 10:03:54.400 \n", + "1 G16 2024-04-08 10:06:17.200 2024-04-08 10:08:54.400 \n", + "2 G16 2024-04-08 10:11:17.200 2024-04-08 10:13:54.400 \n", + "3 G16 2024-04-08 10:16:17.200 2024-04-08 10:18:54.400 \n", + "4 G16 2024-04-08 10:21:17.200 2024-04-08 10:23:54.400 \n", + "5 G16 2024-04-08 10:26:17.200 2024-04-08 10:28:54.500 \n", + "6 G16 2024-04-08 10:31:17.200 2024-04-08 10:33:54.400 \n", + "7 G16 2024-04-08 10:36:17.200 2024-04-08 10:38:54.400 \n", + "8 G16 2024-04-08 10:41:17.200 2024-04-08 10:43:54.400 \n", + "9 G16 2024-04-08 10:46:17.200 2024-04-08 10:48:54.500 \n", + "10 G16 2024-04-08 10:51:17.200 2024-04-08 10:53:54.500 \n", + "11 G16 2024-04-08 10:56:17.200 2024-04-08 10:58:54.400 \n", + "\n", + " creation product mode_bands mode band \n", + "0 2024-04-08 10:05:43.500 ABI-L2-TPWC M6 6 None \n", + "1 2024-04-08 10:10:55.500 ABI-L2-TPWC M6 6 None \n", + "2 2024-04-08 10:16:03.900 ABI-L2-TPWC M6 6 None \n", + "3 2024-04-08 10:21:02.900 ABI-L2-TPWC M6 6 None \n", + "4 2024-04-08 10:25:49.600 ABI-L2-TPWC M6 6 None \n", + "5 2024-04-08 10:30:48.500 ABI-L2-TPWC M6 6 None \n", + "6 2024-04-08 10:35:36.100 ABI-L2-TPWC M6 6 None \n", + "7 2024-04-08 10:40:45.100 ABI-L2-TPWC M6 6 None \n", + "8 2024-04-08 10:45:43.100 ABI-L2-TPWC M6 6 None \n", + "9 2024-04-08 10:50:46.200 ABI-L2-TPWC M6 6 None \n", + "10 2024-04-08 10:55:42.000 ABI-L2-TPWC M6 6 None \n", + "11 2024-04-08 11:00:53.000 ABI-L2-TPWC M6 6 None " + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "g" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'satellite': 'noaa-goes16',\n", + " 'product': 'ABI-L2-TPWC',\n", + " 'start': Timestamp('2024-04-08 10:00:00'),\n", + " 'end': Timestamp('2024-04-08 11:00:00'),\n", + " 'bands': None,\n", + " 'refresh': False,\n", + " 'filePath': WindowsPath('C:/Users/isaac/data')}" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "g.attrs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "### Example 2: \n", + "Download an ABI file from GOES-East for an hour period. Data is returned as an xarray object containing only the single point spatial point and 12 time points." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991001172_e20240991003544_c20240991005435.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991006172_e20240991008544_c20240991010555.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991016172_e20240991018544_c20240991021029.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991021172_e20240991023544_c20240991025496.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991026172_e20240991028545_c20240991030485.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991011172_e20240991013544_c20240991016039.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991031172_e20240991033544_c20240991035361.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991041172_e20240991043544_c20240991045431.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991036172_e20240991038544_c20240991040451.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991046172_e20240991048545_c20240991050462.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991051172_e20240991053545_c20240991055420.nc\n", + " 👮🏻‍♂️ File already exists. Do not overwrite: C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC\\2024\\099\\10\\OR_ABI-L2-TPWC-M6_G16_s20240991056172_e20240991058544_c20240991100530.nc\n", + "📦 Finished downloading [12] files to [C:\\Users\\isaac\\data\\noaa-goes16\\ABI-L2-TPWC].\n" + ] + } + ], + "source": [ + "g2 = goes_single_point_timerange(latitude, longitude,\n", + " start, end,\n", + " satellite='goes16',\n", + " product='ABI-L2-TPW',\n", + " return_as='xarray',\n", + " decimal_coordinates=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                                           (t: 12,\n",
+       "                                                       number_of_time_bounds: 2,\n",
+       "                                                       number_of_image_bounds: 2,\n",
+       "                                                       number_of_LZA_bounds: 2,\n",
+       "                                                       number_of_SZA_bounds: 2,\n",
+       "                                                       number_of_lat_bounds: 2,\n",
+       "                                                       sounding_emissive_bands: 7)\n",
+       "Coordinates:\n",
+       "  * t                                                 (t) datetime64[ns] 2024...\n",
+       "    y                                                 float64 0.1068\n",
+       "    x                                                 float64 -0.00462\n",
+       "    y_image                                           float32 0.08624\n",
+       "    x_image                                           float32 -0.03136\n",
+       "    retrieval_local_zenith_angle                      float32 80.0\n",
+       "    quantitative_local_zenith_angle                   float32 70.0\n",
+       "    solar_zenith_angle                                float32 180.0\n",
+       "    latitude                                          float32 70.0\n",
+       "    sounding_emissive_wavelengths                     (sounding_emissive_bands) float32 dask.array<chunksize=(7,), meta=np.ndarray>\n",
+       "    sounding_emissive_band_ids                        (sounding_emissive_bands) int8 dask.array<chunksize=(7,), meta=np.ndarray>\n",
+       "Dimensions without coordinates: number_of_time_bounds, number_of_image_bounds,\n",
+       "                                number_of_LZA_bounds, number_of_SZA_bounds,\n",
+       "                                number_of_lat_bounds, sounding_emissive_bands\n",
+       "Data variables: (12/29)\n",
+       "    TPW                                               (t) float32 dask.array<chunksize=(1,), meta=np.ndarray>\n",
+       "    DQF_Overall                                       (t) float32 dask.array<chunksize=(1,), meta=np.ndarray>\n",
+       "    DQF_Retrieval                                     (t) float32 dask.array<chunksize=(1,), meta=np.ndarray>\n",
+       "    DQF_SkinTemp                                      (t) float32 dask.array<chunksize=(1,), meta=np.ndarray>\n",
+       "    time_bounds                                       (t, number_of_time_bounds) datetime64[ns] dask.array<chunksize=(1, 2), meta=np.ndarray>\n",
+       "    goes_imager_projection                            (t) int32 -2147483647 ....\n",
+       "    ...                                                ...\n",
+       "    latitude_bounds                                   (t, number_of_lat_bounds) float32 dask.array<chunksize=(1, 2), meta=np.ndarray>\n",
+       "    percent_uncorrectable_L0_errors                   (t) float32 0.0 ... 0.0\n",
+       "    percent_uncorrectable_GRB_errors                  (t) float32 0.0 ... 0.0\n",
+       "    total_attempted_retrievals                        (t) float64 7.855e+04 ....\n",
+       "    mean_obs_modeled_diff_sounding_emissive_bands     (t, sounding_emissive_bands) float32 dask.array<chunksize=(1, 7), meta=np.ndarray>\n",
+       "    std_dev_obs_modeled_diff_sounding_emissive_bands  (t, sounding_emissive_bands) float32 dask.array<chunksize=(1, 7), meta=np.ndarray>\n",
+       "Attributes: (12/29)\n",
+       "    naming_authority:          gov.nesdis.noaa\n",
+       "    Conventions:               CF-1.7\n",
+       "    Metadata_Conventions:      Unidata Dataset Discovery v1.0\n",
+       "    standard_name_vocabulary:  CF Standard Name Table (v35, 20 July 2016)\n",
+       "    institution:               DOC/NOAA/NESDIS > U.S. Department of Commerce,...\n",
+       "    project:                   GOES\n",
+       "    ...                        ...\n",
+       "    cdm_data_type:             Image\n",
+       "    time_coverage_start:       2024-04-08T10:01:17.2Z\n",
+       "    time_coverage_end:         2024-04-08T10:03:54.4Z\n",
+       "    timeline_id:               ABI Mode 6\n",
+       "    production_data_source:    Realtime\n",
+       "    id:                        58e3d05e-c48b-44b0-8171-c6641c4923c3
" + ], + "text/plain": [ + "\n", + "Dimensions: (t: 12,\n", + " number_of_time_bounds: 2,\n", + " number_of_image_bounds: 2,\n", + " number_of_LZA_bounds: 2,\n", + " number_of_SZA_bounds: 2,\n", + " number_of_lat_bounds: 2,\n", + " sounding_emissive_bands: 7)\n", + "Coordinates:\n", + " * t (t) datetime64[ns] 2024...\n", + " y float64 0.1068\n", + " x float64 -0.00462\n", + " y_image float32 0.08624\n", + " x_image float32 -0.03136\n", + " retrieval_local_zenith_angle float32 80.0\n", + " quantitative_local_zenith_angle float32 70.0\n", + " solar_zenith_angle float32 180.0\n", + " latitude float32 70.0\n", + " sounding_emissive_wavelengths (sounding_emissive_bands) float32 dask.array\n", + " sounding_emissive_band_ids (sounding_emissive_bands) int8 dask.array\n", + "Dimensions without coordinates: number_of_time_bounds, number_of_image_bounds,\n", + " number_of_LZA_bounds, number_of_SZA_bounds,\n", + " number_of_lat_bounds, sounding_emissive_bands\n", + "Data variables: (12/29)\n", + " TPW (t) float32 dask.array\n", + " DQF_Overall (t) float32 dask.array\n", + " DQF_Retrieval (t) float32 dask.array\n", + " DQF_SkinTemp (t) float32 dask.array\n", + " time_bounds (t, number_of_time_bounds) datetime64[ns] dask.array\n", + " goes_imager_projection (t) int32 -2147483647 ....\n", + " ... ...\n", + " latitude_bounds (t, number_of_lat_bounds) float32 dask.array\n", + " percent_uncorrectable_L0_errors (t) float32 0.0 ... 0.0\n", + " percent_uncorrectable_GRB_errors (t) float32 0.0 ... 0.0\n", + " total_attempted_retrievals (t) float64 7.855e+04 ....\n", + " mean_obs_modeled_diff_sounding_emissive_bands (t, sounding_emissive_bands) float32 dask.array\n", + " std_dev_obs_modeled_diff_sounding_emissive_bands (t, sounding_emissive_bands) float32 dask.array\n", + "Attributes: (12/29)\n", + " naming_authority: gov.nesdis.noaa\n", + " Conventions: CF-1.7\n", + " Metadata_Conventions: Unidata Dataset Discovery v1.0\n", + " standard_name_vocabulary: CF Standard Name Table (v35, 20 July 2016)\n", + " institution: DOC/NOAA/NESDIS > U.S. Department of Commerce,...\n", + " project: GOES\n", + " ... ...\n", + " cdm_data_type: Image\n", + " time_coverage_start: 2024-04-08T10:01:17.2Z\n", + " time_coverage_end: 2024-04-08T10:03:54.4Z\n", + " timeline_id: ABI Mode 6\n", + " production_data_source: Realtime\n", + " id: 58e3d05e-c48b-44b0-8171-c6641c4923c3" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "g2" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([9.366711, 9.349924, 9.433856, 9.395705, 9.404861, 9.508631,\n", + " 9.476584, 9.494897, 9.565093, 9.546782, 9.517787, 9.667336],\n", + " dtype=float32)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "g2.TPW.values" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/goes2go/NEW.py b/goes2go/NEW.py index a8c2554..ea00adc 100644 --- a/goes2go/NEW.py +++ b/goes2go/NEW.py @@ -18,7 +18,7 @@ import toml from goes2go import config -from goes2go.data import _goes_file_df, goes_latest, goes_nearesttime, goes_timerange +from goes2go.data import _goes_file_df, goes_latest, goes_nearesttime, goes_timerange, goes_single_point_timerange log = logging.getLogger(__name__) @@ -185,7 +185,7 @@ def latest(self, **kwargs): def nearesttime( self, attime, - within=pd.to_timedelta(config["nearesttime"].get("within", "1H")), + within=pd.to_timedelta(config["nearesttime"].get("within", "1h")), **kwargs, ): """Get the GOES data nearest a specified time. @@ -230,6 +230,36 @@ def timerange(self, start=None, end=None, recent=None, **kwargs): **kwargs, ) + def single_point_timerange(self, latitude, longitude, start=None, end=None, recent=None, decimal_coordinates=True, **kwargs): + """Get GOES data for a time range at the scan point nearest to a defined single latitude/longitude point. + + Parameters + ---------- + latitude, longitude : float + Location where you wish to extract the point values from + start, end : datetime + Required if recent is None. + recent : timedelta or pandas-parsable timedelta str + Required if start and end are None. If timedelta(hours=1), will + get the most recent files for the past hour. + decimal_coordinates: bool + If latitude/longitude are specified in decimal or radian coordinates. + """ + + return goes_single_point_timerange( + latitude, + longitude, + start, + end, + recent, + decimal_coordinates, + satellite=self.satellite, + product=self.product, + domain=self.domain, + bands=self.bands, + **kwargs, + ) + def df(self, start, end, refresh=True): """Get list of requested GOES files as pandas.DataFrame. diff --git a/goes2go/data.py b/goes2go/data.py index 407685c..5cd228f 100644 --- a/goes2go/data.py +++ b/goes2go/data.py @@ -18,13 +18,17 @@ import multiprocessing from concurrent.futures import ThreadPoolExecutor, as_completed, wait from datetime import datetime, timedelta +from functools import partial from pathlib import Path +import sys import numpy as np import pandas as pd import s3fs import xarray as xr +from goes2go.tools import lat_lon_to_scan_angles + # NOTE: These config dict values are retrieved from __init__ and read # from the file ${HOME}/.config/goes2go/config.toml from . import config @@ -133,7 +137,7 @@ def _goes_file_df(satellite, product, start, end, bands=None, refresh=True): start = pd.to_datetime(start) end = pd.to_datetime(end) - DATES = pd.date_range(f"{start:%Y-%m-%d %H:00}", f"{end:%Y-%m-%d %H:00}", freq="1H") + DATES = pd.date_range(f"{start:%Y-%m-%d %H:00}", f"{end:%Y-%m-%d %H:00}", freq="1h") # List all files for each date # ---------------------------- @@ -423,6 +427,148 @@ def goes_timerange( elif return_as == "xarray": return _as_xarray(df, **params) +def _preprocess_single_point(ds, target_lat, target_lon, decimal_coordinates=True): + """ + Preprocessing function to select only the single relevant data subset + + Parameters + ---------- + ds: xarray Dataset + The dataset to look through and choose the particular location + target_lat, target_lon : float + Location where you wish to extract the point values from + decimal_coordinates: bool + If latitude/longitude are specified in decimal or radian coordinates. + """ + x_target, y_target = lat_lon_to_scan_angles(target_lat, target_lon, ds["goes_imager_projection"], decimal_coordinates) + return ds.sel(x=x_target, y=y_target, method="nearest") + +def goes_single_point_timerange( + latitude, + longitude, + start=None, + end=None, + recent=None, + decimal_coordinates=True, + *, + satellite=config["timerange"].get("satellite"), + product=config["timerange"].get("product"), + domain=config["timerange"].get("domain"), + return_as=config["timerange"].get("return_as"), + download=config["timerange"].get("download"), + overwrite=config["timerange"].get("overwrite"), + save_dir=config["timerange"].get("save_dir"), + max_cpus=config["timerange"].get("max_cpus"), + bands=None, + s3_refresh=config["timerange"].get("s3_refresh"), + verbose=config["timerange"].get("verbose", True), +): + """ + Get GOES data for a time range at the scan point nearest to a defined single latitude/longitude point. + + Parameters + ---------- + latitude, longitude : float + Location where you wish to extract the point values from + start, end : datetime + Required if recent is None. + recent : timedelta or pandas-parsable timedelta str + Required if start and end are None. If timedelta(hours=1), will + get the most recent files for the past hour. + decimal_coordinates: bool + If latitude/longitude are specified in decimal or radian coordinates. + satellite : {'goes16', 'goes17', 'goes18'} + Specify which GOES satellite. + The following alias may also be used: + + - ``'goes16'``: 16, 'G16', or 'EAST' + - ``'goes17'``: 17, 'G17', or 'WEST' + - ``'goes18'``: 18, 'G18', or 'WEST' + + product : {'ABI', 'GLM', other GOES product} + Specify the product name. + + - 'ABI' is an alias for ABI-L2-MCMIP Multichannel Cloud and Moisture Imagery + - 'GLM' is an alias for GLM-L2-LCFA Geostationary Lightning Mapper + + Others may include ``'ABI-L1b-Rad'``, ``'ABI-L2-DMW'``, etc. + For more available products, look at this `README + `_ + domain : {'C', 'F', 'M'} + ABI scan region indicator. Only required for ABI products if the + given product does not end with C, F, or M. + + - C: Contiguous United States (alias 'CONUS') + - F: Full Disk (alias 'FULL') + - M: Mesoscale (alias 'MESOSCALE') + + return_as : {'xarray', 'filelist'} + Return the data as an xarray.Dataset or as a list of files + download : bool + - True: Download the data to disk to the location set by :guilabel:`save_dir` + - False: Just load the data into memory. + save_dir : pathlib.Path or str + Path to save the data. + overwrite : bool + - True: Download the file even if it exists. + - False Do not download the file if it already exists + max_cpus : int + bands : None, int, or list + ONLY FOR L1b-Rad products; specify the bands you want + s3_refresh : bool + Refresh the s3fs.S3FileSystem object when files are listed. + + """ + # If `start`, or `end` is a string, parse with Pandas + if isinstance(start, str): + start = pd.to_datetime(start) + if isinstance(end, str): + end = pd.to_datetime(end) + # If `recent` is a string (like recent='1H'), parse with Pandas + if isinstance(recent, str): + recent = pd.to_timedelta(recent) + + params = locals() + satellite, product, domain = _check_param_inputs(**params) + params["satellite"] = satellite + params["product"] = product + params["domain"] = domain + + check1 = start is not None and end is not None + check2 = recent is not None + assert check1 or check2, "🤔 `start` and `end` *or* `recent` is required" + + if check1: + assert hasattr(start, "second") and hasattr( + end, "second" + ), "`start` and `end` must be a datetime object" + elif check2: + assert hasattr(recent, "seconds"), "`recent` must be a timedelta object" + + # Parameter Setup + # --------------- + # Create a range of directories to check. The GOES S3 bucket is + # organized by hour of day. + if recent is not None: + start = datetime.utcnow() - recent + end = datetime.utcnow() + + df = _goes_file_df(satellite, product, start, end, bands=bands, refresh=s3_refresh) + + if download: + _download(df, save_dir=save_dir, overwrite=overwrite, verbose=verbose) + + if return_as == "filelist": + df.attrs["filePath"] = save_dir + return df + elif return_as == "xarray": + partial_func = partial(_preprocess_single_point, target_lat=latitude, target_lon=longitude, decimal_coordinates=decimal_coordinates) + preprocessed_ds = xr.open_mfdataset([str(config['timerange']['save_dir']) + "/" + f for f in df['file'].to_list()], + concat_dim='t', + combine='nested', + preprocess=partial_func) + return preprocessed_ds + def goes_latest( *, @@ -516,7 +662,7 @@ def goes_latest( def goes_nearesttime( attime, - within=pd.to_timedelta(config["nearesttime"].get("within", "1H")), + within=pd.to_timedelta(config["nearesttime"].get("within", "1h")), *, satellite=config["nearesttime"].get("satellite"), product=config["nearesttime"].get("product"), diff --git a/goes2go/tools.py b/goes2go/tools.py index d2253a8..3c7da07 100644 --- a/goes2go/tools.py +++ b/goes2go/tools.py @@ -190,3 +190,111 @@ def glm_crs(G, reference_variable="flash_lat"): dat = G.metpy.parse_cf("flash_lat") crs = dat.metpy.cartopy_crs return crs + + +def scan_angles_to_lat_lon(x, y, goes_imager_projection, decimal_coordinates=True): + """ + Convert ABI scan angle coordinates to geodetic latitude and longitude. + + Scan angles are projected onto the GRS80 elliopsoid. + https://www.goes-r.gov/users/docs/PUG-L1b-vol3.pdf (section 5.1.2.8.1) + + Parameters + ---------- + x : float + A scan angle coordinate in the x direction. + y : float + A scan angle coordinate in the y direction. + goes_imager_projection : xarray.Dataset + An xarray.Dataset with the projection information in it's attributes. + decimal_coordinates : bool + A boolean designating if latitude and longitude should be returned in decimal degrees (returns decimal radians if false). + + Returns + ------- + Two objects are returned + 1. latitude on the GSR80 ellipsoid + 2. longitude on the GSR80 ellipsoid + """ + r_pol = goes_imager_projection.semi_minor_axis + r_eq = goes_imager_projection.semi_major_axis + H = goes_imager_projection.perspective_point_height + r_eq + lambda_0 = np.radians( + goes_imager_projection.longitude_of_projection_origin + ) # Always in degrees from the GOES data + + a = np.sin(x) ** 2 + np.cos(x) ** 2 * ( + np.cos(y) ** 2 + r_eq**2 / r_pol**2 * np.sin(y) ** 2 + ) + b = -2 * H * np.cos(x) * np.cos(y) + c = H**2 - r_eq**2 + + r_s = (-b - np.sqrt(b**2 - 4 * a * c)) / ( + 2 * a + ) # Distance from the satellite to point P + + s_x = r_s * np.cos(x) * np.cos(y) + s_y = -r_s * np.sin(x) + s_z = r_s * np.cos(x) * np.sin(y) + + latitude = np.arctan( + (r_eq**2 / r_pol**2) * (s_z / np.sqrt((H - s_x) ** 2 + s_y**2)) + ) + longitude = lambda_0 - np.arctan(s_y / (H - s_x)) + + if decimal_coordinates: + latitude = np.degrees(latitude) + longitude = np.degrees(longitude) + + return latitude, longitude + + +def lat_lon_to_scan_angles(latitude, longitude, goes_imager_projection, decimal_coordinates=True): + """ + Convert geodetic latitude and longitude to ABI scan angle coordinates. + + Latitude and Longitude inputs are assumed to be located on the GRS80 elliopsoid. + https://www.goes-r.gov/users/docs/PUG-L1b-vol3.pdf (section 5.1.2.8.1) + + Parameters + ---------- + latitude : float + Latitude on the GSR80 ellipsoid. + longitude : float + Longitude on the GSR80 ellipsoid. + goes_imager_projection : xarray.Dataset + An xarray.Dataset with the projection information in it's attributes. + decimal_coordinates : bool + A boolean designating if latitude and longitude inputs are in decimal degrees (returns decimal radians if false). + + Returns + ------- + Two objects are returned + 1. A scan angle coordinate in the x direction measured in radians + 2. A scan angle coordinate in the y direction measured in radians + """ + if decimal_coordinates: + latitude = np.radians(latitude) + longitude = np.radians(longitude) + + r_pol = goes_imager_projection.semi_minor_axis + r_eq = goes_imager_projection.semi_major_axis + H = goes_imager_projection.perspective_point_height + r_eq + e = 0.0818191910435 # eccentricity of GRD 1980 ellipsoid + lambda_0 = np.radians(goes_imager_projection.longitude_of_projection_origin) # Always in degrees from the GOES data + + phi_c = np.arctan((r_pol**2/r_eq**2)*np.tan(latitude)) # Geocentric latitude + r_c = r_pol/(np.sqrt(1-(e**2*np.cos(phi_c)**2))) # Geocentric distance to the point on the ellipsoid + + s_x = H - r_c*np.cos(phi_c)*np.cos(longitude-lambda_0) + s_y = -r_c*np.cos(phi_c)*np.sin(longitude-lambda_0) + s_z = r_c*np.sin(phi_c) + + # Confirm location is visible from the satellite + if (H*(H-s_x) < (s_y**2 + (r_eq**2/r_pol**2)*s_z**2)).any(): + raise ValueError("One or more points not visible by satellite") + + y = np.arctan(s_z/s_x) + x = np.arcsin(-s_y / (np.sqrt(s_x**2+s_y**2+s_z**2))) + + return x, y \ No newline at end of file diff --git a/tests/test_GOES.py b/tests/test_GOES.py index 486e95c..2b5d02a 100644 --- a/tests/test_GOES.py +++ b/tests/test_GOES.py @@ -19,10 +19,12 @@ def test_GOES16_nearesttime(): ds = GOES(satellite=16).nearesttime("2022-01-01") +def test_GOES16_single_point_timerange(): + ds = GOES(satellite=16).single_point_timerange(38.897957, -77.036560, "2022-01-01 00:00", "2022-01-01 01:00") + def test_GOES16_timerange(): ds = GOES(satellite=16).timerange("2022-01-01 00:00", "2022-01-01 01:00") - def test_GOES16_df(): df = GOES(satellite=16).df("2022-01-01 00:00", "2022-01-01 01:00")