I am rendering a scattermapbox using plotly in my Flask / Dash Application. I have set a default zoom level and lat,long coords. As the user pans the map via the plotly interface and changes the zoom level, I'd like to update the map with points and data layer.
Here's some code for reference:
import pandas as pd
import geopandas as gpd
# import mapbox
import requests
import plotly.graph_objects as go
# Update with host url
df_geo = gpd.GeoDataFrame.from_features(
requests.get(
"https://eric.clst.org/assets/wiki/uploads/Stuff/gz_2010_us_050_00_20m.json"
).json()
)
import plotly.graph_objects as go
fig = go.Figure(
go.Choroplethmapbox(
geojson=df_geo.set_index("GEO_ID")["geometry"].__geo_interface__,
locations=df_geo["GEO_ID"],
z=df_geo["CENSUSAREA"],
autocolorscale=False,
colorscale="Viridis",
zmin=df_geo["CENSUSAREA"].min(),
zmax=df_geo["CENSUSAREA"].quantile(0.95),
marker_line_width=0,
colorbar={"orientation": "h", "x": 0.5, "yanchor": "middle", "y": 0.1},
)
)
fig.update_layout(
mapbox_style="carto-positron",
# mapbox_accesstoken=token,
mapbox_zoom=3,
mapbox_center={"lat": 37.0902, "lon": -95.7129},
margin={"r": 0, "t": 0, "l": 0, "b": 0},
)
fig.show()
My question is, how do I obtain the geometries / bbox or lat,longs coords of the current map view?
Links to docs for reference:
https://plotly.com/python/reference/#scattermapbox
https://dash.plotly.com/dash-core-components/graph
relayoutData on a mapbox does return center and coordinates
have demonstrated by inserting this into trace using client side callback. This is working, however to improve efficiency I really wants to just update the figure rather than re-render it.
import pandas as pd
import geopandas as gpd
import requests
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
import dash
from dash.dependencies import Input, Output, State
import json
# Update with host url
df_geo = gpd.GeoDataFrame.from_features(
requests.get(
"https://eric.clst.org/assets/wiki/uploads/Stuff/gz_2010_us_050_00_20m.json"
).json()
)
fig = go.Figure(
[
go.Choroplethmapbox(
geojson=df_geo.set_index("GEO_ID")["geometry"].__geo_interface__,
locations=df_geo["GEO_ID"],
z=df_geo["CENSUSAREA"],
autocolorscale=False,
colorscale="Viridis",
zmin=df_geo["CENSUSAREA"].min(),
zmax=df_geo["CENSUSAREA"].quantile(0.95),
marker_line_width=0,
name="choropleth"
# colorbar={"orientation": "h", "x": 0.5, "yanchor": "middle", "y": 0.1},
),
go.Scattermapbox(
name="scatter", marker={"size": 30, "color": "red", "opacity": 1}
),
]
)
fig.update_layout(
mapbox_style="carto-positron",
# mapbox_accesstoken=token,
mapbox_zoom=3,
mapbox_center={"lat": 37.0902, "lon": -95.7129},
margin={"r": 0, "t": 0, "l": 0, "b": 0},
datarevision=0,
height=300,
width=600,
autosize=False,
)
# Build App
app = JupyterDash(__name__)
app.layout = dash.html.Div(
[
dash.dcc.Checklist(
options=[{"label":"refesh", "value":"yes"}],
id="refresh",
),
dash.dcc.Graph(id="mapbox_fig", figure=fig),
dash.html.Div(
id="debug_container",
),
dash.dcc.Store(
id="points-store",
data={
"lat": [],
"lon": [],
},
),
]
)
#app.callback(
Output("points-store", "data"),
Output("debug_container", "children"),
Input("mapbox_fig", "relayoutData"),
Input("refresh","value")
)
def mapbox_cb(mapbox_cfg, refresh):
try:
refresh = refresh[0]=="yes"
except Exception:
refresh = False
if mapbox_cfg and "mapbox.zoom" in mapbox_cfg.keys() and refresh:
bbox = np.array(mapbox_cfg["mapbox._derived"]["coordinates"])
# bbox = bbox * .8
data = {
"lon": bbox[:, 0].tolist() + [mapbox_cfg["mapbox.center"]["lon"]],
"lat": bbox[:, 1].tolist() + [mapbox_cfg["mapbox.center"]["lat"]],
}
return data, [
dash.html.Pre(json.dumps(mapbox_cfg, indent=2)),
dash.html.Pre(json.dumps(data, indent=2)),
]
else:
raise dash.exceptions.PreventUpdate
app.clientside_callback(
"""
function(data, fig) {
fig.data[1]['lat'] = data['lat'];
fig.data[1]['lon'] = data['lon'];
fig.layout.datarevision = fig.layout.datarevision + 1;
/* return fig; */
return JSON.parse(JSON.stringify(fig));
}
""",
Output("mapbox_fig", "figure"),
Input("points-store", "data"),
State("mapbox_fig", "figure"),
)
app.run_server(mode="inline")
Related
I have a list of Plotly figures and I want to create an animation that iterates over each figure on a button press. Similar the examples found on Intro to Animations in Python. I pretty much tried re-creating several of the examples on the page with no luck.
It seems like there should be a simple solution but I have not been able to find one. I should note that I do not want to animate the geocoded cities but rather the weather layout - i.e., mapbox_layers
Below is the code to create the list of figures:
import requests
from bs4 import BeautifulSoup
import pandas as pd
import plotly.express as px
# just some geocoded data from plotly
us_cities = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/us-cities-top-1k.csv")
# GET request to pull the datetime info
r = requests.get('https://geo.weather.gc.ca/geomet?service=WMS&version=1.3.0&request=GetCapabilities&layer=GDPS.DIAG_NW_PT1H')
# create the soup
soup = BeautifulSoup(r.text, 'xml')
# start and end dates in UTC
start, end = soup.findAll('Dimension')[0].text.split('/')[:2]
# create a date range
dates = pd.date_range(start, end, freq='1h').strftime('%Y-%m-%dT%H:%M:%SZ')[0::3]
# iterate over the dates to create the figures
figs = []
for date in dates:
fig = px.scatter_mapbox(us_cities, lat="lat", lon="lon", hover_name="City", hover_data=["State", "Population"],
color_discrete_sequence=["black"], zoom=3, height=600, center={'lat': 42.18845, 'lon':-87.81544},
title=date)
fig.update_layout(
mapbox_style="open-street-map",
mapbox_layers=[
{
"below": 'traces',
"sourcetype": "raster",
"sourceattribution": "Government of Canada",
"source": ["https://geo.weather.gc.ca/geomet/?"
"SERVICE=WMS&VERSION=1.3.0"
"&REQUEST=GetMap"
"&BBOX={bbox-epsg-3857}"
"&CRS=EPSG:3857"
"&WIDTH=1000"
"&HEIGHT=1000"
"&LAYERS=GDPS.DIAG_NW_PT1H"
"&TILED=true"
"&FORMAT=image/png"
f"&TIME={date}"
],
},
]
)
fig.update_layout(margin={"r":0,"t":50,"l":0,"b":0})
figs.append(fig)
figs[0]
figs[6]
figs[12]
I think the most helpful example in the plotly documentation was on visualizing mri volume slices. Instead of creating a list of figure objects, we can store the data and layout of each figure in a list of go.Frame objects and then initialize our figure with these frames with something like fig = go.Figure(frames=[...])
The creation of the buttons and sliders follows the documentation exactly, and these can probably be tweaked to your liking.
Note: the slider will only work if we populate the name argument in each go.Frame object, as pointed out by #It_is_Chris
import requests
from bs4 import BeautifulSoup
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
# just some geocoded data from plotly
us_cities = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/us-cities-top-1k.csv")
# GET request to pull the datetime info
r = requests.get('https://geo.weather.gc.ca/geomet?service=WMS&version=1.3.0&request=GetCapabilities&layer=GDPS.DIAG_NW_PT1H')
# create the soup
soup = BeautifulSoup(r.text, 'xml')
# start and end dates in UTC
start, end = soup.findAll('Dimension')[0].text.split('/')[:2]
# create a date range
dates = pd.date_range(start, end, freq='1h').strftime('%Y-%m-%dT%H:%M:%SZ')[0::3]
# iterate over the dates to create the figures
# figs = []
frames = []
for i, date in enumerate(dates):
fig = px.scatter_mapbox(us_cities, lat="lat", lon="lon", hover_name="City", hover_data=["State", "Population"],
color_discrete_sequence=["black"], zoom=3, height=600, center={'lat': 42.18845, 'lon':-87.81544},
title=date)
fig.update_layout(
mapbox_style="open-street-map",
mapbox_layers=[
{
"below": 'traces',
"sourcetype": "raster",
"sourceattribution": "Government of Canada",
"source": ["https://geo.weather.gc.ca/geomet/?"
"SERVICE=WMS&VERSION=1.3.0"
"&REQUEST=GetMap"
"&BBOX={bbox-epsg-3857}"
"&CRS=EPSG:3857"
"&WIDTH=1000"
"&HEIGHT=1000"
"&LAYERS=GDPS.DIAG_NW_PT1H"
"&TILED=true"
"&FORMAT=image/png"
f"&TIME={date}"
],
},
]
)
fig.update_layout(margin={"r":0,"t":50,"l":0,"b":0})
frames += [go.Frame(data=fig.data[0], layout=fig.layout, name=date)]
## store the first frame to reuse later
if i == 0:
first_fig = fig
fig = go.Figure(frames=frames)
## add the first frame to the figure so it shows up initially
fig.add_trace(first_fig.data[0],)
fig.layout = first_fig.layout
## the rest is coped from the plotly documentation example on mri volume slices
def frame_args(duration):
return {
"frame": {"duration": duration},
"mode": "immediate",
"fromcurrent": True,
"transition": {"duration": duration, "easing": "linear"},
}
sliders = [
{
"pad": {"b": 10, "t": 60},
"len": 0.9,
"x": 0.1,
"y": 0,
"steps": [
{
"args": [[f.name], frame_args(0)],
"label": str(k),
"method": "animate",
}
for k, f in enumerate(fig.frames)
],
}
]
fig.update_layout(
title='Slices in volumetric data',
width=1200,
height=600,
scene=dict(
zaxis=dict(range=[-0.1, 6.8], autorange=False),
aspectratio=dict(x=1, y=1, z=1),
),
updatemenus = [
{
"buttons": [
{
"args": [None, frame_args(50)],
"label": "▶", # play symbol
"method": "animate",
},
{
"args": [[None], frame_args(0)],
"label": "◼", # pause symbol
"method": "animate",
},
],
"direction": "left",
"pad": {"r": 10, "t": 70},
"type": "buttons",
"x": 0.1,
"y": 0,
}
],
sliders=sliders
)
fig.show()
List item
import os
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
import keyring
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.io as pio
import seaborn as sns
from dash import dash, dcc, html
from dash.dependencies import Input, Output, State
from dash_bootstrap_templates import load_figure_template
from sqlalchemy.types import NVARCHAR
from HANA_connect import HANA
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.DARKLY])
load_figure_template("darkly")
colors = {
'background': '#111111',
'text': 'white'
}
login = os.getlogin() # Login holen
pwd=keyring.get_password('HANA_CS4',os.getlogin().upper()) # Passwort holen
cs4 = HANA(login, pwd) # Verbindung ertellen
sqlquery="""SELECT * FROM ZOLS2.korr_plot"""
df = cs4.getDf2(sqlquery)
sqlquery="""SELECT * FROM ZOLS2.hccoop_Treemap WHERE CCM_HC_CD IN ('010112') and NU_AP>50000"""
sqlquery="""SELECT * FROM ZOLS2.HCCOOP_TREEMAP_ZEITREIHE WHERE CCM_HC_CD IN ('010112') """
df1 = cs4.getDf2(sqlquery)
df1.head()
df1.JAHR_MONAT.unique()
ap= '202108'
vp= '202208'
df_ap = df1.loc[df1.JAHR_MONAT==ap,['JAHR_MONAT', 'CCM_HC_CD', 'CCM_HC_TEXT', 'CCM_CATEGORY_CD','CCM_CATEGORY_TEXT', 'CCM_SUBCATEGORY_CD', 'CCM_SUBCATEGORY_TEXT','MARKE_CD', 'MARKE_TEXT', 'NU','DB_KUNDE']]
df_vp = df1.loc[df1.JAHR_MONAT==vp,['JAHR_MONAT', 'CCM_HC_CD', 'CCM_HC_TEXT', 'CCM_CATEGORY_CD','CCM_CATEGORY_TEXT', 'CCM_SUBCATEGORY_CD', 'CCM_SUBCATEGORY_TEXT','MARKE_CD', 'MARKE_TEXT', 'NU','DB_KUNDE']]
df_tree = pd.merge(df_ap, df_vp, how='inner', on = [ 'CCM_HC_CD', 'CCM_HC_TEXT', 'CCM_CATEGORY_CD','CCM_CATEGORY_TEXT', 'CCM_SUBCATEGORY_CD', 'CCM_SUBCATEGORY_TEXT','MARKE_CD', 'MARKE_TEXT'],suffixes=('_AP', '_VP'))
df_tree['NU_INDEX'] = df_tree.NU_AP/df_tree.NU_VP
df_tree['DB_INDEX'] = df_tree.DB_KUNDE_AP/df_tree.DB_KUNDE_VP
df_tree['NU_INDEX'] = df_tree['NU_INDEX'].apply(lambda x : 1 if x==0 else x)
df_tree['NU_INDEX'] = df_tree['NU_INDEX'].astype(float)
df_tree['NU_INDEX'] = round(df_tree['NU_INDEX']*1,4)
df_tree['DB_INDEX'] = df_tree['DB_INDEX'].apply(lambda x : 1 if x==0 else x)
df_tree['DB_INDEX'] = df_tree['DB_INDEX'].astype(float)
df_tree['DB_INDEX'] = round(df_tree['DB_INDEX']*1,4)
df_tree.NU_VP = df_tree.NU_VP.astype(float)
df_tree.NU_AP = df_tree.NU_AP.astype(float)
df_tree.DB_KUNDE_VP = df_tree.DB_KUNDE_VP.astype(float)
df_tree.DB_KUNDE_AP = df_tree.DB_KUNDE_AP.astype(float)
df_tree.CCM_HC_TEXT = df_tree.CCM_HC_TEXT.astype(str)
df_tree.CCM_CATEGORY_TEXT = df_tree.CCM_CATEGORY_TEXT.astype(str)
df_tree.CCM_SUBCATEGORY_TEXT = df_tree.CCM_SUBCATEGORY_TEXT.astype(str)
df_tree.MARKE_TEXT = df_tree.MARKE_TEXT.astype(str)
#df1.CCM_SEGMENT_TEXT = df1.CCM_SEGMENT_TEXT.astype(str)
#df1.CCM_SUBSEGMENT_TEXT = df1.CCM_SUBSEGMENT_TEXT.astype(str)
df_tree.fillna('-', inplace=True)
df_tree = df_tree[df_tree['NU_AP']!=0]
df_tree['INDEX_COLOR_NU'] = df_tree['NU_INDEX'].apply(lambda x : 0.6 if x<0.6 else 1.4 if x>1.4 else x)
df_tree['INDEX_COLOR_DB'] = df_tree['DB_INDEX'].apply(lambda x : 0.6 if x<0.6 else 1.4 if x>1.4 else x)
fig = px.treemap(df_tree, path=[px.Constant("Coop"),"CCM_HC_TEXT" ,"CCM_CATEGORY_TEXT","CCM_SUBCATEGORY_TEXT",'MARKE_TEXT'], values='NU_AP',
color_continuous_scale=[(0, "red"), (0.5, "white"), (1, "green")],
color_continuous_midpoint=1,
color=df_tree.INDEX_COLOR_NU,
width=3840, height=1750)
fig.update_layout(margin = dict(t=100, l=50, r=25, b=25))
app.layout = html.Div(style={'backgroundColor': colors['background']}, children=[
html.H1(
children='Brot/Backwaren NU Dashboard',
style={
'textAlign': 'center',
'color': colors['text']
}
),
dcc.Dropdown([{
"label": html.Div(['08.2022'], style={'text': 'black', 'font-size': 20}),
"value": "202208",
},
{
"label": html.Div(['07.2022'], style={'text': 'black', 'font-size': 20}),
"value": "202207",
},
{
"label": html.Div(['06.2022'], style={'text': 'black', 'font-size': 20}),
"value": "202206",
}, ], value='202208', id='demo1dropdown'),
html.Div(id='dd1outputcontainer'),
dcc.Dropdown([{
"label": html.Div(['08.2022'], style={'text': 'black', 'font-size': 20}),
"value": "202208",
},
{
"label": html.Div(['07.2022'], style={'text': 'black', 'font-size': 20}),
"value": "202207",
},
{
"label": html.Div(['06.2021'], style={'text': 'black', 'font-size': 20}),
"value": "202106",
}, ], placeholder='Select_Datum', value='202208',
id='demo2dropdown'),
html.Div(id='dd2outputcontainer'),
dcc.Graph(
id='life-exp-vs-gdp',
figure = fig
)
])
#app.callback(
Output('dd2outputcontainer', 'children'),
Input('demo2dropdown', 'value'))
def update_figure(selected_date):
filtered_df = df_ap[df_ap.year == selected_date]
fig = px.treemap(filtered_df, path=[px.Constant("Coop"),"CCM_HC_TEXT" ,"CCM_CATEGORY_TEXT","CCM_SUBCATEGORY_TEXT",'MARKE_TEXT'], values='NU_AP',
color_continuous_scale=[(0, "red"), (0.5, "white"), (1, "green")],
color_continuous_midpoint=1, color=df_tree.INDEX_COLOR_NU, width=3840, height=1750)
fig.update_layout(margin = dict(t=100, l=50, r=25, b=25))
return fig
if __name__ == '__main__':
app.run_server(debug=True)
I want to be able to change the Date in the Dashboard so that the Treempa gets updatet.
But in the Dash Dashboard i get this error enter image description here
Traceback (most recent call last):
File "C:\Dev\py\Dashboard heatmap dev folder\from dash import dash, dcc, html.py", line 132, in update_figure
filtered_df = df_ap[df_ap.year == selected_date]
File "C:\Users\DANL2\AppData\Local\Programs\Python\Python310\lib\site-packages\pandas\core\generic.py", line 5907, in __getattr__
return object.__getattribute__(self, name)
AttributeError: 'DataFrame' object has no attribute 'year'
How do i need to edit the code so it Works ?
vp and ap are the two dates that i want to edit so that i can change the different Dates to compare.
I want to campare the two dates and display the difference in Sales. the Treemap plot works but the app callback or the def update_figure dont works i can't change the timeframe in the dashboard so that the codes get updated.
Thanks to Rob Raymond for the previos work. The aim is to represent the regions of a country with scatter_mapbox, I got this situation of the map (Spain is the example):
import requests
import plotly.express as px
import pandas as pd
# get Spain municipal boundaries
res = requests.get(
"https://raw.githubusercontent.com/codeforgermany/click_that_hood/main/public/data/spain-provinces.geojson"
)
# get some cities in Spain
df = (
pd.json_normalize(
requests.get(
"https://opendata.arcgis.com/datasets/6996f03a1b364dbab4008d99380370ed_0.geojson"
).json()["features"]
)
.loc[
lambda d: d["properties.CNTRY_NAME"].eq("Spain"),
["properties.CITY_NAME", "geometry.coordinates"],
]
.assign(
lon=lambda d: d["geometry.coordinates"].apply(lambda v: v[0]),
lat=lambda d: d["geometry.coordinates"].apply(lambda v: v[1]),
)
)
# scatter the cities and add layer that shows municiple boundary
px.scatter_mapbox(df, lat="lat", lon="lon", hover_name="properties.CITY_NAME").update_layout(
mapbox={
"style": "carto-positron",
"zoom": 3.5,
"layers": [
{
"source": res.json(),
"type": "line",
"color": "green",
"line": {"width": 1},
}
],
}
)
How to change cities by regions?
using geopandas https://plotly.com/python/mapbox-county-choropleth/#using-geopandas-data-frames
have created a column measure to define colorscale of province
simple case of doing choropleth instead of scatter
same can be achieved without geopandas referencing geojson and using pandas
import plotly.express as px
import numpy as np
import geopandas as gpd
gdf = gpd.read_file(
"https://raw.githubusercontent.com/codeforgermany/click_that_hood/main/public/data/spain-provinces.geojson",
crs="epsg:4326",
)
# for choropleth...
gdf["measure"] = np.random.randint(1, 1000, len(gdf))
px.choropleth_mapbox(
gdf, geojson=gdf["geometry"].__geo_interface__, locations=gdf.index, color="measure", hover_name="name"
).update_layout(
mapbox={
"style": "carto-positron",
"center": {
"lon": sum(gdf.total_bounds[[0, 2]]) / 2,
"lat": sum(gdf.total_bounds[[1, 3]]) / 2,
},
"zoom":4
},
margin={"l":0,"r":0,"t":0,"b":0}
)
Is there a way to create additional choropleth (ideally Mapbox choropleths, but I'd settle for Plotly's standard choropleth function) maps and layer them on top of a base choropleth so that I can easily show data related to Alaska and Hawaii with the continental US?
Something like this image
Don't believe my existing code is necessarily helpful, but here's how I build my base map (removed my custom style so that anyone should be able to generate).
fig = px.choropleth_mapbox(
df_mar,
geojson=puma,
locations="stpuma",
color="inter_pct",
range_color=(0,25),
color_continuous_scale="Viridis",
labels={"inter_pct": "Marriage (%)"},
center={"lat": 37.0902, "lon": -95.7129},
zoom=4.2,
opacity=1.0,
mapbox_style="white-bg"
)
fig.update_layout(
coloraxis_colorbar=dict(
bgcolor="rgba(22,33,49,1)",
title="Marriage,<br>Percent Share",
titlefont=dict(
color="rgba(255,255,255,1)"
),
tickfont=dict(
color="rgba(255,255,255,1)"
),
),
margin=dict(
l=50,
r=50,
b=50,
t=50,
pad=4
),
paper_bgcolor = "rgba(8,18,23,1)",
plot_bgcolor = "rgba(8,18,23,1)",
showlegend = True,
annotations = [
dict(
x=-0.025,
y=-0.04,
xref='paper',
yref='paper',
text='Source: Census ACS 5 2015-2019',
showarrow = False,
font=dict(
color="rgba(255,255,255,1)"
),
bgcolor="rgba(8,18,23,1)",
)
]
)
fig.update_traces(
marker_line_width=0,
below="waterway"
)
fig.show(width=1920, height=1080)
it can be achieved with mapbox
key concept is modify geometry to meet you layout requirements. Hence have done https://shapely.readthedocs.io/en/stable/manual.html#affine-transformations to move Alaska and Hawaii
also found that Alaska geometry was give issues given it crosses date line. Hence clipped geometry after transformation as well
for good measure added layers to indicate geometry has been manipulated
included maritime boundary to demonstrate how it can be extended to additional geometries
import geopandas as gpd
import shapely.geometry
import numpy as np
import plotly.express as px
import requests, io
from pathlib import Path
from zipfile import ZipFile
import urllib
import pandas as pd
from shapely.affinity import affine_transform as T
# US geometry
urls = [
"https://www2.census.gov/geo/tiger/GENZ2018/shp/cb_2018_us_state_5m.zip",
"https://maritimeboundaries.noaa.gov/downloads/USMaritimeLimitsAndBoundariesSHP.zip",
]
gdfs = {}
for url in urls:
f = Path.cwd().joinpath(urllib.parse.urlparse(url).path.split("/")[-1])
if not f.exists():
r = requests.get(url, stream=True, headers={"User-Agent": "XY"})
with open(f, "wb") as fd:
for chunk in r.iter_content(chunk_size=128):
fd.write(chunk)
zfile = ZipFile(f)
zfile.extractall(f.stem)
gdfs[f.stem] = gpd.read_file(
list(f.parent.joinpath(f.stem).glob("*.shp"))[0]
) # .to_crs("EPSG:4326")
gdf2 = gdfs["cb_2018_us_state_5m"]
gdf2 = gdf2.set_index("STUSPS", drop=False)
gdf2["color"] = gdf2["STATEFP"].astype(int)
# move alaska and hawaii using affine transform
t = {"AK": [0.6, 0, 0, 0.6, -20, -15], "HI": [3, 0, 0, 3, 385, -42]}
clip = (-179, 15, 0, 150)
gdf3 = gdf2.copy()
gdf3.loc[t.keys(), "geometry"] = gdf3.loc[t.keys(), ["geometry"]].apply(
lambda g: shapely.ops.clip_by_rect(T(g["geometry"], t[g.name]), *clip), axis=1
)
gdf_m = gdfs["USMaritimeLimitsAndBoundariesSHP"]
gdf_m = gdf_m.dissolve("REGION")
tm = {"Alaska": "AK", "Hawaiian Islands": "HI"}
gdf_m.loc[tm.keys(), "geometry"] = gdf_m.loc[tm.keys(), ["geometry"]].apply(
lambda g: shapely.ops.clip_by_rect(
T(g["geometry"], t[tm[g.name]]), *gdf3.loc[tm[g.name]].geometry.bounds
),
axis=1,
)
px.choropleth_mapbox(
gdf3,
geojson=gdf3.geometry.__geo_interface__,
locations=gdf3.index,
color="color",
hover_name="NAME",
).update_layout(
mapbox={
"style": "carto-positron",
"center": {"lon": -98, "lat": 33},
"zoom": 2.5,
"layers": [
{
"source": shapely.geometry.box(
*gdf3.loc[box].geometry.bounds
).__geo_interface__,
"type": "fill",
"color": "blue",
"opacity": 0.1,
}
for box in t.keys()
]
+ [
{
"source": gdf_m.geometry.__geo_interface__,
"type": "line",
"color": "blue",
"line": {"width": 1},
}
],
},
margin={"l": 0, "r": 0, "t": 0, "b": 0},
)
Can somebody give me a hint of why this part of my go.Choropleth code is not working?
I'm trying to make my country's borders a little more thicker and black, but it's not working and I don't know what else I could be missing on this layout specs... Here is my map code section and below it you can check a zoomed part of the map generated. Notice the country's borders are still thin and grey, why it does not change?
map_fig_layout = {'coloraxis_colorbar': {'title': 'População (%)',
'thickness': 20,
'ticklabelposition':'outside bottom'},
'margin': {'r':0, 'l':0, 't':0, 'b':0},
'template': 'plotly_dark',
'geo':{'projection': go.layout.geo.Projection(type ='natural earth'),
'landcolor': '#262626',
'showcountries':True,
'showsubunits':True,
'subunitcolor': 'black',
'subunitwidth': 4,
'resolution':110,
'visible':True,
'countrywidth': 4,
'countrycolor' : 'black'},
'uirevision':'not_tracked_key'}
map_graph = go.Figure({'data':[ go.Choropleth(locations = dff['Code'],
z = dff['população_%'], # a column of dff
hovertext = dff['Entity'],
zmin = 0,
zmax = 100,
colorscale = make_colorscale( ['#F53347','#E6C730','#2FF5A8'] ),
geo = 'geo') ],
'layout': map_fig_layout})
Try adding this line to your code:
fig.update_traces(marker_line_width=2.0, selector=dict(type='choropleth'))
or in your case:
map_graph.update_traces(marker_line_width=2.0, selector=dict(type='choropleth'))
You can additionally control the opacity, if so desired:
E.g.,
from urllib.request import urlopen
import json
with urlopen('https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json') as response:
counties = json.load(response)
import pandas as pd
df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/fips-unemp-16.csv",
dtype={"fips": str})
import plotly.express as px
fig = px.choropleth(df, geojson=counties, locations='fips', color='unemp',
color_continuous_scale="Viridis",
range_color=(0, 12),
scope="usa",
labels={'unemp':'unemployment rate'}
)
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
fig.update_traces(marker_line_width=3.0, marker_opacity=0.6, selector=dict(type='choropleth'))
fig.show()
Docs References
https://plotly.com/python/reference/choropleth/#choropleth-marker
using your code (make sure you format it to be PEP8 compliant for future questions)
sourced some data from kaggle to make this a re-producible example
examples of subunits are counties / regions in a country
import kaggle.cli
import sys, requests
import pandas as pd
from pathlib import Path
from zipfile import ZipFile
import urllib
import plotly.graph_objects as go
from plotly.colors import make_colorscale
# fmt: off
# download data set
url = "https://www.kaggle.com/mohaiminul101/population-growth-annual"
sys.argv = [sys.argv[0]] + f"datasets download {urllib.parse.urlparse(url).path[1:]}".split(" ")
kaggle.cli.main()
zfile = ZipFile(f'{urllib.parse.urlparse(url).path.split("/")[-1]}.zip')
dfs = {f.filename: pd.read_csv(zfile.open(f)) for f in zfile.infolist()}
# fmt: on
dff = dfs["world_population_growth.csv"].rename(
columns={"Country Code": "Code", "2019": "população_%", "Country Name": "Entity"}
)
dff["população_%"] = dff["população_%"] * 100
map_fig_layout = {
"coloraxis_colorbar": {
"title": "População (%)",
"thickness": 20,
"ticklabelposition": "outside bottom",
},
"margin": {"r": 0, "l": 0, "t": 0, "b": 0},
"template": "plotly_dark",
"geo": {
"projection": go.layout.geo.Projection(type="natural earth"),
"resolution": 110,
"visible": True,
},
"uirevision": "not_tracked_key",
}
map_graph = go.Figure(
{
"data": [
go.Choropleth(
locations=dff["Code"],
z=dff["população_%"], # a column of dff
hovertext=dff["Entity"],
zmin=0,
zmax=100,
marker={"line":{"width":4, "color":"black"}},
colorscale=make_colorscale(["#F53347", "#E6C730", "#2FF5A8"]),
)
],
"layout": map_fig_layout,
}
)
map_graph