I'm working on a Python project where I'm plotting a bar chart on top of a basemap. My original goal is to create a visualization that mimics a 3D bar chart by plotting vertical bars at specific geographical coordinates to represent values.
So far, I’ve managed to get the bars to plot correctly, but I’m struggling to add value labels (i.e., numeric labels on top of each bar) so the plot is more informative.
Here’s a simplified version of my current code. It generates the plot, but the labels are missing:
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Choroplethmapbox(
geojson=nys_hsa.geometry.__geo_interface__,
locations=nys_hsa.index,
z=[0]*len(nys_hsa), # dummy values
colorscale=[[0, "white"], [1, "white"]],
showscale=False,
marker_opacity=0.2,
marker_line_width=1,
marker_line_color='black',
hoverinfo="skip", # no hover needed for the base map
name="HSA Zones"
))
bar_offset = 0.05
bar_colors = {'Major': 'red', 'Moderate': 'orange', 'Minor': 'green'}
bar_height_scale = 0.05 # converts percentage to degrees latitude
bar_line_width = 10 # thicker bars for clarity
legend_flags = {'Major': False, 'Moderate': False, 'Minor': False}
for _, row in nys_hsa.iterrows():
lon = row['lon']
lat = row['lat']
for i, category in enumerate(['Major', 'Moderate', 'Minor']):
offset = (i - 1) * bar_offset
value = row[category]
bar_height = value * bar_height_scale
# Draw vertical bar
fig.add_trace(go.Scattermapbox(
mode="lines",
lon=[lon + offset, lon + offset],
lat=[lat, lat + bar_height],
line=dict(width=bar_line_width, color=bar_colors[category]),
hoverinfo="text",
text=f"{row['RegionName']}<br>{category}: {value:.2f}%",
showlegend=not legend_flags[category],
name=category
))
legend_flags[category] = True
fig.add_trace(go.Scattermapbox(
mode="markers+text",
lon=[lon + offset],
lat=[lat + bar_height + 0.015], # slightly above bar tip
text=[f"{value:.1f}%"],
textposition="top center",
textfont=dict(size=12, color=bar_colors[category], family="Arial"),
marker=dict(size=1, color=bar_colors[category]), # invisible marker to anchor text
showlegend=False,
hoverinfo="skip"
))
fig.update_layout(
title="New York State HSA Zones with Risk Bar Charts",
mapbox_style="carto-positron",
mapbox_zoom=5.5,
mapbox_center={"lat": nys_hsa['lat'].mean(), "lon": nys_hsa['lon'].mean()},
height=800,
margin=dict(l=0, r=0, b=0, t=50),
legend=dict(
title="Risk Level",
orientation="v",
yanchor="top",
y=0.95,
xanchor="right",
x=1.02,
bgcolor="rgba(255,255,255,0.8)",
bordercolor="black",
borderwidth=1,
itemsizing="trace"
),
showlegend=True
)
fig.show()
This is the current plot image
Sample data can be found here
I don't know why, but it looks like passing textposition="center"
to the go.Scattermapbox
trace is causing plotly to not render the text, even though this should be a supported argument according to the documentation. You also don't need to create an invisible marker, you can just use mode="text"
:
fig.add_trace(go.Scattermapbox(
mode="text",
lon=[lon + offset],
lat=[lat + bar_height + 0.10],
text=[f"{value:.1f}%"],
textfont=dict(size=12, color="black"),
showlegend=False,
hoverinfo="skip",
))