In [1]:
# ------- Imports and setup ------------


import numpy as np
import pandas as pd
import plotly.graph_objs as go
import gpxpy
from dateutil.parser import parse
from imageio import imread
from matplotlib.pyplot import imshow
from plotly.offline import init_notebook_mode, iplot
from urllib.request import urlopen

init_notebook_mode(connected=True)
%matplotlib inline
In [2]:
# ------- Load data ------------


# Load background image - limited to 256 colour depth as this needs to be rendered as a surface in the plot
img = imread('https://data.etches.uk/public/basemap.png')
# Load track data from gpx file
gpx_data = urlopen('https://data.etches.uk/public/Lunch_Run.gpx')
In [3]:
# ------- Get colour map from png ------------

colour_to_value = {}
png_colour_map = []

# Find and show unique colours in the image array ( hint: this will be limited to a max of 256 )
img_array = img[:, :, :3].reshape((img.shape[0] * img.shape[1], 3))
colours = np.unique(img_array, axis=0)

n_colours = len(colours)


# for each unique colour assign an rgb value for a slice of the colour range
for i, colour in enumerate(colours):
  
    colour_to_value[tuple(colour)] = i/(n_colours - 1)

# munge the dictionary into an array of values (z) and an rgb colour
for colour, value in colour_to_value.items():
    
    png_colour_map.append((value, 'rgb({}, {}, {})'.format(*colour)))

# Map pixels to values
find_value = lambda x: colour_to_value[tuple(x[:3])]
values = np.apply_along_axis(find_value, 2, np.flipud(img))
In [4]:
# ------- Process file into GPX file ------------

# using https://pypi.org/project/gpxpy/ to parse 
gpx = gpxpy.parse(gpx_data)
In [5]:
# ------- Convert GPX to Dataframe ------------

df = pd.DataFrame(columns=['lon', 'lat', 'alt', 'time'])

d1 = parse('2020-05-29T11:12:49Z')

for segment in gpx.tracks[0].segments: # all segments

    data = segment.points

    for point in data:
        timesec = (abs((point.time - d1).seconds))
        df = df.append({'lon': point.longitude, 'lat' : point.latitude, 'alt' : point.elevation, 'time' : timesec}, ignore_index=True)
 


df.index = df['time']


# set time from 0
df.loc[:, "time"] = df["time"].apply(lambda x: x - df['time'].min() )
In [6]:
# ------- Display space-time cube ------------

traces = []

trace = go.Scatter3d(
    
    x=df['lon'].values, y=df['lat'].values, z=df['time'].values,
    
    showlegend=False
)
traces.append(trace)  

yy = np.linspace(52.223, 52.242, img.shape[0])
xx = np.linspace(0.242, 0.284, img.shape[0])
zz = np.full(img.shape[:2], -90)



surf = go.Surface(
    x=xx, y=yy, z=zz,
    colorscale=png_colour_map,
    surfacecolor=values,
    showscale=False
)


layout = go.Layout(
    margin=dict(l=0,r=0,b=0,t=0),
    scene=go.layout.Scene(
        xaxis=go.layout.scene.XAxis(title='', showticklabels=False),
        yaxis=go.layout.scene.YAxis(title='', showticklabels=False),
        zaxis=go.layout.scene.ZAxis(title='Time (s)'),
        aspectratio=dict(x=1, y=1, z=1.3),
        camera=go.layout.scene.Camera(
            projection=go.layout.scene.camera.Projection(
                type='orthographic'
            )
        )
    )   
)


fig = go.Figure(data=[surf] + traces, layout=layout)


fig.show()

fig.write_html("adamgoesforarun.html")
In [ ]: