diff options
| author | Claudomator Agent <agent@claudomator> | 2026-03-13 19:42:05 +0000 |
|---|---|---|
| committer | Claudomator Agent <agent@claudomator> | 2026-03-13 19:42:05 +0000 |
| commit | 7e40bd03ab0246552d26d92fda8623b8da4653f3 (patch) | |
| tree | 7e28097068e6d96d8b4d0e786be3d7725784ddb0 | |
| parent | 0b3fc43df0f522a9d2b308c79b35870e3afb08db (diff) | |
docs: add implementation plan for weather/wind map feature
Plans wind/current overlay and 7-day forecast panel using:
- Open-Meteo API (no key required) for weather and marine data
- MapLibre GL Android for map display with wind-arrow symbol layer
- MVVM architecture (ViewModel + Repository + Retrofit)
- TDD workflow documented with step-by-step test-first approach
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| -rw-r--r-- | SESSION_STATE.md | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/SESSION_STATE.md b/SESSION_STATE.md new file mode 100644 index 0000000..db54207 --- /dev/null +++ b/SESSION_STATE.md @@ -0,0 +1,200 @@ +# SESSION STATE + +**Current Task Goal:** Add wind/current map overlay and weather forecast display that loads on application startup. + +**Status:** [IN_PROGRESS] — Planning complete, awaiting user approval to implement. + +--- + +## Project Context + +- **Platform:** Android (Kotlin), API 24+, compileSdk 34 +- **Build system:** Gradle (Groovy DSL) +- **Location:** `android-app/` +- **Current state:** Empty project skeleton (no source code, only build files) +- **Design doc:** `docs/COMPONENT_DESIGN.md` + +--- + +## Completed Items + +- [x] Explored project structure +- [x] Read COMPONENT_DESIGN.md — full architecture documented +- [x] Identified UI framework: MapLibre GL Native (Android) +- [x] Identified weather data sources (see below) +- [x] Written implementation plan (this document) + +--- + +## Data Sources + +| Data | Source | Auth | +|------|--------|------| +| Wind forecast (10m, kt) | Open-Meteo API (`api.open-meteo.com`) | None (free) | +| Weather forecast (temp, precip, code) | Open-Meteo API | None (free) | +| Marine / ocean currents | Open-Meteo Marine API (`marine-api.open-meteo.com`) | None (free) | +| Base map tiles | MapLibre GL + OpenStreetMap tiles (free CDN) | None | + +Open-Meteo is chosen because it requires no API key, covers global marine/weather data, and returns JSON that's easy to parse. The design doc also lists NOAA and Windy as future sources; those can be swapped in later via the repository abstraction. + +### Key API endpoints + +**Weather forecast:** +``` +GET https://api.open-meteo.com/v1/forecast + ?latitude={lat}&longitude={lon} + &hourly=windspeed_10m,winddirection_10m,temperature_2m, + precipitation_probability,weathercode + &forecast_days=7&wind_speed_unit=kn +``` + +**Marine/current:** +``` +GET https://marine-api.open-meteo.com/v1/marine + ?latitude={lat}&longitude={lon} + &hourly=wave_height,wave_direction,ocean_current_velocity, + ocean_current_direction + &forecast_days=7 +``` + +--- + +## Architecture + +``` +Presentation + MainActivity (single-activity host) + ├── MapFragment — MapLibre map + wind-arrow overlay + └── ForecastFragment — 7-day forecast list + +ViewModel (lifecycle-aware state) + MainViewModel + ├── uiState: StateFlow<UiState> (Loading | Success | Error) + ├── windArrows: StateFlow<List<WindArrow>> + └── forecast: StateFlow<List<ForecastItem>> + +Repository + WeatherRepository + ├── fetchWeather(lat, lon): WeatherData + └── fetchMarine(lat, lon): MarineData + +Data / API + WeatherApiService (Retrofit — Open-Meteo weather endpoint) + MarineApiService (Retrofit — Open-Meteo marine endpoint) + ApiClient (OkHttp singleton, base URL config) + +Models + WeatherData, MarineData (API response data classes) + WindArrow (lat, lon, speed_kt, direction_deg) + ForecastItem (time, wind_kt, wind_dir, temp_c, precip_pct, icon) +``` + +**Startup flow:** +1. MainActivity.onCreate → MainViewModel.init +2. ViewModel launches coroutine → FusedLocationProviderClient.lastLocation +3. Location → WeatherRepository.fetchWeather(lat, lon) and .fetchMarine(lat, lon) in parallel +4. Results → StateFlow updates → UI observes: + - MapFragment draws wind arrows at sampled grid points (current hour) + - ForecastFragment renders RecyclerView with 7-day items + +--- + +## Implementation Plan (TDD) + +### Step 1 — Project dependencies (`android-app/app/build.gradle`) +Add: +- `org.maplibre.gl:android-sdk:10.0.2` +- `com.squareup.retrofit2:retrofit:2.9.0` +- `com.squareup.retrofit2:converter-moshi:2.9.0` +- `com.squareup.moshi:moshi-kotlin:1.15.0` +- `com.squareup.okhttp3:okhttp:4.12.0` +- `org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3` +- `androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0` +- `androidx.lifecycle:lifecycle-runtime-ktx:2.7.0` +- `com.google.android.gms:play-services-location:21.2.0` +- `androidx.fragment:fragment-ktx:1.6.2` +- `androidx.recyclerview:recyclerview:1.3.2` +- Test deps: `mockk:1.13.9`, `kotlinx-coroutines-test:1.7.3`, `turbine:1.1.0` + +### Step 2 — AndroidManifest.xml +- INTERNET, ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION permissions +- Declare MainActivity as launcher activity + +### Step 3 — Models (`data/model/`) +**Files:** +- `WindArrow.kt` — `data class WindArrow(val lat: Double, val lon: Double, val speedKt: Double, val directionDeg: Double)` +- `ForecastItem.kt` — hourly weather item +- `WeatherResponse.kt`, `MarineResponse.kt` — Moshi-annotated API response classes + +**Tests (red first):** +- `WindArrowTest.kt` — parse correctly, edge cases (0 kt, 360°) + +### Step 4 — API services (`data/api/`) +**Files:** +- `WeatherApiService.kt` — Retrofit interface with `@GET("v1/forecast")` +- `MarineApiService.kt` — Retrofit interface with `@GET("v1/marine")` +- `ApiClient.kt` — Retrofit singleton factory + +**Tests:** +- `WeatherApiServiceTest.kt` — mock HTTP server (MockWebServer), verify request URL + parse response + +### Step 5 — Repository (`data/repository/WeatherRepository.kt`) +- Coordinates parallel API calls via `async { }` + `awaitAll()` +- Maps raw API responses → domain models (WindArrow, ForecastItem) +- Returns `Result<T>` to propagate errors cleanly + +**Tests:** +- `WeatherRepositoryTest.kt` — mock services, verify data mapping and error handling + +### Step 6 — ViewModel (`ui/MainViewModel.kt`) +- Exposes `uiState: StateFlow<UiState>`, `windArrows`, `forecast` +- On init: request location permission → fetch data + +**Tests:** +- `MainViewModelTest.kt` — mock repository, test Loading/Success/Error states with Turbine + +### Step 7 — Layouts +- `res/layout/activity_main.xml` — BottomNavigationView + FragmentContainerView +- `res/layout/fragment_map.xml` — MapLibre MapView (fullscreen) +- `res/layout/fragment_forecast.xml` — RecyclerView for forecast list +- `res/layout/item_forecast.xml` — single forecast row + +### Step 8 — MainActivity + Fragments +- `MainActivity.kt` — sets up bottom nav, fragment back-stack +- `MapFragment.kt` — initialises MapLibre, adds wind-arrow symbol layer on map ready +- `ForecastFragment.kt` — observes ViewModel.forecast, drives RecyclerView adapter + +### Step 9 — Wind Overlay on Map +- Load wind data for current hour from `windArrows` +- Sample to a sparse grid (~50 points visible on screen) +- Add MapLibre `SymbolLayer` with rotated arrow icons +- Arrow rotation = wind direction; size/opacity proportional to speed + +--- + +## Next 3 Specific Steps (after approval) + +1. **`android-app/app/build.gradle`** — add all dependencies listed in Step 1; enable `buildFeatures { viewBinding true }` +2. **`android-app/app/src/main/AndroidManifest.xml`** — create with INTERNET + LOCATION permissions +3. **Write failing tests for `WindArrow` model** — `src/test/kotlin/.../data/model/WindArrowTest.kt` + +--- + +## Constraints & Notes + +- No API keys needed (Open-Meteo is free/open). +- MapLibre GL requires a style URL; use MapLibre's free demo style or OSM raster tiles. +- Wind arrows will use a bundled SVG/PNG asset (arrow icon) for the symbol layer. +- Location: request at runtime (Android 6+ permission model). +- Min SDK 24 means no Jetpack Compose (needs API 21+, but project uses XML views). +- All implementation follows TDD: write failing test → implement → green. + +--- + +## Scripts Added + +_(none yet)_ + +## Process Improvements + +_(none yet)_ |
