Live scatter plot fetching data from firebase every 2 second - python

I have a mobile application which does some calculation and throws x,y coordinates and are updated on firebase every 2 seconds.
Next i want those coordinates to be plotted on a floor plan live. For that i am using Scatter plot over the floor plan image. But i cannot make it live as soon as the data is fetched need help with that.
Here is the code till now:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
xs = []
ys = []
fig = plt.figure()
scat = plt.scatter(xs, ys, c='r', s=100)
def main():
graph_data = open("testfile.txt","r").read()
lines = graph_data.split("\n")
for line in lines:
if len(line)>1:
x,y = line.split(",")
xs.append(x)
ys.append(y)
plt.scatter(xs,ys)
print(xs)
print(ys)
ani = animation.FuncAnimation(fig,main(),fargs=(scat))
plt.show()
Getting error with animation.FuncAnimation TypeError: NoneType object argument after * must be an iterable, not PathCollection

You can fetch data in a separate thread while updating the plot in the main one. Here is a complete working example:
#!/usr/bin/env python3
import time
from queue import Queue
from threading import Thread, Event
import numpy as np
import matplotlib.pyplot as plt
FETCH_DELAY = 2
def fetch_data(queue, stop):
while not stop.is_set():
x, y = np.random.randn(2)
queue.put((x, y))
time.sleep(FETCH_DELAY)
def limits(array, offset=1):
return array.min() - offset, array.max() + offset
def main():
stop = Event()
queue = Queue()
worker = Thread(target=fetch_data, args=(queue, stop))
worker.start()
plt.ion()
fig, ax = plt.subplots()
plot = ax.scatter([], [])
try:
while True:
data = queue.get()
data = np.array(data)
plt_data = plot.get_offsets()
plt_data = np.vstack((plt_data, data))
plot.set_offsets(plt_data)
fig.canvas.draw()
xmin, xmax = limits(plt_data[:, 0])
ymin, ymax = limits(plt_data[:, 1])
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
queue.task_done()
except KeyboardInterrupt:
pass
finally:
stop.set()
worker.join()
if __name__ == '__main__':
main()
Save it as plot_update.py file and run it from the command line:
python3 plot_update.py

Here is the solution without using threads it becomes very simple:
import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np
from firebase import firebase
firebase = firebase.FirebaseApplication('Firebase url', None)
fig, ax = plt.subplots()
x, y = [],[]
sc = ax.scatter(x,y,c=np.random.rand(3,))
plt.xlim(12,13)
plt.ylim(77,78)
def animate(i):
xs = firebase.get('/Lat',None)
ys = firebase.get('/Long',None)
xs = round(xs,2)
ys = round(ys,2)
file = open("testfile.txt","a+")
file.write("{0},{1} \n".format(xs,ys))
file.close()
graph_data = open("testfile.txt","r").read()
lines = graph_data.split("\n")
for line in lines:
if len(line)>1:
xs,ys = line.split(",")
x.append(xs)
y.append(ys)
sc.set_offsets(np.c_[x,y])
ani = matplotlib.animation.FuncAnimation(fig, animate,
frames=2, interval=500, repeat=True)
plt.show()

Related

matplotlib animation stock one frame over the other

I have the following minimum reproducible example:
import math
import argparse
import os
import json
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation ,FFMpegWriter
line_x=[ 0,1,2,3,4,5,6,7,8,9,10,11,12 ]
line1_y=[ 3,5,7,9,11,19,23,26,29,31,37,40,45 ]
line2_y=[0,2,5,7,10,10,8,5,3,2,1,3,5]
line3_y=[39,38,32,29,26,22,19,13,10,8,7,6,3]
set_lines=[line1_y,line2_y,line3_y]
n_lineas=[1,2,3,1,3,2,3,1,3,2,1,2]
show=True #Here chabnge to False
def get_n(thelist,c):
while(c>=len(thelist)):
c-len(thelist)
return thelist[c]
class Update:
def __init__(self,ax,limit_x):
self.ax = ax
if limit_x!=0:
self.ax.set_xlim(0,limit_x)
self.ax.set_ylim(45)
self.ax.set_aspect('equal')
self.ax.grid(True)
self.lines=()
self.counter=0
def __call__(self, frame):
print("Frame: ",frame)
lines=[]
n_lines_this_time=get_n(n_lineas,self.counter)
self.counter+=1
print(n_lines_this_time,"lines this time")
for myline in range(n_lines_this_time):
#line,=self.ax.plot([],[],'.-',color=gt_color,label=legend)
line,=self.ax.plot([],[],'.-')
x = []
y = []
for v in range(13):
x.append(line_x[v])
y.append(set_lines[myline][v])
line.set_xdata(x)
line.set_ydata(y)
lines.append(line)
self.lines=tuple(lines)
return self.lines
def init(self):
print("Init")
line,=self.ax.plot([],[])
return line,
fig, ax = plt.subplots(1, 1,figsize=(10,10))
plt.gcf().canvas.mpl_connect(
'key_release_event',
lambda event: [exit(0) if event.key == 'escape' else None])
plt.xlabel("Y (meters)")
plt.ylabel("X (meters)")
ug_i = Update(ax,13)
anim = FuncAnimation(fig, ug_i,init_func=ug_i.init, frames=10, interval=1000, blit=True,repeat=False)
if not show: #This is not working. It does not erase the plots
writervideo = FFMpegWriter(fps=60)
anim.save('whatever.mp4', writer=writervideo)
print('done')
plt.close()
else:
plt.legend()
plt.show()
My problem is when show is True, I can see my animation. (it shows a different number of line each frame)
but when show is False, it seems the movie has everything all together because all the lines get shown without change.

