Plotly is one of many plotting libraries in Python, offering attractive plots of many kinds. For the purposes of this, we will be using maps to plot the live location of the international space station and update the map every 10 seconds.
First, we want to import the required libraries and if there isn’t a database already set up, set it up.
The database is optional but a clean way to plot the trajectory.
import sys
import time
import pandas as pd
import sqlite3
import requests
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# Set up the database connection
conn = sqlite3.connect('iss_location.db')
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS iss_location (timestamp INTEGER, latitude REAL, longitude REAL)''')
conn.commit()
conn.close()
Then a simple function to collect the data from the Open Notify API
# Function to fetch ISS location data
def get_iss_location():
url = "http://api.open-notify.org/iss-now.json"
req = requests.get(url)
obj = req.json()
return obj
So that the database doesn’t consume too much memory on the server, there’s also a function to delete the oldest data
def purge_old_data():
# delete rows where more than 5000 rows exist
conn = sqlite3.connect('iss_location.db')
c = conn.cursor()
c.execute("DELETE FROM iss_location WHERE timestamp NOT IN (SELECT timestamp FROM iss_location ORDER BY timestamp DESC LIMIT 5000)")
conn.commit()
conn.close()
This is the complete function to update the plot but we’ll break it down below so that the code can be adapted to your purposes.
# Function to update ISS location plot
def update_iss_location(i):
# Get the latest ISS location
location_data = get_iss_location()
purge_old_data()
latitude = float(location_data['iss_position']['latitude'])
longitude = float(location_data['iss_position']['longitude'])
timestamp = location_data['timestamp']
print(f"Latitude: {latitude}, Longitude: {longitude}, Timestamp: {timestamp}")
# Save the location data to the database
conn = sqlite3.connect('iss_location.db')
c = conn.cursor()
c.execute("INSERT INTO iss_location (timestamp, latitude, longitude) VALUES (?, ?, ?)", (timestamp, latitude, longitude))
conn.commit()
conn.close()
# Retrieve all ISS location data from the database
conn = sqlite3.connect('iss_location.db')
df = pd.read_sql_query("SELECT * FROM iss_location ORDER BY timestamp DESC LIMIT 600", conn)
conn.close()
# convert timestamp to a human-readable format
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='s')
# Create a Plotly figure
fig = make_subplots(rows=1, cols=1,
specs=[[{"type": "scattergeo"}]])
# Add scatter plot for previous locations
fig.add_trace(go.Scattergeo(
lon=df['longitude'][1:],
lat=df['latitude'][1:],
mode='markers',
marker=dict(
size=3,
color='plum',
opacity=0.7,
symbol='circle'
),
name='Previous Positions'
))
# Add scatter plot for the latest location
fig.add_trace(go.Scattergeo(
lon=[df['longitude'].iloc[0]],
lat=[df['latitude'].iloc[0]],
mode='markers',
marker=dict(
size=10,
color='darkred',
opacity=0.9,
symbol='circle'
),
name='Latest Position'
))
# Update layout
fig.update_geos(
resolution=110,
showframe=False,
showcoastlines=True,
coastlinecolor="black",
showocean=True,
oceancolor='lightsteelblue',
showlakes=True,
lakecolor='lightblue',
showland=True,
landcolor='antiquewhite',
showcountries=True,
countrycolor='gray'
)
fig.update_layout(
title=f"ISS Location - Last Updated: {df['timestamp'].iloc[0]}"
)
# Add annotation for latitude and longitude
fig.add_annotation(
text=f"Latitude: {latitude}, Longitude: {longitude}",
xref="paper", yref="paper",
x=0.5, y=-0.1,
showarrow=False,
font=dict(
size=12,
color="black"
)
)
fig.show()
You need to collect the data using the function that was built earlier.
def update_iss_location(i):
# Get the latest ISS location
location_data = get_iss_location()
purge_old_data()
latitude = float(location_data['iss_position']['latitude'])
longitude = float(location_data['iss_position']['longitude'])
timestamp = location_data['timestamp']
print(f"Latitude: {latitude}, Longitude: {longitude}, Timestamp: {timestamp}")
writing the data to a database is optional, but gives a nice option to hold previous positions so that if the server is restarted it can still be plotted.
# Save the location data to the database
conn = sqlite3.connect('iss_location.db')
c = conn.cursor()
c.execute("INSERT INTO iss_location (timestamp, latitude, longitude) VALUES (?, ?, ?)", (timestamp, latitude, longitude))
conn.commit()
conn.close()
we want to retrieve the past 600 rows which should be approximately the past 100 minutes or a bit over a full rotation of the ISS.
# Retrieve all ISS location data from the database
conn = sqlite3.connect('iss_location.db')
df = pd.read_sql_query("SELECT * FROM iss_location ORDER BY timestamp DESC LIMIT 600", conn)
conn.close()
Convert the DateTime to a more human-readable format so that it can be used in the title later.
# convert timestamp to a human-readable format
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='s')
Now comes the time to initialise the plot
# Create a Plotly figure
fig = make_subplots(rows=1, cols=1,
specs=[[{"type": "scattergeo"}]])
Each plot is done separately and layered, essentially we’re going to be treating this as a scatter plot with a background of a map.
We’ll plot the previous positions by setting longitude and latitude with everything but the 1st number in the column.
# Add scatter plot for previous locations
fig.add_trace(go.Scattergeo(
lon=df['longitude'][1:],
lat=df['latitude'][1:],
mode='markers',
marker=dict(
size=3,
color='plum',
opacity=0.7,
symbol='circle'
),
name='Previous Positions'
))
In order to highlight the latest position we’ll plot that a little larger and in a dark red color.
# Add scatter plot for the latest location
fig.add_trace(go.Scattergeo(
lon=[df['longitude'].iloc[0]],
lat=[df['latitude'].iloc[0]],
mode='markers',
marker=dict(
size=10,
color='darkred',
opacity=0.9,
symbol='circle'
),
name='Latest Position'
))
This will add the map, this is also where we can adjust the colour and design of the map.
# Update layout
fig.update_geos(
resolution=110,
showframe=False,
showcoastlines=True,
coastlinecolor="black",
showocean=True,
oceancolor='lightsteelblue',
showlakes=True,
lakecolor='lightblue',
showland=True,
landcolor='antiquewhite',
showcountries=True,
countrycolor='gray'
)
Place a title and subtitle to show the time of last updating and the last known longitude and latitude.
fig.update_layout(
title=f"ISS Location - Last Updated: {df['timestamp'].iloc[0]}"
)
# Add annotation for latitude and longitude
fig.add_annotation(
text=f"Latitude: {latitude}, Longitude: {longitude}",
xref="paper", yref="paper",
x=0.5, y=-0.1,
showarrow=False,
font=dict(
size=12,
color="black"
)
)
Finally, we show the figure
fig.show()
This is then put in a loop so that it runs until interrupted.
# Update ISS location every 10 seconds
while True:
update_iss_location(0)
time.sleep(10)
Here are the final results.
Leave a Reply