{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import knoten\n",
    "import knoten.csm as csm\n",
    "import numpy as np\n",
    "import gdal\n",
    "from gdal import osr, ogr\n",
    "import math, os\n",
    "from string import Template\n",
    "import matplotlib.pyplot as plt\n",
    "import pyproj"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Dem:\n",
    "    def __init__(self, filename):\n",
    "        self.ds = gdal.Open(filename, gdal.GA_ReadOnly)\n",
    "        srs = osr.SpatialReference()\n",
    "        srs.ImportFromWkt(self.ds.GetProjection())\n",
    "        srsLatLong = srs.CloneGeogCS()\n",
    "        self.lat_lon_ct = osr.CoordinateTransformation(srsLatLong, srs)\n",
    "        self.geoc_proj = pyproj.Proj(proj='geocent', a=srs.GetSemiMajor(), b=srs.GetSemiMinor())\n",
    "        self.lat_lon_proj = pyproj.Proj(proj='latlon', a=srs.GetSemiMajor(), b=srs.GetSemiMinor())\n",
    "        geomatrix = self.ds.GetGeoTransform()\n",
    "        self.inv_geomatrix = gdal.InvGeoTransform(geomatrix)\n",
    "    \n",
    "    def get_lat_lon_elevation(self, longitude, latitude):\n",
    "        (X, Y, height) = self.lat_lon_ct.TransformPoint(longitude, latitude)\n",
    "        x = int(self.inv_geomatrix[0] + self.inv_geomatrix[1] * X + self.inv_geomatrix[2] * Y)\n",
    "        y = int(self.inv_geomatrix[3] + self.inv_geomatrix[4] * X + self.inv_geomatrix[5] * Y)\n",
    "        res = self.ds.ReadAsArray(x, y, 1, 1)\n",
    "        return res[0][0]\n",
    "    \n",
    "    def get_rect_elevation(self, geoc_x, geoc_y, geoc_z):\n",
    "        X, Y, height = pyproj.transform(self.geoc_proj, self.lat_lon_proj, geoc_x, geoc_y, geoc_z)\n",
    "        x = int(self.inv_geomatrix[0] + self.inv_geomatrix[1] * X + self.inv_geomatrix[2] * Y)\n",
    "        y = int(self.inv_geomatrix[3] + self.inv_geomatrix[4] * X + self.inv_geomatrix[5] * Y)\n",
    "        res = self.ds.ReadAsArray(x, y, 1, 1)\n",
    "        return res[0][0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def compute_wms_range(cameras, dem_res, radius, pad = True):\n",
    "    combined_footprints = ogr.Geometry(ogr.wkbPolygon)\n",
    "    for camera in cameras:\n",
    "        image_size = camera.getImageSize()\n",
    "        pix_bound = knoten.csm.generate_boundary([image_size.line, image_size.samp])\n",
    "        footprint = knoten.csm.generate_latlon_footprint(camera, pix_bound, radius, radius)\n",
    "        combined_footprints = combined_footprints.Union(footprint)\n",
    "    bounds = combined_footprints.GetEnvelope()\n",
    "    if pad:\n",
    "        bounds = (math.floor(bounds[0]), math.ceil(bounds[1]), math.floor(bounds[2]), math.ceil(bounds[3]))\n",
    "    d_res = (dem_res / radius) * (180.0 / np.pi) # DEM resolution in degrees per pixel\n",
    "    lon_range = bounds[1] - bounds[0]\n",
    "    lat_range = bounds[3] - bounds[2]\n",
    "    x_size = math.floor(lon_range / d_res)\n",
    "    y_size = math.floor(lat_range / d_res)\n",
    "    return bounds, (x_size, y_size)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def download_messenger_dem(bounds, size, xml_filename = \"gdal_WMS.xml\", filename = 'gdal_WMS.tif'):\n",
    "    #build this XML file.\n",
    "    # TODO use a jinja2 template\n",
    "    gdal_XML = Template(\"\"\"<GDAL_WMS>\n",
    "      <Service name=\"WMS\">\n",
    "        <Version>1.1.1</Version>\n",
    "        <ServerUrl>https://planetarymaps.usgs.gov/cgi-bin/mapserv?map=/maps/mercury/mercury_simp_cyl_elev.map</ServerUrl>\n",
    "        <SRS>EPSG:4326</SRS>\n",
    "        <ImageFormat>image/tiff</ImageFormat>\n",
    "        <Layers>Messenger_elev</Layers>\n",
    "      </Service>\n",
    "      <DataWindow>\n",
    "        <UpperLeftX>$minLon</UpperLeftX>\n",
    "        <UpperLeftY>$maxLat</UpperLeftY>\n",
    "        <LowerRightX>$maxLon</LowerRightX>\n",
    "        <LowerRightY>$minLat</LowerRightY>\n",
    "         <SizeX>$xSize</SizeX>\n",
    "         <SizeY>$ySize</SizeY>\n",
    "      </DataWindow>\n",
    "      <Projection>EPSG:4326</Projection>\n",
    "      <BandsCount>1</BandsCount>\n",
    "      <DataType>Int16</DataType>\n",
    "    </GDAL_WMS>\n",
    "    \"\"\")\n",
    "\n",
    "    gdal_WMS = gdal_XML.substitute(minLon=bounds[0], maxLon=bounds[1], minLat=bounds[2], maxLat=bounds[3], xSize=size[0], ySize=size[1])\n",
    "    with open( xml_filename ,'w') as outFile:\n",
    "        outFile.write( gdal_WMS  )\n",
    "    \n",
    "    wms_ds = gdal.Open(xml_filename)\n",
    "    gdal.Translate(filename, wms_ds, outputSRS='GEOGCS[\"Mercury 2015\",DATUM[\"D_Mercury_2015\",SPHEROID[\"Mercury_2015_IAU\",2439400.0,0]],PRIMEM[\"Reference_Meridian\",0],UNIT[\"Degree\",0.0174532925199433],AUTHORITY[\"IAU2015\",\"19900\"]]')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def intersect_dem(camera, dem, image_pt, tolerance = 1, max_its = 20):\n",
    "    intersection = camera.imageToGround(image_pt, 0.0)\n",
    "    iterations = 1\n",
    "    converged = False\n",
    "    while (not converged):\n",
    "        height = dem.get_rect_elevation(intersection.x, intersection.y, intersection.z)\n",
    "        next_intersection = camera.imageToGround(image_pt, float(height))\n",
    "        dist = max(abs(intersection.x) - abs(next_intersection.x),\n",
    "                   abs(intersection.y) - abs(next_intersection.y),\n",
    "                   abs(intersection.z) - abs(next_intersection.z))\n",
    "        intersection = next_intersection\n",
    "        if dist < tolerance:\n",
    "            converged = True\n",
    "        iterations += 1\n",
    "        if iterations > max_its:\n",
    "            converged = True\n",
    "    return intersection"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "image_dir = 'data'\n",
    "EN0213023991M = os.path.join(image_dir, 'EN0213023991M.IMG')\n",
    "EN0213110924M = os.path.join(image_dir, 'EN0213110924M.IMG')\n",
    "dem = os.path.join(image_dir, 'example_mids_dem.tif')\n",
    "wms_xml = os.path.join(image_dir, 'example_mids_wms.xml')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "EN0213023991M_cam = knoten.csm.create_csm(EN0213023991M)\n",
    "EN0213110924M_cam = knoten.csm.create_csm(EN0213110924M)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Note MESSENGER DEM is 665 m/pix and MERCURY has a radius of 2439400 m\n",
    "bounds, size = compute_wms_range([EN0213023991M_cam, EN0213110924M_cam], 665, 2439400)\n",
    "download_messenger_dem(bounds, size = size, filename=dem, xml_filename=wms_xml)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(-177, -173, 30, 33) (256, 192)\n"
     ]
    }
   ],
   "source": [
    "print(bounds, size)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x1277aeef0>"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 65504 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "dem_ds = gdal.Open(dem)\n",
    "plt.imshow(dem_ds.ReadAsArray())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_dem = Dem(dem)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "pt1 = EN0213023991M_cam.imageToGround(knoten.csmapi.ImageCoord(0,0), 0.0)\n",
    "pt2 = EN0213023991M_cam.imageToGround(knoten.csmapi.ImageCoord(1,1), 0.0)\n",
    "delta_x = pt1.x - pt2.x\n",
    "delta_y = pt1.y - pt2.y\n",
    "delta_z = pt1.z - pt2.z\n",
    "ground_res = math.sqrt((delta_x*delta_x + delta_y*delta_y + delta_z*delta_z)/2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-2067702.8126083945 -158324.31039881706 1287567.1225179185\n",
      "-2066435.7005952501 -159381.98791860323 1286512.0431743076\n"
     ]
    }
   ],
   "source": [
    "dem_pt = intersect_dem(EN0213023991M_cam, test_dem, knoten.csmapi.ImageCoord(216, 216), tolerance=ground_res/100)\n",
    "sphr_pt = EN0213023991M_cam.imageToGround(knoten.csmapi.ImageCoord(216, 216), 0.0)\n",
    "print(dem_pt.x, dem_pt.y, dem_pt.z)\n",
    "print(sphr_pt.x, sphr_pt.y, sphr_pt.z)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "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.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
