{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Intro to REST APIs\n",
    "\n",
    "This notebook demonstrates how to create requests and parse responses for the Data and Orders API with a quick code snippet at the end dedicated to the subscritption API. We will create a search request for the Data API using  a `Geojson`. We will parse the response for image IDs. We will use those image IDs to place a order request.\n",
    "\n",
    "\n",
    "More reference information can be found at [Ordering & Delivery](https://developers.planet.com/apis/orders/)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import json\n",
    "import os\n",
    "import pathlib\n",
    "import time\n",
    "from ipyleaflet import Map, Polygon\n",
    "\n",
    "import requests\n",
    "from requests.auth import HTTPBasicAuth"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#!pip install requests"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Authenticating\n",
    "You can also just paste your API key instead of the ```os.getenv('PL_API_KEY')```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# API Key stored as an env variable\n",
    "PLANET_API_KEY = os.getenv('PL_API_KEY')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "orders_url = 'https://api.planet.com/compute/ops/orders/v2/'\n",
    "data_url = \"https://api.planet.com/data/v1\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "auth = HTTPBasicAuth(PLANET_API_KEY, '')\n",
    "response = requests.get(data_url, auth=auth)\n",
    "response"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Searching with the Data API\n",
    "We can use the [data API](https://developers.planet.com/docs/apis/data/) in order to automate searching based on the search criterias like: date range, cloud cover, area cover, aoi. We can create and AOI using https://geojson.io/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def parse_geojson(filename):\n",
    "    features = json.load(open(filename))\n",
    "    if \"type\" in features and features[\"type\"] == \"FeatureCollection\":\n",
    "        for f in features[\"features\"]:\n",
    "            geoms =  f[\"geometry\"]\n",
    "    elif \"type\" in features and features[\"type\"] == \"Feature\":\n",
    "        geoms= features[\"geometry\"]\n",
    "    else:\n",
    "        geoms = features\n",
    "\n",
    "    return geoms\n",
    "\n",
    "def map_polygon(geom):\n",
    "    # To switch to (longitude, latitude)\n",
    "    polygon_coords = [(lon, lat) for lat, lon in geom['coordinates'][0]]\n",
    "    \n",
    "    # Calculate the center of your polygon\n",
    "    latitudes = [p[0] for p in polygon_coords]\n",
    "    longitudes = [p[1] for p in polygon_coords]\n",
    "    \n",
    "    # Map's center is the average of the polygon's coordinates\n",
    "    center = (sum(latitudes) / len(polygon_coords), sum(longitudes) / len(polygon_coords))\n",
    "    m = Map(center=center, zoom=14)\n",
    "\n",
    "    # Create a polygon and add it to the map\n",
    "    polygon = Polygon(\n",
    "        locations=polygon_coords,\n",
    "        color=\"green\",\n",
    "        fill_color=\"green\"\n",
    "    )\n",
    "    m.add_layer(polygon)\n",
    "    return m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "geometry = parse_geojson(\"lake-lagunita-large.geojson\")\n",
    "map_polygon(geometry)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# get images that overlap with our AOI \n",
    "geometry_filter = {\n",
    "  \"type\": \"GeometryFilter\",\n",
    "  \"field_name\": \"geometry\",\n",
    "  \"config\": geometry\n",
    "}\n",
    "\n",
    "# get images acquired within a date range\n",
    "date_range_filter = {\n",
    "  \"type\": \"DateRangeFilter\",\n",
    "  \"field_name\": \"acquired\",\n",
    "  \"config\": {\n",
    "    \"gte\":\"2023-10-07T00:00:00Z\",\n",
    "    \"lte\":\"2023-10-11T00:00:00Z\"\n",
    "  }\n",
    "}\n",
    "\n",
    "# only get images which have <50% cloud coverage\n",
    "cloud_cover_filter = {\n",
    "  \"type\": \"RangeFilter\",\n",
    "  \"field_name\": \"cloud_cover\",\n",
    "  \"config\": {\n",
    "    \"lte\": 1\n",
    "  }\n",
    "}\n",
    "\n",
    "# combine our geo, date, cloud filters\n",
    "combined_filter = {\n",
    "  \"type\": \"AndFilter\",\n",
    "  \"config\": [geometry_filter, date_range_filter, cloud_cover_filter]\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "item_type = \"PSScene\"\n",
    "\n",
    "# API request object\n",
    "search_request = {\n",
    "  \"item_types\": [item_type], \n",
    "  \"filter\": combined_filter\n",
    "}\n",
    "\n",
    "\n",
    "search_request"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# fire off the POST request\n",
    "search_result = \\\n",
    "  requests.post(\n",
    "    'https://api.planet.com/data/v1/quick-search',\n",
    "    auth=HTTPBasicAuth(PLANET_API_KEY, ''),\n",
    "    json=search_request)\n",
    "\n",
    "# extract image IDs only\n",
    "image_ids = [feature['id'] for feature in search_result.json()['features']]\n",
    "print(image_ids)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "map_polygon(search_result.json()['features'][1]['geometry'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Requests example\n",
    "\n",
    "In this notebook, we will be using `requests` to communicate with the orders v2 API. First, we will check our orders list to make sure authentication and communication is working as expected.\n",
    "\n",
    "We want to get a response code of `200` from this API call. To troubleshoot other response codes, see the [List Orders](https://developers.planet.com/apis/orders/reference/) AOI reference."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "auth = HTTPBasicAuth(PLANET_API_KEY, '')\n",
    "response = requests.get(orders_url, auth=auth)\n",
    "response"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ordering\n",
    "\n",
    "In this example, we will order two `PSScene` analytic images. For variations on this kind of order, see [Ordering Data](https://developers.planet.com/apis/orders/scenes/).\n",
    "\n",
    "In this order, we request an `analytic` bundle. A bundle is a group of assets for an item. The `analytic` bundle for the  `PSScene` item contains 3 assets: the analytic image, the analytic xml file, and the udm. See the [Product bundles reference](https://developers.planet.com/docs/orders/product-bundles-reference/) to learn about other bundles and other items."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we will list the names of orders we have created thus far. Your list may be empty if you have not created an order yet."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "orders = response.json()['orders']\n",
    "[r['name'] for r in orders[:5]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Place Order"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# set content type to json\n",
    "headers = {'content-type': 'application/json'}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# define products part of order\n",
    "single_product = [\n",
    "    {\n",
    "      \"item_ids\": [\"20231008_180635_41_24bb\"],\n",
    "      \"item_type\": \"PSScene\",\n",
    "      \"product_bundle\": \"analytic_udm2\"\n",
    "    }\n",
    "]\n",
    "\n",
    "same_src_products = [\n",
    "    {\n",
    "      \"item_ids\": [\"20151119_025740_0c74\",\n",
    "                   \"20151119_025739_0c74\"],\n",
    "      \"item_type\": \"PSScene\",\n",
    "      \"product_bundle\": \"analytic_udm2\"\n",
    "    }\n",
    "]\n",
    "\n",
    "multi_src_products = [\n",
    "    {\n",
    "      \"item_ids\": [\"20151119_025740_0c74\"],\n",
    "      \"item_type\": \"PSScene\",\n",
    "      \"product_bundle\": \"analytic_udm2\"\n",
    "    },\n",
    "    {\n",
    "      \"item_ids\": [\"20220628_183020_20_248c\"],\n",
    "      \"item_type\": \"PSScene\",\n",
    "      \"product_bundle\": \"visual\"\n",
    "    },\n",
    "    \n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "product = [\n",
    "    {\n",
    "      \"item_ids\": [image_ids[0]],\n",
    "      \"item_type\": \"PSScene\",\n",
    "      \"product_bundle\": \"visual\"\n",
    "    }]\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "request = {  \n",
    "   \"name\":\"Stanford demo\",\n",
    "   \"products\": product,\n",
    "    \"delivery\": {\"single_archive\": True, \"archive_type\": \"zip\"}\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def place_order(request, auth):\n",
    "    response = requests.post(orders_url, data=json.dumps(request), auth=auth, headers=headers)\n",
    "    print(response.json())\n",
    "    order_id = response.json()['id']\n",
    "    print(order_id)\n",
    "    order_url = orders_url + '/' + order_id\n",
    "    return order_url"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "order_url = place_order(request, auth)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Poll for Order Success"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def poll_for_success(order_url, auth, num_loops=10):\n",
    "    count = 0\n",
    "    while(count < num_loops):\n",
    "        count += 1\n",
    "        r = requests.get(order_url, auth=auth)\n",
    "        response = r.json()\n",
    "        state = response[  'state']\n",
    "        print(state)\n",
    "        end_states = ['success', 'failed', 'partial']\n",
    "        if state in end_states:\n",
    "            break\n",
    "        time.sleep(10)\n",
    "        \n",
    "poll_for_success(order_url, auth)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bandmath and clip\n",
    "Now lets use the toolchain in order to combine a few opperations at once.\n",
    "\n",
    "To create a polygon to clip by lets go to [geojson.io](https://geojson.io)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# define the clip tool\n",
    "clip = {\n",
    "    \"clip\": {\n",
    "        \"aoi\": geometry\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bandmath = {\n",
    "  \"bandmath\": {\n",
    "    \"b1\": \"b1\",\n",
    "    \"b2\": \"b2\",\n",
    "    \"b3\": \"b3\",\n",
    "    \"b4\": \"b4\",\n",
    "    \"b5\": \"(b4 - b3) / (b4 + b3)\",\n",
    "    \"pixel_type\": \"32R\",\n",
    "  }\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "product = [\n",
    "    {\n",
    "      \"item_ids\": [image_ids[0]],\n",
    "      \"item_type\": \"PSScene\",\n",
    "      \"product_bundle\": \"analytic_udm2\"\n",
    "    }]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tool_request = { \n",
    "    \"name\":\"San Francisco Clipped Bandmath\",\n",
    "    \"products\": product,\n",
    "    \"tools\": [clip, bandmath],\n",
    "    \"delivery\": {\"single_archive\": True, \"archive_type\": \"zip\"}\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tool_order_url = place_order(tool_request, auth)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#old_url = orders_url+\"37580e28-3a1a-4e41-8974-4704faceb529\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "### View Results\n",
    "Now lets review our previous order and download it"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "requests.get(order_url, auth=auth).json()['state']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "r = requests.get(order_url, auth=auth)\n",
    "response = r.json()\n",
    "results = response['_links']['results']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "[r['name'] for r in results]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Download\n",
    "\n",
    "### Downloading each asset individually"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def download_results(results, overwrite=False):\n",
    "    results_urls = [r['location'] for r in results]\n",
    "    results_names = [r['name'] for r in results]\n",
    "    print('{} items to download'.format(len(results_urls)))\n",
    "    \n",
    "    for url, name in zip(results_urls, results_names):\n",
    "        path = pathlib.Path(os.path.join('data', name))\n",
    "        \n",
    "        if overwrite or not path.exists():\n",
    "            print('downloading {} to {}'.format(name, path))\n",
    "            r = requests.get(url, allow_redirects=True)\n",
    "            path.parent.mkdir(parents=True, exist_ok=True)\n",
    "            open(path, 'wb').write(r.content)\n",
    "        else:\n",
    "            print('{} already exists, skipping {}'.format(path, name))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "download_results(results)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Subscription API\n",
    "#### Creating a subscription\n",
    "[Here](https://developers.planet.com/docs/subscriptions/delivery/) is more info on our various cloud delivery options"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# set content type to json\n",
    "headers = {'content-type': 'application/json'}\n",
    "\n",
    "# set your delivery details\n",
    "BUCKET_NAME = 'subscriptions_api_demo'\n",
    "GCS_CREDENTIALS= #64-bit string"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "request = {\n",
    "    \"name\": \"Tampa_Bay\",\n",
    "    \"source\": {\n",
    "        \"type\": \"catalog\",\n",
    "        \"parameters\": {\n",
    "            \"geometry\": {\n",
    "                \"coordinates\": [[[-82.775,27.48],[-82.365,27.48],[-82.365,28.07],[-82.775,28.07],[-82.775,27.48]]],\n",
    "                \"type\": \"Polygon\"\n",
    "            },\n",
    "            \"start_time\": \"2023-01-01T00:00:00Z\",\n",
    "            \"end_time\": \"2024-03-31T00:00:00Z\",\n",
    "            \"rrule\": \"FREQ=MONTHLY\",\n",
    "            \"item_types\": [\"PSScene\"],\n",
    "            \"asset_types\": [\"ortho_analytic_4b\"]\n",
    "        }\n",
    "    },\n",
    "    \"delivery\": { \n",
    "        \"type\": \"google_cloud_storage\",\n",
    "        \"parameters\": {\n",
    "            \"bucket\": BUCKET_NAME,\n",
    "            \"credentials\": GCS_CREDENTIALS,\n",
    "                \n",
    "        }\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def place_subscription(request, auth):\n",
    "    response = requests.post(subscriptions_url, data=json.dumps(request), auth=auth, headers=headers)\n",
    "    print(response.json())\n",
    "    subscriptions_id = response.json()['id']\n",
    "    print(subscriptions_id)\n",
    "    subscription_url = subscriptions_url + '/' + subscriptions_id\n",
    "    return subscription_url"
   ]
  }
 ],
 "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.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