Matplotlib animation, why no legends?

I have the following matplotlib animation. The lines get plotted with the correct color but for some reason no legend is shown when showing the plot. Notice tat plt.legend() is called. A curious thing is that the legends get shown when it is saved to a movie
import math
import argparse
import os
import json
import sys
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation ,FFMpegWriter
line_x=[ 0,1,2,3,4,5,6,7,8,9,10,11,12 ]
line1_y=[ 3,5,7,9,11,19,23,26,29,31,37,40,45 ]
line2_y=[0,2,5,7,10,10,8,5,3,2,1,3,5]
line3_y=[39,38,32,29,26,22,19,13,10,8,7,6,3]
set_lines=[line1_y,line2_y,line3_y]
n_lineas=[1,2,3,1,3,2,3,1,3,2,1,2]
show=True
thecolors=['blue','red','violet']
thelegends=['unus','duo','tres']
print(sys.argv)
if len(sys.argv)==2 and sys.argv[1]=='movie':
show=False
def get_n(thelist,c):
while(c>=len(thelist)):
c-len(thelist)
return thelist[c]
class Update:
def __init__(self,ax,limit_x):
self.ax = ax
self.lx=limit_x
if limit_x!=0:
self.ax.set_xlim(0,limit_x)
self.ax.set_ylim(0,45)
self.ax.set_aspect('equal')
self.ax.grid(True)
self.lines=()
self.counter=0
def __call__(self, frame):
print("Frame: ",frame)
lines=[]
self.ax.cla()
self.ax.set_xlim(0,self.lx)
self.ax.set_ylim(0,45)
n_lines_this_time=get_n(n_lineas,self.counter)
self.counter+=1
print(n_lines_this_time,"lines this time")
for myline in range(n_lines_this_time):
#line,=self.ax.plot([],[],'.-',color=gt_color,label=legend)
line,=self.ax.plot([],[],'.-',color=thecolors[myline],label=thelegends[myline])
x = []
y = []
for v in range(13):
x.append(line_x[v])
y.append(set_lines[myline][v])
line.set_xdata(x)
line.set_ydata(y)
lines.append(line)
plt.legend()
self.lines=tuple(lines)
return self.lines
def init(self):
print("Init")
line,=self.ax.plot([],[])
return line,
fig, ax = plt.subplots(1, 1,figsize=(10,10))
plt.gcf().canvas.mpl_connect(
'key_release_event',
lambda event: [exit(0) if event.key == 'escape' else None])
plt.xlabel("Y (meters)")
plt.ylabel("X (meters)")
ug_i = Update(ax,13)
anim = FuncAnimation(fig, ug_i,init_func=ug_i.init, frames=10, interval=1000, blit=True,repeat=False)
if not show: #This is not working. It does not erase the plots
writervideo = FFMpegWriter(fps=1)
anim.save('whatever.mp4', writer=writervideo)
print('done')
plt.close()
else:
#plt.legend()
plt.show()
Another thing I noticed is:
-In the animation there is a grid and X,Y labels but not legends but
-In the movie there is no grid and no X,Y labels but there are legends

How to embed a python graph to a webserver created by an esp8266

