This tutorial uses data published by NASA:
Hat tip to https://fosstodon.org/@65dBnoise/108251277108722231 for providing the pointers
Known issues:
import numpy as np
import pandas as pd
import geopandas as gpd
import movingpandas as mpd
import shapely as shp
import hvplot.pandas
import matplotlib.pyplot as plt
from geopandas import GeoDataFrame, read_file
from shapely.geometry import Point, LineString, Polygon
from datetime import datetime, timedelta
from holoviews import opts, dim
from os.path import exists
from urllib.request import urlretrieve
import warnings
warnings.filterwarnings('ignore')
plot_defaults = {'linewidth':5, 'capstyle':'round', 'figsize':(9,3), 'legend':True}
opts.defaults(opts.Overlay(active_tools=['wheel_zoom'], frame_width=500, frame_height=400))
hvplot_defaults = {'tiles':None, 'cmap':'Viridis', 'colorbar':True}
mpd.show_versions()
"The car-sized Perseverance and its little helicopter buddy Ingenuity landed together inside Mars' Jezero Crater on Feb. 18." https://www.space.com/perseverance-rover-100-mars-days (by Mike Wall published June 02, 2021)
"One sol lasts about 24 hours and 40 minutes, slightly longer than an Earth day." https://www.space.com/perseverance-rover-100-mars-days
def to_timestamp(row):
start_time = datetime(2021,2,18,0,0,0) # sol 0
try:
sol = row['sol'] # rover
except KeyError:
sol = row['Sol'] # heli
td = timedelta(hours=24*sol, minutes=40*sol)
return start_time + td
def get_df_from_url(url):
file = url.split('/')[-1]
if not exists(file):
urlretrieve(url, file)
gdf = read_file(file)
gdf['time'] = gdf.apply(to_timestamp, axis=1)
gdf.set_index('time', inplace=True)
return gdf
m20_waypoints_json = "https://mars.nasa.gov/mmgis-maps/M20/Layers/json/M20_waypoints.json"
heli_waypoints_json = "https://mars.nasa.gov/mmgis-maps/M20/Layers/json/m20_heli_waypoints.json"
m20_df = get_df_from_url(m20_waypoints_json)
heli_df = get_df_from_url(heli_waypoints_json)
print(f'M20 records: {len(m20_df)}')
print(f'Heli records: {len(heli_df)}')
m20_df.describe()
m20_df.hvplot(title="M20 & heli waypoints", hover_cols=['sol'], **hvplot_defaults) * heli_df.hvplot()
m20_traj = mpd.Trajectory(m20_df, 'm20')
heli_traj = mpd.Trajectory(heli_df, 'heli')
traj_plot = m20_traj.hvplot(title="M20 & heli trajectories", line_width=3, **hvplot_defaults) * heli_traj.hvplot(line_width=3, **hvplot_defaults)
traj_plot
m20_traj.hvplot(title="Rover speed (only suitable for relative comparison)",
c='speed', line_width=7, **hvplot_defaults)
m20_detector = mpd.TrajectoryStopDetector(m20_traj)
stop_points = m20_detector.get_stop_points(min_duration=timedelta(seconds=60), max_diameter=100)
stop_points['duration_days'] = stop_points['duration_s']/(60*60*24)
stop_points.head()
heli_detector = mpd.TrajectoryStopDetector(heli_traj)
heli_stop_points = heli_detector.get_stop_points(min_duration=timedelta(seconds=60), max_diameter=100)
heli_stop_points['duration_days'] = heli_stop_points['duration_s']/(60*60*24)
heli_stop_points.head()
stop_point_plot = stop_points.hvplot(title='M20 & heli stops ',
geo=True, size=np.log(dim('duration_days'))*10,
hover_cols=['duration_days'], color='blue', alpha=0.5)
heli_stop_plot = heli_stop_points.hvplot(geo=True, size=np.log(dim('duration_days'))*10,
hover_cols=['duration_days'], color='red', alpha=0.5)
stop_point_plot * heli_stop_plot * traj_plot
Compare to https://mars.nasa.gov/mars2020/mission/where-is-the-rover/
from bokeh.models import TMSTileSource
tile_url = 'http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/celestia_mars-shaded-16k_global/{Z}/{X}/{Y}.png'
def mars_tiles(plot, element):
plot.state.add_tile(TMSTileSource(url=tile_url), level='underlay')
traj_map = m20_traj.hvplot(title="M20 & heli trajectories", tiles=None) * heli_traj.hvplot(**hvplot_defaults)
traj_map.opts(hooks=[mars_tiles])
from geoviews.element import WMTS
MarsImagery = WMTS(
'https://trek.nasa.gov/tiles/Mars/EQ/Mars_MGS_MOLA_ClrShade_merge_global_463m/1.0.0/default/default028mm/{Z}/{Y}/{X}.jpg',
name="Mars")
m20_traj.hvplot(title="M20 & heli trajectories", tiles=MarsImagery) * heli_traj.hvplot(**hvplot_defaults)