I've created a map and I am reading in a CSV of latitude and longitude coordinates into a Pandas DataFrame. I've been successful in plotting multiple great arcs using a 'for' loop after reading in the DataFrame.
I am now trying to animate the multiple great arcs. The reason for the animation is to live update the plot when the CSV is updated. I eventually will use a SQL table for the DataFrame rather than a CSV, but I wanted to get the animation portion working first.
I read and watched multiple tutorials on matplotlib and animation. I've read the source documentation and attempted to apply multiple examples. I've been able to successfully animate a single great arc but can't seem to expand the code to multiple arcs.
Edit1: I want all great circle arcs from the file to be drawn on the map (I've got this part working). When a new set of latitude and longitude is added to the file I want it to update automatically within the same figure. I am trying to use this as a realtime display.
**Edit2: I've paired down the code as much as possible. It is minimal with a basic map, a few long/lat coordinates in a data frame and my attempt to animate the lines. I had to keep a CSV input as it is the only way to verify that the map updates once the CSV is updated.
The CSV contains the following:
sourcelon sourcelat destlon destlat
50.44 30.51 -80.84 35.22
52.52 13.4 -80.84 35.22
43.18 -22.97 -80.84 35.22
35.18 -22.97 -80.84 35.22
The arcs are being drawn, but are not being updated when the CSV is updated.
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import matplotlib.animation
# create new figure, axes instances.
fig=plt.figure()
# setup mercator map projection.
plt.figure(figsize=(27, 20))
m = Basemap(projection='mill', lon_0=0)
m.drawcoastlines(color='r', linewidth=1.0)
def animate(i):
df = pd.read_csv('c:/python/scripts/test2.csv', sep='\s*,\s*',header=0, encoding='ascii', engine='python'); df
for x,y,z,w in zip(df['sourcelon'], df['sourcelat'], df['destlon'], df['destlat']):
line, = m.drawgreatcircle(x,y,z,w,color='r')
line, = plt.plot([],[])
ani = matplotlib.animation.FuncAnimation(fig, animate, interval=1000)
plt.tight_layout()
plt.show()
I answered my own question. I am able to get the map to update when I add coordinates to the CSV. However, I cant remove them. I'll work on that as well.
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import matplotlib.animation
# setup mercator map projection.
fig = plt.figure(figsize=(27, 20))
m = Basemap(projection='mill', lon_0=0)
m.drawcoastlines(color='r', linewidth=1.0)
def animate(i):
df = pd.read_csv('c:/python/scripts/test2.csv', sep='\s*,\s*',header=0, encoding='ascii', engine='python'); df
for x,y,z,w in zip(df['sourcelon'], df['sourcelat'], df['destlon'], df['destlat']):
line, = m.drawgreatcircle(x,y,z,w,color='r')
ani = matplotlib.animation.FuncAnimation(fig, animate, interval=100)
plt.tight_layout()
plt.show()
Related
I have a single column data frame constantly collecting data from an external device running in a while loop in real time. In order to plot the data frame I have used tkinter but due to its resource intensive issues I abandoned it, I have tried using matplotlib, but couldn't get it to update accordingly.
Any other suggestions or an example would be great
Okay so I found some help online and managed to neatly and efficiently display the data incoming from my sensors. So here are my steps to achieve the result, Open the python command window, and type python YourDataretrievingScript.py this will run your script of retrieving data from your sensors, just make sure you save it onto a csv file, then I run the following code and the animation window pops up
import pandas as pd
import numpy as np
from itertools import count
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.ticker import MaxNLocator
plt.style.use('fivethirtyeight')
index = count()
def animate(i):
data = pd.read_csv('Predict.csv')
x1 = data['Target']
x2 = data['Gesture']
plt.cla()
plt.plot(x1, label = 'Positions')
plt.plot(x2, label = 'Gesture')
plt.xticks([])
plt.legend(loc='upper left')
plt.tight_layout()
ani = FuncAnimation(plt.gcf(), animate, interval = 500)
plt.tight_layout()
plt.show()
I have plotted two different columns of data of the same csv file, and removed the xticks as per my specifications, if you don't want that just remove the plt.xticks([])
So, what I am having trouble with is how I am supposed to plot the data I have on top of a global map. I have an array of data, and two arrays of coordinates in latitude and longitude, where each datapoint was taken, but I am not sure of how to plot it on top of a global map. Creating the map itself is not too difficult, I just use:
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
fig = plt.figure(figsize=(10, 8))
m = Basemap(projection='cyl', resolution='c',
llcrnrlat=-90, urcrnrlat=90,
llcrnrlon=-180, urcrnrlon=180, )
m.shadedrelief(scale=0.5)
m.drawcoastlines(color='black')
But the next step is where I am having problems. I have tried doing both a colormesh plot and scatter plot, but they haven't worked so far. How should I go about it so that the data is plotted in the correct coordinate locations for the global map?
Thanks a lot for any help!
Maybe a bit late, but I have this piece of code I used to plot multiple linear plot over a map in Basemap that worked for me.
map = Basemap(projection='cyl', resolution='c',
llcrnrlat=mins[1], urcrnrlat=maxs[1],
llcrnrlon=mins[0], urcrnrlon=50, )
plt.figure(figsize=(15, 15))
for i in range(1259):
filepath = filename[i]
data = pd.read_csv(filepath, index_col=0)
map.plot(data.x,data.y,'k-', alpha=0.1) ### Calling the plot in a loop!!
map.drawcoastlines(linewidth=1)
map.drawcountries(linewidth=0.5, linestyle='solid', color='k' )
plt.show()
The loop calls data from different folders, and I just use the map.plot command to plot. By doing it like that, you can plot all data in the same map.
I am attempting to plot multiple great circles using a for loop in conjunction with a set of lat/lon points. I am using the animation function with matplotlib to make the plots update when the data source is updated. This is all working well.
I noticed that plotting greatcircles where the shortest distance is wrapping the image, the plot will use that and appear on the other side of the map. Is there an argument that prevents this?
Also, depending on where the plot is I notice the "middle" of the plot arc is missing. What could be causing this? Map and code below:
The CSV uses the following points:(Moscow and Tokyo)
sourcelon sourcelat destlon destlat
55.44 37.51 -80.84 35.22
139 35.6 -80.84 35.22
Minimal code:
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import matplotlib.animation
# setup mercator map projection.
fig = plt.figure(figsize=(27, 20))
m = Basemap(projection='mill', lon_0=0)
m.drawcoastlines(color='r', linewidth=1.0)
def animate(i):
df = pd.read_csv('c:/python/scripts/test2.csv', sep='\s*,\s*',header=0, encoding='ascii', engine='python'); df
for x,y,z,w in zip(df['sourcelon'], df['sourcelat'], df['destlon'], df['destlat']):
line, = m.drawgreatcircle(x,y,z,w,color='r')
ani = matplotlib.animation.FuncAnimation(fig, animate, interval=1000)
plt.tight_layout()
plt.show()
As wikipedia tells us
The great-circle distance or orthodromic distance is the shortest distance between two points on the surface of a sphere, measured along the surface of the sphere.
So the path shown goes the shortest distance, which might wrap from one side of the image to the other.
The missing points in the line are a bit of a mystery, but it might be that there is some problem with the projection in use. Using a different projection, this works fine, e.g. projection='robin':
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,6))
m = Basemap(projection='robin',lon_0=0,resolution='c')
m.drawcoastlines(color='grey', linewidth=1.0)
a = [[55.44, 37.51, -80.84, 35.22],[139, 35.6, -80.84, 35.22]]
x,y,z,w = a[0]
line, = m.drawgreatcircle(x,y,z,w,color='r')
plt.show()
The problem can be circumvented if the distance between points in enlarged, e.g.
line, = m.drawgreatcircle(x,y,z,w,del_s=1000,color='r')
would give
Another workaround would be to get the data from the line and plot it manually,
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,6))
m = Basemap(projection='mill', lon_0=0)
m.drawcoastlines(color='grey', linewidth=1.0)
a = [[55.44, 37.51, -80.84, 35.22],[139, 35.6, -80.84, 35.22]]
x,y,z,w = a[0]
line, = m.drawgreatcircle(x,y,z,w,color='r')
line.remove()
mx,my = line.get_data()
m.plot(mx,my, color="limegreen")
plt.show()
I have multiple CSV files that I am trying to plot in same the figure to have a comparison between them. I already read some information about pandas problem not keeping memory plot and creating the new one every time. People were talking about using an ax var, but I do not understand it...
For now I have:
def scatter_plot(csvfile,param,exp):
for i in range (1,10):
df = pd.read_csv('{}{}.csv'.format(csvfile,i))
ax = df.plot(kind='scatter',x=param,y ='Adjusted')
df.plot.line(x=param,y='Adjusted',ax=ax,style='b')
plt.show()
plt.savefig('plot/{}/{}'.format(exp,param),dpi=100)
But it's showing me ten plot and only save the last one.
Any idea?
The structure is
create an axes to plot to
run the loop to populate the axes
save and/or show (save before show)
In terms of code:
import matplotlib.pyplot as plt
import pandas as pd
ax = plt.gca()
for i in range (1,10):
df = pd.read_csv(...)
df.plot(..., ax=ax)
df.plot.line(..., ax=ax)
plt.savefig(...)
plt.show()
Some seaborn methods like JointPlot create new figures on each call. This makes it impossible to create a simple animation like in matplotlib where iterative calls to plt.cla() or plt.clf() allow to update the contents of a figure without closing/opening the window each time.
The only solution I currently see is:
for t in range(iterations):
# .. update your data ..
if 'jp' in locals():
plt.close(jp.fig)
jp = sns.jointplot(x=data[0], y=data[1])
plt.pause(0.01)
This works because we close the previous window right before creating a new one. But of course, this is far from ideal.
Is there a better way? Can the plot somehow be done directly on a previously generated Figure object? Or is there a way to prevent these methods to generate new figures on each call?
sns.jointplot creates a figure by itself. In order to animate the jointplot, one might therefore reuse this created figure instead of recreating a new one in each iteration.
jointplot internally creates a JointGrid, so it makes sense to directly use this and plot the joint axes and the marginals individually. In each step of the animation one would then update the data, clear the axes and set them up just as during creation of the grid. Unfortunately, this last step involves a lot of code lines.
The final code may then look like:
import matplotlib.pyplot as plt
import matplotlib.animation
import seaborn as sns
import numpy as np
def get_data(i=0):
x,y = np.random.normal(loc=i,scale=3,size=(2, 260))
return x,y
x,y = get_data()
g = sns.JointGrid(x=x, y=y, size=4)
lim = (-10,10)
def prep_axes(g, xlim, ylim):
g.ax_joint.clear()
g.ax_joint.set_xlim(xlim)
g.ax_joint.set_ylim(ylim)
g.ax_marg_x.clear()
g.ax_marg_x.set_xlim(xlim)
g.ax_marg_y.clear()
g.ax_marg_y.set_ylim(ylim)
plt.setp(g.ax_marg_x.get_xticklabels(), visible=False)
plt.setp(g.ax_marg_y.get_yticklabels(), visible=False)
plt.setp(g.ax_marg_x.yaxis.get_majorticklines(), visible=False)
plt.setp(g.ax_marg_x.yaxis.get_minorticklines(), visible=False)
plt.setp(g.ax_marg_y.xaxis.get_majorticklines(), visible=False)
plt.setp(g.ax_marg_y.xaxis.get_minorticklines(), visible=False)
plt.setp(g.ax_marg_x.get_yticklabels(), visible=False)
plt.setp(g.ax_marg_y.get_xticklabels(), visible=False)
def animate(i):
g.x, g.y = get_data(i)
prep_axes(g, lim, lim)
g.plot_joint(sns.kdeplot, cmap="Purples_d")
g.plot_marginals(sns.kdeplot, color="m", shade=True)
frames=np.sin(np.linspace(0,2*np.pi,17))*5
ani = matplotlib.animation.FuncAnimation(g.fig, animate, frames=frames, repeat=True)
plt.show()
using the celluloid package (https://github.com/jwkvam/celluloid) I was able to animate seaborn plots without much hassle:
import numpy as np
from celluloid import Camera
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
fig = plt.figure()
camera = Camera(fig)
# animation draws one data point at a time
for i in range(0, data.shape[0]):
plot = sns.scatterplot(x=data.x[:i], y=data.y[:i])
camera.snap()
anim = camera.animate(blit=False)
anim.save('animation.mp4')
I'm sure similar code could be written for jointplots