Plotly Choropleth map successfully loads in Jupyter, but not Dash browser - python

I have been able to successfully create a custom shapefile Choropleth map using plotly and geopandas. It loads successfully in my Jupyter window. The following image is what the map looks like when running in Jupyter.
Successfully rendered map in Jupyter
This is the code used to render the above map:
data = go.Choroplethmapbox(
geojson = gdf_json,
locations = df.index.astype(str),
z = df['2016']['Total'],
text = df.index.astype(str),
colorbar=dict(thickness=20,ticklen=3,tickformat=',.2r',outlinewidth=0),
marker_line_width=0.5,
marker_opacity=0.4,
colorscale='gnbu',
zmin=zmin,
zmax=zmax,
hovertemplate = "<b>%{text}</b><br>" +
"%{z:,.4r}<br>" +
"<extra></extra>")
#mapbox geographic map layout
layout = go.Layout(
title='Expenditure, 2020',
autosize=True,
#hovermode='closest',
#showlegend=True,
height=600,
margin=dict(l=0, r=0, t=40, b=0),
mapbox = dict(
domain = {'x': [0, 1],'y': [0, 1]},
center = dict(lat=-27.628296146695863, lon=152.74025612708698),
accesstoken = MAPBOX_ACCESSTOKEN,
#pitch=0,
zoom=10,
style='light')
)
fig = go.Figure(data=data, layout=layout)
fig.show()
However, when I attempt to load the map into Dash, it returns as an empty graph. (Note I cancel out the fig.show() line of code during this process).
app.layout = html.Div([
dcc.Graph(id = 'plot', figure = fig)
])
if __name__ == '__main__':
app.run_server()
Unsuccessful map in Dash
What I have tried
To narrow down the potential problems, I have tried to load in other elements such as dropdowns, radio buttons and a range slider. These loaded successfully, just not the map itself. I am also using the correct public Mapbox API token, as I have previously loaded up a Scattermapbox graph (created a bubble map), which has been successful on Dash.
Possible reasons
Obviously the starting point is whether there might be a very small minor syntax error in my code.
However I'm also wondering if there's something wrong with the loading of the geojson? Even though it loads successfully in Jupyter, maybe there is a slightly different method used when loading it into Dash?
To convert my geopandas polygons into geojson:
I converted it into an appropriate CRS --> gdf.to_crs(epsg=4326)
Set the index to my unique id --> gdf.set_index(id)
Applied this function --> gdf_json = gdf.__geo_interface__
Example of the geojson file
Would love some help if possible. Thank you!