So, three python graphs are created using matplotlib.pyplot while getting data from Serial via arduino.My goal is to get those graphs and someway display on a web server created by an esp8266.
Python code looks like this
import serial
import matplotlib.pyplot as plt
from drawnow import *
import atexit
import numpy as np
values = []
values1 = []
values2 = []
values3 = []
y=0
values4 = []
plt.ion()
cnt=0
BBox = ((24.5242, 26.4025, 40.2523,41.0255 ))
serialArduino = serial.Serial('COM7', 9600)
def plotValues():
plt.subplot(2, 2, 1)
plt.title('Serial value from Arduino')
plt.grid(True)
plt.ylabel('Values')
plt.plot(values, 'g', label='temp values')
#axes.legend(loc=4)
plt.plot(values1, 'r', label='ph values')
plt.legend(loc='upper left')
x = values2
#y = np.array([10, 20, 30, 40])
plt.subplot(2,2,2)
plt.plot(x, 'b', label='turb')
plt.legend(loc='upper left')
ruh_m = plt.imread(r'C:\Users\lordjimas\Desktop\map.png')
ax= plt.subplot(2,2,(3,4))
ax.set_title('Plotting Spatial Data on Alexandroupolis Map')
ax.set_xlim(BBox[0],BBox[1])
ax.set_ylim(BBox[2],BBox[3])
ax.imshow(ruh_m, extent = BBox, aspect= 'equal')
ax.scatter(values4, values3, color = 'black')
ax.fill_between((1,2), 0, 1, facecolor='green', alpha=0.2) # blocked area for first axes
ax.fill_between((5,6), 0, 1, facecolor='orange', alpha=0.2) # blocked area for second axes
def doAtExit():
serialArduino.close()
print("Close serial")
print("serialArduino.isOpen() = " + str(serialArduino.isOpen()))
atexit.register(doAtExit)
print("serialArduino.isOpen() = " + str(serialArduino.isOpen()))
#pre-load dummy data
while True:
while (serialArduino.inWaiting()==0):
pass
print("readline()")
valueRead = serialArduino.readline()
valueRead1 = serialArduino.readline()
valueRead2 = serialArduino.readline()
#valueRead3 = serialArduino.readline()
#valueRead4 = serialArduino.readline()
#check if valid value can be casted
try:
valueInInt = float(valueRead)
valueInInt1 = float(valueRead1)
valueInInt2 = float(valueRead2)
#valueInInt3 = float(valueRead3)
#valueInInt4 = float(valueRead4)
if valueInInt <= 8000000:
if valueInInt1 < 14:
values.append(valueInInt)
values1.append(valueInInt1)
values2.append(valueInInt2)
#values3.append(valueInInt3)
#values4.append(valueInInt4)
y=y+5
#values.pop(0)
#values4.pop(0)
#values3.pop(0)
drawnow(plotValues)
else:
print("Invalid!problem with serial ")
else:
print("Invalid! too large")
except ValueError:
print("Invalid! cannot cast")
I have tried converting those graphs to html script using the mpld3 library but I cant find a way to include that script to my esp8266 code

Real-time plotting with delay

Hi to everybody and thanks for your help.
I'm trying to show people a real-time seismogram with Python for educational purposes. I get the data through serial port connected to an arduino, but the problem is that there is a big delay (8-20 seconds) between the signal and the graph on screen seismogram plot.
I have checked the code, but I can't see anything wrong, at least as far as I understand the code. Can anybody help me?
Thanks in advance.
import time
import serial
import sys
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
arduino = serial.Serial('COM4', baudrate=115200, timeout=1.0)
start = time.time()
ad = []
ed = []
ec = []
es = []
delta=0
duracion=120
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure(figsize=(16,8))
ax = plt.axes(xlim=(0, duracion), ylim=(-1000, 1000))
ax.set_xlabel('segundos')
ax.set_ylabel('cuentas')
plt.title('Haz tu propio sismograma')
fig.canvas.set_window_title("Sismograma")
line, = ax.plot([], [], lw=0.5)
line2, = ax.plot([],[],lw=0.5)
line3, = ax.plot([],[],lw=0.5)
# initialization function: plot the background of each frame
def init():
arduino.readline()
ad = []
ed = []
ec = []
es = []
start = time.time()
delta=0
line.set_data([],[])
line2.set_data([],[])
line3.set_data([],[])
return line, line2, line3
# animation function. This is called sequentially
def animate(i):
global start
global ad,ed,ec,es
global delta
delta = time.time() - start
linea = arduino.readline()
texto=linea.decode('ascii', errors='replace')
valora=texto.split(',')
try:
valor=int(valora[0])+400
valor2=int(valora[1])
valor3=int(valora[2])-400
#print(valor)
ad.append(delta)
ed.append(valor)
ec.append(valor2)
es.append(valor3)
line.set_data(ad, ed)
line2.set_data(ad, ec)
line3.set_data(ad, es)
except ValueError:
ed.append(ed[-1])
ec.append(ec[-1])
es.append(es[-1])
ad.append(delta)
except RuntimeError:
ed.append(ed[-1])
ec.append(ec[-1])
es.append(es[-1])
ad.append(delta)
except IndexError:
ed.append(ed[-1])
ec.append(ec[-1])
es.append(es[-1])
ad.append(delta)
#print(delta)
if delta>duracion:
i=0
ad=[0]
ed=[0]
ec=[0]
es=[0]
delta=0
start = time.time()
fig.clf()
run_animation
return line, line2,line3
#time.sleep(0)
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate,frames=30, interval=1, init_func=init, blit=True)
def run_animation():
init
delta=0
start = time.time()
animate(0)
plt.show()
plt.show()

