- Dokumentation
- Bereinigung von Code
This commit is contained in:
@@ -0,0 +1,387 @@
|
||||
# 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<DamageWithDistance> | 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<DamageWithDistance>
|
||||
```
|
||||
|
||||
**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<DamageWithDistance> - 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)
|
||||
|
||||
Reference in New Issue
Block a user