I'm working on a very similar problem and when I converted from jupyter to dash I do not recall having to change the process of the geojson. But don't reject that possibility yet. The fact that it isn't producing any errors from the geojson is likely indicative of an error in how you build your layout. So the first two things you should try first.
Run the debugger (if you aren't already) because this will pick up on errors that will not be ported to your terminal.
app.run_server(debug=True)
Put your map rendering code into a function
def build_map():
return fig #after building the map figure
app.layout = html.Div[(dcc.Graph(figure = build_map())]
I ran into one error with building larger maps where it would time out.
There are so many things that could go wrong here, so process of elimination is key. Feel free to comment below and I will continue to try and help you out!

Related

How can I resolve the error: TypeError: invalid Figure property: Layout Layout Bad property path: Layout ^^^^^^ [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
Hello I am working through a Python Crash Course book and one of the tasks is to build a simple map of earthquake locations using plotly. The goal is to have the program open an HTML on my Safari browser that displays a map of where the earthquakes have taken place. However, when I try to run the code I receive the error:
TypeError: invalid Figure property: Layout
Layout
Bad property path:
Layout
^^^^^^
No HTML or pop up occurs in the browser.
The code I am running:
`import json
from plotly.graph_objs import Scattergeo, Layout
from plotly import offline
#explore the sturcture of the data
filename = 'eq_data_1_day_m1.json'
with open(filename) as f:
all_eq_data = json.load(f)
readable_file = 'new_readable_eq_data.json'
with open(readable_file, 'w') as f:
json.dump(all_eq_data, f, indent=4)
all_eq_dicts = all_eq_data['features']
print(len(all_eq_dicts))
mags, lons, lats = [], [], []
for eq_dict in all_eq_dicts:
mag = eq_dict['properties']['mag']
lon = eq_dict['geometry']['coordinates'][0]
lat = eq_dict['geometry']['coordinates'][1]
mags.append(mag)
lons.append(lon)
lats.append(lat)
#map the earthquakes
data = [Scattergeo(lon=lons, lat=lats)]
my_layout = Layout(title='Global Earthquakes')
fig = {'data': data, 'Layout': my_layout}
offline.plot(fig, filename='global_earthquakes.html')
`
I have tried to rewrite the code and copy it over and over again from the book but it won't run. Plotly is up to date. I have been able to extract data from the 'new_readable_eq_data.json' file so I do not think that is the problem. The error is specifically found on the last line of code:
offline.plot(fig, filename='global_earthquakes.html')
What are some ways to get around this problem?
Thanks.
It seems that with the release of v4 the offline features have been replaced by 'the renderers framework'. You can find information on this here: https://plotly.com/python/v4-migration/#offline-features-plotlyoffline-replaced-by-renderers-framework--html-export
In version 3, the plotly.offline.plot function was used to export figures to HTML files. In version 4, this function has been reimplemented on top of the new to_html and write_html functions from the plotly.io module. These functions have a slightly more consistent API (see docstrings for details), and going forward we recommend using them directly when performing HTML export. When working with a graph object figure, these functions are also available as the .to_html and .write_html figure methods.
You should be able to use:
fig.write_html("global_earthquakes.html")
Edit:
To construct the fig, you need to use the following. (I found it here)
import plotly.graph_objects as go
fig = go.Figure(go.Scattergeo())
fig.update_layout(height=300, margin={"r":0,"t":0,"l":0,"b":0})
In terms of plotting points onto the map, i've found this here:
import plotly.express as px
df = px.data.gapminder().query("year == 2007")
fig = px.scatter_geo(df, locations="iso_alpha",
size="pop", # size of markers, "pop" is one of the columns of gapminder
)
fig.show()

Plotly Mapbox images showing blank with blue background (Jupyter Notebook)

I am running the following code to plot points against a city backdrop using Mapbox within Plotly in a Jupyter Notebook, but the plot does not show up, I just get a blue background.
I suspect that I am not using the token correctly?
import plotly.express as px
MBToken = 'pk.[mypublickey]'
px.set_mapbox_access_token(MBToken)
fig = px.scatter_mapbox(dfMaster.dropna()
, lat="latitude"
, lon="longitude"
, color="nta"
, size="count_of_testers"
#, color_continuous_scale=px.colors.cyclical.IceFire
#, size_max=15
#, zoom=10
)
fig.show()
#fig = px.scatter(x='latitude',y='longitude',data_frame=df)
#fig.show()
Running that gives me:
It does not appear to be a Plotly issue, the commented out code creates a scatter plot (although that has the same blue background, but the points show)
Some other posts have pointed to Jupyter offline mode being the possible culprit, but adding this did not resolve
import plotly.offline as pyo
pyo.init_notebook_mode()
Additionally, tried starting up the Jupyter notebook with a higher data rate limit as suggested, but no luck there either
This ended up being a silent data integrity error, as the size field was a string and needed to be converted into numeric

Plotly world Choropleth Map offline in python?

I'm trying to recreate the world Choropleth map given in plotlys example page here: https://plot.ly/python/choropleth-maps/ with the intention of reusing some of the code, but changing the column which informs the shadings and the labelling.
However when I run the exact code given in the example I receive the following error.
plotly.exceptions.PlotlyError: Because you didn't supply a 'file_id' in the call, we're assuming you're trying to snag a figure from a url. You supplied the url, '', we expected it to start with 'https://plot.ly'.
Run help on this function for more information.
I have no idea where this error arises from and my question really is how do I adapt the code so that it produces the said figure offline? Secondly is there a simple method for saving the figure directly to a png? Apologies if this is trivial I'm completely new to the package.
Here is the code:
import plotly.plotly as py
import pandas as pd
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/2014_world_gdp_with_codes.csv')
data = [dict(
type='choropleth',
locations=df['CODE'],
z=df['GDP (BILLIONS)'],
text=df['COUNTRY'],
colorscale=[[0, "rgb(5, 10, 172)"], [0.35, "rgb(40, 60, 190)"], [0.5, "rgb(70, 100, 245)"],\
[0.6, "rgb(90, 120, 245)"], [0.7, "rgb(106, 137, 247)"], [1, "rgb(220, 220, 220)"]],
autocolorscale=False,
reversescale=True,
marker=dict(
line=dict(
color='rgb(180,180,180)',
width=0.5
)),
colorbar=dict(
autotick=False,
tickprefix='$',
title='GDP<br>Billions US$'),
)]
layout = dict(
title='2014 Global GDP<br>Source:\
<a href="https://www.cia.gov/library/publications/the-world-factbook/fields/2195.html">\
CIA World Factbook</a>',
geo=dict(
showframe=False,
showcoastlines=False,
projection=dict(
type='Mercator'
)
)
)
fig = dict(data=data, layout=layout)
py.iplot(fig,validate=False, filename='d3-world-map')
You need to import the offline specific functions, which allow you to plot inline in a jupyter notebook:
import plotly.figure_factory as ff
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
init_notebook_mode(connected=True)
# All of your code
# ....
# Just change the last line from py.iplot to iplot
iplot(fig,validate=False, filename='d3-world-map')
This renders the image inline in a jupyter notebook, and there's a button that allows you to Download plot as a png in the upper right, along with other functionality.
If you need to save the image as a png, you can try changing the last line to:
plot(fig, validate=False, filename='d3-world-map.html', image='png')
This actually creates an .html file and will open a browser. You can then manually save this as a .png. The last step can be automated with other libraries like selenium, but not sure there is a simple way around it given their documentation:
Note that you must generate the graph and open the file to save the
image.

Big data multiple plots, multiple web pages Plotly

I am trying to work with big data plotting around 20 plots on plotly and embed them on web page. I can very well plot individual plots with username and one api_key that found in the profile.
The Problem comes is when: I have to rerun all the 20 plots with python program after interval of every 15 mins and every time I am getting new windows. Instead I need the same plot to update/redraw.
How do I get that? I tried reading the plot.ly document and also few tutorials outside. Cannot find how to get it done. Can anyone please help me with steps or refer me to some document where I can know how to work with multiple plots that will update at same time.
I am following the steps given in plotly tutorial not sure if I should use stream_ids ? Or can I create a new api_key for every plot ?Confused !!! Thanks in Advance for the suggestions.
Edit: I could make access tokens and Initiate the credentials from the following tutorial.
The code below works perfect: But now I am looking for required fixing in the below code by trying to minimize the code with annotations and where to include the streaming API Access Tokens while having sizable scatter plots ?
import plotly.plotly as py
import plotly.tools as tls
from plotly.graph_objs import *
import csv
import pandas as pd
import numpy as np
df = pd.read_csv('finally.csv')
df1=df[['NAME','COUNT']]
sizemode='area'
sizeref=df1['COUNT'].max()/1000
def Trace(X,PLACE,sizes):
return Scatter(
x=X['NAME'],
y=X['COUNT'].sum(),
name=PLACE,
mode='marker',
marker=Marker(
line=Line(width=0.9),
size=sizes,
sizeref=sizeref,
opacity=0.9,
)
)
data=Data()
for PLACE, X in df1.groupby('NAME'):
sizes=X['COUNT'].sum()/1000
data.append(Trace(X,PLACE,sizes))
title = "Fig 1.1 : All NAMES"
x_title = "Names".format()
y_title = "Count"
# Define a dictionary of axis style options
axis_style = dict(
zeroline=False, # remove thick zero line
gridcolor='#FFFFFF', # white grid lines
ticks='outside', # draw ticks outside axes
ticklen=8, # tick length
tickwidth=1.5 # and width
)
# Make layout object
layout = Layout(
title=title, # set plot title
plot_bgcolor='#EFECEA', # set plot color to grey
xaxis=XAxis(
axis_style, # add axis style dictionary
title=x_title, # x-axis title
),
yaxis=YAxis(
axis_style, # add axis style dictionary
title=y_title, # y-axis title
),
showlegend=False,
)
fig = Figure(data=data,layout=layout)
plot_url=py.plot(fig,filename=' plotting')
In plot/ iplot there is 'fileopt' option which should help you. For example, if you would want to add new traces to your existing data you can run
plot_url = py.plot(fig, filename='my-file', fileopt='append')
You're right it is not well documented yet. But if you run help(py.plot) you would get a small document on it as follow:
plot(figure_or_data, validate=True, **plot_options)
Create a unique url for this plot in Plotly and optionally open url.
plot_options keyword agruments:
filename (string) -- the name that will be associated with this figure
fileopt ('new' | 'overwrite' | 'extend' | 'append') -- 'new' creates a
'new': create a new, unique url for this plot
'overwrite': overwrite the file associated with `filename` with this
'extend': add additional numbers (data) to existing traces
'append': add additional traces to existing data lists
world_readable (default=True) -- make this figure private/public
auto_open (default=True) -- Toggle browser options
True: open this plot in a new browser tab
False: do not open plot in the browser, but do return the unique url

Python matlplotlib add hyperlink to text

I've created a plot in Python using matplotlib. After annotating each line, I'd like to make the label a hyperlink (or alternatively, make the line itself a hyperlink). The text item has a property called 'url', but I've tried it and I can't figure out what, if anything, it does.
Is it possible to make text or line objects into hyperlinks?
This example shows how to set hyperlinks if you're outputting an SVG. Note that this only makes sense for SVG. If the plot is just an image, it's just an image, and images can't have hyperlinks in them.
If you want to be able to click on the object in the interactive plotting window and have that act like a hyperlink, you could create an event handler to handle the "pick" event, and have that open a browser or whatever. See this example for how to do pick events. Matplotlib plots aren't web pages or even really documents, they're just windows with graphics displayed in them, so they don't support hyperlinks as such; using a pick event you can emulate a hyperlink by opening a web browser when an object is clicked.
Edit: You are right, it doesn't work. It seems that the URL property is only read and used for certain types of objects. Googling, I see some old matplotlib mailing list discussion of it, where it seems the idea was to gradually add URL support to different artist types, but I guess they never got around to it. I would suggest you raise a bug about this on the matplotlib bug tracker.
In the meantime, there is a way to do it, but it is somewhat roundabout. The URL is drawn for PathCollection objects, so you could make a Path out of your text, then make a PathCollection out of that path, and then add that PathCollection to your plot. Here's an example:
pyplot.scatter([1, 2, 3], [4, 5, 6])
t = mpl.text.TextPath((2, 4), 'This is text', size=0.1)
pc = mpl.collections.PathCollection([t])
pc.set_urls(['http://www.google.com'])
ax = pyplot.gca()
ax.add_collection(pc)
pyplot.draw()
f = pyplot.gcf()
f.canvas.print_figure('fig.svg')
Note that you must use set_urls and not set_url. This method produces an SVG with clickable text, but it has some drawbacks. Most notably, it seems you have to set the text size manually in data coordinates, so it may take some fiddling to find a text size that isn't too ridiculously huge or tiny relative to the magnitude of your plotted data.
Adding a hyperlink makes sense when e.g. using an SVG file.
The url property works in newer matplotlib versions:
text = plt.annotate("Link", xy=(2,5), xytext=(2.2,5.5),
url='http://matplotlib.org',
bbox=dict(color='w', alpha=1e-6, url='http://matplotlib.org'))
For example, in a Jupyter notebook, which runs in a browser anyways, one could display an SVG with hyperlinks like this:
import matplotlib.pyplot as plt
from IPython.display import set_matplotlib_formats
set_matplotlib_formats("svg")
fig, ax = plt.subplots()
ax.scatter([1, 2, 3], [4, 5, 6])
text = ax.annotate("Link", xy=(2,5), xytext=(2.2,5.5),
url='http://matplotlib.org',
bbox=dict(color='w', alpha=1e-6, url='http://matplotlib.org'))
In the figure produced this way you may click on the link and be directed to matplotlib.org.
This is possible with pgf backend:
#!/usr/bin/env python3
import matplotlib
matplotlib.use("pgf")
pgf_with_custom_preamble = {
"text.usetex": True,
"pgf.preamble": [
r"\usepackage{hyperref}"
]
}
matplotlib.rcParams.update(pgf_with_custom_preamble)
from matplotlib import pyplot as plt
x = range(5)
y = range(5)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, "r-", label=r"Hyperlink: \url{http://google.com}")
ax.legend()
fig.savefig("mwe.pdf")

Categories

Resources