How do I animate a scatterplot over a basemap in matplotlib?

The code below generates a animated basemap, but not exactly the one I want: I want the scatterplot from the previous frame to disappear, but it persists through the remainder of the animation.
I suspect it has something to do with my not understanding what the basemap really is. I understand calling it on lat/lons to project them to x/y, but I don't entirely get what's going on when I call event_map.scatter().
import random
import os
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from matplotlib import animation
import pandas as pd
from IPython.display import HTML
# Enables animation display directly in IPython
#(http://jakevdp.github.io/blog/2013/05/12/embedding-matplotlib-animations/)
from tempfile import NamedTemporaryFile
VIDEO_TAG = """<video controls>
<source src="data:video/x-m4v;base64,{0}" type="video/mp4">
Your browser does not support the video tag.
</video>"""
def anim_to_html(anim):
if not hasattr(anim, '_encoded_video'):
with NamedTemporaryFile(suffix='.mp4') as f:
anim.save(f.name, fps=20, extra_args=['-vcodec', 'libx264'])
video = open(f.name, "rb").read()
anim._encoded_video = video.encode("base64")
return VIDEO_TAG.format(anim._encoded_video)
def display_animation(anim):
plt.close(anim._fig)
return HTML(anim_to_html(anim))
animation.Animation._repr_html_ = anim_to_html
FRAMES = 20
POINTS_PER_FRAME = 30
LAT_MIN = 40.5
LAT_MAX = 40.95
LON_MIN = -74.15
LON_MAX = -73.85
FIGSIZE = (10,10)
MAP_BACKGROUND = '.95'
MARKERSIZE = 20
#Make Sample Data
data_frames = {}
for i in range(FRAMES):
lats = [random.uniform(LAT_MIN, LAT_MAX) for x in range(POINTS_PER_FRAME)]
lons = [random.uniform(LON_MIN, LON_MAX) for x in range(POINTS_PER_FRAME)]
data_frames[i] = pd.DataFrame({'lat':lats, 'lon':lons})
class AnimatedMap(object):
""" An animated scatter plot over a basemap"""
def __init__(self, data_frames):
self.dfs = data_frames
self.fig = plt.figure(figsize=FIGSIZE)
self.event_map = Basemap(projection='merc',
resolution='i', area_thresh=1.0, # Medium resolution
lat_0 = (LAT_MIN + LAT_MAX)/2, lon_0=(LON_MIN + LON_MAX)/2, # Map center
llcrnrlon=LON_MIN, llcrnrlat=LAT_MIN, # Lower left corner
urcrnrlon=LON_MAX, urcrnrlat=LAT_MAX) # Upper right corner
self.ani = animation.FuncAnimation(self.fig, self.update, frames=FRAMES, interval=1000,
init_func=self.setup_plot, blit=True,
repeat=False)
def setup_plot(self):
self.event_map.drawcoastlines()
self.event_map.drawcounties()
self.event_map.fillcontinents(color=MAP_BACKGROUND) # Light gray
self.event_map.drawmapboundary()
self.scat = self.event_map.scatter(x = [], y=[], s=MARKERSIZE,marker='o', zorder=10)
return self.scat
def project_lat_lons(self, i):
df = data_frames[i]
x, y = self.event_map(df.lon.values, df.lat.values)
x_y = pd.DataFrame({'x': x, 'y': y}, index=df.index)
df = df.join(x_y)
return df
def update(self, i):
"""Update the scatter plot."""
df = self.project_lat_lons(i)
self.scat = self.event_map.scatter(x = df.x.values, y=df.y.values, marker='o', zorder=10)
return self.scat,
s = AnimatedMap(data_frames)
s.ani
It looks like you're simply adding a new scatter plot at each update. What you should do instead is change the data in the existing path collection at each update. Try something along the lines of
def update(self, i):
"""Update the scatter plot."""
df = self.project_lat_lons(i)
new_offsets = np.vstack([df.x.values, df.y.values]).T
self.scat.set_offsets(new_offsets)
return self.scat,
Note that I haven't tested this.

Categories

Resources