We have a Panel app that has two linked DynamicMaps that have gridded data with point observation data overlayed. The data has 3 different output fields from the model (severity, probability, and SLD) and we have a parameter to cycle between these. We’ve also defined the colors, tick labels, and ticks for the colorbar for each field. However, when we run the app, all of the fields change appropriately and the colorbar changes to have the correct colors, but it will not change the ticks or tick labels.
COLORS = {
“Severity”: [“#FFFFFF”, “#CCFFFF”, “#98CCFF”, “#6699FE”, “#3333FE”],
“Probability”: [
“#FFFFFF”,
“#CCFFFF”,
“#99FFCC”,
“#99FF66”,
“#CCFF66”,
“#FFFF00”,
“#FFCC00”,
“#FF9900”,
“#FF6600”,
],
“SLD”: [“#FFFFFF”, “#FFA500”, “#FF0000”],
}
BOUNDS = {
“Severity”: [0, 1, 2, 3, 4, 5],
“Probability”: [0.0, 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 1.0],
“SLD”: [0.0, 0.05, 0.25, 1.0],
}
CLIMS = {
“Severity”: (0, 5),
“Probability”: (0, 1),
“SLD”: (0, 1),
}
CTICKS = {
“Severity”: [0.5, 1.5, 2.5, 3.5, 4.5],
“Probability”: [0, 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 1],
“SLD”: [0, 0.05, 0.25, 1],
}
field = param.ObjectSelector(
default=“Severity”, objects=[“Severity”, “Probability”, “SLD”]
)
lead = param.ObjectSelector(default=3, objects=[3, 6, 9, 12, 15, 18])
map_time = param.Selector(default=datetime.datetime(2024, 10, 28, 21))
level = param.Selector(
default=6000, objects=list(np.arange(1000, 31000, 1000, dtype=int))
)
deps = [“field”, “lead”, “map_time”, “level”]
def __init__(self, **params):
"""
View product fields
"""
super().__init__(**params)
# self.colorbar_opts = self.get_colorbar_opts()
self.plot = self.create_plot()
def get_colorbar_opts(self):
"""Dynamically generate colorbar options based on selected field."""
field = self.field
ct = CTICKS[field]
if field == "Severity":
ticks = [
(0.5, "No Icing"),
(1.5, "Trace"),
(2.5, "Light"),
(3.5, "Moderate"),
(4.5, "Heavy"),
]
print(ticks)
return ticks
# return {
# "major_label_overrides": {
# 0.5: "No Icing",
# 1.5: "Trace",
# 2.5: "Light",
# 3.5: "Moderate",
# 4.5: "Heavy",
# },
# "ticker": FixedTicker(ticks=ct),
# }
else:
ticks = [(tick, f"{tick}") for tick in ct]
print(ticks)
return ticks
# return {
# "major_label_overrides": {tick: f"{tick}" for tick in ct},
# "ticker": FixedTicker(ticks=ct),
# }
@param.depends(*deps, watch=True)
def dynamic_plot(self):
"""
The mapping function for the dynamic plots.
"""
def plot_limits(plot):
"""
plot limits
"""
plot.handles["x_range"].min_interval = 10
plot.handles["x_range"].max_interval = 1299
plot.handles["y_range"].min_interval = 10
plot.handles["y_range"].max_interval = 919
valid = self.map_time
level = self.level
field = self.field
lead = self.lead
ds1, ds2 = self.prepare_data(field, lead)
colorbar_opts = self.get_colorbar_opts()
img1 = hv.Image(ds1).opts(hv.opts.Image(aspect="equal"))
img1.opts(
width=WIDTH,
cmap=COLORS[field],
color_levels=BOUNDS[field],
clim=CLIMS[field],
colorbar=True,
# cticks=colorbar_opts,
# colorbar_opts=colorbar_opts,
colorbar_position="top",
)
img2 = hv.Image(ds2).opts(hv.opts.Image(aspect="equal"))
img2.opts(
width=WIDTH,
cmap=COLORS[field],
color_levels=BOUNDS[field],
clim=CLIMS[field],
colorbar=True,
# cticks=colorbar_opts,
colorbar_position="top",
# colorbar_opts=colorbar_opts,
hooks=[plot_limits],
)
# load and plot coastline paths
paths = list(
np.load(
"user-path",
allow_pickle=True,
)
)
coasts = hv.Path(paths).opts(
xlim=(0, 1299), ylim=(0, 919), aspect="equal", color="black"
)
subtitle = f"Valid Time: {valid} | Lead: {lead} | Level: {level:,} ft"
pirep_overlay, tamdar_overlay, metars_overlay = self.get_obs()
plot1 = img1 * coasts * pirep_overlay * metars_overlay * tamdar_overlay
plot1.opts(
title=f"IPA-F {field}\n{subtitle}",
xlabel="",
ylabel="",
fontsize=FONTSIZE,
xticks=0,
yticks=0,
legend_position="bottom",
legend_opts={"click_policy": "hide", "ncols": 5},
)
plot2 = img2 * coasts * pirep_overlay * metars_overlay * tamdar_overlay
plot2.opts(
title=f"FIP2-AK {field}\n{subtitle}",
xlabel="",
ylabel="",
fontsize=FONTSIZE,
xticks=0,
yticks=0,
legend_position="bottom",
legend_opts={"click_policy": "hide", "ncols": 5},
)
return plot1 + plot2
def create_plot(self):
"""
Create the map
"""
return hv.DynamicMap(self.dynamic_plot, kdims=[])
Some of the code is commented out, representing our attempts to make the updated colorbar use the ticks and labels we want. But so far, only the first load has the correct labels for the severity field. When we change the “field” parameter, the colorbar will change to the right colors and spacing, but will retain the first set of ticks and labels (ex: 0.5 on probability is labeled “No Icing”, there are no other labels or ticks present).
All help appreciated.