{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tutorial: Generating a CSM Camera Model from PDS Image in Knoten with Basic Camera Operations\n",
    "\n",
    "## 1. Prerequisites\n",
    "\n",
    "### Install Knoten\n",
    "This tutorial requires Knoten version 0.4 or higher.\n",
    "\n",
    "`conda install -c conda-forge knoten=0.4`\n",
    "\n",
    "### Install Matplotlib\n",
    "This tutorial requires matplotlib.\n",
    "\n",
    "`conda install -c conda-forge matplotlib`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Create a CSM camera for a PDS image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import json\n",
    "import shapely.wkt\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import urllib.request\n",
    "from csmapi import csmapi\n",
    "import pandas as pd\n",
    "\n",
    "import ale\n",
    "from knoten import csm, sensor_utils\n",
    "from knoten.shape import Ellipsoid\n",
    "from knoten.illuminator import Illuminator"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Download the image from the PDS archives"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data_dir = '../data/image_to_ground'\n",
    "filename = os.path.join(data_dir, \"B10_013341_1010_XN_79S172W.IMG\")\n",
    "\n",
    "downloader = urllib.request.URLopener()\n",
    "downloader.retrieve(\"https://asc-pds-mars-reconnaissance-orbiter.s3.us-west-2.amazonaws.com/CTX/mrox_0826/data/B10_013341_1010_XN_79S172W.IMG\", filename)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. Set Ale SPICE Data Area\n",
    "If an ale driver is going to be used that leverages SPICE data, it is necessary to set the ALESPICEROOT.\n",
    "\n",
    "For this example, the necessary SPICE data is preselected. In most cases, you do not know what kernels are needed for a particular image.  \n",
    "\n",
    "To set your spice area, one can do this using normal shell syntax, e.g.:\n",
    "\n",
    "`export ALESPICEROOT=/path/to/ale/spice`\n",
    "\n",
    "or inside of a conda environment:\n",
    "\n",
    "`conda env config vars set ALESPICEROOT=/path/to/ale/spice.`\n",
    "\n",
    "See instructions for installing SPICE data [here](https://github.com/DOI-USGS/ISIS3?tab=readme-ov-file#full-isis-data-download)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "props = {\n",
    "    'kernels': [\n",
    "        os.path.join(data_dir, 'B10_013341_1010_XN_79S172W_0.bsp'),\n",
    "        os.path.join(data_dir, 'B10_013341_1010_XN_79S172W_1.bsp'),\n",
    "        os.path.join(data_dir, 'mro_ctx_v11.ti'),\n",
    "        os.path.join(data_dir, 'mro_sc_psp_090526_090601_0_sliced_-74000.bc'),\n",
    "        os.path.join(data_dir, 'mro_sc_psp_090526_090601_1_sliced_-74000.bc'),\n",
    "        os.path.join(data_dir, 'mro_sclkscet_00082_65536.tsc'),\n",
    "        os.path.join(data_dir, 'mro_v16.tf'),\n",
    "        os.path.join(data_dir, 'naif0012.tls'),\n",
    "        os.path.join(data_dir, 'pck00008.tpc')\n",
    "    ]\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. Generate and Write an Ale ISD"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "isd_string = ale.loads(filename, props=props)\n",
    "csm_isd = os.path.splitext(filename)[0] + '.json'\n",
    "\n",
    "with open(csm_isd, 'w') as isd_file:\n",
    "    isd_file.write(isd_string)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. Create a CSM Camera Model from ISD"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "camera = csm.create_csm(csm_isd)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7. Get the footprint using the camera model and plot it"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'CSM footprint')"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 30532 characters]",
      "text/plain": [
       "<Figure size 500x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "with open(csm_isd, 'r') as fp:\n",
    "    aledict = json.load(fp)\n",
    "\n",
    "nlines, nsamples = aledict[\"image_lines\"], aledict[\"image_samples\"]\n",
    "\n",
    "boundary = csm.generate_boundary((nlines, nsamples))\n",
    "\n",
    "lons, lats, alts = csm.generate_latlon_boundary(camera, boundary)\n",
    "\n",
    "plt.rcParams[\"figure.figsize\"] = [5, 5]\n",
    "plt.axes().set_aspect('equal','datalim')\n",
    "plt.plot(lons, lats)\n",
    "plt.xlabel(\"Longitude (deg)\")\n",
    "plt.ylabel(\"Latitude (deg)\")\n",
    "plt.title(\"CSM footprint\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 1.0, 'Body Fixed Footprint')"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 41748 characters]",
      "text/plain": [
       "<Figure size 500x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "bodyfixed_footprint = csm.generate_bodyfixed_footprint(camera, boundary)\n",
    "\n",
    "csm_wkt = bodyfixed_footprint.ExportToWkt()\n",
    "\n",
    "csm_geom = shapely.wkt.loads(csm_wkt)\n",
    "csm_x, csm_y = csm_geom.geoms[0].exterior.coords.xy\n",
    "\n",
    "plt.xlabel(\"x\")\n",
    "plt.ylabel(\"y\")\n",
    "plt.plot(csm_x, csm_y)\n",
    "plt.xticks(\n",
    "    rotation=45, \n",
    "    horizontalalignment='right',\n",
    "    fontweight='light'\n",
    ")\n",
    "plt.title(\"Body Fixed Footprint\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8. Get Camera Info at an Image Point"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "image_pt = csmapi.ImageCoord(nlines/2, nsamples/2)\n",
    "shape = Ellipsoid.from_csm_sensor(camera)\n",
    "illuminator = Illuminator()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "129387.64878271958\n",
      "251123.7546265136\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'PhaseAngle': 58.986916499957154,\n",
       " 'EmissionAngle': 0.10161336902837881,\n",
       " 'SlantDistance': 251124.1495516768,\n",
       " 'TargetCenterDistance': 3628059.1980234315,\n",
       " 'SubSpacecraftLatitude': -78.89842320708418,\n",
       " 'SubSpacecraftLongitude': 187.2644730283536,\n",
       " 'LocalRadius': 3376935.3088204544,\n",
       " 'RightAscension': 6.933803726271131,\n",
       " 'Declination': 78.9463718229981,\n",
       " 'LineResolution': 6.0485566304149945,\n",
       " 'SampleResolution': 5.017404463546378,\n",
       " 'PixelResolution': 5.532980546980687}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "caminfo = {\n",
    "    \"PhaseAngle\": sensor_utils.phase_angle(image_pt, camera, shape, illuminator),\n",
    "    \"EmissionAngle\": sensor_utils.emission_angle(image_pt, camera, shape),\n",
    "    \"SlantDistance\": sensor_utils.slant_distance(image_pt, camera, shape),\n",
    "    \"TargetCenterDistance\": sensor_utils.target_center_distance(image_pt, camera),\n",
    "    \"SubSpacecraftLatitude\": sensor_utils.sub_spacecraft_point(image_pt, camera).lat,\n",
    "    \"SubSpacecraftLongitude\": sensor_utils.sub_spacecraft_point(image_pt, camera).lon,\n",
    "    \"LocalRadius\": sensor_utils.local_radius(image_pt, camera, shape),\n",
    "    \"RightAscension\": sensor_utils.right_ascension_declination(image_pt, camera)[0],\n",
    "    \"Declination\": sensor_utils.right_ascension_declination(image_pt, camera)[1],\n",
    "    \"LineResolution\": sensor_utils.line_resolution(image_pt, camera, shape),\n",
    "    \"SampleResolution\": sensor_utils.sample_resolution(image_pt, camera, shape),\n",
    "    \"PixelResolution\": sensor_utils.pixel_resolution(image_pt, camera, shape),\n",
    "}\n",
    "\n",
    "caminfo"
   ]
  }
 ],
 "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": 4
}
