Rundet das Altitude-Tab um ein reales Gelaendeprofil ab, damit sichtbar wird, ob die geplante Flughoehe ausreichend Bodenabstand haelt: - domain/mission/terrain_math.dart: reine Sampling-/Geometrie-Funktionen (routeKeyForTerrain fuer Caching, distanzbasiertes Sampling alle 30m, lineare Sollhoehen-Interpolation, terrainDangerRanges fuer Abschnitte mit < 10m Bodenabstand) - unabhaengig testbar ohne Netzwerk/UI. - services/terrain/terrain_service.dart: laedt AWS-Terrarium-PNG-Kacheln (elevation-tiles-prod, zoom 13) und dekodiert sie ueber dart:ui (instantiateImageCodec/toByteData), ohne zusaetzliches Bildpaket. Elevation = R*256 + G + B/256 - 32768 pro Pixel; Kacheln werden pro Route nur einmal geladen (nach Kachel gruppierte Sample-Punkte). - ui/providers/terrain_provider.dart: cached das geladene Profil ueber routeKeyForTerrain - reine Werteaenderungen (Hoehe/Speed) loesen keinen erneuten Kachel-Download aus, nur eine tatsaechliche Ortsverschiebung. - ui/widgets/full_value_chart.dart: _paintDangerBands/_paintTerrain zeichnen rote Gefahrenbaender bzw. die braune Gelaende-Flaeche, in der gleichen Reihenfolge wie im HTML-Demonstrator (dangerHtml, grid, terrainHtml, dann Soll-Kurve obenauf). - ui/widgets/waypoint_list_panel.dart: triggert ensureFor() beim Wechsel auf den Altitude-Tab sowie bei Routenaenderungen waehrend dieser aktiv ist (ref.listenManual auf currentMissionProvider). Getestet: 13 neue Unit-Tests fuer die reine Geometrie/Sampling-Logik, 3 Service-Tests mit einer zur Testzeit synthetisch erzeugten PNG (dart:ui-Encoding, keine Testasset-Datei noetig). Alle 80 Tests sowie flutter analyze bestehen. Manuell auf dem Pixel_10a-Emulator verifiziert: Altitude-Tab zeigt das reale Amsterdam-Gelaendeprofil (nahe Meereshoehe) korrekt als Flaeche unter der Sollhoehen-Linie. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
55 lines
2.0 KiB
Dart
55 lines
2.0 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../../domain/mission/terrain_math.dart';
|
|
import '../../domain/waypoint/flat_waypoint_list.dart';
|
|
import '../../services/terrain/terrain_service.dart';
|
|
|
|
final terrainServiceProvider = Provider<TerrainService>((ref) => TerrainService());
|
|
|
|
/// Zustand des Gelaendeprofils fuer die aktuelle Route (Doku 3.9).
|
|
class TerrainState {
|
|
const TerrainState({this.profile, this.loading = false});
|
|
|
|
final TerrainProfile? profile;
|
|
final bool loading;
|
|
|
|
TerrainState copyWith({TerrainProfile? profile, bool? loading}) {
|
|
return TerrainState(
|
|
profile: profile ?? this.profile,
|
|
loading: loading ?? this.loading,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Laedt das Gelaendeprofil nur, wenn sich die Route (routeKeyForTerrain)
|
|
/// seit dem letzten Laden geaendert hat - analog dem
|
|
/// `terrainSamples.routeKey === key`-Cache im HTML-Demonstrator. Ein
|
|
/// reiner Werte-/Aktionswechsel (Hoehe, Geschwindigkeit) an bestehenden
|
|
/// Wegpunkten loest daher keinen erneuten Kachel-Download aus.
|
|
class TerrainNotifier extends Notifier<TerrainState> {
|
|
@override
|
|
TerrainState build() => const TerrainState();
|
|
|
|
Future<void> ensureFor(List<Waypoint> waypoints) async {
|
|
if (waypoints.length < 2) {
|
|
if (state.profile != null) state = const TerrainState();
|
|
return;
|
|
}
|
|
final routeKey = routeKeyForTerrain(waypoints);
|
|
if (state.profile?.routeKey == routeKey) return;
|
|
if (state.loading) return;
|
|
|
|
state = state.copyWith(loading: true);
|
|
final profile = await ref.read(terrainServiceProvider).fetchProfile(waypoints);
|
|
// Waehrenddessen koennte sich die Route erneut geaendert haben - nur
|
|
// uebernehmen, wenn das Ergebnis noch zur aktuell angefragten Route passt.
|
|
if (profile != null && profile.routeKey != routeKeyForTerrain(waypoints)) {
|
|
state = state.copyWith(loading: false);
|
|
return;
|
|
}
|
|
state = TerrainState(profile: profile ?? state.profile, loading: false);
|
|
}
|
|
}
|
|
|
|
final terrainProvider = NotifierProvider<TerrainNotifier, TerrainState>(TerrainNotifier.new);
|