# DamageListDialog **Zweck:** Dialog zur Anzeige von Straßenschäden in der Nähe mit Foto-Vorschau, Entfernungs- und Relevanz-Filter. **Features:** GPS-basierte Entfernungsberechnung, Sortierung nach Distanz/Relevanz, dynamischer Radius-Filter. --- ## Data Classes ### DamageWithDistance ```kotlin data class DamageWithDistance( val feature: ArcGISFeature, val distanceInMeters: Double?, val typ: String, val beschreibung: String, val objectId: Long, val rating: Int, val photo: ImageBitmap? = null ) ``` **Zweck:** Container für Feature mit berechneter Distanz und geladenen Daten. **Properties:** | Name | Typ | Beschreibung | |------|-----|--------------| | `feature` | ArcGISFeature | Originales ArcGIS Feature | | `distanceInMeters` | Double? | Entfernung zum Nutzer in Metern | | `typ` | String | Schadenstyp (z.B. "Straße") | | `beschreibung` | String | Schadensbeschreibung | | `objectId` | Long | Feature OBJECTID | | `rating` | Int | Community-Bewertungen (communitycounter) | | `photo` | ImageBitmap? | Erstes Attachment als Bitmap (optional) | --- ## Enums ### SortBy ```kotlin enum class SortBy { DISTANCE, // Nach Entfernung sortieren (nächste zuerst) RELEVANCE // Nach Relevanz sortieren (höchste communitycounter zuerst) } ``` **Zweck:** Sortierungs-Modi für Schadensliste. --- ## Functions ### calculateDistance ```kotlin fun calculateDistance( lat1: Double, lon1: Double, lat2: Double, lon2: Double ): Double ``` **Zweck:** Berechnet Luftlinie zwischen zwei GPS-Koordinaten. **Parameter:** | Name | Typ | Beschreibung | |------|-----|--------------| | `lat1` | Double | Breitengrad Punkt 1 | | `lon1` | Double | Längengrad Punkt 1 | | `lat2` | Double | Breitengrad Punkt 2 | | `lon2` | Double | Längengrad Punkt 2 | **Return:** Entfernung in Metern **Algorithmus:** Haversine-Formel ```kotlin R = 6371000.0 // Erdradius in Metern dLat = toRadians(lat2 - lat1) dLon = toRadians(lon2 - lon1) a = sin²(dLat/2) + cos(lat1) * cos(lat2) * sin²(dLon/2) c = 2 * atan2(√a, √(1-a)) distance = R * c ``` --- ## Composables ### DamageListDialog ```kotlin @Composable fun DamageListDialog( onDismiss: () -> Unit, onDamageClick: (ArcGISFeature) -> Unit, mapViewModel: MapViewModel ) ``` **Zweck:** Haupt-Dialog mit Filter- und Sortieroptionen. **Parameter:** | Name | Typ | Beschreibung | |------|-----|--------------| | `onDismiss` | () -> Unit | Callback zum Schließen | | `onDamageClick` | (ArcGISFeature) -> Unit | Callback bei Feature-Auswahl | | `mapViewModel` | MapViewModel | ViewModel für Feature-Zugriff | ### State Management | State | Typ | Default | Beschreibung | |-------|-----|---------|--------------| | `damages` | List | emptyList() | Geladene Schäden | | `isLoading` | Boolean | true |Lade-Status | | `maxDistance` | Float | 1000f | Radius-Filter in Metern | | `userLocation` | Point? | null | GPS-Position | | `errorMessage` | String? | null | Fehlermeldung | | `sortBy` | SortBy | DISTANCE | Sortierungs-Modus | | `minRelevance` | Int | 0 | Minimale Bewertungen | ### Filter-Optionen **1. Entfernungs-Filter:** - **Range:** 100m - 5000m (5km) - **Steps:** 48 - **UI:** Slider mit Icon 📍 - **Anzeige:** "{Distanz} m" oder "{Distanz} km" **2. Sortierung:** - **DISTANCE:** Nach Entfernung (nächste zuerst) - **RELEVANCE:** Nach communitycounter (höchste zuerst) - **UI:** FilterChips "📍 Entfernung" / "👥 Relevanz" **3. Relevanz-Filter (nur bei RELEVANCE):** - **Range:** 0 - 50+ - **Steps:** 49 - **UI:** Slider mit Icon 📈 - **Anzeige:** "Min. Bewertungen: {count}+" ### UI-Zustände **Loading:** ``` CircularProgressIndicator "Lade Schäden..." ``` **Error:** ``` ⚠️ Emoji Fehlermeldung (rot) ``` **Empty:** ``` 🔍 Emoji "Keine Schäden im Umkreis" oder "Keine Schäden mit dieser Bewertung" ``` **Success:** ``` LazyColumn mit DamageListItemWithPhoto ``` --- ### DamageListItemWithPhoto ```kotlin @Composable fun DamageListItemWithPhoto( damage: DamageWithDistance, onClick: () -> Unit ) ``` **Zweck:** Einzelne Zeile in der Schadensliste. **Parameter:** | Name | Typ | Beschreibung | |------|-----|--------------| | `damage` | DamageWithDistance | Anzuzeigender Schaden | | `onClick` | () -> Unit | Callback bei Klick | **Layout:** ``` ┌─────────────────────────────────────┐ │ ┌──────┐ {Emoji} {Typ} │ │ │ │ {Beschreibung} │ │ │ Foto │ 📍 {Distanz} 👥 {Rating} │ │ └──────┘ │ └─────────────────────────────────────┘ ``` **Foto-Box:** - **Größe:** 100x100 dp - **Shape:** RoundedCornerShape (links abgerundet) - **Fallback:** 📷 Emoji auf grauem Hintergrund **Info-Bereich:** - **Typ:** Emoji + Name (Bold) - **Beschreibung:** Max. 40 Zeichen + "..." - **Distanz:** 📍 + formatDistance() - **Rating:** 👥 + communitycounter (farbcodiert) --- ## Extension Functions ### MapViewModel.loadDamagesNearby ```kotlin suspend fun MapViewModel.loadDamagesNearby( userLocation: Point, maxDistanceMeters: Double, context: Context ): List ``` **Zweck:** Lädt alle Features im Umkreis mit Distanz-Berechnung und Foto-Loading. **Parameter:** | Name | Typ | Beschreibung | |------|-----|--------------| | `userLocation` | Point | GPS-Position des Nutzers | | `maxDistanceMeters` | Double | Maximaler Radius | | `context` | Context | Für Toast-Nachrichten | **Return:** List - Sortiert nach Entfernung **Ablauf:** 1. **Query:** Alle Features (`whereClause = "1=1"`) 2. **Für jedes Feature:** - Load Feature-Daten - Geometrie extrahieren - Koordinaten-Transformation (GeometryEngine.projectOrNull) - Distanz-Berechnung (Haversine) - Filter: nur wenn ≤ maxDistanceMeters - Attribute extrahieren (Typ, Beschreibung, OBJECTID, communitycounter) - Erstes Attachment laden und zu Bitmap konvertieren 3. **Return:** Nach Distanz sortierte Liste **Koordinaten-Transformation:** ```kotlin GeometryEngine.projectOrNull( featureGeometry, userLocation.spatialReference ) ``` **Foto-Loading:** ```kotlin val attachments = feature.fetchAttachments().getOrNull() val firstAttachment = attachments?.firstOrNull() val data = firstAttachment?.fetchData().getOrNull() val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size) ``` **Error Handling:** - Try-Catch um gesamte Funktion - Toast bei Fehler - Return emptyList() bei Fehler **Debug-Logging:** - Query-Status - Feature-Count - Distanz-Berechnungen - Transformation-Fehler --- ## Helper Functions ### formatDistance ```kotlin fun formatDistance(meters: Double?): String ``` **Zweck:** Formatiert Distanz-Anzeige. **Logic:** - `< 1000m`: "{meters} m" - `≥ 1000m`: "{km} km" (1 Dezimalstelle) **Beispiele:** - `250.0` → "250 m" - `1500.0` → "1.5 km" ### getEmojiForType ```kotlin fun getEmojiForType(typ: String): String ``` **Zweck:** Mapt Schadenstyp zu Emoji. **Mapping:** | Typ | Emoji | |-----|-------| | "Straße" | 🛣️ | | "Gehweg" | 🚶 | | "Fahrradweg" | 🚴 | | "Beleuchtung" | 💡 | | "Sonstiges" | 📍 | --- ## Verwendungsbeispiel ```kotlin // In MainScreen var showDamageList by remember { mutableStateOf(false) } if (showDamageList) { DamageListDialog( onDismiss = { showDamageList = false }, onDamageClick = { feature -> mapViewModel.selectedFeature = feature mapViewModel.showFeatureInfo = true showDamageList = false }, mapViewModel = mapViewModel ) } // Aus SideSlider SliderMenuItem( text = "Schadensliste", icon = Icons.Default.FormatListNumbered, onClick = { showDamageList = true } ) ``` --- ## Feature-Attribute **Erforderliche Felder:** | Feldname | Typ | Beschreibung | |----------|-----|--------------| | `Typ` | String | Schadenstyp | | `Beschreibung` | String | Schadensbeschreibung | | `OBJECTID` | Long | Feature-ID | | `communitycounter` | Int | Community-Bewertungen | | Attachments | Blob | Fotos (optional) | --- ## Performance-Hinweise **Optimierungen:** - Fotos werden parallel geladen (async per Feature) - Distanz-Filter reduziert verarbeitete Features - LazyColumn für effizientes Rendering **Potenzielle Bottlenecks:** - Foto-Loading bei vielen Features (async pro Feature) - Koordinaten-Transformation (GeometryEngine) - Haversine-Berechnung (für jedes Feature) --- ## Technische Details - **UI Framework:** Jetpack Compose - **Threading:** Dispatchers.IO für Feature-Loading - **Geometrie:** ArcGIS GeometryEngine für Transformation - **Distanz:** Haversine-Formel - **Fotos:** BitmapFactory + ImageBitmap - **State:** remember/mutableStateOf (lokaler State)