I am implementing a map in folium taking the information from a dataframe df. I contains the longitude, latitude, name and region information.
Using a GeoJson file I have taken here: https://github.com/martinjc/UK-GeoJSON, I am able to define the regions in UK, highlight them when hovering with the mouse pointer and show some information. I would like to create a popup for each region with a button. If the button is clicked all the entries in the df which are in that region appear like markers. Is it possible? The code I am using right now is below:
...operations on df and definition of the map m...
width = 10
heigth = 4
resolution = 75
def style_function(feature):
return {
'fillColor': '#ffaf00',
'color': 'grey',
'weight': 1.5,
'dashArray': '5, 5'
}
def highlight_function(feature):
return {
'fillColor': '#ffaf00',
'color': 'black',
'weight': 3,
'dashArray': '5, 5'
}
c = folium.GeoJson(geojsonFile,
name = 'Highlight',
overlay=True,
style_function = style_function,
highlight_function = highlight_function)
html = '{}<br> Info: {} <br> <buttononclick="window.location.reload()">Reload</button>'.format
frame = IFrame(html(name, info), width=(width*resolution)+20, height=(height*resolution)+20)
popup_text = folium.Popup(iframe, max_width=2650)
c.add_child(folium.Popup(popup_text))
c.add_to(m)
I can add the button in the popup; what I am trying to do is to define a function like:
def testFunction(df, region):
markerCluster = folium.plugins.MarkerCluster().add_to(map)
for index, row in df.iterrows():
if row['region'] == region:
folium.Marker([row['lat'], row['lon']], popup='some text').add_to(markerCluster)
that will be run when the button is clicked. The final effect I am looking for should be: open the map, click on the selected region, click the button, the markers on the region appear. But I haven't found any solution yet (I have tried with Flask without success).
Related
So I have a sidebar. When "Go" in the sidebar is selected, the main dashboard should update. This works.
What doesn't work is when the radio button (marketing consent) within the main dashboard is selected, the entire dashboard resets. It will revert to the original view as opposed to what it looked like after "Go" was selected. It's as if the "Go" button resets and gets unselected, when the radio button is clicked...
"""
# My first app
Here's our first attempt at using data to create a table:
"""
##Packages
from unicodedata import numeric
import streamlit as st
import numpy as np
import pandas as pd
from PIL import Image
import pandasql as ps
import altair as alt
import plotly.express as px
st.set_page_config(page_title = "Dashboard")
st.title("Dashboard 💪")
##Dataframe
df = pd.DataFrame({
'first': [1, 2, 3, 4],
'second': [10, 20, 30, 40],
'third': ['apple', 'banana', 'grape', 'grape'],
'fourth': ['walter', 'skyler', 'hank', 'marie']
})
####
###Sidebar things
####
###Logo
with st.sidebar.container():
st.sidebar.title("Sidebar")
with st.sidebar.container():
add_selectbox = st.sidebar.multiselect(
'Choose some properties',
(df.third.unique())
)
fourth_box = st.sidebar.selectbox(
'Choose your country',
(df.fourth.unique()),
index = 1
)
####Page 1
if st.sidebar.button('Go!'):
with st.container():
status = st.radio(
"Marketing Consent Status",
('Yes', 'All'))
df_iris = px.data.iris()
if marketing_status == 'Yes':
fig_1 = px.bar(df_iris, x="sepal_width", y="sepal_length", color="species",
hover_data=['petal_width'], barmode = 'stack')
st.plotly_chart(fig_1, use_container_width=True)
elif marketing_status == 'All':
st.write('Hello, *World!')
else:
with st.container():
df_map = px.data.gapminder().query("year==2007")
fig_map = px.choropleth(df_map, locations="iso_alpha",
color="lifeExp", # lifeExp is a column of gapminder
hover_name="country", # column to add to hover information
color_continuous_scale=px.colors.sequential.Plasma)
st.plotly_chart(fig_map, use_container_width=True)
As you'll see, when you use the radio button (Marketing Consent Button) after it appears , it will revert to the map view of the main.
To prevent widget interactions from causing the entire app to rerun, you can make your widgets part of a form with a submit button, e.g.:
with st.form("my_form"):
st.write("Inside the form")
slider_val = st.slider("Form slider")
checkbox_val = st.checkbox("Form checkbox")
# Every form must have a submit button.
submitted = st.form_submit_button("Submit")
if submitted:
st.write("slider", slider_val, "checkbox", checkbox_val)
st.write("Outside the form")
I have a JSON file which contains coordinate list and some other information.
My JSON structure look like this;
"annotations": [
{
"type": "Box",
"color": "red",
"box_top": 406.0,
"box_left": 656.0,
"box_height": 73.0,
"box_width": 40.0
}
],
"annotations": [
{
"type": "Box",
"color": "green",
"box_top": 450.0,
"box_left": 700.0,
"box_height": 95.0,
"box_width": 47.0
}
]
By taking the box values (box_top,box_left,box_height,box_width) I have drawn Rectangle using QGraphicsView and QGraphicsScene. The code is given below;
def load_image(self,image_item):
self.scene = QGraphicsScene(self.centralWidget) # Created a QGraphicsScene
self.pic = QPixmap(str(image_item.text())) # Loaded Image
self.brush = QBrush()
self.pen = QPen(Qt.red)
self.pen.setWidth(2)
self.pixItem = QGraphicsPixmapItem(self.pic)
self.load_view = self.scene.addItem(self.pixItem) # Image Added to Scene
# Opening JSON and fetching data
# …
for rect in json_file['annotations']:
# Taken type and color and stored in variable
self.box_type = rect['type']
self.box_color = rect['color']
# Taken box_top,box_left,box_height,box_width values
self.rect_item = self.scene.addRect(rect['box_top'],rect['box_left'],rect['box_width'],rect['box_length'],self.pen,self.brush) # x,y,w,h
self.rect_item.setFlag(QGraphicsItem.ItemIsSelectable) #Item is Selectable
#self.rect_item.setFlag(QGraphicsItem.ItemIsMovable) # Item is Movable
self.fit_view = self.gView.setScene(self.scene)
self.gView.fitInView(self.pixItem,Qt.KeepAspectRatio)
self.gView.setRenderHint(QPainter.Antialiasing)
self.gView.show()
Now What I want is when I click one box (say which has color red) from the GraphicsScene, I want to print its corresponding type and color. In a simple way, I want to print all data related to that box. A sample images also attached for reference. Note : Image is the output of this program.
Thank you.
Use the code provided in the other answer I gave you (the second and last method are probably better) and get the data you need from the item. The "type" is the object class, the color is the pen() color.
I have some problem with updating the plot values with Bokeh. Select and Slider don't change the plot. The code is supposed to plot 'budget' along with 'vote_average' in different years. Slider is for showing data (release_date) from 1970 to 2016 years. I'm working in the Jupyter notebook. Code is below:
source = ColumnDataSource(data = {
'x': movies.budget,
'y': movies.vote_average,
'revenue': movies.revenue,
'profit': movies.profit,
'original_title': movies.original_title,
'release_date': movies.release_date
})
p = figure(x_axis_label='Budget in millions $', y_axis_label='Rank',
tools = [HoverTool(tooltips = '#original_title')])
p.circle(x = 'x', y = 'y', source=source)
def update_plot(attr, old, new):
yr = slider.value
# Set new_data
new_data = {
'x' : data.budget.loc[data.release_date == str(yr)].values,
'y' : data.vote_average.loc[data.release_date == str(yr).values
}
# Assign new_data to source.data
source.data = new_data
slider = Slider(start=1970, end=2016, step=1, value=1970, title='Year')
slider.on_change('value', update_plot)
layout = row(widgetbox(slider), p)
show(layout)
What's supposed to be in 'update plot' function? It seems that this func just doesn't work.
Binding widgets in Jupyter notebook requires custom Javascript callbacks as far as I know. Your example would only work on a bokeh serve app. Check out this notebook to see how.
I am new to using plotly and I am attempting to build a dynamic visualisation using python and plotly. I hope to be able to switch between a world choropleth map and a scatter plot using a drop-down menu.
So far I have been able to successfully get a dropdown menu to appear and show the required labels and even show a single plot by removing either the choropleth map or scatter plot trace from the data variable. The problem is that I when I try to have both plots implemented the choropleth map is drawn over the top of the scatterplot regardless of the menu option I choose.
A screenshot of the output.
Areas I Have Looked For A Solution
The plotly reference and looked through the updatemenus and layout sections among many others.
Reviewed the ploty python tutorial page for dropdowns and implementing parts of the suggestion in my code with a focus on the update method.
I have found a StackOverflow page that seemed to be very close to the answer I needed however not quite.
Finally, I also searched the plotly community forum.
The Code
Note I have removed a portion of the code such as imports and data at the beginning.
scatterplot = go.Scatter(
y = df2['Renewable energy consumption (% of total final energy consumption) 2015'],
x = df2['GDP per capita, PPP (constant 2011 international $) 2015'],
mode='markers',
ids=df2['Country Name'],
showlegend = False,
marker = dict(
size = 8,
color = np.random.randn(500),
),
textfont = dict(
size = 14,
color = 'black')
)
choropleth_map = dict(
type = 'choropleth',
locations = df['ISO3166_alpha3'],
z = df['renewables_mtoe'],
text = df['Country'],
colorscale = [[0,"rgb(106, 240, 255)"],[0.10,"rgb(106, 199, 255)"],[0.70,"rgb(50, 100, 255)"],[0.93,"rgb(0, 43, 198)"],\
[0.99999,"rgb(0, 24, 109)"],[1,"rgb(220, 220, 220)"]],
autocolorscale = False,
reversescale = True,
marker = dict(
line = dict (
color = 'rgb(180,180,180)',
width = 0.5
) ),
colorbar = dict(
title = 'mtoe<br>',
tickfont = dict(
size = 16),
titlefont = dict(
size = 16)),
)
data = [choropleth_map, scatterplot]
updatemenus = list([
dict(active=0,
buttons=list([
dict(label = 'choropleth_map',
method = 'update',
args = [{'visible': [True,False]},
{'title': 'The Map'}]),
dict(label = 'scatterplot',
method = 'update',
args = [{'visible': [False,True]},
{'title': 'Scatterplot'}]),
]),
)
])
layout = dict(title='default', showlegend=False,
updatemenus=updatemenus,
geo = dict(showframe = True,
showcoastlines = False,
showland = True,
landcolor = '#dcdcdc',
projection = dict(type = 'natural earth'))
)
fig = dict( data=data, layout=layout )
plotly.offline.iplot(fig, validate=True)
A big thank you in advance to anyone who can help. I have spent days trying to solve this problem, it has even driven me to make my first post on StackOverflow.
I'm using plotly for R, although I'm open to using the Python version, as well. When I hover over a datapoint, is there a way to make the popup contain another chart? Ideally the chart would be created from the data, although I can use a static image as a fallback.
I'm unsure where to start on this, and apologize in advance for not having an MWE.
Solution 1: Stick to R
Thanks to #MLavoie. The following example use pure R to create two plot, the "mainplot" and the "hover" which reacts to the hover event of the first one.
library(shiny)
library(plotly)
ui <- fluidPage(
plotlyOutput("mainplot"),
plotlyOutput("hover")
)
server <- function(input, output) {
output$mainplot <- renderPlotly({
# https://plot.ly/r/
d <- diamonds[sample(nrow(diamonds), 1000), ]
plot_ly(d, x = carat, y = price, text = paste("Clarity: ", clarity), mode = "markers", color = carat, size = carat, source="main")
})
output$hover <- renderPlotly({
eventdat <- event_data('plotly_hover', source="main") # get event data from source main
if(is.null(eventdat) == T) return(NULL) # If NULL dont do anything
point <- as.numeric(eventdat[['pointNumber']]) # Index of the data point being charted
# draw plot according to the point number on hover
plot_ly( x = c(1,2,3), y = c(point, point*2, point*3), mode = "scatter")
})
}
shinyApp(ui, server)
This example use the shiny binds for plotly. For every hover event, a POST request is sent to the server, then the server will update the popup-chart. It's very inefficient thus may not work well on slow connections.
The above code is just for demo, and not yet tested. See a working and much more complicated example here (with source).
Solution 2: Javascript
Yes, you can do it using the plotly Javascript API.
Short answer
Create your graph using R or Python or any other supported language.
Insert the graph into a new HTML page and add a callback function as shown in the example below. If you have good knowledge about DOM, you can also add the JS to the original HTML instead of creating a new one.
Draw the popup graph inside the callback function which accepts parameters containing the data of the datapoint on-hover.
Details
As #MLavoie mentioned, a good example is shown in plotly.hover-events
Let's dig into the code. In the JS file, there is a simple callback function attached to Plot:
Plot.onHover = function(message) {
var artist = message.points[0].x.toLowerCase().replace(/ /g, '-');
var imgSrc = blankImg;
if(artistToUrl[artist] !== undefined) imgSrc = artistToUrl[artist];
Plot.hoverImg.src = imgSrc;
};
Above, artistToUrl is a huge object filled with base64 string which I will not paste here to overflow the post. But you can see it under the JS tab of the example page. It has such structure:
var artistToUrl = { 'bob-dylan': 'data:image/jpeg;base64,/...',...}
Working example:
For demonstration, I prepare a simple example here (click to try):
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
<iframe id="plot" style="width: 900px; height: 600px;" src="https://plot.ly/~jackp/10816.embed" seamless></iframe>
<div id="myDiv"></div>
<script>
(function main() {
var Plot = { id: 'plot', domain: 'https://plot.ly' };
Plot.onHover = function(message) {
var y = message.points[0].y; /*** y value of the data point(bar) under hover ***/
var line1 = {
x: [0.25,0.5,1], /*** dummy x array in popup-chart ***/
y: [1/y, 2, y], /*** dummy y array in popup-chart ***/
mode: 'lines+markers'
};
var layout = {
title:'Popup graph on hover',
height: 400,
width: 480
};
Plotly.newPlot('myDiv', [ line1 ], layout); // this finally draws your popup-chart
};
Plot.init = function init() {
var pinger = setInterval(function() {
Plot.post({task: 'ping'});
}, 500);
function messageListener(e) {
var message = e.data;
if(message.pong) {
console.log('Initial pong, frame is ready to receive');
clearInterval(pinger);
Plot.post({
'task': 'listen',
'events': ['hover']
});
}
else if(message.type === 'hover') {
Plot.onHover(message);
}
}
window.removeEventListener('message', messageListener);
window.addEventListener('message', messageListener);
};
Plot.post = function post(o) {
document.getElementById(Plot.id).contentWindow.postMessage(o, Plot.domain);
};
Plot.init();
})();
</script>
</body>
</html>
This is modified from the poltly.hover-events example for python. Instead of poping up an image, I change the onhover callback to plot a curve based on the y value of the each bar.
The main chart is generated by python and inserted here as iframe. You can make your own by any language including R. In this page we add a <div id="myDiv"></div> and use the plotly.js to draw the popup-chart whithin it.
Export R data frame to JS enviornment
Shiny uses jsonlite to convert R objects to json and send them to the client. We can use the same mechanism to pack and send our data frame so that the JS callback can use the data to render the popup chart.
server.r
output$json <- reactive({
paste('<script>data =', RJSONIO::toJSON(your_data_frame, byrow=T, colNames=T),'</script>')
ui.r
fluidPage(..., htmlOutput("json"), ...)
In the JS callback function, you can use data as any other JS objects.
More detail goes here and here.
If you want to stick with R you could use Shiny to get almost the result you want. When you hover each point an image will be render under the main plot. For the example below, I used the first three rows of the mtcars datasets. To run the code, you only need 3 logos/images corresponding to the name of the first three rows (under mtcars$name, Mazda RX4, Mazda RX4 Wag, Datsun 710 in this example).
library(shiny)
library(plotly)
datatest <- diamonds %>% count(cut)
datatest$ImageNumber <- c(0, 1, 2, 3, 4)
datatest$name <- c("Image0", "Image1", "Image2", "Image3", "Image4")
ui <- fluidPage(
plotlyOutput("plot"),
# verbatimTextOutput("hover2"),
#imageOutput("hover"),
plotlyOutput("hover3")
)
server <- function(input, output, session) {
output$plot <- renderPlotly({
plot_ly(datatest, x = cut, y = n, type = "bar", marker = list(color = toRGB("black")))
})
selected_image <- reactive({
eventdat <- event_data('plotly_hover', source = 'A')
ImagePick <- as.numeric(eventdat[['pointNumber']])
sub <- datatest[datatest$ImageNumber %in% ImagePick, ]
return(sub)
})
# output$hover2 <- renderPrint({
#d <- event_data("plotly_hover")
#if (is.null(d)) "Hover events appear here (unhover to clear)" else d
#})
# output$hover <- renderImage({
# datag <- selected_image()
#filename <- normalizePath(file.path('/Users/drisk/Desktop/temp',
# paste(datag$name, '.png', sep='')))
# Return a list containing the filename and alt text
# list(src = filename,
# alt = paste("Image number", datag$name))
# }, deleteFile = FALSE)
output$hover3 <- renderPlotly({
datag <- selected_image()
# draw plot according to the point number on hover
plot_ly(data=datag, x = ImageNumber, y = n, mode = "scatter")
})
}
shinyApp(ui, server)
Seems the answers posted aren't working for you #Adam_G. I have been exploring similar libraries for my own work and determined that Plot.ly is not always the right path when you want advanced features. Have you seen bokeh? It is basically designed for this type of task and much easier to implement (also a D3.js library like Plot.ly). Here is a copy of an example they posted where you can move a slider to change a graph of data (similar to the example posted by #gdlmx for Plot.ly but you can use it without hosting it on a website). I added the flexx package so you can use this writing pure Python (no JavaScript - it can translate Python functions to JavaScript (CustomJS.from_py_func(callback)) https://github.com/zoofIO/flexx-notebooks/blob/master/flexx_tutorial_pyscript.ipynb):
from bokeh.io import vform
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.plotting import figure, output_file, show
import flexx
output_file("callback.html")
x = [x*0.005 for x in range(0, 200)]
y = x
source = ColumnDataSource(data=dict(x=x, y=y))
plot = figure(plot_width=400, plot_height=400)
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
def callback(source=source):
data = source.get('data')
f = cb_obj.get('value') #this is the bokeh callback object, linked to the slider below
x, y = data['x'], data['y']
for i in range(len(x)):
y[i] = x[i]**f #the slider value passed to this function as f will alter chart as a function of x and y
source.trigger('change') #as the slider moves, the chart will change
slider = Slider(start=0.1, end=4, value=1, step=.1, title="power", callback=CustomJS.from_py_func(callback))
layout = vform(slider, plot)
show(layout)
See here for the actual example in action: http://docs.bokeh.org/en/0.10.0/docs/user_guide/interaction.html#customjs-for-widgets
To integrate with hover events see here ( from bokeh.models import HoverTool):
http://docs.bokeh.org/en/0.10.0/docs/user_guide/interaction.html#customjs-for-hover
Hover example:
from bokeh.plotting import figure, output_file, show, ColumnDataSource
from bokeh.models import HoverTool
output_file("toolbar.html")
source = ColumnDataSource(
data=dict(
x=[1, 2, 3, 4, 5],
y=[2, 5, 8, 2, 7],
desc=['A', 'b', 'C', 'd', 'E'],
)
)
hover = HoverTool(
tooltips=[
("index", "$index"),
("(x,y)", "($x, $y)"),
("desc", "#desc"),
]
)
p = figure(plot_width=400, plot_height=400, tools=[hover], title="Mouse over the dots")
p.circle('x', 'y', size=20, source=source)
show(p)
Looking at the 1st code you could put whatever formula you want under the def callback function - some playing around required. You can get the hover to alter a graph next to it (hform(leftchart, rightchart) or above / below it (vform(topchart, bottomchart)). This is passed as CustomJS which bokeh uses to allow extendability and flexx allows you to write it in Python.
The alternative is to put whatever you want customized on the hover tooltips using HTML (although this example is placing images in dictionaries instead of new plots from the underlying data): http://docs.bokeh.org/en/0.10.0/docs/user_guide/tools.html#custom-tooltip