Bokeh World Map and Coloring Waterbodies - python-3.x

Is there an equivalent way in Bokeh to Basemap's drawmapboundary where you can specify certain colors? See the first example here:
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
# setup Lambert Conformal basemap.
m = Basemap(width=12000000,height=9000000,projection='lcc',
resolution='c',lat_1=45.,lat_2=55,lat_0=50,lon_0=-107.)
# draw coastlines.
m.drawcoastlines()
# draw a boundary around the map, fill the background.
# this background will end up being the ocean color, since
# the continents will be drawn on top.
m.drawmapboundary(fill_color='aqua')
# fill continents, set lake color same as ocean color.
m.fillcontinents(color='coral',lake_color='aqua')
plt.show()
I would like to fill waterbodies (e.g., oceans) with the color "aqua". I'm able to generate a black-and-white world map, but how do I color oceans specifically?
I'm using the JSON file for countries from here, and then loading it with GeoJSONDataSource.
import bokeh.plotting as bkp
import bokeh.models as bkm
filename = "test.html"
tools = "pan,wheel_zoom,box_zoom,reset,previewsave"
with open("./countries.geo.json", "r") as f:
countries = bkm.GeoJSONDataSource(geojson=f.read())
p = bkp.figure(width=1000, height=600, tools=tools, title='World Countries', x_axis_label='Longitude', y_axis_label='Latitude')
p.x_range = bkm.Range1d(start=-180, end=180)
p.y_range = bkm.Range1d(start=-90, end=90)
p.patches("xs", "ys", color="white", line_color="black", source=countries)
bkp.output_file(filename)
bkp.save(p, filename)

Figured out by looking at what drawmapboundary does. Just need to set the background color. :)
import bokeh.plotting as bkp
import bokeh.models as bkm
filename = "test.html"
tools = "pan,wheel_zoom,box_zoom,reset,previewsave"
with open("./countries.geo.json", "r") as f:
countries = bkm.GeoJSONDataSource(geojson=f.read())
p = bkp.figure(width=1000, height=600, tools=tools, title='World Countries', x_axis_label='Longitude', y_axis_label='Latitude')
p.background_fill_color = "aqua"
p.x_range = bkm.Range1d(start=-180, end=180)
p.y_range = bkm.Range1d(start=-90, end=90)
p.patches("xs", "ys", color="white", line_color="black", source=countries)
bkp.output_file(filename)
bkp.save(p, filename)

Related

Plotly Choropleth_mapbox plots with Dash interactivity

I am a GIS person fairly new to Plotly and exceptionally new to Dash. I'm trying to mostly copy an example solution from a post here:
drop down menu with dash / plotly
To build an interactive app to look at various choropleth maps based on choropleth_mapbox figures. The last solution from the above post, using Plotly and Dash by Rob Raymond, looks brilliant and close to what I am trying to do. But in my case, my figures built on several data 'columns' also require an individual update_layout call and a hovertemplate built for each data column; and I cannot figure out where to place those definitions within the solution posted above.
This is my code for a single data column's figure, which gives me the functionality I want in the layout and hover tool:
fig = px.choropleth_mapbox(
gdf_blks_results,
geojson = gdf_blks.geometry,
locations = gdf_blks_results.index,
color=classme.yb,
color_continuous_scale = "YlOrRd",
center={"lat": 18.2208, "lon": -66.49},
mapbox_style="open-street-map",
width=800,
height=500,
custom_data = [gdf_blks_results['GEOID'],
gdf_blks_results['overallBurden']]
)
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0},
coloraxis_colorbar=dict(
title="burden",
thicknessmode="pixels",
lenmode="pixels",
yanchor="top",y=1,
ticks="outside",
tickvals=[0,1,2,3,4],
ticktext=myclasses,
dtick=5
))
# hover template
hovertemp = '<i>Census ID :</i> %{customdata[0]}<br>'
hovertemp += '<i>burden : </i> %{customdata[1]:.5f}<br>'
fig.update_traces(hovertemplate=hovertemp)
fig.show()
My question is, how do I incorporate that into the list of figures for a set of columns of data with custom template and figure update info for each? I tried to add it to the figure definitions in the cited post example before the "for c, color in zip(...)" statement, but I cannot get the syntax right, and I am not sure why not.
I think you should create a Dropdown list with Options as gdf_blks_results columns the returns it with callback to update choropleth map. Please refer below code:
import pandas as pd
import numpy as np
import plotly.express as px
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
import dash_table
import dash_bootstrap_components as dbc
columns_list = list(gdf_blks_results.columns)
app = dash.Dash(__name__,external_stylesheets=[dbc.themes.LUX])
app.layout = html.Div([
dbc.Row([
dbc.Col([
html.H5('Columns',className='text-center'),
],width={'size':2,"offset":0,'order':1}),
dbc.Col([
dcc.Dropdown(id='columns',placeholder="Please select columns",
options=[{'label':x,'value':x} for x in columns_list],
value=[],
multi=False,
disabled=False,
clearable=True,
searchable=True),
],width={'size':10,"offset":0,'order':1})
], className='p-2 align-items-stretch'),
dbc.Row([
dbc.Col([
dcc.Graph(id="choropleth_maps",figure={},style={'height':500}), #Heatmap plot
],width={'size':12,'offset':0,'order':2}),
]),
])
#app.callback(Output('choropleth_maps', 'figure'),
[Input('columns', 'value')])
def update_graph(columns):
fig = px.choropleth_mapbox(
gdf_blks_results,
geojson = gdf_blks.geometry,
locations = gdf_blks_results.index,
color=columns,
color_continuous_scale = "YlOrRd",
center={"lat": 18.2208, "lon": -66.49},
mapbox_style="open-street-map",
width=800,
height=500,
custom_data = [gdf_blks_results['GEOID'],
gdf_blks_results['overallBurden']])
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0},
coloraxis_colorbar=dict(
title="burden",
thicknessmode="pixels",
lenmode="pixels",
yanchor="top",y=1,
ticks="outside",
tickvals=[0,1,2,3,4],
ticktext=myclasses,
dtick=5
))
# hover template
hovertemp = '<i>Census ID :</i> %{customdata[0]}<br>'
hovertemp += '<i>burden : </i> %{customdata[1]:.5f}<br>'
fig.update_traces(hovertemplate=hovertemp)
return fig
if __name__ == "__main__":
app.run_server(debug=False,port=1116)

