{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "f12fe8fa",
   "metadata": {},
   "source": [
    "# Plotting IAGOS data\n",
    "\n",
    "This notebook is a demonstration on how to plot data from the IAGOS project in python\n",
    "\n",
    "## Source Data\n",
    "\n",
    "The source data for any plots in this notebook are the from [IAGOS](https://www.iagos.org/) research data which are gained by installing packages on passenger flights and are publicly available and licensed under the [Creative Commons Attribution 4.0 International licence (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/).\n",
    "\n",
    "The Source data is provided as NetCDF files that among others usually have the following variables to analyse:\n",
    "\n",
    "Base data:\n",
    "- UTC_time\n",
    "- lon\n",
    "- lat\n",
    "\n",
    "Data with validity flags (data_name_validity_flag)\n",
    "- baro_alt_AC\n",
    "- radio_alt_AC\n",
    "- air_press_AC\n",
    "- air_temp_AC\n",
    "- air_speed_AC\n",
    "- ground_speed_AC\n",
    "- wind_dir_AC\n",
    "- wind_speed_AC\n",
    "\n",
    "Chemical measurement data (with name_error, name_validity_flag and name_process_flag):\n",
    "- O3_PM\n",
    "- CO_PM\n",
    "- NOy_PM\n",
    "- NO_PM\n",
    "- NOx_PM\n",
    "\n",
    "These variables of course might differ, depending on when the data was recorded. Newer files will have a `air_stag_temp_AC` instead of `air_temp_AC`\n",
    "\n",
    "\n",
    "\n",
    "## Requirements\n",
    "\n",
    "The data is plotted using the python cartopy package, which is based on matplotlib and shapely. Additionally, requests is used to request the data from the IAGOS API.\n",
    "\n",
    "In case you do not have all of these requirements installed, try installing cartopy and requests via your preferred way (usually pip or conda)\n",
    "\n",
    "### Troubleshooting Installation\n",
    "\n",
    "Cartopy installation sadly tends to fail, especially when not installing it via conda, mainly because it requires GEOS\n",
    "\n",
    "#### Debian\n",
    "\n",
    "On Debian, you can install the required system libraries using the system package manager:\n",
    "\n",
    "`sudo apt -y install libgeos-dev`\n",
    "\n",
    "#### Windows\n",
    "\n",
    "On Windows, you can try installing [OSGEO](https://www.osgeo.org/projects/osgeo4w/), and then install from the git repository:\n",
    "\n",
    "```commandline\n",
    "git clone https://github.com/SciTools/cartopy.git\n",
    "cd cartopy\n",
    "python setup.py build_ext -LC:\\OSGeo4W\\lib -IC:\\OSGeo4W\\include\n",
    "python setup.py install\n",
    "```\n",
    "\n",
    "#### MacOS\n",
    "\n",
    "On MacOS, you'll be able to install dependencies via:\n",
    "\n",
    "```\n",
    "brew install geos\n",
    "pip3 install --upgrade pyshp\n",
    "# shapely needs to be built from source to link to geos. If it is already\n",
    "# installed, uninstall it by: pip3 uninstall shapely\n",
    "pip3 install \"shapely<2\" --no-binary shapely\n",
    "```\n",
    "\n",
    "After installing the dependencies, please try\n",
    "\n",
    "`pip3 install cartopy`\n",
    "\n",
    "again."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9b6cec08",
   "metadata": {},
   "source": [
    "## Implementation\n",
    "\n",
    "### Imports\n",
    "\n",
    "Firstly, we'll need to import several packages."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "789066f0",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import sys\n",
    "import matplotlib.pyplot as plt\n",
    "import cartopy.crs as ccrs\n",
    "import cartopy.feature as cfeature\n",
    "import cartopy.io.img_tiles as cimgt\n",
    "import netCDF4 as nC\n",
    "import numpy as np\n",
    "from requests import TooManyRedirects, Timeout, HTTPError, get"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e97b0373",
   "metadata": {},
   "source": [
    "### Getting Data from IAGOS\n",
    "\n",
    "Then, let's get the flight path data from IAGOS using python requests:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "27d1313d",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_file(flight_id):\n",
    "    url = f\"https://services.iagos-data.fr/prod/v2.0/downloads/{flight_id}?level=2&format=netcdf&type=timeseries\"\n",
    "    try:\n",
    "        response = get(url)\n",
    "    except ConnectionError:\n",
    "        print(\"There was a network problem. Try again please\")\n",
    "        return\n",
    "    except HTTPError:\n",
    "        print(\"HTTP request returned an unsuccessful status code. Try again please.\")\n",
    "        return\n",
    "    except Timeout:\n",
    "        print(\"The request timed out. Try again please\")\n",
    "        return\n",
    "    except TooManyRedirects:\n",
    "        print(\"The request was redirected the maximum number of times. Try again please\")\n",
    "        return\n",
    "    if response.status_code != 200:\n",
    "        print(\n",
    "            \"It looks like getting the file for that flight ID was not possible. If you provided the flight ID yourself, it is likely \"\n",
    "            \"the case that the ID provided did not exist.\"\n",
    "        )\n",
    "        sys.exit()\n",
    "    open(\"source.nc4\", \"wb\").write(response.content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0adbecc6",
   "metadata": {},
   "source": [
    "### Plotting functionality\n",
    "Let's also create a class that is able to plot data.\n",
    "\n",
    "The dataset is read from the file we downloaded above and then plotted onto a custom map using the functions we just defined"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b68f8eae",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Plot:\n",
    "\n",
    "    def __init__(self, param):\n",
    "        self.dataset: nC.Dataset = nC.Dataset(\"source.nc4\")\n",
    "        self.fig: plt.figure = plt.figure()\n",
    "        self.ax = self.fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree())\n",
    "        self.data = param\n",
    "        os.remove(\"source.nc4\")\n",
    "\n",
    "    def get_lon(self):\n",
    "        return self.dataset[\"lon\"][:]\n",
    "\n",
    "    def get_lat(self):\n",
    "        return self.dataset[\"lat\"][:]\n",
    "\n",
    "    def get_data(self):\n",
    "        return self.dataset[self.data][:]\n",
    "\n",
    "    def handle_03_data(self):\n",
    "        data = self.get_data()\n",
    "        data[data < 0] = 0\n",
    "        return data\n",
    "\n",
    "    def plot(self):\n",
    "        # use terrain\n",
    "        stamen_terrain = cimgt.Stamen(\"terrain-background\")\n",
    "        self.ax.add_image(stamen_terrain, 3)\n",
    "        # plot the points measured\n",
    "        self.ax.plot(self.get_lon(), self.get_lat(), linewidth=1, color=\"blue\", transform=ccrs.PlateCarree())\n",
    "        # scatter according to data points\n",
    "        self.ax.scatter(self.get_lon(), self.get_lat(), c=np.sqrt(self.handle_03_data()), cmap=\"jet\", s=np.divide(self.handle_03_data(), 2), transform=ccrs.PlateCarree())\n",
    "        # customize map\n",
    "        west = -135\n",
    "        east = 20\n",
    "        south = 10\n",
    "        north = 60\n",
    "        self.ax.set_extent([west, east, south, north])\n",
    "        self.ax.add_feature(cfeature.LAND)\n",
    "        self.ax.add_feature(cfeature.OCEAN)\n",
    "        self.ax.add_feature(cfeature.COASTLINE, edgecolor=\"#888888\", linestyle=\"-\", linewidth=0.5)\n",
    "        self.ax.add_feature(cfeature.BORDERS, linestyle=\":\", linewidth=0.5)  # '', ' ', 'None', '--', '-.', '-', ':'\n",
    "        self.ax.add_feature(cfeature.LAKES, alpha=0.5)        \n",
    "    \n",
    "    def show(self):\n",
    "        plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ce74e1ee",
   "metadata": {},
   "source": [
    "### Plotting an example flight path\n",
    " Finally, let's plot the flight `2005010612051151` with regard to Ozone. (size and colour)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6fae1e9c",
   "metadata": {},
   "outputs": [],
   "source": [
    "flight_id = \"2005010612051151\"\n",
    "param = \"O3_PM\"\n",
    "get_file(flight_id)\n",
    "plot = Plot(param)\n",
    "plot.plot()\n",
    "plot.show()"
   ]
  }
 ],
 "metadata": {
  "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.9.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
