{ "cells": [ { "cell_type": "markdown", "id": "06eecdd2-f107-4ae7-bb8d-39bb6c6ed6a0", "metadata": {}, "source": [ "# ADS-B API\n", "\n", "The [Contrails API](https://api.contrails.org) enables authorized users to access a common ADS-B dataset for contrails research. \n", "\n", "The underlying ADS-B data is provided by [Spire Aviation](https://aviation-docs.spire.com/).\n", "\n", "E-mail [api@contrails.org](mailto:api@contrails.org?subject=Common%20ADS-B%20Access) with subject **Common ADS-B Access** to learn more about how your organization can participate in this program." ] }, { "cell_type": "code", "execution_count": 1, "id": "25cdb008-d720-4614-9dcf-d1785e40349d", "metadata": {}, "outputs": [], "source": [ "import os" ] }, { "cell_type": "code", "execution_count": 2, "id": "d5aaa134-eed6-40e6-83e1-e968c4750da3", "metadata": {}, "outputs": [], "source": [ "# Load API key\n", "# (contact api@contrails.org if you need an API key)\n", "URL = \"https://api.contrails.org\"\n", "API_KEY = os.environ[\"CONTRAILS_API_KEY\"]\n", "HEADERS = {\"x-api-key\": API_KEY}" ] }, { "cell_type": "markdown", "id": "8fe4646c-0521-4c64-94ab-2cc4def86532", "metadata": {}, "source": [ "## Telemetry\n", "\n", "**GET [/v1/adsb/telemetry](https://api.contrails.org/openapi#/ADS-B/get_telemetry_v1_adsb_telemetry_get)**\n", "\n", "> Note this endpoint can take up to 30 seconds to return depending on bandwidth\n", "\n", "This endpoint returns 1 hour range of all global ADS-B telemetry data as an [Apache Parquet](https://parquet.apache.org/) file.\n", "\n", "Input date must be an ISO 8601 datetime string (UTC) with hourly resolution, e.g. `\"2025-01-06T00\"`. \n", "Any minute or second resolution is ignored.\n", "\n", "See the [ADS-B schema](https://apidocs.contrails.org/_static/adsb-schema.json) for the description of each data key in the Parquet file." ] }, { "cell_type": "code", "execution_count": 3, "id": "bb7d9661-2e40-407e-95f8-6cffb3dfd0c1", "metadata": {}, "outputs": [], "source": [ "import requests # pip install requests\n", "import matplotlib.pyplot as plt # pip install matplotlib\n", "import pandas as pd # pip install pandas" ] }, { "cell_type": "markdown", "id": "02244627-ddb4-4503-9ffe-31c0ef0190e4", "metadata": {}, "source": [ "### Get data for a single hour" ] }, { "cell_type": "code", "execution_count": 4, "id": "71be1e61-0ff6-46cd-81f5-9952910ebcc8", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "HTTP Response Code: 200 OK\n", "\n" ] } ], "source": [ "params = {\n", " \"date\": \"2025-01-24T02\" # ISO 8601 (UTC)\n", "}\n", "\n", "r = requests.get(f\"{URL}/v1/adsb/telemetry\", params=params, headers=HEADERS)\n", "print(f\"HTTP Response Code: {r.status_code} {r.reason}\\n\")\n", "\n", "# write out response content as parquet file\n", "with open(f\"{params['date']}.pq\", \"wb\") as f:\n", " f.write(r.content)" ] }, { "cell_type": "code", "execution_count": 5, "id": "58f47a94-4a68-48ee-83b5-64a421501900", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Number of unique flights: 17103\n", "Number of unique waypoints: 1093322\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
timestamplatitudelongitudecollection_typealtitude_baroicao_addressflight_idcallsigntail_numberflight_numberaircraft_type_icaoairline_iatadeparture_airport_icaodeparture_scheduled_timearrival_airport_icaoarrival_scheduled_time
02025-01-24 02:59:5937.882629-80.429001terrestrial31000A9198693a5cd24-1e7b-4dca-a07d-ad391a2e8237PDT5701N686AEAA5701E145PTKCLT2025-01-24 01:53:00KERI2025-01-24 03:48:00
12025-01-24 02:59:5936.193588-112.395912terrestrial35000AB415Ed1dfe570-9e39-4323-a6cf-f4cf602b4149SCX618N824SYSY618B738SYKPSP2025-01-24 02:24:00KMSP2025-01-24 05:41:00
22025-01-24 02:59:59-44.230362171.841019terrestrial33950C81D8E12d6d993-c01e-4553-80e1-944a34119f69ANZ689ZK-OABNZ689A320NZNZWN2025-01-24 02:05:00NZDN2025-01-24 03:25:00
32025-01-24 02:59:5943.00835426.135494terrestrial380004B187F0e7d48c3-a4e2-4489-aaa3-4c9b9bea05c2SWR155HB-JHFLX155A333LXVABB2025-01-23 19:50:00LSZH2025-01-24 05:10:00
42025-01-24 02:59:5928.975525-109.411362terrestrial370000D09D5b4050af1-1fc8-4997-ac5a-1d46b690c869VOI1743XA-VLUY41743A321Y4KLAS2025-01-24 01:31:00MMGL2025-01-24 04:43:00
\n", "
" ], "text/plain": [ " timestamp latitude longitude collection_type altitude_baro \\\n", "0 2025-01-24 02:59:59 37.882629 -80.429001 terrestrial 31000 \n", "1 2025-01-24 02:59:59 36.193588 -112.395912 terrestrial 35000 \n", "2 2025-01-24 02:59:59 -44.230362 171.841019 terrestrial 33950 \n", "3 2025-01-24 02:59:59 43.008354 26.135494 terrestrial 38000 \n", "4 2025-01-24 02:59:59 28.975525 -109.411362 terrestrial 37000 \n", "\n", " icao_address flight_id callsign tail_number \\\n", "0 A91986 93a5cd24-1e7b-4dca-a07d-ad391a2e8237 PDT5701 N686AE \n", "1 AB415E d1dfe570-9e39-4323-a6cf-f4cf602b4149 SCX618 N824SY \n", "2 C81D8E 12d6d993-c01e-4553-80e1-944a34119f69 ANZ689 ZK-OAB \n", "3 4B187F 0e7d48c3-a4e2-4489-aaa3-4c9b9bea05c2 SWR155 HB-JHF \n", "4 0D09D5 b4050af1-1fc8-4997-ac5a-1d46b690c869 VOI1743 XA-VLU \n", "\n", " flight_number aircraft_type_icao airline_iata departure_airport_icao \\\n", "0 AA5701 E145 PT KCLT \n", "1 SY618 B738 SY KPSP \n", "2 NZ689 A320 NZ NZWN \n", "3 LX155 A333 LX VABB \n", "4 Y41743 A321 Y4 KLAS \n", "\n", " departure_scheduled_time arrival_airport_icao arrival_scheduled_time \n", "0 2025-01-24 01:53:00 KERI 2025-01-24 03:48:00 \n", "1 2025-01-24 02:24:00 KMSP 2025-01-24 05:41:00 \n", "2 2025-01-24 02:05:00 NZDN 2025-01-24 03:25:00 \n", "3 2025-01-23 19:50:00 LSZH 2025-01-24 05:10:00 \n", "4 2025-01-24 01:31:00 MMGL 2025-01-24 04:43:00 " ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# read parquet file with pandas\n", "df = pd.read_parquet(f\"{params['date']}.pq\")\n", "\n", "print(\"Number of unique flights:\", df[\"flight_id\"].nunique())\n", "print(\"Number of unique waypoints:\", len(df[\"flight_id\"]))\n", "\n", "df.head()" ] }, { "cell_type": "code", "execution_count": 6, "id": "d64172ec-beb0-465b-8f56-d439b4fd15f4", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# select single flight and plot\n", "flight_id = df.iloc[0][\"flight_id\"]\n", "flight = df.loc[df[\"flight_id\"] == flight_id]\n", "flight.plot.scatter(x=\"longitude\", y=\"latitude\", c=\"altitude_baro\", cmap=\"bwr\", s=2);" ] }, { "cell_type": "markdown", "id": "bf7c6766-3585-4353-bad4-5587adf23106", "metadata": {}, "source": [ "### Aggregate data over multiple hours" ] }, { "cell_type": "code", "execution_count": 7, "id": "566809d5-60ef-4dc8-a356-5da708e79e0d", "metadata": {}, "outputs": [], "source": [ "start = \"2025-01-15T02\"\n", "end = \"2025-01-15T03\"\n", "times = pd.date_range(start=start, end=end, freq=\"h\")\n", "times_str = [t.strftime(\"%Y-%m-%dT%H\") for t in times]" ] }, { "cell_type": "code", "execution_count": 8, "id": "6e0909b6-c4d5-46cb-bf83-fdffc8691199", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Downloading hour: 2025-01-15T02\n", "HTTP Response Code: 200 OK\n", "\n", "Downloading hour: 2025-01-15T03\n", "HTTP Response Code: 200 OK\n", "\n" ] } ], "source": [ "for t in times_str:\n", " print(f\"Downloading hour: {t}\")\n", "\n", " r = requests.get(f\"{URL}/v1/adsb/telemetry\", params={\"date\": t}, headers=HEADERS)\n", " print(f\"HTTP Response Code: {r.status_code} {r.reason}\\n\")\n", "\n", " # write out response content as parquet file\n", " with open(f\"{t}.pq\", \"wb\") as f:\n", " f.write(r.content)" ] }, { "cell_type": "code", "execution_count": 9, "id": "1cb80397-83a8-4319-8979-705fd81611c1", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Number of unique flights: 21240\n", "Number of unique waypoints: 1944787\n" ] } ], "source": [ "dfs = []\n", "for t in times_str:\n", " dfs.append(pd.read_parquet(f\"{t}.pq\"))\n", "\n", "df = pd.concat(dfs)\n", "\n", "print(\"Number of unique flights:\", df[\"flight_id\"].nunique())\n", "print(\"Number of unique waypoints:\", len(df[\"flight_id\"]))" ] }, { "cell_type": "code", "execution_count": 10, "id": "90e10332-a007-412a-92ad-0284365a2191", "metadata": {}, "outputs": [ { "data": { "image/png": "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CxcuhEajQadOnbBz585yg81rk6TzRO3duxcDBgwot37ixImIi4srVzY0SU5ONv+FtmjRApMmTUJcXBwA47wSKSkpuH79Opo0aYLevXvjH//4B1q1alXluDhPFBERVVWtzhO1YgVUjRqJb+f2bahnzuT3m41IWonq378/7pfDVSW/u3DhgsX7r7/+urphERERyROfnScrdnF3HhEREYFJlMzwahIRERGJwEoUERGRvWAlSlaYRBEREdkLJlGywqtJREREJAIrUURERPbC9ADi6nyebIZJFBERkb1gd56sMIkiIiKyF0yiZIVXk4iIiEgEVqKIiIjsBStRssIkioiIyF4wiZIVXk0iIiIiEViJIiIishesRMkKkygiIiJ7wSRKVng1iYiIiERgJYqIiMhesBIlK0yiiIiI7AWTKFlhEkVERLVDrwfWrQP+/BN48kmgZ0+pIyKqFqakRERUO06dAn75BcjNBRITAUGQOiL7Y6pEVWchm2ElioiIaoenJ6BQGJMn02uyjkJRvUSI19ymmEQREVHtaNYMmD8f+OsvIDxc6mjsE8dEyQqTKCIiqj1t2xoXojqASRQREZG9YCVKVphEERER2QsmUbLCq0lEREQkAitRRERE9oKVKFlhEkVERGQvmETJCq8mERERkQisRBERUe0qKAAyMoxTHbi5SR2NfWElyiYuX74MAGjWrFm12uHVJCKi2lNaCsyaBcTFAa++ChQVSR2RfeFjX0QzGAxYvHgx1Go1goKCEBQUBHd3dyxZsgQGg0FUm6xEERFR7bl+3fjsPAC4dg24ehUIDJQ2JnvCSpRor7/+Or744gu888476NWrFwBg//79iIuLQ1FREf7xj39Y3SaTKCIiqj2+vkCfPsYHEffoATRtKnVEVE+sW7cOq1evxpNPPmle17FjRzRt2hQvv/wykygiIpI5hQKYOxeIjQUa8CvIanwAsWg3btxASEhIufUhISG4ceOGqDbrb12PiIikwwRKHI6JEi0sLAwrV64st37lypUICwsT1SZ/iomIiKjOW7p0KYYOHYpdu3YhIiICAJCamopLly5hx44dotqsvykpERGRvWElSrR+/frhzz//xMiRI5GXl4e8vDyMGjUKGRkZ6NOnj6g2WYkiIiKyF7w7T5TS0lIMHjwYCQkJogaQV6Z+Xk0iIiKqNxo2bIjjx4/bvF0mUURERPaC3XmiPf/88/jiiy9s2ia784iIiOwFu/NEKysrw5o1a7Br1y506dIFLi4uFts/+OADq9tkEkVERER13smTJ/HII48AAP7880+LbQqR82cxiSIiIrIXrESJlpycbPM2mUQRERHZCyZRssIkioiIas/Nm8A33wBNmgAjR/JL3VpMoqrlyJEj2LhxI7KyslBSUmKxbfPmzVa3V7+vJhER1a4lS4DERGDpUmD7dqmjoXrk66+/Rs+ePXH69Gls2bIFpaWlOHXqFPbs2QO1Wi2qTSZRRERUe3S6O6/z86WLw16ZHkAsdqnHDyD+5z//ieXLl+PHH3+EUqnEihUrcObMGTzzzDNo3ry5qDaZRBERUe2ZNw/o3h0YNszYnUfW4TxRop0/fx5Dhw4FACiVShQWFkKhUCAmJgafffaZqDY5JoqIiGpPmzbAhx9KHQXVQx4eHsj/u/rZtGlTnDx5Eh06dEBeXh5u3bolqk0mUURERPaCA8tF69u3L5KSktChQwc8/fTTmDlzJvbs2YOkpCQMHDhQVJuik6hffvkFn376Kc6fP49NmzahadOm+Pe//43g4GD07t1bbLNERERUGSZRoq1cuRJFRUUAgNdffx0NGzbEgQMHMHr0aLzxxhui2hSVRH333XcYP348xo0bh2PHjqG4uBgAoNVq8c9//hM7duwQFQwREdVBZ84A69YBbdsCkybV68HNJB1PT0/zawcHB8yfP7/abYpKot5++20kJCRgwoQJ+Prrr83re/XqhbfffrvaQRERUR1x+zbw//6f8a68nTuB4GCgf3+po7JfrERVi16vx5YtW3D69GkAQGhoKIYPH44GDcR1zIn6VEZGBvr27VtuvVqtRl5enqhAiIioDvroI0CrNb423Z5P4jGJEu3UqVN48sknodFo0K5dOwDAu+++iyZNmuDHH3/Eww8/bHWboq6mn58fzp07V279/v370bJlSzFNEhFRXZSXZ0yeFAqgUyegTx+pI6J66oUXXsBDDz2Ey5cv4+jRozh69CguXbqEjh074sUXXxTVpqgkatq0aZg5cyYOHToEhUKB7OxsrF+/HrNnz8b06dNFBUJERHXQjBlAr17GLrz33uN4qOqSYJ6olJQUDBs2DAEBAVAoFNi6davF9kmTJkGhUFgsgwcPttjnxo0bGDduHFQqFdzd3TF16lQUFBRY7HP8+HH0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MGPGDCxevNjczkcffYQFCxZg5MiRAICVK1dix44dlR7X09MTjo6OcHNzg5+fn1Ux/+tf/8KCBQswatQoAEBCQgJ+/vln8/bi4mL885//xK5duxAREQEAaNmyJfbv349PP/0U/fr1s+p4REQ1iUkUkZ06ffo0GjRogPDwcPM6Ly8vtGvXDqdPnzava9y4sTmBAgB/f39cuXIFAKDVapGbm4vu3bubtzs6OqJLly4wGAw2jVer1SInJ8ci3gYNGqBr167mLr1z587h1q1beOyxxyw+W1JSgs6dO9s0HiKi6mISRVTH3X1XHQAoFIpy45BswcHBoVy7d3crVkVBQQEAYPv27WjatKnFNicnp+oFSERkYxwTRWSn2rdvj7KyMhw6dMi87vr168jIyEBoaGiV2lCr1fD19cXhw4fN6/R6PY4ePXrfzymVSuj1eot1TZo0gUajsUik0tPTLY7l7+9vEW9ZWRnS0tLM70NDQ+Hk5ISsrCy0bt3aYgkMDKzSORER1RZWoojsVJs2bTB8+HBMmzYNn376Kdzc3DB//nw0bdoUw4cPr3I7r7zyCuLj49G6dWuEhITgo48+ws2bN8133VWkRYsWSElJwZgxY+Dk5ARvb2/0798fV69exdKlS/HUU09h586d+Omnn6BSqcyfmzlzJt555x20adMGISEh+OCDD5CXl2fe7ubmhtmzZyMmJgYGgwG9e/eGVqvFr7/+CpVKhYkTJ4q6VkRENYGVKCI7tnbtWnTp0gVPPPEEIiIiIAgCduzYUa4L737mzZuHsWPHYsKECYiIiICrqyuioqLg7Oxc6WcWL16MCxcuoFWrVmjSpAkAY2Xs448/xqpVqxAWFobffvvN4i5AAHjttdcwfvx4TJw4EREREXBzczMPaDdZsmQJ3nzzTcTHx6N9+/YYPHgwtm/fjuDgYCuuDBFRzVMINTE4gojslsFgQPv27fHMM89gyZIlUodDRCRb7M4jqucuXryI//73v+jXrx+Ki4uxcuVKZGZm4rnnnpM6NCIiWWN3HlE95+DggMTERHTr1g29evXCiRMnsGvXLrRv317q0IiIZI3deUREREQisBJFREREJAKTKCIiIiIRmEQRERERicAkioiIiEgEJlFEREREIjCJIiIiIhKBSRQRERGRCEyiiIiIiET4/77M062JYyDlAAAAAElFTkSuQmCC", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# select single flight and plot\n", "flight_id = df.iloc[0][\"flight_id\"]\n", "flight = df.loc[df[\"flight_id\"] == flight_id]\n", "flight.plot.scatter(x=\"longitude\", y=\"latitude\", c=\"altitude_baro\", cmap=\"bwr\", s=2);" ] }, { "cell_type": "markdown", "id": "c5c8816c-ad6e-47d1-be33-7d3e49dc6808", "metadata": {}, "source": [ "## Bulk Load ADS-B into external datastore\n", "\n", "> This section requires a fresh notebook kernel.\n", "> Restart the kernel if you have already run the section above.\n", "\n", "This section will provide a tutorial that covers:\n", "\n", "- Fetching a range of ADS-B data from the Contrails API\n", "- Loading those data into an external database/datastore\n", "\n", "This tutorial will focus on loading data into a [Google BigQuery table](https://cloud.google.com/bigquery).\n", "The same approach can be adapted to load these data into other database / datastores.\n", "\n", "This process is useful if you want to perform advanced queries on the dataset.\n", "\n", "### Prerequisites\n", "\n", "You must have a [Google Cloud account](https://cloud.google.com/), and the [Google Cloud CLI](https://cloud.google.com/sdk/docs/install) (`gcloud`) installed on your machine.\n", "\n", "You must also have set up a [BigQuery table](https://console.cloud.google.com/bigquery) and given your account [the required permissions](https://cloud.google.com/bigquery/docs/loading-data-cloud-storage-parquet#required_permissions) to load data into this table." ] }, { "cell_type": "code", "execution_count": 1, "id": "2a99c66a-7861-4c53-b77d-1f2c6bc40a54", "metadata": {}, "outputs": [], "source": [ "import json\n", "import os\n", "from pathlib import Path\n", "\n", "# NOTE: grequests *must* be imported before requests, or you will see a MonekyPatchWarning\n", "import grequests # pip install grequests (for parallel REST requests)\n", "import pandas as pd # pip install pandas\n", "\n", "from google.cloud import bigquery # pip install google-cloud-bigquery\n", "from google.cloud.bigquery import LoadJobConfig" ] }, { "cell_type": "code", "execution_count": 2, "id": "05bb4abf-845a-4add-9baa-ec7ad13612af", "metadata": {}, "outputs": [], "source": [ "# Load API key\n", "URL = \"https://api.contrails.org\"\n", "API_KEY = os.environ[\"CONTRAILS_API_KEY\"]\n", "HEADERS = {\"x-api-key\": API_KEY}" ] }, { "cell_type": "markdown", "id": "a1ea9b43-a03d-48db-871d-254dbf49b0b4", "metadata": {}, "source": [ "### Download ADS-B data files to your machine\n", "\n", "Set target hours for ADS-B data, then fetch ADS-B data from the Contrails API in a parallel, saving parquet files to the local machine." ] }, { "cell_type": "code", "execution_count": 3, "id": "bb74d1a6-b223-4762-b703-450ba0a99322", "metadata": {}, "outputs": [], "source": [ "# 6 hours of data\n", "start = \"2025-01-16T00\"\n", "end = \"2025-01-16T06\"\n", "times = pd.date_range(start=start, end=end, freq=\"h\")\n", "times_str = [t.strftime(\"%Y-%m-%dT%H\") for t in times]" ] }, { "cell_type": "code", "execution_count": 4, "id": "4fecb7aa-806a-4486-8360-240b0c29a755", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2025-01-16T00: 200 OK\n", "2025-01-16T01: 200 OK\n", "2025-01-16T02: 200 OK\n", "2025-01-16T03: 200 OK\n", "2025-01-16T04: 200 OK\n", "2025-01-16T05: 200 OK\n", "2025-01-16T06: 200 OK\n" ] } ], "source": [ "# Use `grequests` to send out parallel API requests\n", "# (this cell can take minutes to evaluate depending on bandwidth)\n", "req = (\n", " grequests.get(f\"{URL}/v1/adsb/telemetry\", params={\"date\": t}, headers=HEADERS)\n", " for t in times_str\n", ")\n", "responses = grequests.map(req, size=25)\n", "\n", "# create local directory to store local parquet files\n", "os.makedirs(\"adsb\", exist_ok=True)\n", "\n", "# Write out each hour as a parquet file in subdirectory `adsb`\n", "for t, r in zip(times_str, responses):\n", " print(f\"{t}: {r.status_code} {r.reason}\")\n", "\n", " # write out response content as parquet file\n", " path = Path(f\"adsb/{t}.pq\")\n", " with open(path, \"wb\") as f:\n", " f.write(r.content)" ] }, { "cell_type": "markdown", "id": "077c5666-ed0c-46fa-b674-6380956e948f", "metadata": {}, "source": [ "### (Optional) Create the target BigQuery table\n", "\n", "If a target BigQuery table does not exist, then create one prior to inserting the target data.\n", "\n", "The table must have a schema compatible with the fields present in the parquet ADS-B data.\n", "\n", "You can create a table using the `bq mk` command (`bq` comes bundled with the `gcloud` CLI).\n", "\n", "```bash\n", "bq mk --table project_id:dataset_id.table_id adsb-schema.json\n", "```\n", "\n", "- `project_id` is the GCP project ID for your account.\n", "- `dataset_id` is the BigQuery dataset where you want to create a new table.\n", "\n", "> If the dataset does not already exist, you will have to create it first with the \n", "> [`bq mk --dataset` command](https://cloud.google.com/bigquery/docs/datasets#bq) \n", "> (or via the web Console...)\n", "\n", "- `table_id` is the table name for the new table you are creating.\n", "- `adsb-schema.json` is the filepath to a local JSON file with the schema definition for the new table. Download the [ADS-B schema](https://apidocs.contrails.org/_static/adsb-schema.json) provided in the documentation - this schema is compatible with the BigQuery API\n", "\n", "```bash\n", "curl -X GET https://apidocs.contrails.org/_static/adsb-schema.json > adsb-schema.json\n", "```" ] }, { "cell_type": "code", "execution_count": 5, "id": "85c82714-622c-4cb8-a318-ca8626b78875", "metadata": {}, "outputs": [], "source": [ "# !bq mk --table project_id:dataset_id.table_id adsb-schema.json" ] }, { "cell_type": "markdown", "id": "04cf0db4-e6f7-48b8-84cd-f70381574b5a", "metadata": {}, "source": [ "### Load data into a BigQuery table\n", "\n", "Assuming you have an empty BigQuery table created, the following loads local data into the BigQuery table on file at a time.\n", "\n", "> **PRO TIP**\n", "> \n", "> To maximize BigQuery load speed, \n", "> consider moving the dataset into a Google Cloud Storage Bucket.\n", ">\n", "> See [client.load_table_from_uri(..)](https://cloud.google.com/python/docs/reference/bigquery/latest/google.cloud.bigquery.client.Client#google_cloud_bigquery_client_Client_load_table_from_uri) or the [`bq load` command](https://cloud.google.com/bigquery/docs/batch-loading-data#permissions-load-data-from-cloud-storage).\n", "> \n", "> Uploading from a GCS bucket will increase upload speed both due to \n", "> the bucket being in the Google network (high uplink speed), and the commands above supporting wildcards for GCS URI paths." ] }, { "cell_type": "code", "execution_count": 6, "id": "cf144e4b-7151-488d-8ca7-977f700ac36d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Loading 2025-01-16T00\n", "Loaded 1158825 rows into contrails-301217.sandbox.adsb3\n", "Loading 2025-01-16T01\n", "Loaded 1197240 rows into contrails-301217.sandbox.adsb3\n", "Loading 2025-01-16T02\n", "Loaded 1111672 rows into contrails-301217.sandbox.adsb3\n", "Loading 2025-01-16T03\n", "Loaded 966330 rows into contrails-301217.sandbox.adsb3\n", "Loading 2025-01-16T04\n", "Loaded 895878 rows into contrails-301217.sandbox.adsb3\n", "Loading 2025-01-16T05\n", "Loaded 736327 rows into contrails-301217.sandbox.adsb3\n", "Loading 2025-01-16T06\n", "Loaded 601999 rows into contrails-301217.sandbox.adsb3\n" ] } ], "source": [ "# Initialize BigQuery client\n", "client = bigquery.Client() # Uses your default GCP \"project\" - see `gcloud config list`\n", "\n", "# Create table reference\n", "project_id = \"\" # REPLACE WITH YOUR GCP PROJECT\n", "dataset_id = \"\" # REPLACE WITH YOUR BQ DATASET\n", "table_id = \"\" # REPLACE WITH YOUR BQ TABLE\n", "bigquery_id = f\"{project_id}.{dataset_id}.{table_id}\"\n", "\n", "# Load schema\n", "with open(\"adsb-schema.json\", \"r\") as f:\n", " schema = json.load(f)\n", "\n", "# Configure the loading job\n", "job_config = LoadJobConfig(source_format=bigquery.SourceFormat.PARQUET, schema=schema)\n", "\n", "for t in times_str:\n", " # read in parquet file\n", " path = Path(f\"adsb/{t}.pq\")\n", " print(f\"Loading {t}\")\n", "\n", " # Open the local parquet file\n", " with open(path, \"rb\") as f:\n", " # Start the load job\n", " load_job = client.load_table_from_file(f, bigquery_id, job_config=job_config)\n", "\n", " # Wait for job completion\n", " load_job.result()\n", "\n", " print(f\"Loaded {load_job.output_rows} rows into {bigquery_id}\")" ] } ], "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.11.4" } }, "nbformat": 4, "nbformat_minor": 5 }