Get pixel color on screen

How to use Jython (Robot) to get a pixel color in R,G,B format and then convert it to HEX.
This is my code so far.
import sys
import os
import java.awt.Robot
import java.awt.Color
def get_pixels(posX, posY):
robot = Robot()
Color = getPixelColor(posX, posY)
r = color.getRed()
g = color.getGreen()
b = color.getBlue()
color = "#{:02x}{:02x}{:02x}".format(r,g,b)
return Color
get_pixels(200, 300)
Well, i found out how to make it work, am gonna share my code anyways.
#Jython
import sys
import os
from java.awt import Robot, Color
def get_pixels(posX, posY):
robot = Robot()
colors = robot.getPixelColor(posX, posY)
r = colors.getRed()
g = colors.getGreen()
b = colors.getBlue()
colors = "#{:02x}{:02x}{:02x}".format(r,g,b)
print (colors)
get_pixels(500, 500)
Thanks, regards!

Bokeh: Unable to plot legend

I am not sure how to add legend to my plots based on updates to the Bokeh Library. Here is my code -
import numpy as np
import pandas as pd
url = 'https://raw.githubusercontent.com/Deepakgthomas/Lemonade_Sales/main/Lemonade_Lab8.csv'
lemon = pd.read_csv(url)
from bokeh.models import ColumnDataSource
source_Q4 = ColumnDataSource(lemon)
from bokeh.io import output_notebook, show
from bokeh.plotting import figure
output_notebook()
p = figure(title = "Lemon and Orange Sales by Temperature")
p.circle("Temperature", "Lemon", source = source_Q4, color = "green", size = 8, legend = dict(value = "Lemon"))
p.triangle("Temperature", "Lemon", source = source_Q4, color = "orange", size = 8, legend = dict(value = "Orange"))
p.legend.location = "top_left"
show(p)
However, this gives me the warning -
"BokehDeprecationWarning: 'legend' keyword is deprecated, use explicit 'legend_label', 'legend_field', or 'legend_group' keywords instead
BokehDeprecationWarning: 'legend' keyword is deprecated, use explicit 'legend_label', 'legend_field', or 'legend_group' keywords instead"
As the Warning states, use legend_label instead of legend. For more information, check the user guide.
import numpy as np
import pandas as pd
url = 'https://raw.githubusercontent.com/Deepakgthomas/Lemonade_Sales/main/Lemonade_Lab8.csv'
lemon = pd.read_csv(url)
from bokeh.models import ColumnDataSource
source_Q4 = ColumnDataSource(lemon)
from bokeh.io import output_notebook, show
from bokeh.plotting import figure
output_notebook()
p = figure(title = "Lemon and Orange Sales by Temperature")
p.circle("Temperature", "Lemon", source = source_Q4, color = "green", size = 8, legend_label = "Lemon")
p.triangle("Temperature", "Orange", source = source_Q4, color = "orange", size = 8, legend_label = "Orange")
p.legend.location = "top_left"
show(p)

