How to animate matplotlib's drawgreatcircle function?
Asked Answered
H

1

7

I have created a small program which takes an NHL city and then draws the path the team travels throughout their season.

The resulting graphic is messy:

Messy Graphic

So I got the idea that it would be interesting if I animated the flight paths, sort of like watching an Indiana Jones movie, where the line grows from one point to another.

My understanding from looking at other matplotlib samples is that the animation function takes in a function, calculates it's output, and then updates the graphic. I don't see how this would be possible with drawgreatcircle since whenever I call it I am given a completed line.

Any idea on how I can approach this?

Here's a sample image from the sample code below

Sample Image

import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap

fig = plt.figure(figsize=(10, 10))
m = Basemap(projection='merc', resolution=None,
                    llcrnrlon=-125, llcrnrlat=25, # LL = lower left
                    urcrnrlon=-60, urcrnrlat=55) #UR = upper right 
m.etopo(scale=0.5, alpha=0.5)


# Ottawa to Anaheim
# Ottawa
lat1 = 45.4215
lon1 = -75.6972

# Anaheim
lat2 = 33.8353
lon2 = -117.9145

m.drawgreatcircle(lon1,lat1,lon2,lat2)
Houseleek answered 22/2, 2017 at 22:53 Comment(4)
if you don't care too much about math niceties then just linear interpolation in lat, lon should look OK if you avoid the polesNevski
What is linear interpolation?Houseleek
What is an NHL city?Glissando
A city that has an National Hockey League team.Houseleek
G
3

drawgreatcicle returns matplotlib line2D from which one can obtain the data using get_data. So the idea would be to draw the great circle, get the data and afterwards delete it. Using the data, one can perform an animation, iterating over the array.

import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import matplotlib.animation

fig = plt.figure(figsize=(6, 4))
m = Basemap(projection='merc', resolution=None,
                    llcrnrlon=-125, llcrnrlat=25, # LL = lower left
                    urcrnrlon=-60, urcrnrlat=55) #UR = upper right 
m.etopo(scale=0.5, alpha=0.5)

# Ottawa to Anaheim
# Ottawa
lat1 = 45.4215
lon1 = -75.6972

# Anaheim
lat2 = 33.8353
lon2 = -117.9145

line, = m.drawgreatcircle(lon1,lat1,lon2,lat2)
x,y = line.get_data()

line.remove()
del line

line, = plt.plot([],[])

def update(i):
    line.set_data(x[:i], y[:i])

ani = matplotlib.animation.FuncAnimation(fig, update, frames=len(x), interval=100)
ani.save(__file__+".gif", writer="imagemagick", fps=10)
plt.tight_layout()
plt.show()

enter image description here

Glissando answered 23/2, 2017 at 8:15 Comment(4)
When I copied this code into Spyder, I got this error, "TypeError: 'Path' object is not iterable". It happened on line, = m.drawgreatcircle(lon1,lat1,lon2,lat2). If I remove the comma on "line," I get back a Path object which I see is an array but I can't index or access the values.Houseleek
Well, according to the documentation drawgreatcircle returns a matplotlib.lines.Line2D object. The solution above uses this fact and it works for me on basemap version 1.0.7 which was released in 2013 and which is still the latest official release as I see it. The issue was introduced in 2014 and got fixed late 2016, so the current development version should also return a line2D.Glissando
If you don't want to up- or downgrade your basemap installation, it should be possible to obtain the points from the path. If p is a path, points = p.vertices gives the points in a 2D array, such that x = points[:,0] and y = points[:,1] should give you the coordinates. Unfortunately I cannot test it at the moment.Glissando
Thanks a ton. I had a great battle with trying to upgrade Basemap (I use Windows) and then trying to get ImageMagick or FFMpeg but I finally got your code running on my machine. It's very much appreciated!Houseleek

© 2022 - 2024 — McMap. All rights reserved.