Add terrain/elevation data fetching (Doku 3.9)
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>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
5505bb3741
commit
b176ad294e
@@ -0,0 +1,54 @@
|
||||
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);
|
||||
Reference in New Issue
Block a user