How can the `_property_values` of an element of a bokeh `figure.renderers` be changed directly?

How can the _property_values of an element of a bokeh figure.renderers be changed directly? I learned that the lements of renderers have an id, so I expect to do something like renderers['12345']. But as it is a list (a PropertyValueList to be more precise), this doesn't work. Instead, the only solution I found is to iterate over the list, storing the correct element in a new pointer (?), modifying the pointer and thus modifying the original element.
Here is my toy example where a vertical line in a histogram is updated based on some widget's value:
import hvplot.pandas
import ipywidgets as widgets
import numpy as np
from bokeh.io import push_notebook, show, output_notebook
from bokeh.models import Span
from bokeh.plotting import figure
%matplotlib inline
hist, edges = np.histogram([1, 2, 2])
p = figure()
r = p.quad(top=hist, bottom=0, left=edges[:-1], right=edges[1:])
vline = Span(location=0, dimension='height')
p.renderers.extend([vline])
def update_hist(x):
myspan = [x for x in p.renderers if x.id==vline.id][0]
myspan._property_values['location'] = x
show(p, notebook_handle=True)
widgets.interact(update_hist, x = widgets.FloatSlider(min=1, max=2))
Bigreddot pointed me into the right direction: I don't have to update p directly, but the elements used to generate p (here the Span). By this I found the this question where the code bears the solution: update vline.location.
Full code:
import hvplot.pandas
import ipywidgets as widgets
import numpy as np
from bokeh.io import push_notebook, show, output_notebook
from bokeh.models import Span
from bokeh.plotting import figure
%matplotlib inline
hist, edges = np.histogram([1, 2, 2])
p = figure()
r = p.quad(top=hist, bottom=0, left=edges[:-1], right=edges[1:])
vline = Span(location=0, dimension='height')
p.renderers.extend([vline])
show(p, notebook_handle=True)
def update_hist(x):
vline.location = x
push_notebook()
widgets.interact(update_hist, x = widgets.FloatSlider(min=1, max=2, step = 0.01))
As a Python beginner, I still often oversee, that Python does not have variables. So we can change an element x by changing y.
x = ['alice']
y = x
y[0] = 'bob'
x # is now ['bob] too

prevent camera resetting after plotting with Plotly-python

I am trying to plot some data for a 3d Quiver or Cone using dash and plotly and I want to update the Graph periodically through an interval Input!
So I managed to animate the graph but the problem is that the camera angle and zoom keep resetting after each update.
i have the following code:
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
from dash.dependencies import Output, Input
import pickle
#reading initial data
with open("shared.pkl", "rb") as f:
quivDic = pickle.load(f)
quiver_3d = go.Cone(x = quivDic["X"], y = quivDic["Y"], z = quivDic["Z"],
u = quivDic["U"], v = quivDic["V"], w = quivDic["W"],
colorscale = 'Blues', name = "testScatter")
data = [quiver_3d]
layout = dict(title ="Test Quiver", showlegend=False, aspectratio=dict(x=1, y=1, z=0.8),
camera_eye=dict(x=1.2, y=1.2, z=0.6))
fig = dict(data=data, layout=layout)
app = dash.Dash()
app.layout = html.Div([
html.Div(html.H4("TEST CONE")),
html.Div(dcc.Graph(id = "testCone", figure=fig)),
dcc.Interval(
id='graph-update',
interval=1000,
n_intervals = 0
),
])
#app.callback(Output('testCone', 'figure'),
[Input('graph-update', 'n_intervals')])
def refresh(n):
#reading new data
with open("shared.pkl", "rb") as f:
quivDic = pickle.load(f)
quiver_3d.x = quivDic["X"]
quiver_3d.y = quivDic["Y"]
quiver_3d.z = quivDic["Z"]
quiver_3d.u = quivDic["U"]
quiver_3d.v = quivDic["V"]
quiver_3d.w = quivDic["W"]
data = [quiver_3d]
#creating new figure
fig = dict(data=data)
return fig
app.run_server(debug=True)
Does anyone know how to avoid this problem?
Ideally I'd like to update the data without redrawing the whole frame, something like "set_data" from matplotlib. Otherwise is there a way to keep track of the latest camera angle and update the layout through the callback?
and Thanks ^^
Yes, you can use the uirevision attribute, as detailed here: https://community.plot.ly/t/preserving-ui-state-like-zoom-in-dcc-graph-with-uirevision/15793

Resources