Environment view¶
| Garmin | Wear OS | Apple Watch | |
|---|---|---|---|
| Status | ✅ | ✅ | ⏳ |
| Code | source/views/EnvironmentView.mc, source/TideService.mc, source/MoonCalc.mc, source/SunCalc.mc |
wear/…/presentation/views/EnvironmentView.kt, core/…/SunCalc.kt (sun and moon), wear/…/recording/TripEngine.kt (applyTides) |
— |
Verified against code: 2026-10-18
Purpose¶
Instrument screen 3. Everything about the surroundings in one glance: how far the day (or night) has run, the time, sunrise and sunset, the position in the format of a paper log, the next tides, the barometer with its recent trend, and the moon. It is the third screen after the Dashboard and the Track view (input.md). It takes no input of its own.
The maths behind the values (sunrise / sunset, moon phase, barometer sampling) is in shared/calculations.md. Tides come from the server with each upload (shared/track-sync.md). Fonts and base colours are in visual-system.md.
Layout / behaviour¶
The screen is black (0x000000). From the top:
- Solar arc around the upper rim
- Clock
- Sunrise / sunset (two lines)
- Position
- Tide pill
- Barometer and trend chart
- Moon phase
Rows are placed from measured font heights, not from a pixel grid (see Vertical layout). All rows below the clock use one font: the row font (Garmin FONT_XTINY; Wear OS Roboto Condensed Bold at 7.5 % of the screen width).
Colours¶
| Name | Hex | Used for |
|---|---|---|
| Arc track | 0x383838 |
Unlit part of the solar arc |
| Day amber | 0xFBBF24 |
Lit arc and pin by day, RISE / SET captions |
| Night blue | 0x60A5FA |
Lit arc and pin by night |
| Trend emerald | 0x10B981 |
Barometer trend bars |
| Divider | 0x444444 |
Sunrise/sunset divider, tide pill outline and dividers, moon outline |
| Tide cyan | 0x22D3EE |
Tide wave symbol; tide times when the symbol is dropped |
| Tide range cyan | 0x06B6D4 |
Tide range |
| Muted | 0xD4D4D4 |
Tide times, moon phase name, position without a fix |
| Card | 0x181818 |
Tide pill fill, moon disc base |
| Moonlight | 0xE0E0E0 |
Lit part of the moon disc |
| White | 0xFFFFFF |
Clock, sunrise/sunset times, position, pressure |
Solar arc¶
| Element | Position | Size | Colour | Content | Constraints |
|---|---|---|---|---|---|
| Arc track | Centred on the screen, radius 0.475 × width | Pen width/70 (at least 2 px) | Arc track | From −100° to +100°, 0° = 12 o'clock, clockwise | Always drawn |
| Lit arc | Same circle | Same pen | Day amber by day, night blue by night | From −100° to the current angle | — |
| Pin | On the arc at the current angle | Radius pen + width/120 + 1, with a 1 px black halo | Same as the lit arc | Filled dot | Always drawn |
- The arc spans 200°: it starts and ends 10° below the horizontal centre line.
- Day is from sunrise to sunset (local time, minute resolution). Progress = (now − sunrise) / (sunset − sunrise).
- Night is from sunset to the next sunrise. Progress = time since sunset / night length.
- The current angle is −100° + 200° × progress. The lit part grows clockwise from the left end.
- The pin is a plain dot in both cases (no sun or moon glyph).
- Without a fix, or when the sun does not rise or set that day, the arc uses 06:00 and 18:00.
Clock¶
| Element | Position | Font | Colour | Content & format |
|---|---|---|---|---|
| Clock | x 0.50, y 0.12 (rounded), centred | Numerals font | White | Local time HH:MM, 24 h, hour zero-padded, e.g. 09:05 |
The clock sits at the same height and in the same font as the clock on the Dashboard.
Sunrise / sunset¶
Two columns, caption over value.
| Element | Position | Font | Colour | Content & format | Constraints |
|---|---|---|---|---|---|
RISE caption |
Left column centre, first line | Row font | Day amber | RISE |
— |
SET caption |
Right column centre, first line | Row font | Day amber | SET |
— |
| Sunrise time | Left column, second line | Row font | White | Local HH:MM, zero-padded; --:-- without a fix or when the sun does not rise |
— |
| Sunset time | Right column, second line | Row font | White | Same, --:-- when it does not set |
— |
| Divider | x 0.50, from the top of the caption line to the bottom of the value line | 1 px | Divider | Vertical hairline | Dropped when the columns would reach the middle |
- The column centres sit at x 0.50 ± half of the available half-width, measured at the caption line (the narrower of the two lines, nearer the arc).
- Times come from the on-watch sunrise / sunset calculation for the current position and date, converted to watch time.
Position¶
| Element | Position | Font | Colour | Content & format | Constraints |
|---|---|---|---|---|---|
| Latitude, longitude | One line, centred as a group at x 0.50; latitude left, longitude right, separated by a gap of width/48 + 2 | Row font | White with a fix, muted without | Degrees and decimal minutes: 43°30.123'N 16°26.456'E |
Degrees are not zero-padded. 3, 2 or 1 decimals of minutes, whatever fits |
- Hemisphere letters:
N/S,E/W(NandEfor zero). - The number of decimals is chosen once per screen size: the widest possible text (
00°00.000'Nand000°00.000'W) must fit the drawable width at that row, else one decimal less, down to 1. - Without a fix the row shows
0°00.000'N/0°00.000'E(at the chosen decimals) in muted grey. - "Fix" here means a latitude or longitude other than 0.0. The last known position stays white after GPS is lost.
Tide pill¶
A rounded box centred at x 0.50 with up to three parts: wave symbol, tide times, tide range.
| Element | Position | Font | Colour | Content & format | Constraints |
|---|---|---|---|---|---|
| Pill | Centred at x 0.50; height row height + 2 × (height/90 + 2) | — | Fill card, 1 px outline divider | Rounded rectangle, corner radius width/40 + 2 | Never wider than the drawable width |
| Wave symbol | Left part | Row font | Tide cyan | Three ~ drawn 4 px apart vertically |
Dropped first-to-third (see below) |
| Divider 1, 2 | Between the parts | 1 px | Divider | Short vertical hairlines | Dropped first |
| Tide times | Middle part | Row font | Muted; tide cyan when the wave symbol is dropped | Hi 14:32 Lo 20:47 (two spaces between); tight form Hi14:32 Lo20:47 |
Always shown |
| Tide range | Right part | Row font | Tide range cyan | Metres with one decimal: 0.4m |
Dropped when nothing else fits |
- Placeholders. Until tides arrive:
Hi --:-- Lo --:--and--m. - Where tides come from. Each segment upload sends the current position; the server answers with the next high and low tide time and the range for that position (shared/track-sync.md). The watch shows the time strings exactly as received and formats the range as
%.1f+m. Only the fields that are present are replaced. - When real values show. Only on a paired watch, and only after an upload with a position succeeded: about 15 s after the first fix, then hourly (sync-and-pairing.md). Unpaired watches always show the placeholders.
- Lifetime. Tide values live in memory for the current track. They are not saved: a new track, or resuming a paused track after the app was closed, starts with placeholders again. Resuming from the pause screen keeps them. They are not checked against the clock: a tide time that has passed stays until the next upload replaces it.
Degradation order¶
Layout tries these combinations in order and takes the first whose content plus padding fits the drawable width at the pill's row:
| # | Wave symbol | Dividers | Range | Times |
|---|---|---|---|---|
| 1 | ✓ | ✓ | ✓ | normal |
| 2 | ✓ | — | ✓ | normal |
| 3 | ✓ | — | ✓ | tight |
| 4 | — | — | ✓ | normal |
| 5 | — | — | ✓ | tight |
| 6 | — | — | — | normal |
| 7 | — | — | — | tight |
The gap between parts is width/48 + 2. If even option 7 is too wide, the pill's own side padding shrinks (down to 1 px), so the box stays inside the chord even when its text doesn't.
Barometer and trend chart¶
| Element | Position | Font / size | Colour | Content & format | Constraints |
|---|---|---|---|---|---|
| Pressure | Left part of a centred group | Row font | White | Whole hPa: 1013 hPa; -- hPa without a reading |
Width reserved for 1013 hPa |
| Trend chart | Right part, width/48 + 2 after the pressure | 10 bars, each width/80 wide (at least 2 px), gap width/240 (at least 1 px), up to height/14 tall | Trend emerald | One bar per sample, oldest left, newest right | Always 10 bar slots |
- The pressure text and the chart together are centred at x 0.50. The bars stand on a common baseline (bottom of the row).
- The chart shows the last 10 barometer samples (at most). The sampling interval grows with the track's age: every 2 min for the first 30 min, every 5 min until 2 h, then every 10 min (shared/calculations.md). So the 10 bars cover 20 min to 100 min.
- Bar height = full height × (sample − lowest of the shown samples) / span, where span is the highest minus the lowest, but at least 2 hPa. Each sampled bar is at least 15 % tall, so the lowest sample stays visible.
- Slots without a sample yet are drawn at 40 % height, as placeholders.
- There is no trend arrow on this screen; the arrow is on the Dashboard.
Moon phase¶
| Element | Position | Size | Colour | Content & format | Constraints |
|---|---|---|---|---|---|
| Moon disc | Left part of a centred group | Radius row height / 2.2 (at least 4 px) | Base card, 1 px outline divider, lit part moonlight | Lit part from the phase: waxing lit on the right, waning lit on the left | Never dropped |
| Phase name | width/48 + 2 right of the disc | Row font | Muted | Uppercase name, full or short | Short name when the full one does not fit |
| Full name | Short name |
|---|---|
NEW MOON |
NEW |
WAXING CRESCENT |
WAX CRESC |
FIRST QUARTER |
FIRST QTR |
WAXING GIBBOUS |
WAX GIBB |
FULL MOON |
FULL |
WANING GIBBOUS |
WAN GIBB |
LAST QUARTER |
LAST QTR |
WANING CRESCENT |
WAN CRESC |
The choice between full and short name is made on every redraw, for the current phase only: phases whose full name fits keep it. Phase boundaries are in shared/calculations.md.
Vertical layout¶
Every row is as tall as its font measures; the height left over is shared out as equal gaps.
| Row | Height |
|---|---|
| Clock | Fixed centre at y 0.12; bottom = 0.12 × height + half the numerals font height |
| Sunrise / sunset | 2 × row height |
| Position | Row height |
| Tide pill | Row height + 2 × (height/90 + 2) |
| Barometer | The larger of row height and bar height (height/14) |
| Moon | The larger of row height and moon disc diameter |
- Space for the five rows below the clock = (height − bottom margin of 0.07 × height) − clock bottom.
- Gap = (space − sum of the five row heights) / 5, clamped to at least height/90 + 1 and at most height/25. One gap sits between the clock and the first row and between each row; the bottom margin takes the rest.
- Drawable width at a row is the narrower of the round screen's chord and the inside of the solar arc (arc radius minus pen minus width/60), taken at whichever edge of the row is further from the centre line. Below the arc's ends (centre line + 0.174 × arc radius) only the chord counts.
- Layout is computed once per screen size and cached; the screen redraws every second.
What gives way on small screens¶
No row is ever dropped. When content is too wide, each row degrades on its own:
| Row | Gives way by |
|---|---|
| Sunrise / sunset | Divider dropped |
| Position | Decimals of minutes 3 → 2 → 1 |
| Tide pill | Dividers, then tight times, then wave symbol, then range (table above); then pill padding |
| Moon phase | Short name; the disc always stays |
| Barometer, clock | Never degrade |
Garmin¶
- Layout self-check. Debug builds print every computed row, its width against the drawable width, collisions, and which degradations fired (
EnvironmentView.reportLayout). Run it on one device per screen family after changing the layout:monkeydo build.prg fenix5 | grep -E "OVERFLOWS|COLLIDES|OFF SCREEN". Release builds compile it out. - Fonts: the clock uses the generated numerals sheet (Roboto Condensed Bold, digits at 9 % of the width); rows use the system
FONT_XTINY.
Wear OS¶
- No layout self-check output.
- Fonts: numerals sheet emulated as Roboto Condensed Bold at 9 % of the width; row font Roboto Condensed Bold at 7.5 % of the width.
- Watches without a barometer show
-- hPaand ten placeholder bars.
Apple Watch¶
⏳ Same screen, as planned in docs/apple_watch_port_plan.md. Pressure from CMAltimeter (kPa, shown in hPa). Tides from the upload response, as on the other platforms.
States and edge cases¶
| Situation | What the screen shows |
|---|---|
| No fix yet | Arc based on 06:00 / 18:00; --:-- for sunrise and sunset; position 0°00.000'N 0°00.000'E in muted grey; moon and clock as normal |
| Polar day or night (sun does not cross the horizon) | That time shows --:--; the arc uses 06:00 / 18:00 for it |
| Fix lost after having one | Last known position stays, in white; sunrise / sunset for that position |
| No barometer reading (0 or none) | -- hPa; chart keeps its last samples, or placeholders if there are none |
| Pressure flat | Bars scale to a span of at least 2 hPa, so a flat trend shows as short, even bars |
| Fewer than 10 samples | Missing slots at 40 % height on the right |
| Unpaired watch, or no upload yet | Tide placeholders Hi --:-- Lo --:--, --m |
| Server answer without tides | Previous tide values (or placeholders) stay |
| Smallest screens (218 px) | Degradations as in the table above; the moon disc is never dropped |
Known deviations¶
- Wear OS — barometer hidden on watches without one. The Wear OS port plan says watches without a barometer hide the barometer. The code shows
-- hPaand placeholder bars instead. Code behaviour described above. - Both — position without a fix. The position row shows
0°00.000'N 0°00.000'Ein muted grey instead of a clear "no fix" placeholder. It is a valid-looking coordinate. - Both — stale tides. Tide times are not compared with the clock, so a past high or low tide stays visible for up to an hour.