I've been working on a project in Python using Matplotlib in tandem with Django. Right now I just want to display the graph in the webpage. Before, I saved the figure as a PNG, and the graph showed up. Unfortunately, this disables some interactive features that I had planned on including. In order to work around this, I used MPLD3's function, fig_to_html, which I saw worked in other examples. However, when I used the line in my own code, I got the error, "350 is not JSON serializable".
Here is my code:
def index(request):
# template = loader.get_template('')
noaaNmbr='11809'
#for right now, this is the only active region that I'm pulling data for. When I get the graph up and running, I will make it more interactive for the user so that he can
urlData = "http://www.lmsal.com/hek/hcr?cmd=search-events3&outputformat=json&instrument=IRIS&noaanum="+ noaaNmbr +"&hasData=true"
webUrl = urlopen(urlData)
counter = 0
data = webUrl.read().decode('utf-8')
hekJSON = json.loads(data)
getInfo(counter, hekJSON)
sort()
# Sorts the data from earliest to most recent
fig, ax = plt.subplots(figsize=(30, 30))
circle = Circle((0, 0), 980, facecolor='none', edgecolor=(0, 0.8, 0.8), linewidth=3, alpha=0.5)
ax.add_patch(circle)
# Creates circular representation of the sun on the graph because that's all I really need for now
plt.plot(xcen, ycen, color="red")
plt.plot(xcen, ycen, 'ro', color = 'blue')
#first plots the points in red, then draws lines between the points in blue
plt.xlim([setXMin(hekJSON), setXMax(hekJSON)])
plt.ylim([setYMin(hekJSON), setYMax(hekJSON)])
#sets the boundaries of the graph
ax.set_xticks(np.arange(round_multiple(setXMin(hekJSON),50), round_multiple(setXMax(hekJSON), 50), 50))
ax.set_yticks(np.arange(round_multiple(setYMin(hekJSON),50), round_multiple(setYMax(hekJSON), 50), 50))
#sets the ticks
for i in range(getNumberOfEntries(hekJSON)):
if xfov[i] != 0:
xStart = xcen[i] - xfov[i]/14
yStart = ycen[i] - yfov[i]/14
ax.add_patch(Rectangle((xStart, yStart), xfov[i]/7, yfov[i]/7, facecolor='none'))
plt.grid()
# texts = fixAnnotations(createAnnotations(hekJSON, noaaNmbr))
# adjust_text(texts, arrowprops=dict(arrowstyle="-", color='k', lw=0.5))
# canvas = FigureCanvasAgg(fig)
# response = HttpResponse(content_type='image/png')
# canvas.print_png(response)
# g = mpld3.fig_to_html(fig)
g = mpld3.display(fig)
return HttpResponse(g)
# return response
This is the section of code that uses JSON stuff:
noaaNmbr='11809'
#for right now, this is the only active region that I'm pulling data for. When I get the graph up and running, I will make it more interactive for the user so that he can
urlData = "http://www.lmsal.com/hek/hcr?cmd=search-events3&outputformat=json&instrument=IRIS&noaanum="+ noaaNmbr +"&hasData=true"
webUrl = urlopen(urlData)
counter = 0
data = webUrl.read().decode('utf-8')
hekJSON = json.loads(data)
getInfo(counter, hekJSON)
Can somebody please explain what the issue is and how to fix it?
Related
for i in range(5):
for j in range(5):
sub_image = self.image[i*8:i*8+8, j*8:j*8+8]
ax = plt.subplot(gs[4 - i, j], picker = True, label = self.iD)
c = ax.pcolormesh(sub_image, vmin=0, vmax=maxZ, cmap="viridis")
ax.axis("off")
ax.set_aspect("equal")
self.grid[4-i,j] = self.iD
self.iD += 1
fig.subplots_adjust(right=0.71, left=.285, top=0.9, bottom=0.1)
self.cbar_ax = fig.add_axes([0.85, 0.15, 0.05, 0.7])
self.cbar = fig.colorbar(c, cax=self.cbar_ax)
self.cbar.set_label("Charge (Photoelectrons)", rotation=270, size=24, labelpad=24)
self.cbar_ax.tick_params(labelsize=15)
fig.suptitle(f"Run {self.run} Event {self.ev}", fontsize=30)
fig.canvas.mpl_connect("pick_event",self.nextWindow)
self.layout.addWidget(self.static_canvas,1,1)#,1,2)
self.toolBar = NavigationToolbar(self.static_canvas, self)
self.layout.addWidget(self.toolBar,2,1,1,2)
snapshot of plot
I am trying to create the functionality for when a pick_event is processed, the value that corresponds with the colorbar is printed. When the cursor is over any part of the plot, the NavigationToolbar2QT displays the value I want on the bottom right, but I dont know how to access it otherwise.
This is how I was able to access the data. I then formatted the string to get the data I want.
data = self.toolBar._mouse_event_to_message(event.mouseevent)
I am trying to make a virtual flight tracker with Python and API data, and I have managed so far to draw the points, update their location every 10 seconds and annotate them. Unfortunately, they are being annotated for each position they are drawn in when in fact I want them to only be shown in the spot where the point is at that time, or else there is a constant trail of labels.
The code is below:
import requests
import json
import time
import cartopy.crs as ccrs
import cartopy
import matplotlib.pyplot as plt
from cartopy.io.img_tiles import GoogleTiles
from matplotlib import animation
#SET AXES
fig, ax = plt.subplots()
ax=plt.axes(projection=ccrs.PlateCarree())
'''
ax.set_ylim(48,61.5)
ax.set_xlim(-12.5, 3.3)
'''
#ADD OSM BASEMAP
osm_tiles=GoogleTiles(style='satellite')
ax.add_image(osm_tiles,8)
ax.stock_img()
ax.set_extent([-12.5, 3.3, 48, 61.55])
ax.add_feature(cartopy.feature.BORDERS)
#mplcursors.cursor(hover=True)
#PLOT TRACK
track, = ax.plot([], [], 'wo')
# RELOAD API EVERY 15"
def update (self):
vapi = requests.get("https://data.vatsim.net/v3/vatsim-data.json")
# LOAD DATA AS JSON
data = vapi.text
parse_json = json.loads(data)
# LOAD PILOTS
pilots = parse_json['pilots']
no_pilots = len(pilots)
# GET INFO FOR EACH AIRCRAFT
x = 0
callsigns, lat, lon, alt, head, gspeed = [], [], [], [], [], []
while x < no_pilots:
xcall = pilots[x]['callsign']
xlat = pilots[x]['latitude']
xlon = pilots[x]['longitude']
xgspeed = pilots[x]['groundspeed']
xalt = pilots[x]['altitude']
xhead = pilots[x]['heading']
callsigns.append(xcall)
lat.append(xlat)
lon.append(xlon)
alt.append(xalt)
head.append(xhead)
gspeed.append(xgspeed)
x += 1
for i, txt in enumerate(callsigns):
ax.annotate(txt, (lon[i], lat[i]))
track.set_data(lon,lat)
return track, callsigns,
anim = animation.FuncAnimation(fig, update,interval=10000, blit=False)
plt.show()
It leaves a trail of annotations like this
I have a html file that displays the images sent from the flask backend.
<body>
<h3> Count plot of data according to Lead origin.</h3>
<img src = "/graph_visualization/" alt = "graph it is" height = "300" width = "300">
<h3> Count plot of data according to cities</h3>
<img src = "/graph_visualization2/" alt = "graph it is" height = "300" width = "300">
<h3> Count plot of data according to Countries</h3>
<img src = "/graph_visualization3/" alt = "graph it is" height = "300" width = "300">
</body>
The three plots are sent as a png file from multiple functions in flask backend as below:
#app.route('/graph_visualization/')
def graph_visualization():
fig, ax = plt.subplots()
df = pd.read_csv(r'C:\Users\Admin\Downloads\export.csv')
sns.countplot(x="Lead Origin", hue="Converted", data=df)
canvas = FigureCanvas(fig)
img = BytesIO()
fig.savefig(img, format='png')
img.seek(0)
return send_file(img, mimetype='image/png', cache_timeout=-1)
#app.route('/graph_visualization2/')
def graph_visualization2():
fig1, ax = plt.subplots()
df = pd.read_csv(r'C:\Users\Admin\Downloads\export.csv')
sns.countplot(x="Cities", hue="Converted", data=df)
xticks(rotation=90)
plt.tight_layout()
canvas = FigureCanvas(fig1)
img1 = BytesIO()
fig1.savefig(img1, format='png')
img1.seek(0)
return send_file(img1, mimetype='image/png', cache_timeout=-1)
#app.route('/graph_visualization3/')
def graph_visualization3():
fig2, ax = plt.subplots()
df = pd.read_csv(r'C:\Users\Admin\Downloads\export.csv')
sns.barplot(x="Countries", y="Converted", data=df)
xticks(rotation=90)
plt.tight_layout()
plt.ylabel('Converted')
plt.xlabel('Countries')
canvas = FigureCanvas(fig2)
img2 = BytesIO()
fig2.savefig(img2, formar='png')
img2.seek(0)
return send_file(img2, mimetype='image/png', cache_timeout=-1)
Note: Individual graph shows when i comment any two functions and only keep one. The problem is when i uncomment all of them.
I have tried following methods:
Changing the figurename and image name in each functions.
plt.clf() below fig.savefig()
ax.clear() below fig, ax = plt.subplots()
Run in Incognito mode.
Note: I don't want to make multiple subplots.
Please let me know if my approach to the problem is incorrect.
Need help with below code, my hover over is not showing any data just ???. Im guessing its because I havent defined the source properly or I need to include an argument in the vbar code. Do I need to add more info to the source e.g. column names etc or do I also need to refer to the source and column names in the vbar arguments?
Thanks
def get_width():
mindate = df['local_time'].min()
maxdate = df['local_time'].max()
return 0.8 * (maxdate-mindate).total_seconds()*1000 / len(df['local_time'])
plots = []
sliders = []
for t in df['timeframeID'].unique():
inc = df[df['timeframeID'] == t].close > df[df['timeframeID'] == t].open
dec = df[df['timeframeID'] == t].open > df[df['timeframeID'] == t].close
source = ColumnDataSource(data=df)
TOOLS = "pan,wheel_zoom,box_zoom,crosshair,reset,save"
TOOLTIPS = [('open', '#open'),('high', '#high'),('low', '#low'),('close', '#close')]
name1= figure(plot_width=1600, plot_height = 900, title="Instrument AUDUSD: "+t, tools = TOOLS, tooltips=TOOLTIPS)
name1.xaxis.major_label_overrides = {
i: date.strftime('%b %d') for i, date in enumerate(pd.to_datetime(df["local_time"]))
}
name1.xaxis.bounds = (0, df.index[-1])
name1.segment(df[df['timeframeID'] == t].index[inc], df[df['timeframeID'] == t].high[inc],
df[df['timeframeID'] == t].index[inc],df[df['timeframeID'] == t].low[inc], color="black")
name1.segment(df[df['timeframeID'] == t].index[dec], df[df['timeframeID'] == t].high[dec],
df[df['timeframeID'] == t].index[dec],df[df['timeframeID'] == t].low[dec], color="black")
#name1.y_range.range_padding = 0.05
name1.vbar(df[df['timeframeID']== t].index[inc], 0.5, df[df['timeframeID']== t].open[inc], df[df['timeframeID']== t].close[inc],
fill_color="green", line_color="green")#, width=get_width())
name1.vbar(df[df['timeframeID']== t].index[dec], 0.5, df[df['timeframeID']== t].open[dec], df[df['timeframeID']== t].close[dec],
fill_color="#F2583E", line_color="#F2583E")#, width=get_width())
r = name1.circle(df[df['timeframeID']== t].index, df[df['timeframeID']== t].AV, alpha = 1, radius = .20)
name1.y_range.range_padding = 0.05
callback = CustomJS(args=dict(renderer=r), code="""
renderer.glyph.radius = cb_obj.value;
""")
s = Slider(start=0, end=1.5, value=.20, step=.05, title="Radius - " + t)
s.js_on_change('value', callback)
output_notebook()
output_file("candlestick.html", title="candlestick.py example")
sliders.append(s)
plots.append(name1)
show(column(
row(
*plots),*sliders))
Here is what my dataframe, df, looks like:
Currently you are directly providing data only for the x and y coordinate. Bokeh does not know anything about the other data. To make bokeh aware of all the data you must pass a source via source=source in your vbar method. When you pass the source, bokeh gets all the data so that it can look at different columns to show when hovering.
When you pass a source, you cannot pass the x, top and bottom coordinates directly, because otherwise bokeh would not know how to associate these values to the source you passed¹. So when you pass a source, you want to pass the names of the x, top and bottom coordinate columns, instead of the data directly. So you want to write something like:
name1.vbar("index", "open", "close", source=source, fill_color="green", line_color="green")
To do this you need to construct a source/DataFrame which already has the data you want, instead of doing the filtering you are doing in the vbar call. Without seeing your data I cannot tell you how you would construct such a Dataframe though.
1: Actually bokeh associates directly passed data via the index, so the first value is associated with the first line in the source.
I have the following bokeh code how do you add a horizontal line or glyph to the chart dynamically do I need a callback?
In the documentation http://docs.bokeh.org/en/latest/docs/user_guide/interaction/callbacks.html it says "Custom callbacks like these can be set using a CustomJS object and passing it as the callback argument to a Widget object."
So do I set the call back before or after the update...
source = ColumnDataSource(dict(
time=[], average=[], low=[], high=[], open=[], close=[],
ma=[], macd=[], macd9=[], macdh=[], color=[]
))
#main chart
p = figure(plot_height=500, tools="xpan,xwheel_zoom,xbox_zoom,reset,crosshair,hover", x_axis_type=None, y_axis_location="right")
p.x_range.follow = "end"
p.x_range.follow_interval = 100
p.x_range.range_padding = 0
p.axis.minor_tick_in = -2
p.axis.minor_tick_out = 5
p.segment(x0='time', y0='low', x1='time', y1='high', line_width=2, color='black', source=source)
p.segment(x0='time', y0='open', x1='time', y1='close', line_width=8, color='color', source=source)
...
#count()
def update(t):
open, high, low, close, average = _get_prices(t)
color = "green" if open < close else "red"
new_data = dict(
time=[t],
open=[open],
high=[high],
low=[low],
close=[close],
color=[color],
)
print(open)
source.stream(new_data, 300)
output_notebook()
curdoc().add_periodic_callback(update, 50)
curdoc().title = "OHLC"