diff --git a/.binder/environment-python_and_r.yml b/.binder/environment-python_and_r.yml index 8db45885..931f04b6 100644 --- a/.binder/environment-python_and_r.yml +++ b/.binder/environment-python_and_r.yml @@ -10,6 +10,7 @@ dependencies: - cc-plugin-ncei - cf_xarray - cftime + - ckanapi - compliance-checker - cython - descartes diff --git a/.binder/environment.yml b/.binder/environment.yml index 78f9e7f9..abf5e3ca 100644 --- a/.binder/environment.yml +++ b/.binder/environment.yml @@ -10,6 +10,7 @@ dependencies: - cc-plugin-ncei - cf_xarray - cftime + - ckanapi - compliance-checker - descartes - easyargs diff --git a/jupyterbook/content/code_gallery/data_access_notebooks/2024-09-17-CKAN_API_Query.ipynb b/jupyterbook/content/code_gallery/data_access_notebooks/2024-09-17-CKAN_API_Query.ipynb new file mode 100644 index 00000000..58a9d2cf --- /dev/null +++ b/jupyterbook/content/code_gallery/data_access_notebooks/2024-09-17-CKAN_API_Query.ipynb @@ -0,0 +1,2030 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "AIX-_9o2P07V", + "outputId": "09d2de4a-1f34-4518-c51f-487e14b55b66" + }, + "outputs": [], + "source": [ + "# For Google Colab\n", + "\n", + "import subprocess\n", + "import sys\n", + "\n", + "COLAB = \"google.colab\" in sys.modules\n", + "\n", + "\n", + "def _install(package):\n", + " if COLAB:\n", + " ans = input(f\"Install { package }? [y/n]:\")\n", + " if ans.lower() in [\"y\", \"yes\"]:\n", + " subprocess.check_call(\n", + " [sys.executable, \"-m\", \"pip\", \"install\", \"--quiet\", package]\n", + " )\n", + " print(f\"{ package } installed!\")\n", + "\n", + "\n", + "def _colab_install_missing_deps(deps):\n", + " import importlib\n", + "\n", + " for dep in deps:\n", + " if importlib.util.find_spec(dep) is None:\n", + " if dep == \"iris\":\n", + " dep = \"scitools-iris\"\n", + " _install(dep)\n", + "\n", + "\n", + "deps = [\"ckanapi\", \"geopandas\"]\n", + "\n", + "\n", + "_colab_install_missing_deps(deps)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Programmatically query the IOOS Data Catalog for a specific observation type" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Created: 2024-09-17\n", + "\n", + "Updated: 2024-09-18\n", + "\n", + "Author: [Mathew Biddle](mailto:mathew.biddle@noaa.gov)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dl6UQcydrdtx" + }, + "source": [ + "In this notebook we highlight the ability to search the [IOOS Data Catalog](https://data.ioos.us/) for a specific subset of observations using the [CKAN](https://ckan.org/) web accessible Application Programming Interface (API). \n", + "\n", + "For this example, we want to look for observations of oxygen in the water column across the IOOS Catalog. As part of the [IOOS Metadata Profile](https://ioos.github.io/ioos-metadata/), which the US IOOS community uses to publish datasets, we know that each Regional Association and DAC will be following the [Climate and Forecast (CF) Conventions](http://cfconventions.org/) and using CF `standard_names` to describe their datasets. So, with that assumption, we can search across the IOOS Data catalog for datasets with the CF standard names that contain `oxygen` and `sea_water`. Then, we can build a simple map to show the geographical distribution of those datasets." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Fz_XVNHerUus" + }, + "source": [ + "## Build CKAN API query base.\n", + "\n", + "Uses https://github.com/ckan/ckanapi" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "8ilaNW-tPtVy", + "outputId": "9bf22340-1404-48e6-b3e8-fdc2cd6a3d23" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from ckanapi import RemoteCKAN\n", + "ua = 'ckanapiioos/1.0 (+https://ioos.us/)'\n", + "\n", + "#ioos_catalog = RemoteCKAN('https://data.ioos.us', user_agent=ua, get_only=True)\n", + "ioos_catalog = RemoteCKAN('https://data.ioos.us', user_agent=ua)\n", + "ioos_catalog" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9DISgdWPrRd0" + }, + "source": [ + "## What organizations are in the catalog?\n", + "\n", + "Tell me what organizations are there." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "O4joF0z8Px-m", + "outputId": "95a91429-b9aa-4350-b67c-6d0f07e4a12c" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['aoos', 'caricoos', 'cdip', 'cencoos', 'comt', 'gcoos', 'glider-dac', 'glos', 'hf-radar-dac', 'ioos', 'maracoos', 'nanoos', 'neracoos', 'noaa-co-ops', 'noaa-ndbc', 'oceansites', 'pacioos', 'sccoos', 'secoora', 'unidata', 'usgs', 'us-navy']\n" + ] + } + ], + "source": [ + "orgs = ioos_catalog.action.organization_list()\n", + "print(orgs)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yN2ELDQZrNah" + }, + "source": [ + "## How many datasets are we searching across?\n", + "\n", + "Grab all the datasets available and return the count." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "_ov9sSwpP8VP", + "outputId": "4d06f0ca-0ad6-4260-ecd5-52f8019652be" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "43574" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#datasets = ioos_catalog.action.package_search(fq='+cf_standard_names:mass_concentration_of_oxygen_in_sea_water', rows=50)\n", + "datasets = ioos_catalog.action.package_search()\n", + "datasets['count']" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rkgC5oLfmGB1" + }, + "source": [ + "## Grab the most recent applicable CF standard names\n", + "\n", + "Collect [CF standard names](https://cfconventions.org/Data/cf-standard-names/current/build/cf-standard-name-table.html) that contain `oxygen` and `sea_water` from the CF standard name list." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 394 + }, + "id": "enKjucgnXivM", + "outputId": "9d2c266f-755a-42dc-9b5e-40607552c56f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CF Standard Name Table: 86\n" + ] + }, + { + "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", + "
iddescription
469depth_at_shallowest_local_minimum_in_vertical_...Depth is the vertical distance below the surfa...
624fractional_saturation_of_oxygen_in_sea_waterFractional saturation is the ratio of some mea...
1357mass_concentration_of_oxygen_in_sea_waterMass concentration means mass per unit volume ...
1725mole_concentration_of_dissolved_molecular_oxyg...Mole concentration means number of moles per u...
1726mole_concentration_of_dissolved_molecular_oxyg...\"Mole concentration at saturation\" means the m...
1727mole_concentration_of_dissolved_molecular_oxyg...Mole concentration means number of moles per u...
1825mole_concentration_of_preformed_dissolved_mole...\"Mole concentration\" means the number of moles...
1996moles_of_oxygen_per_unit_mass_in_sea_watermoles_of_X_per_unit_mass_inY is also called \"m...
3203surface_molecular_oxygen_partial_pressure_diff...The surface called \"surface\" means the lower b...
3700temperature_of_sensor_for_oxygen_in_sea_waterTemperature_of_sensor_for_oxygen_in_sea_water ...
4776volume_fraction_of_oxygen_in_sea_water\"Volume fraction\" is used in the construction ...
4780volume_mixing_ratio_of_oxygen_at_stp_in_sea_water\"ratio_of_X_to_Y\" means X/Y. \"stp\" means stand...
\n", + "
" + ], + "text/plain": [ + " id \\\n", + "469 depth_at_shallowest_local_minimum_in_vertical_... \n", + "624 fractional_saturation_of_oxygen_in_sea_water \n", + "1357 mass_concentration_of_oxygen_in_sea_water \n", + "1725 mole_concentration_of_dissolved_molecular_oxyg... \n", + "1726 mole_concentration_of_dissolved_molecular_oxyg... \n", + "1727 mole_concentration_of_dissolved_molecular_oxyg... \n", + "1825 mole_concentration_of_preformed_dissolved_mole... \n", + "1996 moles_of_oxygen_per_unit_mass_in_sea_water \n", + "3203 surface_molecular_oxygen_partial_pressure_diff... \n", + "3700 temperature_of_sensor_for_oxygen_in_sea_water \n", + "4776 volume_fraction_of_oxygen_in_sea_water \n", + "4780 volume_mixing_ratio_of_oxygen_at_stp_in_sea_water \n", + "\n", + " description \n", + "469 Depth is the vertical distance below the surfa... \n", + "624 Fractional saturation is the ratio of some mea... \n", + "1357 Mass concentration means mass per unit volume ... \n", + "1725 Mole concentration means number of moles per u... \n", + "1726 \"Mole concentration at saturation\" means the m... \n", + "1727 Mole concentration means number of moles per u... \n", + "1825 \"Mole concentration\" means the number of moles... \n", + "1996 moles_of_X_per_unit_mass_inY is also called \"m... \n", + "3203 The surface called \"surface\" means the lower b... \n", + "3700 Temperature_of_sensor_for_oxygen_in_sea_water ... \n", + "4776 \"Volume fraction\" is used in the construction ... \n", + "4780 \"ratio_of_X_to_Y\" means X/Y. \"stp\" means stand... " + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "\n", + "url = \"https://cfconventions.org/Data/cf-standard-names/current/src/cf-standard-name-table.xml\"\n", + "\n", + "tbl_version = pd.read_xml(url, xpath=\"./*\")['version_number'][0].astype(int)\n", + "\n", + "df = pd.read_xml(url, xpath=\"entry\")\n", + "\n", + "std_names = df.loc[(df['id'].str.contains('oxygen') & df['id'].str.contains('sea_water'))]\n", + "\n", + "print('CF Standard Name Table: {}'.format(tbl_version))\n", + "\n", + "std_names[['id','description']]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "G-KxN2RlpLOu" + }, + "source": [ + "## Search across IOOS Data Catalog using CKAN API\n", + "\n", + "Search the IOOS Data Catalog for CF standard names that match those above." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "xI6wTAPqXnt1", + "outputId": "4101e59a-dd91-45d4-95d9-0b8821b0013d" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "depth_at_shallowest_local_minimum_in_vertical_profile_of_mole_concentration_of_dissolved_molecular_oxygen_in_sea_water\n", + "num_results: 0, result_count: 0\n", + "num_results: 0, result_count: 0\n", + "fractional_saturation_of_oxygen_in_sea_water\n", + "num_results: 987, result_count: 0\n", + "num_results: 987, result_count: 500\n", + "num_results: 987, result_count: 1000\n", + "mass_concentration_of_oxygen_in_sea_water\n", + "num_results: 2735, result_count: 0\n", + "num_results: 2735, result_count: 500\n", + "num_results: 2735, result_count: 1000\n", + "num_results: 2735, result_count: 1500\n", + "num_results: 2735, result_count: 2000\n", + "num_results: 2735, result_count: 2500\n", + "num_results: 2735, result_count: 3000\n", + "mole_concentration_of_dissolved_molecular_oxygen_in_sea_water\n", + "num_results: 300, result_count: 0\n", + "num_results: 300, result_count: 300\n", + "mole_concentration_of_dissolved_molecular_oxygen_in_sea_water_at_saturation\n", + "num_results: 0, result_count: 0\n", + "num_results: 0, result_count: 0\n", + "mole_concentration_of_dissolved_molecular_oxygen_in_sea_water_at_shallowest_local_minimum_in_vertical_profile\n", + "num_results: 0, result_count: 0\n", + "num_results: 0, result_count: 0\n", + "mole_concentration_of_preformed_dissolved_molecular_oxygen_in_sea_water\n", + "num_results: 0, result_count: 0\n", + "num_results: 0, result_count: 0\n", + "moles_of_oxygen_per_unit_mass_in_sea_water\n", + "num_results: 813, result_count: 0\n", + "num_results: 813, result_count: 500\n", + "num_results: 813, result_count: 1000\n", + "surface_molecular_oxygen_partial_pressure_difference_between_sea_water_and_air\n", + "num_results: 0, result_count: 0\n", + "num_results: 0, result_count: 0\n", + "temperature_of_sensor_for_oxygen_in_sea_water\n", + "num_results: 167, result_count: 0\n", + "num_results: 167, result_count: 167\n", + "volume_fraction_of_oxygen_in_sea_water\n", + "num_results: 18, result_count: 0\n", + "num_results: 18, result_count: 18\n", + "volume_mixing_ratio_of_oxygen_at_stp_in_sea_water\n", + "num_results: 0, result_count: 0\n", + "num_results: 0, result_count: 0\n" + ] + }, + { + "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", + "
titleurlorgstd_name
0St. Lucie Estuary - South Fork 2 (SLE-SF2)https://erddap.secoora.org/erddap/tabledap/st-...SECOORAfractional_saturation_of_oxygen_in_sea_water
0Neuse River at Marker 15 (ModMon 70, AWS J8903...https://erddap.secoora.org/erddap/tabledap/neu...SECOORAfractional_saturation_of_oxygen_in_sea_water
0Pamlico Sound at PS9 (ModMon)https://erddap.secoora.org/erddap/tabledap/pam...SECOORAfractional_saturation_of_oxygen_in_sea_water
0Indian River Lagoon - Jensen Beach (IRL-JB)https://erddap.secoora.org/erddap/tabledap/ind...SECOORAfractional_saturation_of_oxygen_in_sea_water
0Indian River Lagoon - Sebastian (IRL-SB)https://erddap.secoora.org/erddap/tabledap/ind...SECOORAfractional_saturation_of_oxygen_in_sea_water
...............
0Walton-Smith CTD, WS22215, WS22215_2022_08_Wea...https://gcoos5.geos.tamu.edu/erddap/tabledap/W...GCOOSvolume_fraction_of_oxygen_in_sea_water
0Walton-Smith CTD, WS22215, WS22215_2022_08_Wea...https://gcoos5.geos.tamu.edu/erddap/tabledap/W...GCOOSvolume_fraction_of_oxygen_in_sea_water
0Walton-Smith CTD, WS22215, WS22215_2022_08_Wea...https://gcoos5.geos.tamu.edu/erddap/tabledap/W...GCOOSvolume_fraction_of_oxygen_in_sea_water
0Walton-Smith CTD, WS22215, WS22215_2022_08_Wea...https://gcoos5.geos.tamu.edu/erddap/tabledap/W...GCOOSvolume_fraction_of_oxygen_in_sea_water
0Walton-Smith CTD, WS22215, WS22215_2022_08_Wea...https://gcoos5.geos.tamu.edu/erddap/tabledap/W...GCOOSvolume_fraction_of_oxygen_in_sea_water
\n", + "

5485 rows × 4 columns

\n", + "
" + ], + "text/plain": [ + " title \\\n", + "0 St. Lucie Estuary - South Fork 2 (SLE-SF2) \n", + "0 Neuse River at Marker 15 (ModMon 70, AWS J8903... \n", + "0 Pamlico Sound at PS9 (ModMon) \n", + "0 Indian River Lagoon - Jensen Beach (IRL-JB) \n", + "0 Indian River Lagoon - Sebastian (IRL-SB) \n", + ".. ... \n", + "0 Walton-Smith CTD, WS22215, WS22215_2022_08_Wea... \n", + "0 Walton-Smith CTD, WS22215, WS22215_2022_08_Wea... \n", + "0 Walton-Smith CTD, WS22215, WS22215_2022_08_Wea... \n", + "0 Walton-Smith CTD, WS22215, WS22215_2022_08_Wea... \n", + "0 Walton-Smith CTD, WS22215, WS22215_2022_08_Wea... \n", + "\n", + " url org \\\n", + "0 https://erddap.secoora.org/erddap/tabledap/st-... SECOORA \n", + "0 https://erddap.secoora.org/erddap/tabledap/neu... SECOORA \n", + "0 https://erddap.secoora.org/erddap/tabledap/pam... SECOORA \n", + "0 https://erddap.secoora.org/erddap/tabledap/ind... SECOORA \n", + "0 https://erddap.secoora.org/erddap/tabledap/ind... SECOORA \n", + ".. ... ... \n", + "0 https://gcoos5.geos.tamu.edu/erddap/tabledap/W... GCOOS \n", + "0 https://gcoos5.geos.tamu.edu/erddap/tabledap/W... GCOOS \n", + "0 https://gcoos5.geos.tamu.edu/erddap/tabledap/W... GCOOS \n", + "0 https://gcoos5.geos.tamu.edu/erddap/tabledap/W... GCOOS \n", + "0 https://gcoos5.geos.tamu.edu/erddap/tabledap/W... GCOOS \n", + "\n", + " std_name \n", + "0 fractional_saturation_of_oxygen_in_sea_water \n", + "0 fractional_saturation_of_oxygen_in_sea_water \n", + "0 fractional_saturation_of_oxygen_in_sea_water \n", + "0 fractional_saturation_of_oxygen_in_sea_water \n", + "0 fractional_saturation_of_oxygen_in_sea_water \n", + ".. ... \n", + "0 volume_fraction_of_oxygen_in_sea_water \n", + "0 volume_fraction_of_oxygen_in_sea_water \n", + "0 volume_fraction_of_oxygen_in_sea_water \n", + "0 volume_fraction_of_oxygen_in_sea_water \n", + "0 volume_fraction_of_oxygen_in_sea_water \n", + "\n", + "[5485 rows x 4 columns]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from ckanapi import RemoteCKAN\n", + "import time\n", + "ua = 'ckanapiioos/1.0 (+https://ioos.us/)'\n", + "\n", + "#ioos_catalog = RemoteCKAN('https://data.ioos.us', user_agent=ua, get_only=True)\n", + "ioos_catalog = RemoteCKAN('https://data.ioos.us', user_agent=ua)\n", + "ioos_catalog\n", + "\n", + "df_out = pd.DataFrame()\n", + "\n", + "for std_name in std_names['id']:\n", + "\n", + " print(std_name)\n", + "\n", + " fq = '+cf_standard_names:{}'.format(std_name)\n", + "\n", + " result_count = 0\n", + "\n", + " while True:\n", + " datasets = ioos_catalog.action.package_search(fq=fq, rows=500)\n", + "\n", + " num_results = datasets['count']\n", + "\n", + " print(f\"num_results: {num_results}, result_count: {result_count}\")\n", + "\n", + " for dataset in datasets['results']:\n", + " #print(dataset['title'])\n", + " df = pd.DataFrame({'title': [dataset['title']],\n", + " 'url': [dataset['resources'][0]['url']],\n", + " 'org': [dataset['organization']['title']],\n", + " 'std_name':std_name})\n", + "\n", + " df_out = pd.concat([df_out, df])\n", + "\n", + " result_count = result_count + 1\n", + "\n", + " time.sleep(1)\n", + "\n", + " if(result_count >= num_results):\n", + " print(f\"num_results: {num_results}, result_count: {result_count}\")\n", + " break\n", + "\n", + "df_out" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "A0atXi0kEnbr" + }, + "source": [ + "## Do some summarizing of the responses\n", + "\n", + "The DataFrame of the matching datasets is quite large. I wonder what the distribution of those datasets across organizations looks like? Let's use [pandas.groupby()](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.groupby.html) to generate some statistics about how many datasets are provided, matching our criteria, by which organization." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 363 + }, + "id": "K3tcW2iyDpFd", + "outputId": "aacb7d85-9c9c-4fe8-ba2e-74d805a7deb5" + }, + "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", + "
titleurlstd_name
org
CeNCOOS222
GCOOS220822082208
Glider DAC262526252625
NERACOOS282828
PacIOOS161616
SECOORA606606606
\n", + "
" + ], + "text/plain": [ + " title url std_name\n", + "org \n", + "CeNCOOS 2 2 2\n", + "GCOOS 2208 2208 2208\n", + "Glider DAC 2625 2625 2625\n", + "NERACOOS 28 28 28\n", + "PacIOOS 16 16 16\n", + "SECOORA 606 606 606" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_out.groupby(by='org').count()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "t9PwQW_7Gj0a" + }, + "source": [ + "## Drop the Glider DAC data\n", + "\n", + "Glider DAC data are already making it to NCEI, so we can drop those entries." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 423 + }, + "id": "u0y8KMRYGhl9", + "outputId": "21f54f00-02e1-497b-a45e-83ee0004ca4b" + }, + "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", + "
titleurlstd_name
org
CeNCOOS222
GCOOS220822082208
NERACOOS282828
PacIOOS161616
SECOORA606606606
\n", + "
" + ], + "text/plain": [ + " title url std_name\n", + "org \n", + "CeNCOOS 2 2 2\n", + "GCOOS 2208 2208 2208\n", + "NERACOOS 28 28 28\n", + "PacIOOS 16 16 16\n", + "SECOORA 606 606 606" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_out_no_glider = df_out.loc[~df_out['org'].str.contains('Glider DAC')]\n", + "df_out_no_glider.groupby(by='org').count()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yVz8vdNkEt6X" + }, + "source": [ + "## Digging into some of the nuances\n", + "\n", + "There are still quite a lot of datasets from each organization. As our search above looked for each CF standard_name across all the datasets, there might be duplicate datasets which have multiple matching CF standard names. ie. one dataset might have both `mass_concentration_of_oxygen_in_sea_water` and `fractional_saturation_of_oxygen_in_sea_water`, but we only need to know that it's one dataset.\n", + "\n", + "As we only need to know about the unique datasets, let's count how many unique dataset urls we have." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 455 + }, + "id": "0wygCC8X5Tpc", + "outputId": "48be015f-7e8d-45af-b2cd-e7a37fa6bf86" + }, + "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", + "
titleorgstd_name
url
http://www.neracoos.org/erddap/tabledap/A01_optode_all333
http://www.neracoos.org/erddap/tabledap/GRBGBWQ_NERRS444
http://www.neracoos.org/erddap/tabledap/GRBLRWQ_NERRS444
http://www.neracoos.org/erddap/tabledap/LOBO_CSV_65111
http://www.neracoos.org/erddap/tabledap/URI_168-MV_BottomSonde161616
............
https://gcoos5.geos.tamu.edu/erddap/tabledap/deepwater_pe972250_ctd666
https://gcoos5.geos.tamu.edu/erddap/tabledap/deepwater_pe972274_ctd666
https://gcoos5.geos.tamu.edu/erddap/tabledap/deepwater_st093lay_ctd666
https://pae-paha.pacioos.hawaii.edu/erddap/tabledap/hui_water_quality888
https://pae-paha.pacioos.hawaii.edu/erddap/tabledap/nss_012888
\n", + "

422 rows × 3 columns

\n", + "
" + ], + "text/plain": [ + " title org std_name\n", + "url \n", + "http://www.neracoos.org/erddap/tabledap/A01_opt... 3 3 3\n", + "http://www.neracoos.org/erddap/tabledap/GRBGBWQ... 4 4 4\n", + "http://www.neracoos.org/erddap/tabledap/GRBLRWQ... 4 4 4\n", + "http://www.neracoos.org/erddap/tabledap/LOBO_CS... 1 1 1\n", + "http://www.neracoos.org/erddap/tabledap/URI_168... 16 16 16\n", + "... ... ... ...\n", + "https://gcoos5.geos.tamu.edu/erddap/tabledap/de... 6 6 6\n", + "https://gcoos5.geos.tamu.edu/erddap/tabledap/de... 6 6 6\n", + "https://gcoos5.geos.tamu.edu/erddap/tabledap/de... 6 6 6\n", + "https://pae-paha.pacioos.hawaii.edu/erddap/tabl... 8 8 8\n", + "https://pae-paha.pacioos.hawaii.edu/erddap/tabl... 8 8 8\n", + "\n", + "[422 rows x 3 columns]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_out_no_glider.groupby(by='url').count()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Drop duplicate records\n", + "\n", + "As you can see above, there are a lot of duplicate dataset urls which we can simplify down. We identify duplicates by looking at the URL, which should be unique for each dataset, and drop the duplicates." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 423 + }, + "id": "H6rQj51d7cAm", + "outputId": "0942f042-834a-415c-e276-ad0e89e732ed" + }, + "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", + "
titleurlorgstd_name
0Neuse River near the south shore (ModMon 96)https://erddap.secoora.org/erddap/tabledap/neu...SECOORAfractional_saturation_of_oxygen_in_sea_water
0Great Bay,NH. Lamprey River WQ stationhttp://www.neracoos.org/erddap/tabledap/GRBLRW...NERACOOSfractional_saturation_of_oxygen_in_sea_water
0Great Bay,NH. Great Bay WQ stationhttp://www.neracoos.org/erddap/tabledap/GRBGBW...NERACOOSfractional_saturation_of_oxygen_in_sea_water
0WACCASASSA RIVER NR GULF HAMMOCK, FLA. (USGS 0...https://erddap.secoora.org/erddap/tabledap/gov...SECOORAmass_concentration_of_oxygen_in_sea_water
0LITTLE BACK RIVER AT HOG ISLAND, NEAR SAVANNAH...https://erddap.secoora.org/erddap/tabledap/gov...SECOORAmass_concentration_of_oxygen_in_sea_water
...............
0Walton-Smith CTD, WS22215, WS22215_2022_08_Wea...https://gcoos5.geos.tamu.edu/erddap/tabledap/W...GCOOSvolume_fraction_of_oxygen_in_sea_water
0Walton-Smith CTD, WS22215, WS22215_2022_08_Wea...https://gcoos5.geos.tamu.edu/erddap/tabledap/W...GCOOSvolume_fraction_of_oxygen_in_sea_water
0Walton-Smith CTD, WS22215, WS22215_2022_08_Wea...https://gcoos5.geos.tamu.edu/erddap/tabledap/W...GCOOSvolume_fraction_of_oxygen_in_sea_water
0Walton-Smith CTD, WS22215, WS22215_2022_08_Wea...https://gcoos5.geos.tamu.edu/erddap/tabledap/W...GCOOSvolume_fraction_of_oxygen_in_sea_water
0Walton-Smith CTD, WS22215, WS22215_2022_08_Wea...https://gcoos5.geos.tamu.edu/erddap/tabledap/W...GCOOSvolume_fraction_of_oxygen_in_sea_water
\n", + "

422 rows × 4 columns

\n", + "
" + ], + "text/plain": [ + " title \\\n", + "0 Neuse River near the south shore (ModMon 96) \n", + "0 Great Bay,NH. Lamprey River WQ station \n", + "0 Great Bay,NH. Great Bay WQ station \n", + "0 WACCASASSA RIVER NR GULF HAMMOCK, FLA. (USGS 0... \n", + "0 LITTLE BACK RIVER AT HOG ISLAND, NEAR SAVANNAH... \n", + ".. ... \n", + "0 Walton-Smith CTD, WS22215, WS22215_2022_08_Wea... \n", + "0 Walton-Smith CTD, WS22215, WS22215_2022_08_Wea... \n", + "0 Walton-Smith CTD, WS22215, WS22215_2022_08_Wea... \n", + "0 Walton-Smith CTD, WS22215, WS22215_2022_08_Wea... \n", + "0 Walton-Smith CTD, WS22215, WS22215_2022_08_Wea... \n", + "\n", + " url org \\\n", + "0 https://erddap.secoora.org/erddap/tabledap/neu... SECOORA \n", + "0 http://www.neracoos.org/erddap/tabledap/GRBLRW... NERACOOS \n", + "0 http://www.neracoos.org/erddap/tabledap/GRBGBW... NERACOOS \n", + "0 https://erddap.secoora.org/erddap/tabledap/gov... SECOORA \n", + "0 https://erddap.secoora.org/erddap/tabledap/gov... SECOORA \n", + ".. ... ... \n", + "0 https://gcoos5.geos.tamu.edu/erddap/tabledap/W... GCOOS \n", + "0 https://gcoos5.geos.tamu.edu/erddap/tabledap/W... GCOOS \n", + "0 https://gcoos5.geos.tamu.edu/erddap/tabledap/W... GCOOS \n", + "0 https://gcoos5.geos.tamu.edu/erddap/tabledap/W... GCOOS \n", + "0 https://gcoos5.geos.tamu.edu/erddap/tabledap/W... GCOOS \n", + "\n", + " std_name \n", + "0 fractional_saturation_of_oxygen_in_sea_water \n", + "0 fractional_saturation_of_oxygen_in_sea_water \n", + "0 fractional_saturation_of_oxygen_in_sea_water \n", + "0 mass_concentration_of_oxygen_in_sea_water \n", + "0 mass_concentration_of_oxygen_in_sea_water \n", + ".. ... \n", + "0 volume_fraction_of_oxygen_in_sea_water \n", + "0 volume_fraction_of_oxygen_in_sea_water \n", + "0 volume_fraction_of_oxygen_in_sea_water \n", + "0 volume_fraction_of_oxygen_in_sea_water \n", + "0 volume_fraction_of_oxygen_in_sea_water \n", + "\n", + "[422 rows x 4 columns]" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_out_nodups_no_glider = df_out_no_glider.drop_duplicates(subset=['url'],keep='last')\n", + "\n", + "df_out_nodups_no_glider" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UDsBe8k9E-47" + }, + "source": [ + "## How many endpoints are not ERDDAP?\n", + "\n", + "Now we have a unique list of datasets which match our CF standard name criteria. Since we have some background in using [ERDDAP to query for data](https://ioos.github.io/ioos_code_lab/content/code_gallery/data_access_notebooks/2017-03-21-ERDDAP_IOOS_Sensor_Map.html), let's take a look at what other endpoints each of the datasets are using.\n", + "\n", + "_Hint: We know ERDDAP systems typically have `erddap` in their urls._" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 423 + }, + "id": "saDpOVP5778u", + "outputId": "f0a8f4bb-e77a-4020-b295-f66cfd57fb64" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
titleurlorgstd_name
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: [title, url, org, std_name]\n", + "Index: []" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_out_nodups_no_glider.loc[~df_out_nodups_no_glider['url'].str.contains('erddap')]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "f_PGG_wsHYwF" + }, + "source": [ + "## What's the remaining distribution?\n", + "\n", + "This is the distribution of unique datasets found in the IOOS Data Catalog which have a CF Standard Name that contains the work `oxygen` and `sea_water`. We've dropped out the Glider DAC datasets as, theoretically, those are in NCEI already." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 331 + }, + "id": "fRHL-7lPGwOL", + "outputId": "b72e8a38-1e63-44b1-9644-5eaa7df9abc6" + }, + "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", + "
titleurlstd_name
org
CeNCOOS222
GCOOS378378378
NERACOOS555
PacIOOS222
SECOORA353535
\n", + "
" + ], + "text/plain": [ + " title url std_name\n", + "org \n", + "CeNCOOS 2 2 2\n", + "GCOOS 378 378 378\n", + "NERACOOS 5 5 5\n", + "PacIOOS 2 2 2\n", + "SECOORA 35 35 35" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_out_nodups_no_glider.groupby(by='org').count()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Ingest data\n", + "\n", + "Let's rip through all of the datasets, grab the data as a table (including units) and make a monster dictionary. This takes a bit." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "id": "Wk9myBdgBUxH" + }, + "outputs": [], + "source": [ + "dict_out_final = {}\n", + "\n", + "for index,row in df_out_nodups_no_glider.iterrows():\n", + " #print(row)\n", + " dict_out_final['{}'.format(row['title'])] = pd.read_csv('{}.csvp'.format(row['url']), low_memory=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's take a quick look at one of the DataFrames.\n", + "\n", + "Transpose it when we print, so we can see all the columns." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
01234
profileNaNNaNNaNNaNNaN
time (UTC)1968-01-20T03:14:07Z1968-01-20T03:14:07Z1968-01-20T03:14:07Z1968-01-20T03:14:07Z1968-01-20T03:14:07Z
latitude (degrees_north)29.247229.247229.247229.247229.2472
longitude (degrees_east)-87.888901-87.888901-87.888901-87.888901-87.888901
numberOfLevel351351351351351
depth (m)7.08.09.010.011.0
temperature (degree_C)20.82320.83200120.83720.83799920.854
salinity (PSU)35.32199935.33200135.33599935.33800135.345001
oxygen (milliliters per liter)4.594.624.634.634.75
nitrite (micromols)0.00.00.00.00.01
nitrate (micromols)0.080.080.110.110.1
phosphate (micromols)0.020.010.00.010.01
silicate (micromols)1.331.181.131.051.41
salinity2 (PSU)36.10300136.09299936.09400236.09600136.285999
qualityFlag0.00.00.00.00.0
instrumentNaNNaNNaNNaNNaN
instrument1NaNNaNNaNNaNNaN
instrument2NaNNaNNaNNaNNaN
instrument3NaNNaNNaNNaNNaN
instrument4NaNNaNNaNNaNNaN
instrument5NaNNaNNaNNaNNaN
\n", + "
" + ], + "text/plain": [ + " 0 1 \\\n", + "profile NaN NaN \n", + "time (UTC) 1968-01-20T03:14:07Z 1968-01-20T03:14:07Z \n", + "latitude (degrees_north) 29.2472 29.2472 \n", + "longitude (degrees_east) -87.888901 -87.888901 \n", + "numberOfLevel 351 351 \n", + "depth (m) 7.0 8.0 \n", + "temperature (degree_C) 20.823 20.832001 \n", + "salinity (PSU) 35.321999 35.332001 \n", + "oxygen (milliliters per liter) 4.59 4.62 \n", + "nitrite (micromols) 0.0 0.0 \n", + "nitrate (micromols) 0.08 0.08 \n", + "phosphate (micromols) 0.02 0.01 \n", + "silicate (micromols) 1.33 1.18 \n", + "salinity2 (PSU) 36.103001 36.092999 \n", + "qualityFlag 0.0 0.0 \n", + "instrument NaN NaN \n", + "instrument1 NaN NaN \n", + "instrument2 NaN NaN \n", + "instrument3 NaN NaN \n", + "instrument4 NaN NaN \n", + "instrument5 NaN NaN \n", + "\n", + " 2 3 \\\n", + "profile NaN NaN \n", + "time (UTC) 1968-01-20T03:14:07Z 1968-01-20T03:14:07Z \n", + "latitude (degrees_north) 29.2472 29.2472 \n", + "longitude (degrees_east) -87.888901 -87.888901 \n", + "numberOfLevel 351 351 \n", + "depth (m) 9.0 10.0 \n", + "temperature (degree_C) 20.837 20.837999 \n", + "salinity (PSU) 35.335999 35.338001 \n", + "oxygen (milliliters per liter) 4.63 4.63 \n", + "nitrite (micromols) 0.0 0.0 \n", + "nitrate (micromols) 0.11 0.11 \n", + "phosphate (micromols) 0.0 0.01 \n", + "silicate (micromols) 1.13 1.05 \n", + "salinity2 (PSU) 36.094002 36.096001 \n", + "qualityFlag 0.0 0.0 \n", + "instrument NaN NaN \n", + "instrument1 NaN NaN \n", + "instrument2 NaN NaN \n", + "instrument3 NaN NaN \n", + "instrument4 NaN NaN \n", + "instrument5 NaN NaN \n", + "\n", + " 4 \n", + "profile NaN \n", + "time (UTC) 1968-01-20T03:14:07Z \n", + "latitude (degrees_north) 29.2472 \n", + "longitude (degrees_east) -87.888901 \n", + "numberOfLevel 351 \n", + "depth (m) 11.0 \n", + "temperature (degree_C) 20.854 \n", + "salinity (PSU) 35.345001 \n", + "oxygen (milliliters per liter) 4.75 \n", + "nitrite (micromols) 0.01 \n", + "nitrate (micromols) 0.1 \n", + "phosphate (micromols) 0.01 \n", + "silicate (micromols) 1.41 \n", + "salinity2 (PSU) 36.285999 \n", + "qualityFlag 0.0 \n", + "instrument NaN \n", + "instrument1 NaN \n", + "instrument2 NaN \n", + "instrument3 NaN \n", + "instrument4 NaN \n", + "instrument5 NaN " + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict_out_final[\"\\\"Deepwater CTD - pe972218.ctd.nc - 29.25N, -87.89W - 1997-03-21\\\"\"].head(5).T" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Let's make a nice map of the distribution of observations\n", + "\n", + "Below we create a mapping function to plot the unique dataset points on a map. Then, we use that function with our full response. We have to do a little reorganizing of the data to build one DataFrame for all the coordinates." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "import geopandas as gpd\n", + "import matplotlib.pyplot as plt\n", + "\n", + "def make_map(df):\n", + " # initialize an axis\n", + " fig, ax = plt.subplots(figsize=(8,6))# plot map on axis\n", + " countries = gpd.read_file( \n", + " gpd.datasets.get_path(\"naturalearth_lowres\"))\n", + "\n", + " countries[countries[\"name\"] == \"United States of America\"].plot(color=\"lightgrey\",\n", + " ax=ax)\n", + "\n", + " # plot points\n", + " df.plot(x=\"longitude (degrees_east)\", y=\"latitude (degrees_north)\", \n", + " kind=\"scatter\",\n", + " ax=ax)# add grid\n", + "\n", + " ax.grid(visible=True, alpha=0.5)\n", + "\n", + " return ax" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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ixcvZnBNY263G/X19QOuG8zIyEB2dnafnSWIUkn3phddXV0wm83JLoeIiBIs5vA6b948fP755xg3blwi6qEECgQCaGpqgt1u7/ccjUaDnJwc/nFCacVsNkMURbZkIyIaA6IKr2+99Vb43xdddBF+/vOfY9++fZg0aVKvmblLLrkkvhVSXLS3t6OhoaHXEgGVSgWtVguNRgONRsOZVkpbDK5ERGNDVOH10ksv7XXsvvvu63VMEIR+109ScomiiFAoFBFUNRoN17ISERFRWokqvB7fDovST0ZGRngjASIiIqJ0FXOS2bBhQ58tlXw+HzZs2BCXoij+BEFgcCUiIqK0F3Oaue6669DR0dHruNPpxHXXXReXooiIiIiI+hJzeO3vit6jR4/CaDTGpSgiIiIior5E3SprypQpEAQBgiDg29/+dsQONsFgEFVVVbjgggsSUiQRERERERBDeO3uOLBnzx7MmzcvYhcbhUKBkpIS/OAHP4h7gURERERE3aIOr/feey+CwSCKi4sxb9485ObmJrIuIiIiIqJeYlrzKpVKcfPNNw+4rSgRERERUaLEfMHWpEmTUFlZmYhaiIiIiIgGFHN4ffDBB3HXXXfh73//OxoaGuBwOCI+iIiIiIgSJeo1r926OwpccsklES2zultocXtYIiIiIkqUmMPrBx98kIg6iIiIiIgGFXN4nT17diLqICIiIiIaVMzhFQDa29vx/PPPY//+/RAEARMnTsT111/PHbaIiIiIKKFivmDr888/x/jx4/HYY4/BZrOhtbUVa9euxfjx47Fr165E1EhEREREBGAIM6933HEHLrnkEjz33HPhLWIDgQB++tOfYsmSJdi6dWvciyQiIiIiAoYQXj///POI4AoAMpkMy5Ytw9SpU+NaHBERERFRTzEvGzAYDDhy5Eiv47W1tdDr9XEpioiIiIioLzGH1x/96EdYuHAh/vznP6O2thZHjx7Fn/70J/z0pz/FggULElEjERERERGAISwb+PWvfw1BEHD11VcjEAgAAORyOX72s5/hoYceinuBRERERETdYg6vCoUCjz/+OFavXo3Dhw9DFEWUlZVBo9Ekoj4iIiIiorAh9XkFAI1Gg0mTJsWzFiIiIiKiAcUcXjs7O/HQQw/hX//6F5qbmxEKhSJur6ysjFtxREREREQ9xRxef/rTn2LLli246qqrkJub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KwsWiyXlc13Ml5GFQiHcf//9yM/Ph06nC1/p9qtf/QrPP/983AskIkonoigyvBIlUaosHbDZbCkRXOscfnxe14U6R//vCGk0GpSVlcFqtaZ8cAWGEF4feOABvPjii1izZg0UCkX4+KRJk/D73/8+rsUREaWbUCiUVuvaiEabVAivbW1tqK+vT2oNTm8Q9/yrCTe9VY8VHzTjprfqcc+/muDyftPpQKPRoLi4GKWlpVAqlUmsNjYxh9cNGzbg2WefxZVXXhmxFdvkyZPxn//8J67FERGlG866EiVXsttltba2oqGhIak1AMAj21qxpzHytdjT6MGaba3Q6/UYN24cxo0bB71enxazrT3FvCikrq4OZWVlvY6HQiFepEBEYx5/DhIlVzJnXtva2tDY2Ji05+9W5/BjV0PvEB8SgV0NHoS0Fmg0miRUFh8xz7yefPLJ+Oijj3odf/XVVzFlypS4FEVElK4480qUXIFAIGITgJF83qamphF/3r40OAf+OVTdlt7b6MY883rvvffiqquuQl1dHUKhEF5//XUcOHAAGzZswN///vdE1EhElDYYXomSz+v1jvjMYnNzM0Kh0Ig+Z39y9QPHuxJzem+hG3N4vfjii/HnP/8Zq1atgiAIuOeee3D66afjb3/7G+bOnZuIGomI0obL5Up2CURjns/nG3Z49fl8aGlp6bUMob8LMru6uob1fMOxq74LB1q9mGBR4vQ8NU4pzsKscj+2V7Qi2KNeqSBgZpkFpZYxFl4BYN68eZg3b168ayEiSmuhUAidnen9dhzRaDDc4Gq321FXVxenahKnwenHne80wuH9ZsbXoJTgzVuK8MSCKbht425sPdQSvm1mmQVPLEj/JZ6p0cWXiGgUcLlcbJNFlGQajSailedQ6HQ6SCSSlFkG0J/jgysAOLwhXPbMJ9h9z/nYsPAsVLV2orqtEyVmbdrPuHaLKryaTKao2yjYbLZhFURElK64ZIAo+YxG47AfQy6XQ6fTweFwxKGixNhV39UruHazu/346FALzi23otQyekJrt6jC67p168L/bmtrwwMPPIB58+Zh+vTpAICPP/4Y7777Ln71q18lpEgiolQniiKcTmeyyyAa8+IRXgFArVandHg90DpwS7BdR+w4t9w6QtWMrKjC6zXXXBP+9w9+8APcd999uPXWW8PHFi9ejCeffBLvvfce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fx4MBrF06dLwcy1btgyiKEbcRxRFrFmzBuPGjYNarcapp56Kv/zlLwO+Dq+88gpOPfVUFBQURBx/8cUXUVRUBI1Gg+9///toa2vrdd+//e1vOOOMM6BSqTBu3DisXLkSgUAgfPt//vMfnHPOOVCpVJg4cSLee+89CIKAv/71rwCA6upqCIKAV155BXPmzIFKpcIf//hHAMALL7yAk046CSqVCieeeCJ++9vfRjx3XV0dfvSjH8FkMsFsNmP+/Pmorq4O3/7hhx/irLPOglarRUZGBmbOnImampoBX4tov661a9di0qRJ0Gq1KCwsxKJFi+ByucK319TU4OKLL4bJZIJWq8XJJ5+Mf/7zn6iursZ5550HADCZTBAEAddee234fpdccgk2btwYVY1ElHwMr0Q0oq699lp8/vnneOutt/Dxxx9DFEV897vfjZj5c7vd+PWvf40//OEP2Lp1K44cOYK77rorfPvDDz+Ml156CS+88AK2b98Oh8MRDmZ9ef3111FQUID77rsPDQ0NEUF4MI8++ij+7//+D88//zy2bdsGm82GN954I+KcX/7yl3jhhRfw9NNP4+uvv8Ydd9yBn/zkJ9iyZUu/j7t161ZMnTo14tinn36K66+/HosWLcKePXtw3nnn4YEHHog4591338VPfvITLF68GPv27cPvfvc7vPjii3jwwQcBAKFQCJdeeik0Gg0+/fRTPPvss1i+fHmfNfziF7/A4sWLsX//fsybNw/PPfccli9fjgcffBD79+/HqlWr8Ktf/Qrr168HcGxczjvvPOh0OmzduhXbtm0L/3Hh8/kQCARw6aWXYvbs2fjyyy/x8ccf48Ybbwz/sTCQwb4uAJBIJPjNb36Dr776CuvXr8f777+PZcuWhW+/5ZZb4PV6sXXrVuzduxcPP/wwdDodCgsL8dprrwE4ts64oaEBjz/+ePh+Z511Fj777DN4vd5B6ySiFCASESXQ7Nmzxdtvv10URVE8ePCgCEDcvn17+PbW1lZRrVaLr7zyiiiKovjCCy+IAMSKiorwOU899ZSYnZ0d/jw7O1t85JFHwp8HAgGxqKhInD9/fp/PK4qiWFxcLD722GMRtd17773iqaeeGnHsscceE4uLi8Of5+bmig899FD4c7/fLxYUFISfy+VyiSqVStyxY0fE4yxcuFBcsGBBv6/LqaeeKt53330RxxYsWCBecMEFEcd+9KMfiUajMfz5ueeeK65atSrinD/84Q9ibm6uKIqi+Pbbb4symUxsaGgI375582YRgPjGG2+IoiiKVVVVIgBx3bp1EY9TWFgovvzyyxHH7r//fnH69OmiKIri888/L06YMEEMhULh271er6hWq8V3331XbGtrEwGIH374Yb9fd38G+7r68sorr4hmszn8+aRJk8QVK1b0ee4HH3wgAhDtdnuv2/7973+LAMTq6uqY6yaikSdLYm4mojFm//79kMlkmDZtWviY2WzGhAkTsH///vAxjUaD8ePHhz/Pzc1Fc3MzAKCjowNNTU0466yzwrdLpVKcccYZCIVCca23o6MDDQ0NmD59eviYTCbD1KlTw0sH9u3bB4/Hg7lz50bc1+fzYcqUKf0+dldXF1QqVcSx/fv34/vf/37EsenTp+Odd94Jf/7FF19g586dETOSwWAQHo8HbrcbBw4cQGFhIXJycsK393yteuo589vS0oLa2losXLgQN9xwQ/h4IBCA0WgMP3dFRUWvtcQejweHDx/G+eefj2uvvRbz5s3D3Llz8Z3vfAeXX345cnNz+30dov26NBoNPvjgA6xatQr79u2Dw+FAIBCAx+NBZ2cntFotFi9ejJ/97GfYtGkTvvOd7+AHP/gBJk+ePOhzq9VqAMdmloko9TG8EtGIEY9bK9rzeM+3luVyecTtgiD0uu/xb0X399gDkUgkve4X64VL3YH5H//4B/Lz8yNuUyqV/d7PYrHAbrdHHIvmawiFQli5ciUuu+yyXrepVKper+VAtFptxOMCwHPPPRfxxwVw7I+D7nPOOOOMPi9uslqtAI6tmV28eDHeeecd/PnPf8Yvf/lLbN68GWefffawvq6amhp897vfxc0334z7778fmZmZ2LZtGxYuXBges5/+9KeYN28e/vGPf2DTpk1YvXo1Hn30Udx2220DPrfNZov4GogotTG8EtGImThxIgKBAD799FPMmDEDANDW1oaDBw/ipJNOiuoxjEYjsrOz8dlnn+Hcc88FcGyGbvfu3b0uvupJoVAgGAxGHLNarWhsbIwIfHv27Il4rtzcXHzyySeYNWsWgGMzkV988QVOP/308NekVCpx5MgRzJ49O6qvAQCmTJmCffv2RRybOHEiPvnkk4hjx39++umn48CBAygrK+vzcU888UQcOXIETU1NyM7OBgDs3Llz0Hqys7ORn5+PyspKXHnllX2ec/rpp+PPf/4zsrKyYDAYBvzapkyZgrvvvhvTp0/Hyy+/PGh4Hezr+vzzzxEIBPDoo49CIjl2ucYrr7zS67zCwkLcfPPNuPnmm3H33Xfjueeew2233QaFQgEAvb4HAOCrr75CQUEBLBbLgDUSUWpgeCWiEVNeXo758+fjhhtuwO9+9zvo9Xr8z//8D/Lz8zF//vyoH+e2227D6tWrUVZWhhNPPBFPPPEE7Hb7gDOOJSUl2Lp1K3784x9DqVTCYrFgzpw5aGlpwZo1a/D//t//wzvvvIO33347IpjdfvvteOihh1BeXo6TTjoJa9eujegVqtfrcdddd+GOO+5AKBTCOeecA4fDgR07dkCn0+Gaa67ps5558+bhpz/9KYLBYHhmc/HixZgxYwbWrFmDSy+9FJs2bYpYMgAc60v6ve99D4WFhfjhD38IiUSCL7/8Env37sUDDzyAuXPnYvz48bjmmmuwZs0aOJ3O8AVbg83IrlixAosXL4bBYMCFF14Ir9eLzz//HHa7HUuXLsWVV16JRx55BPPnz8d9992HgoICHDlyBK+//jp+/vOfw+/349lnn8Ull1yCvLw8HDhwAAcPHsTVV1894PNG83WNHz8egUAATzzxBC6++GJs374dzzzzTMRjLFmyBBdeeCFOOOEE2O12vP/+++E/ioqLiyEIAv7+97/ju9/9LtRqNXQ6HQDgo48+wvnnnz9ojUSUIpK22paIxoTjL5yy2WziVVddJRqNRlGtVovz5s0TDx48GL79hRdeiLhASRRF8Y033hB7/rjy+/3irbfeKhoMBtFkMom/+MUvxB/+8Ifij3/8436f9+OPPxYnT54sKpXKiMd6+umnxcLCQlGr1YpXX321+OCDD0ZcsOX3+8Xbb79dNBgMYkZGhrh06VLx6quvjrg4LBQKiY8//rg4YcIEUS6Xi1arVZw3b564ZcuWfl+XQCAg5ufni++8807E8eeff14sKCgQ1Wq1ePHFF4u//vWve70e77zzjjhjxgxRrVaLBoNBPOuss8Rnn302fPv+/fvFmTNnigqFQjzxxBPFv/3tbyKA8HN1X7C1e/fuXnW99NJL4mmnnSYqFArRZDKJs2bNEl9//fXw7Q0NDeLVV18tWiwWUalUiuPGjRNvuOEGsaOjQ2xsbBQvvfRSMTc3V1QoFGJxcbF4zz33iMFgsN/XIZava+3atWJubm74+2bDhg0RF2Hdeuut4vjx40WlUilarVbxqquuEltbW8P3v++++8ScnBxREATxmmuuEUVRFLu6ukSDwSB+/PHHUdVIRMkniOIQFooREaWQUCiEk046CZdffnnErlqp7re//S3efPNNvPvuuwl9nu3bt+Occ85BRUVFxIVwBDz11FN48803sWnTpmSXQkRR4rIBIko7NTU12LRpE2bPng2v14snn3wSVVVVuOKKK5JdWkxuvPFG2O12OJ3OuO4G9sYbb0Cn06G8vBwVFRW4/fbbMXPmTAbXPsjlcjzxxBPJLoOIYsCZVyJKO7W1tfjxj3+Mr776CqIo4pRTTsFDDz0UvqhqrNuwYQPuv/9+1NbWwmKx4Dvf+Q4effRRmM3mpNV08skn97vT1u9+97t+LxIjIjoewysRESVcTU1Nv23IsrOz4zrzTESjG8MrEREREaUNSbILICIiIiKKFsMrEREREaUNhlciIiIiShsMr0RERESUNhheiYiIiChtMLwSERERUdpgeCUiIiKitMHwSkRERERp4/8DBw7XGLnmGAcAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "df_coords = pd.DataFrame(columns=['latitude (degrees_north)','longitude (degrees_east)'])\n", + "\n", + "for key in dict_out_final.keys():\n", + " df_coords = pd.concat([df_coords, dict_out_final[key][['latitude (degrees_north)','longitude (degrees_east)']]])\n", + "\n", + "# drop all duplicates\n", + "df_coords_clean = df_coords.drop_duplicates(ignore_index=True)\n", + "\n", + "# make the map\n", + "make_map(df_coords_clean)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Lets explore those points on an interactive map\n", + "\n", + "Just for fun, we can us [`geopandas.explore()`](https://geopandas.org/en/stable/docs/reference/api/geopandas.GeoDataFrame.explore.html) to plot these points on an interactive map to browse around." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
Make this Notebook Trusted to load map: File -> Trust Notebook
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gdf = gpd.GeoDataFrame(\n", + " df_coords_clean, geometry=gpd.points_from_xy(df_coords_clean['longitude (degrees_east)'], df_coords_clean['latitude (degrees_north)']), crs=\"EPSG:4326\"\n", + ")\n", + "\n", + "gdf.explore()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We hope this example demonstrates the flexibility of direct requests to the IOOS Data Catalog CKAN server and all the possibilities it provides. In this notebook we:\n", + "\n", + "* Search the IOOS Data Catalog CKAN API with keywords.\n", + "* Found datasets matching our specified criteria.\n", + "* Collected all the data from each of the datasets matching our criteria.\n", + "* Created a simple map of the distribution of datasets which match our criteria.\n", + "\n", + "To take this one step further, since we collected all the data from each of the datasets (in the dictionary `dict_out_final`) a user could integrate all of the oxygen observations together and start to build a comprehensive dataset. \n", + "\n", + "Additionally, a user could modify the CKAN query to search for terms outside of the CF standard names to potentially gather more datasets. " + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyOq6Zm4CP25L4Z2jB+P61RB", + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.11.0" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +}