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)
Cache Staheli
  • 3,510
  • 7
  • 32
  • 51
Bryan Stafford
  • 301
  • 2
  • 12

1 Answers1

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

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • 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. – Bryan Stafford Feb 23 '17 at 13:10
  • Well, according to [the documentation](http://matplotlib.org/basemap/api/basemap_api.html#mpl_toolkits.basemap.Basemap.drawgreatcircle) `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](https://github.com/matplotlib/basemap/issues/322) late 2016, so the current development version should also return a line2D. – ImportanceOfBeingErnest Feb 23 '17 at 15:14
  • 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. – ImportanceOfBeingErnest Feb 23 '17 at 15:19
  • 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! – Bryan Stafford Feb 24 '17 at 21:47