Skip to content

Add tappable cloud cover override - #27

Open
JWAY21 wants to merge 4 commits into
jackjackbits:mainfrom
JWAY21:feature/cloud-cover-override
Open

Add tappable cloud cover override#27
JWAY21 wants to merge 4 commits into
jackjackbits:mainfrom
JWAY21:feature/cloud-cover-override

Conversation

@JWAY21

@JWAY21 JWAY21 commented Jun 10, 2026

Copy link
Copy Markdown

Summary

The "xx% clouds" label on the home screen is now tappable. Tapping presents a sheet with five preset cloud cover levels; selecting one proportionally adjusts the displayed UV index to match actual observed conditions, overriding the API value.

  • Tappable labelButton wraps the existing cloud/icon HStack; no layout change when inactive
  • 5 override options: ☀️ 0 %, 🌤 25 %, ⛅️ 50 %, 🌥 75 %, 🌧 100 %
  • UV adjustment — uses a non-linear cloud-transmission lookup based on WMO empirical data (UV attenuation is not linear — thick cloud disproportionately blocks UVB):
    • 0 % → 1.00×, 25 % → 0.90×, 50 % → 0.72×, 75 % → 0.45×, 100 % → 0.17× (interpolated between breakpoints)
  • Per-hour clear-sky estimate — back-calculates `clearSkyUV = apiUV / transmission(apiCloudCover)` then applies `clearSkyUV × transmission(override)`. Self-consistent and works correctly at any hour (a daily max ratio approach was evaluated and discarded — the daily peak occurs when clouds are thinnest so the ratio is ≈ 1.0 regardless of current conditions)
  • Override indicator — label turns yellow and shows a ✏️ pencil icon while an override is active
  • Auto-reset — `cloudCoverOverride` is cleared to `nil` on the next successful (or cached) API fetch, so stale overrides don't persist across refreshes
  • "Reset to weather data" destructive button restores original API values immediately

Verified in simulator

At 59 % clouds / UV 8.2:

  • Override 0 % → UV 13.1 ✓ (8.2 ÷ 0.623 × 1.00)
  • Override 100 % → UV 1.4 ✓ (8.2 ÷ 0.623 × 0.17)

Files changed

File Change
`Sources/Services/UVService.swift` `cloudCoverOverride: Double?` published property; `applyCloudOverride(_:)` and `clearCloudOverride()` methods; private `apiUV`/`apiCloudCover` backing store; non-linear WMO transmission lookup
`Sources/Views/ContentView.swift` Cloud `HStack` → `Button`; `.confirmationDialog` on parent `VStack`; override indicator; `showCloudOverridePicker` state

Test plan

  • Tap cloud label → confirmation dialog appears with 5 options + message
  • Select 0 % clear sky → UV increases above API value, label turns yellow with ✏️
  • Select 100 % overcast → UV drops to ~17 % of API value
  • "Reset to weather data" → original API value restored, label back to grey
  • Force-refresh → override clears automatically
  • No override active → no yellow tint, no ✏️, UI unchanged

🤖 Generated with Claude Code

JWAY21 and others added 4 commits June 10, 2026 12:42
Tapping the "xx% clouds" label on the home screen now presents a
confirmation dialog with five presets (0/25/50/75/100 %). Selecting one
stores the choice in a new `cloudCoverOverride` published property and
proportionally adjusts `currentUV` using a linear cloud-transmission
model (0 % → 1.00×, 100 % → 0.15×), back-calculated through the
API cloud cover at fetch time. The override clears automatically on the
next successful API fetch. When active the label turns yellow and shows
a pencil indicator; a "Reset to weather data" option restores the
original values.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move .confirmationDialog from the cloud Button to its parent VStack so
it reliably presents on iOS. Also add .contentShape(Rectangle()) and
vertical padding to the Button label to give a larger tap target.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two improvements to the cloud cover override calculation:

1. Anchor to real clear-sky UV — the API returns uvIndexClearSkyMax
   alongside uvIndexMax. The ratio between them gives the actual cloud
   transmission factor Open-Meteo computed, so we scale the current
   hourly UV by (clearSkyMax / cloudMax) to get an accurate clear-sky
   hourly estimate, rather than back-calculating through a guessed model.

2. Non-linear transmission lookup — cloud cover vs UV attenuation is
   not linear (WMO empirical data: 50% clouds ≈ 28% reduction, not 42%).
   Replace the linear formula with a 5-point interpolated lookup:
   0%→1.00, 25%→0.90, 50%→0.72, 75%→0.45, 100%→0.17.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… ratio

The uvIndexClearSkyMax/uvIndexMax daily ratio was ≈1.0 because the daily
peak UV typically occurs when clouds are thinnest — making the ratio useless
for non-peak hours. Drop the clearSkyUV stored property and instead compute
clear-sky on demand: clearSkyUV = apiUV / cloudTransmission(apiCloudCover).
Self-consistent with the override table and works correctly per hour.

Result: at 59% cloud / UV 8.2, selecting 0% now gives UV 13.1 (correct),
and 100% gives UV 1.4 (correct). Previously 0% was stuck at 8.2.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant