├── .gitignore ├── INSTALL.md ├── LICENSE ├── README.md ├── data_statistics.ipynb ├── intro_trajectory_data.ipynb └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .ipynb_checkpoints 2 | .DS_Store 3 | __pycache__/ 4 | *.py[cod] 5 | .idea/i24_tutorial.iml 6 | *.xml 7 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This installation will aid you to install the python environment and its packages, to run the tutorial. 4 | 5 | We recommend the use of `miniconda` as the python environment creation, in order to avoid polluting your existing python installation. To install miniconda (if you do not already have it) visit https://docs.conda.io/projects/miniconda/en/latest/miniconda-install.html 6 | 7 | # Create a conda environment 8 | 9 | ``` 10 | conda create -y -n i24motion python=3.10 11 | ``` 12 | And activate your environment: 13 | ``` 14 | conda activate i24motion 15 | ``` 16 | 17 | # Install the prerequisite packages 18 | 19 | ``` 20 | pip install -r requirements.txt 21 | ``` 22 | 23 | You should now be ready to run the tutorial. 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | Copyright (c) 2023 Vanderbilt University. All rights reserved. 3 | Developed by: I-24 MOTION development team 4 | Institute for Software Integrated Systems 5 | Vanderbilt University 6 | https://lab-work.github.io 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal with 9 | the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 11 | of the Software, and to permit persons to whom the Software is furnished to 12 | do so, subject to the following conditions: 13 | * Redistributions of source code must retain the above copyright notice, 14 | this list of conditions and the following disclaimers. 15 | * Redistributions in binary form must reproduce the above copyright notice, 16 | this list of conditions and the following disclaimers in the documentation 17 | and/or other materials provided with the distribution. 18 | * Neither the names of the I-24 MOTION project, Vanderbilt University, 19 | Tennessee Department of Transportation, nor the names of its contributors 20 | may be used to endorse or promote products derived from this Software 21 | without specific prior written permission. 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE 28 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # I-24 MOTION basic data tutorial 2 | ##### Version: 1.0 3 | ##### Last updated: 2024-09-19 4 | 5 | 6 | ## Overview 7 | 8 | This repository includes example Python notebooks that demonstrate iterative parsing of the large JSON files that make up the MOTION datasets, basic visualization of trajectories, and calculation of summary statistics of trajectories. 9 | 10 | ## Use agreement 11 | 12 | Use of this software is subject to the license (BSD 3-clause) included in the code repository. The following terms also apply to the I-24 MOTION project's data and software. 13 | 14 | 1. You are free to use this software and data in academic and commercial work. 15 | 2. I-24 MOTION datasets contain anonymous trajectories. Any activities to re-identify individuals in the dataset or activities that may cause harm to individuals in the dataset are prohibited. 16 | 3. When you use I-24 MOTION software and/or data in published academic work, you are required to include the following relevant citations. This allows us to aggregate statistics on the data use in publications: 17 | 18 | I24-MOTION System: 19 | 20 | > Gloudemans, D., Wang, Y., Ji, J., Zachár, G., Barbour, W., Hall, E., Cebelak, M., Smith, L. and Work, D.B., 2023. I-24 MOTION: An instrument for freeway traffic science. Transportation Research Part C: Emerging Technologies, 155, p.104311. 21 | 22 | ``` 23 | @article{gloudemans202324, 24 | title={I-24 MOTION: An instrument for freeway traffic science}, 25 | author={Gloudemans, Derek and Wang, Yanbing and Ji, Junyi and Zach{\'a}r, Gergely and Barbour, William and Hall, Eric and Cebelak, Meredith and Smith, Lee and Work, Daniel B}, 26 | journal={Transportation Research Part C: Emerging Technologies}, 27 | volume={155}, 28 | pages={104311}, 29 | year={2023}, 30 | publisher={Elsevier} 31 | } 32 | ``` 33 | 34 | 4. You are free to create and share derivative products as long as you maintain the terms above. 35 | 5. The data and software is provided “As is.” We make no other warranties, express or implied, and hereby disclaim all implied warranties, including any warranty of merchantability and warranty of fitness for a particular purpose. 36 | 37 | ## Requirements 38 | 39 | The requirements for running the code in this tutorial are as follows: 40 | - Python 3.10 41 | - Supporting Python libraries: Pandas, Numpy, Matplotlib, ijson, Jupyter-lab 42 | - A `requirements.txt` file is provided for creating a new Python environment for the tutorial if you wish. This is the officially supported method for running the tutorial, since the code was validated on this exact set of dependency versions. The process for creating a new environment is below. 43 | 44 | ## Data access for this tutorial 45 | You will need to download the data file from i24motion.org/data using your account credentials. 46 | The data used in this tutorial is in `I24MOTION_PUBLIC_v1.0/trajectory_data_by_date/11-22-2022/data_demo` 47 | 48 | ## Clean installation 49 | 50 | Starting with a clean Python environment (i.e., through Conda or venv) with the correct Python version and dependencies is the officially supported method for running VT-tools. The instructions for doing so using the Anaconda Python distribution are as follows. 51 | 52 | - Create a new environment with Python 3.10 (you can substitute "i24_tutorial" for your own name if you wish): `conda create -n i24_tutorial python=3.10`. 53 | - Activate the new environment (substitute "i24_tutorial" for your own name if you altered it): `conda activate i24_tutorial`. 54 | - Change directory to the i24_tutorial location: `cd [PATH_TO_TUTORIAL]`. 55 | - Install Python library dependencies: `pip install -r requirements.txt`. 56 | -------------------------------------------------------------------------------- /data_statistics.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "b12ec0da-8ec0-4298-8ba4-602046c19c87", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import os\n", 11 | "import ijson\n", 12 | "import pandas as pd" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "id": "initial_id", 19 | "metadata": {}, 20 | "outputs": [], 21 | "source": [ 22 | "def get_traj_stats(input_filename):\n", 23 | " '''\n", 24 | " This function reads the input file and calculates the travel distance, travel time and travel speed for each trajectory from INCEPTION dataset.\n", 25 | " \n", 26 | " :param input_filename: the input file name\n", 27 | " :type input_filename: str\n", 28 | " :return: the dataframe containing the trajectory statistics with columns traj_id, travel_distance, travel_time, travel_speed\n", 29 | " :rtype: pandas.DataFrame\n", 30 | " \n", 31 | " example: get_traj_stats('/.../I24M_INCEPTION_v1/11-22-2022/637c399add50d54aa5af0cf4__post2.json')\n", 32 | " '''\n", 33 | " # Store stats in a list of tuples first.\n", 34 | " traj_stats_list = []\n", 35 | " # Read the input file by parsing the json lines iteratively (for memory conservation).\n", 36 | " with open(input_filename, 'r') as input_file:\n", 37 | " parser = ijson.items(input_file, 'item')\n", 38 | " for doc in parser:\n", 39 | " # Filter out the trajectories with direction = -1 (Westbound) and length > 0\n", 40 | " if ((int(doc['direction']) == -1) & (int(doc['length']) > 0)):\n", 41 | " # Calculate the travel distance, travel time, and travel speed.\n", 42 | " travel_distance = - float(doc['ending_x']) + float(doc['starting_x'])\n", 43 | " travel_time = float(doc['last_timestamp']) - float(doc['first_timestamp'])\n", 44 | " travel_speed = travel_distance / travel_time\n", 45 | " traj_stats_list.append((doc['_id']['$oid'], travel_distance, travel_time, travel_speed))\n", 46 | " # Turn the list of tuples into a DataFrame\n", 47 | " traj_stats = pd.DataFrame(traj_stats_list, columns=['traj_id', 'travel_distance', 'travel_time', 'travel_speed'])\n", 48 | " return traj_stats" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": null, 54 | "id": "18d7b1c10f8fb6ca", 55 | "metadata": { 56 | "collapsed": false, 57 | "jupyter": { 58 | "outputs_hidden": false 59 | } 60 | }, 61 | "outputs": [], 62 | "source": [ 63 | "inception_root = '' # the root directory of the INCEPTION dataset\n", 64 | "date_dir = '' # the date directory, like '11-22-2022'\n", 65 | "file_name = '' # the file name, like '637c399add50d54aa5af0cf4__post2.json' for '11-22-2022'\n", 66 | "input_filename = os.path.join(inception_root, date_dir, file_name)" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": null, 72 | "id": "fb357a80c30428e4", 73 | "metadata": { 74 | "collapsed": false, 75 | "jupyter": { 76 | "outputs_hidden": false 77 | } 78 | }, 79 | "outputs": [], 80 | "source": [ 81 | "traj_stats = get_traj_stats(input_filename)\n", 82 | "traj_stats" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "id": "b6823c9d-c53d-4574-b968-86f27fb481cb", 89 | "metadata": {}, 90 | "outputs": [], 91 | "source": [ 92 | "import matplotlib.pyplot as plt" 93 | ] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "execution_count": null, 98 | "id": "bbd82a3b-aca3-4d6d-b0dc-6456dcfc1ffc", 99 | "metadata": {}, 100 | "outputs": [], 101 | "source": [ 102 | "fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(10, 8))\n", 103 | "\n", 104 | "distances = traj_stats[traj_stats['travel_distance'] < traj_stats['travel_distance'].quantile(.995)]['travel_distance']\n", 105 | "ax1.hist(traj_stats['travel_distance'], bins=40, \n", 106 | " color='c', edgecolor='k', alpha=0.7)\n", 107 | "ax1.axvline(distances.mean(), color='k', linestyle='dashed', linewidth=2)\n", 108 | "ax1.set_xlabel('trajectory distance (ft)')\n", 109 | "\n", 110 | "times = traj_stats[traj_stats['travel_time'] < traj_stats['travel_time'].quantile(.995)]['travel_time']\n", 111 | "ax2.hist(times, bins=40, \n", 112 | " color='m', edgecolor='k', alpha=0.7)\n", 113 | "ax2.axvline(times.mean(), color='k', linestyle='dashed', linewidth=2)\n", 114 | "ax2.set_xlabel('trajectory duration (s)')\n", 115 | "\n", 116 | "speeds = traj_stats[traj_stats['travel_speed'] < traj_stats['travel_speed'].quantile(.995)]['travel_speed']\n", 117 | "ax3.hist(speeds, bins=40, color='y', edgecolor='k', alpha=0.7)\n", 118 | "ax3.axvline(traj_stats['travel_speed'].mean(), \n", 119 | " color='k', linestyle='dashed', linewidth=2)\n", 120 | "ax3.set_xlabel('trajectory speed (ft/s)')\n", 121 | "\n", 122 | "plt.tight_layout()\n", 123 | "plt.show()" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": null, 129 | "id": "bed86950-fd1a-47af-997e-8133de65fac2", 130 | "metadata": { 131 | "collapsed": false, 132 | "jupyter": { 133 | "outputs_hidden": false 134 | } 135 | }, 136 | "outputs": [], 137 | "source": [ 138 | "# Save the DataFrame of trajectory stats to a csv file as needed.\n", 139 | "traj_stats.to_csv('temp.csv', index=False) # Fill in the filename." 140 | ] 141 | } 142 | ], 143 | "metadata": { 144 | "kernelspec": { 145 | "display_name": "Python 3 (ipykernel)", 146 | "language": "python", 147 | "name": "python3" 148 | }, 149 | "language_info": { 150 | "codemirror_mode": { 151 | "name": "ipython", 152 | "version": 3 153 | }, 154 | "file_extension": ".py", 155 | "mimetype": "text/x-python", 156 | "name": "python", 157 | "nbconvert_exporter": "python", 158 | "pygments_lexer": "ipython3", 159 | "version": "3.10.13" 160 | } 161 | }, 162 | "nbformat": 4, 163 | "nbformat_minor": 5 164 | } 165 | -------------------------------------------------------------------------------- /intro_trajectory_data.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "attachments": {}, 5 | "cell_type": "markdown", 6 | "metadata": {}, 7 | "source": [ 8 | "# An introduction to I-24 MOTION trajectory data" 9 | ] 10 | }, 11 | { 12 | "attachments": {}, 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "## Useful links\n", 17 | "- I24 MOTION website: https://i24motion.org/\n", 18 | "- Request for data access: https://i24motion.org/data\n", 19 | "- Data documentation: https://github.com/I24-MOTION/I24M_documentation\n", 20 | "- VT tools: https://github.com/I24-MOTION/VT_tools \n", 21 | "- Improvement tracker: https://github.com/I24-MOTION/I24M_improvement_tracker" 22 | ] 23 | }, 24 | { 25 | "attachments": {}, 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "## Directory structure\n", 30 | "```\n", 31 | "├── data_demo\n", 32 | "│ ├── INCEPTION.22-11-22.tutorial.json\n", 33 | "├── I24_tutorial_code\n", 34 | "│ ├── intro_trajectory_data.ipynb\n", 35 | "```" 36 | ] 37 | }, 38 | { 39 | "attachments": {}, 40 | "cell_type": "markdown", 41 | "metadata": {}, 42 | "source": [ 43 | "## This tutorial will cover:\n", 44 | "- Load JSON file using an iterative JSON parser\n", 45 | "- Trajectory data schema\n", 46 | "- Compute derivative quantities (e.g., speed)\n", 47 | "- Visualize a trajectory\n", 48 | "- Plot a time-space diagram" 49 | ] 50 | }, 51 | { 52 | "attachments": {}, 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "## Import packages" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "metadata": {}, 63 | "outputs": [], 64 | "source": [ 65 | "import ijson\n", 66 | "import os\n", 67 | "import numpy as np\n", 68 | "import matplotlib.pyplot as plt\n", 69 | "from matplotlib.colors import LinearSegmentedColormap\n", 70 | "import matplotlib.ticker as mticker\n", 71 | "from mpl_toolkits.axes_grid1 import make_axes_locatable\n", 72 | "import datetime" 73 | ] 74 | }, 75 | { 76 | "attachments": {}, 77 | "cell_type": "markdown", 78 | "metadata": {}, 79 | "source": [ 80 | "### Read a JSON file using an iterative JSON parser" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": null, 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [ 89 | "# This data file contains approximately 41,000 trajectories\n", 90 | "input_filename = \"INCEPTION.22-11-22.tutorial.json\"\n", 91 | "file_path = os.path.join(\"../data_demo/\", input_filename)\n", 92 | "\n", 93 | "i = 0\n", 94 | "# Select one trajectory from westbound that is more than 10 sec long\n", 95 | "with open(file_path, 'r') as input_file:\n", 96 | " parser = ijson.items(input_file, 'item', use_float=True)\n", 97 | " for record in parser:\n", 98 | " if record[\"direction\"] == -1 and (record[\"last_timestamp\"] - record[\"first_timestamp\"] > 10):\n", 99 | " print(\"Found a trajectory example\")\n", 100 | " break" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": null, 106 | "metadata": {}, 107 | "outputs": [], 108 | "source": [ 109 | "print(\"TRAJECTORY KEYS:\", list(record.keys()))\n", 110 | "print(\"\\n\\n\")\n", 111 | "for key, val in record.items():\n", 112 | " if isinstance(val, list):\n", 113 | " print(f\"{key}: {val[:min(len(val), 20)]}\")\n", 114 | " else:\n", 115 | " print(f\"{key}: {val}\")" 116 | ] 117 | }, 118 | { 119 | "attachments": {}, 120 | "cell_type": "markdown", 121 | "metadata": {}, 122 | "source": [ 123 | "### Compute the derivative quantities" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": null, 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": [ 132 | "# Compute the longitudinal speed using numerical differentiation of the x_position, \n", 133 | "# i.e., v = dx/dt. np.diff() computes the first-order numerical derivative.\n", 134 | "# Multiply by record[\"direction\"] to indicate the direction, \n", 135 | "# i.e., negative speed means traveling westbound, and positive speed means traveling eastbound.\n", 136 | "speed = np.diff(record[\"x_position\"])/np.diff(record[\"timestamp\"])*record[\"direction\"]\n", 137 | "\n", 138 | "# Speed array is truncated by one after the numerical differentiation np.diff(). \n", 139 | "# Append the first item to the array such that the speed array is the same length as the x_position array.\n", 140 | "speed = np.append(speed[0], speed) \n", 141 | "\n", 142 | "# Convert unit from ft/sec to mph\n", 143 | "speed *= 0.681818\n", 144 | "\n", 145 | "# Print the first 10 records of speed\n", 146 | "print(f\"Speed (mph): {speed[:10]}\")" 147 | ] 148 | }, 149 | { 150 | "cell_type": "code", 151 | "execution_count": null, 152 | "metadata": {}, 153 | "outputs": [], 154 | "source": [ 155 | "# Similarly, compute the 2nd-order numerical differentiation of the x_position to obtain acceleration. \n", 156 | "# np.diff(record[\"x_position\"], n=2) means the second order differentation of x_position.\n", 157 | "# The unit for acceleration is ft/sec^2.\n", 158 | "accel = np.diff(record[\"x_position\"], n=2)/(np.diff(record[\"timestamp\"][:-1])**2)*record[\"direction\"]\n", 159 | "print(f\"Acceleration (ft/sec^2): {accel[:10]}\")" 160 | ] 161 | }, 162 | { 163 | "attachments": {}, 164 | "cell_type": "markdown", 165 | "metadata": {}, 166 | "source": [ 167 | "### Visualize a trajectory" 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": null, 173 | "metadata": {}, 174 | "outputs": [], 175 | "source": [ 176 | "import time" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": null, 182 | "metadata": {}, 183 | "outputs": [], 184 | "source": [ 185 | "# Set up the plot\n", 186 | "plt.rc('font', family='serif', size=14)\n", 187 | "fig, ax = plt.subplots(figsize=(10,3))\n", 188 | "divider = make_axes_locatable(ax)\n", 189 | "cax = divider.append_axes(\"right\", size=\"3%\", pad=0.05)\n", 190 | "\n", 191 | "# Define the color range\n", 192 | "jet = plt.cm.jet\n", 193 | "colors = [jet(x) for x in np.linspace(1, 0.5, 256)]\n", 194 | "green_to_red = LinearSegmentedColormap.from_list('GreenToRed', colors, N=256)\n", 195 | "\n", 196 | "# -------------------------------------------------------------------\n", 197 | "# Plot the position (y-axis) vs. time (x-axis), colored by the speed.\n", 198 | "# Position in ft is converted to mile-marker by dividing 5280.\n", 199 | "trajectory_times = record[\"timestamp\"]\n", 200 | "trajectory_xvals = np.array(record[\"x_position\"])/5280\n", 201 | "im = ax.scatter(trajectory_times, trajectory_xvals, c=speed, cmap=green_to_red, vmin=0, vmax=80, marker ='s', s=5)\n", 202 | "plt.colorbar(im, cax=cax).set_label('Speed (mph)', rotation=90, labelpad=20)\n", 203 | "ax.set_xlabel(\"Time\")\n", 204 | "ax.set_ylabel(\"Mile marker\")\n", 205 | "# -------------------------------------------------------------------\n", 206 | "\n", 207 | "# Update x-axis time to readable format\n", 208 | "ticks_loc = ax.get_xticks().tolist()\n", 209 | "ax.xaxis.set_major_locator(mticker.FixedLocator(ticks_loc))\n", 210 | "x_datetime = [datetime.datetime.fromtimestamp(ts) for ts in ticks_loc]\n", 211 | "labels = [d.strftime('%H:%M:%S') for d in x_datetime]\n", 212 | "ax.set_xticklabels(labels, rotation=45)\n", 213 | "\n", 214 | "# Invert vertically\n", 215 | "ax.invert_yaxis()\n" 216 | ] 217 | }, 218 | { 219 | "attachments": {}, 220 | "cell_type": "markdown", 221 | "metadata": {}, 222 | "source": [ 223 | "### Plot a time-space diagram (approx. 8:20-8:23 AM, Westbound)" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": null, 229 | "metadata": {}, 230 | "outputs": [], 231 | "source": [ 232 | "# Set up the plot\n", 233 | "plt.rc('font', family='serif', size=14)\n", 234 | "fig, ax = plt.subplots(figsize=(10,3))\n", 235 | "divider = make_axes_locatable(ax)\n", 236 | "cax = divider.append_axes(\"right\", size=\"3%\", pad=0.05)\n", 237 | "\n", 238 | "# Define the color range\n", 239 | "jet = plt.cm.jet\n", 240 | "colors = [jet(x) for x in np.linspace(1, 0.5, 256)]\n", 241 | "green_to_red = LinearSegmentedColormap.from_list('GreenToRed', colors, N=256)\n", 242 | "\n", 243 | "# -------------------------------------------------------------------\n", 244 | "# Iterate over each trajectory record across the file, compute the speed, and plot the position and speed simultaneously\n", 245 | "# ijson allows us to read and plot each trajectory one by one instead of loading them all into the memory\n", 246 | "t = time.time()\n", 247 | "i = 0\n", 248 | "with open(file_path, 'r') as input_file:\n", 249 | " parser = ijson.items(input_file, 'item', use_float=True)\n", 250 | " for record in parser: \n", 251 | " # select westbound trajectories to plot\n", 252 | " if record[\"direction\"] == -1: \n", 253 | " position = np.array(record[\"x_position\"]) / 5280\n", 254 | " speed = np.diff(record[\"x_position\"]) / np.diff(record[\"timestamp\"])\n", 255 | " speed = np.append(speed[0], speed)\n", 256 | " speed = -speed * 0.681818 # convert unit, and\n", 257 | " im = ax.scatter(record[\"timestamp\"], position, c=speed, cmap=green_to_red, vmin=0, vmax=80, s=0.1)\n", 258 | " i += 1\n", 259 | " if i % 1000 == 0:\n", 260 | " print(f\"Plotted {i} trajectories\")\n", 261 | " if i > 5000:\n", 262 | " break\n", 263 | "print(f\"Elapsed: {time.time() - t}\")\n", 264 | "# -------------------------------------------------------------------\n", 265 | "\n", 266 | "# Update colorbar and axes labels\n", 267 | "plt.colorbar(im, cax=cax).set_label('Speed (mph)', rotation=90, labelpad=20)\n", 268 | "ax.set_xlabel(\"Time\")\n", 269 | "ax.set_ylabel(\"Mile marker\")\n", 270 | "\n", 271 | "# Update x-axis time to readable format\n", 272 | "ticks_loc = ax.get_xticks().tolist()\n", 273 | "ax.xaxis.set_major_locator(mticker.FixedLocator(ticks_loc))\n", 274 | "x_datetime = [datetime.datetime.fromtimestamp(ts) for ts in ticks_loc]\n", 275 | "labels = [d.strftime('%H:%M:%S') for d in x_datetime]\n", 276 | "ax.set_xticklabels(labels, rotation=45)\n", 277 | "\n", 278 | "# Invert vertically\n", 279 | "ax.invert_yaxis()\n", 280 | "plt.show()" 281 | ] 282 | }, 283 | { 284 | "attachments": {}, 285 | "cell_type": "markdown", 286 | "metadata": {}, 287 | "source": [ 288 | "#### In order to plot these additional data, you will need to download the appropriate file from i24motion.org/data using your account credentials. Download it to the same directory where you found `INCEPTION.22-11-22.tutorial.json`, or else change the path location." 289 | ] 290 | }, 291 | { 292 | "cell_type": "code", 293 | "execution_count": null, 294 | "metadata": {}, 295 | "outputs": [], 296 | "source": [] 297 | }, 298 | { 299 | "attachments": {}, 300 | "cell_type": "markdown", 301 | "metadata": {}, 302 | "source": [] 303 | } 304 | ], 305 | "metadata": { 306 | "kernelspec": { 307 | "display_name": "Python 3 (ipykernel)", 308 | "language": "python", 309 | "name": "python3" 310 | }, 311 | "language_info": { 312 | "codemirror_mode": { 313 | "name": "ipython", 314 | "version": 3 315 | }, 316 | "file_extension": ".py", 317 | "mimetype": "text/x-python", 318 | "name": "python", 319 | "nbconvert_exporter": "python", 320 | "pygments_lexer": "ipython3", 321 | "version": "3.10.13" 322 | } 323 | }, 324 | "nbformat": 4, 325 | "nbformat_minor": 4 326 | } 327 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | anyio==4.3.0 2 | appnope==0.1.4 3 | argon2-cffi==23.1.0 4 | argon2-cffi-bindings==21.2.0 5 | arrow==1.3.0 6 | asttokens==2.4.1 7 | async-lru==2.0.4 8 | attrs==23.2.0 9 | Babel==2.14.0 10 | beautifulsoup4==4.12.3 11 | bleach==6.1.0 12 | certifi==2024.2.2 13 | cffi==1.16.0 14 | charset-normalizer==3.3.2 15 | comm==0.2.1 16 | contourpy==1.2.0 17 | cycler==0.12.1 18 | debugpy==1.8.1 19 | decorator==5.1.1 20 | defusedxml==0.7.1 21 | exceptiongroup==1.2.0 22 | executing==2.0.1 23 | fastjsonschema==2.19.1 24 | fonttools==4.49.0 25 | fqdn==1.5.1 26 | h11==0.14.0 27 | httpcore==1.0.4 28 | httpx==0.27.0 29 | idna==3.6 30 | ijson==3.2.3 31 | ipykernel==6.29.2 32 | ipython==8.21.0 33 | isoduration==20.11.0 34 | jedi==0.19.1 35 | Jinja2==3.1.3 36 | json5==0.9.17 37 | jsonpointer==2.4 38 | jsonschema==4.21.1 39 | jsonschema-specifications==2023.12.1 40 | jupyter-events==0.9.0 41 | jupyter-lsp==2.2.2 42 | jupyter_client==8.6.0 43 | jupyter_core==5.7.1 44 | jupyter_server==2.12.5 45 | jupyter_server_terminals==0.5.2 46 | jupyterlab==4.1.2 47 | jupyterlab_pygments==0.3.0 48 | jupyterlab_server==2.25.3 49 | kiwisolver==1.4.5 50 | MarkupSafe==2.1.5 51 | matplotlib==3.8.3 52 | matplotlib-inline==0.1.6 53 | mistune==3.0.2 54 | nbclient==0.9.0 55 | nbconvert==7.16.1 56 | nbformat==5.9.2 57 | nest-asyncio==1.6.0 58 | notebook_shim==0.2.4 59 | numpy==1.26.4 60 | overrides==7.7.0 61 | packaging==23.2 62 | pandas==2.2.0 63 | pandocfilters==1.5.1 64 | parso==0.8.3 65 | pexpect==4.9.0 66 | pillow==10.2.0 67 | platformdirs==4.2.0 68 | prometheus_client==0.20.0 69 | prompt-toolkit==3.0.43 70 | psutil==5.9.8 71 | ptyprocess==0.7.0 72 | pure-eval==0.2.2 73 | pyarrow==15.0.0 74 | pycparser==2.21 75 | Pygments==2.17.2 76 | pyparsing==3.1.1 77 | python-dateutil==2.8.2 78 | python-json-logger==2.0.7 79 | pytz==2024.1 80 | PyYAML==6.0.1 81 | pyzmq==25.1.2 82 | referencing==0.33.0 83 | requests==2.31.0 84 | rfc3339-validator==0.1.4 85 | rfc3986-validator==0.1.1 86 | rpds-py==0.18.0 87 | Send2Trash==1.8.2 88 | six==1.16.0 89 | sniffio==1.3.0 90 | soupsieve==2.5 91 | stack-data==0.6.3 92 | terminado==0.18.0 93 | tinycss2==1.2.1 94 | tomli==2.0.1 95 | tornado==6.4 96 | traitlets==5.14.1 97 | types-python-dateutil==2.8.19.20240106 98 | typing_extensions==4.9.0 99 | tzdata==2024.1 100 | uri-template==1.3.0 101 | urllib3==2.2.1 102 | wcwidth==0.2.13 103 | webcolors==1.13 104 | webencodings==0.5.1 105 | websocket-client==1.7.0 106 | --------------------------------------------------------------------------------