bitsofchris Claude Opus 4.7 (1M context) commited on
Commit
24b9903
Β·
1 Parent(s): f51c526

Hero gauges: switch to horizontal thermometer bars (mobile-friendly)

Browse files

The three radial dial gauges packed badly on portrait phones β€” they
shrank to thumbnail size with unreadable needles and values. Replace
with three stacked horizontal bars on a shared 20-100Β°F scale:

- πŸ“‘ Ecowitt (now) β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘ 63.5Β°F
- πŸ€– Toto (next hr) β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘ 64.3Β°F (+0.8)
- 🌎 NWS (next hr) β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ 61.0Β°F (-2.5)

Same cool/warm background bands as before so the eye still reads
'where on the temperature spectrum'. Bar color matches each source's
chart trace. Delta vs current temperature annotated in muted gray on
the right of each forecast bar. Bars take the full width of the
container so they stay legible at any breakpoint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Files changed (1) hide show
  1. src/weather_ui.py +63 -36
src/weather_ui.py CHANGED
@@ -186,46 +186,73 @@ def hero_gauges(
186
  nws_next: float | None,
187
  temp_range: tuple[float, float] = (20.0, 100.0),
188
  ) -> go.Figure:
189
- """Three side-by-side gauges: current Ecowitt temperature, plus each
190
- model's prediction for the next round hour. Each forecast gauge also
191
- shows its delta vs the current reading."""
192
- cool_to_warm = [
193
- {"range": [20, 40], "color": "rgba(31,119,180,0.18)"},
194
- {"range": [40, 60], "color": "rgba(31,119,180,0.08)"},
195
- {"range": [60, 80], "color": "rgba(214,39,40,0.08)"},
196
- {"range": [80, 100], "color": "rgba(214,39,40,0.18)"},
197
  ]
198
- specs = [[{"type": "indicator"}, {"type": "indicator"}, {"type": "indicator"}]]
199
- fig = make_subplots(rows=1, cols=3, specs=specs)
200
-
201
- def _ind(value, title, bar_color, with_delta: bool):
202
- ind = go.Indicator(
203
- mode="gauge+number+delta" if with_delta else "gauge+number",
204
- value=value if value is not None else float("nan"),
205
- title={"text": title, "font": {"size": 14}},
206
- number={"suffix": " Β°F", "font": {"size": 30}},
207
- gauge=dict(
208
- axis=dict(range=list(temp_range), tickwidth=1, tickcolor="#888"),
209
- bar=dict(color=bar_color, thickness=0.25),
210
- bgcolor="white",
211
- borderwidth=1,
212
- bordercolor="#e0e0e0",
213
- steps=cool_to_warm,
214
- threshold=dict(line=dict(color=bar_color, width=4), value=value or 0),
215
- ),
216
- delta=(
217
- dict(reference=cur_temp, suffix=" Β°F", increasing={"color": "#d62728"}, decreasing={"color": "#1f77b4"})
218
- if with_delta else None
219
- ),
220
  )
221
- return ind
222
 
223
- fig.add_trace(_ind(cur_temp, "πŸ“‘ Ecowitt (now)", "#222", False), row=1, col=1)
224
- fig.add_trace(_ind(toto_next, "πŸ€– Toto (next hour)", "#1f77b4", True), row=1, col=2)
225
- fig.add_trace(_ind(nws_next, "🌎 NWS (next hour)", "#d62728", True), row=1, col=3)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  fig.update_layout(
227
- height=260,
228
- margin=dict(l=10, r=10, t=50, b=10),
 
 
229
  paper_bgcolor="rgba(0,0,0,0)",
230
  )
231
  return fig
 
186
  nws_next: float | None,
187
  temp_range: tuple[float, float] = (20.0, 100.0),
188
  ) -> go.Figure:
189
+ """Three horizontal bar 'thermometers' stacked vertically β€” readable
190
+ on mobile portrait without squishing. Cool/warm background bands
191
+ give a sense of where on the temperature scale each value sits."""
192
+ rows = [
193
+ ("πŸ“‘ Ecowitt (now)", cur_temp, "#222", None),
194
+ ("πŸ€– Toto (next hr)", toto_next, "#1f77b4", cur_temp),
195
+ ("🌎 NWS (next hr)", nws_next, "#d62728", cur_temp),
 
196
  ]
197
+
198
+ fig = go.Figure()
199
+ lo, hi = temp_range
200
+ # Background shading: cooler on the left, warmer on the right.
201
+ for x0, x1, color in [
202
+ (lo, lo + (hi - lo) * 0.25, "rgba(31,119,180,0.18)"),
203
+ (lo + (hi - lo) * 0.25, lo + (hi - lo) * 0.50, "rgba(31,119,180,0.08)"),
204
+ (lo + (hi - lo) * 0.50, lo + (hi - lo) * 0.75, "rgba(214,39,40,0.08)"),
205
+ (lo + (hi - lo) * 0.75, hi, "rgba(214,39,40,0.18)"),
206
+ ]:
207
+ fig.add_shape(
208
+ type="rect",
209
+ x0=x0, x1=x1, y0=-0.5, y1=len(rows) - 0.5,
210
+ fillcolor=color, line=dict(width=0),
211
+ layer="below",
 
 
 
 
 
 
 
212
  )
 
213
 
214
+ y_labels = [r[0] for r in rows]
215
+ for label, value, color, ref in rows:
216
+ if value is None or (isinstance(value, float) and value != value):
217
+ # No data β€” skip the bar but keep the row label by drawing 0-length.
218
+ fig.add_trace(go.Bar(
219
+ y=[label], x=[lo], orientation="h",
220
+ marker_color="rgba(0,0,0,0)",
221
+ text=["β€”"], textposition="outside",
222
+ textfont=dict(size=14, color="#888"),
223
+ showlegend=False, hoverinfo="skip",
224
+ ))
225
+ continue
226
+ suffix = ""
227
+ if ref is not None:
228
+ d = value - ref
229
+ sign = "+" if d >= 0 else ""
230
+ suffix = f" <span style='color:#888'>({sign}{d:.1f})</span>"
231
+ fig.add_trace(go.Bar(
232
+ y=[label], x=[value - lo], base=[lo], orientation="h",
233
+ marker=dict(color=color, line=dict(color=color, width=0)),
234
+ text=[f"<b>{value:.1f}Β°F</b>{suffix}"],
235
+ textposition="outside",
236
+ textfont=dict(size=14),
237
+ cliponaxis=False,
238
+ showlegend=False,
239
+ hovertemplate=f"{label}: %{{x:.1f}}Β°F<extra></extra>",
240
+ ))
241
+
242
+ fig.update_xaxes(
243
+ range=[lo, hi], title_text="Β°F",
244
+ showgrid=True, gridcolor="rgba(0,0,0,0.08)",
245
+ zeroline=False,
246
+ )
247
+ fig.update_yaxes(
248
+ categoryorder="array", categoryarray=list(reversed(y_labels)),
249
+ showgrid=False,
250
+ )
251
  fig.update_layout(
252
+ height=240,
253
+ margin=dict(l=130, r=90, t=20, b=40),
254
+ bargap=0.35,
255
+ plot_bgcolor="rgba(0,0,0,0)",
256
  paper_bgcolor="rgba(0,0,0,0)",
257
  )
258
  return fig