- README hinzugefügt

- BugFixing eigenen Standort.
This commit is contained in:
2026-02-14 12:52:03 +01:00
parent 3b81269a57
commit 7b5abed587
4 changed files with 149 additions and 28 deletions

View File

@@ -119,61 +119,73 @@ class MapViewModel(application: Application, context: Context) : AndroidViewMode
} }
fun pickCurrentLocation() { fun pickCurrentLocation() {
// keine Coroutine nötig, das ist alles sync
val pos = locationDisplay?.location?.value?.position val pos = locationDisplay?.location?.value?.position
if (pos == null) { if (pos == null) {
snackBarMessage = "Kein GPS Signal. Bitte kurz warten oder Standort aktivieren." snackBarMessage = "Kein GPS Signal. Bitte kurz warten oder Standort aktivieren."
return return
} }
// pos ist ggf. schon Point, aber wir erzwingen WGS84 (sicher für deinen Feature-Service) // pos ist ggf. schon Point, aber wir erzwingen WGS84
val pointWgs84 = val pointWgs84 = if (pos.spatialReference == SpatialReference.wgs84()) {
if (pos.spatialReference == SpatialReference.wgs84()) pos pos
else GeometryEngine.projectOrNull(pos, SpatialReference.wgs84()) as Point } else {
GeometryEngine.projectOrNull(pos, SpatialReference.wgs84()) as Point
}
// ===== KEIN Z hinzufügen! =====
updateReportDraft { copy(point = pointWgs84) } updateReportDraft { copy(point = pointWgs84) }
pointGraphic.geometry = pointWgs84 pointGraphic.geometry = pointWgs84
println("DEBUG pickCurrentLocation: point = $pointWgs84")
snackBarMessage = "Position aus GPS gesetzt." snackBarMessage = "Position aus GPS gesetzt."
} }
private suspend fun applyEditsWithPhotos(feature: ArcGISFeature, photos: List<ImageBitmap>) { private suspend fun applyEditsWithPhotos(feature: ArcGISFeature, photos: List<ImageBitmap>) {
println("DEBUG applyEditsWithPhotos: START")
println("DEBUG applyEditsWithPhotos: photos.size = ${photos.size}")
// ERST: Feature zum Server senden
serviceFeatureTable.applyEdits().onSuccess { editResults -> serviceFeatureTable.applyEdits().onSuccess { editResults ->
println("DEBUG applyEditsWithPhotos: applyEdits SUCCESS")
val result = editResults.firstOrNull() val result = editResults.firstOrNull()
if (result != null && result.error == null) { if (result != null && result.error == null) {
val serverObjectId = result.objectId val serverObjectId = result.objectId
println("DEBUG: Server-Erfolg! Echte ObjectID: $serverObjectId") println("DEBUG: Server-Erfolg! ObjectID: $serverObjectId")
if (photos.isNotEmpty()) { if (photos.isNotEmpty()) {
// Fix: erstellen eine Abfrage für die neue ID // Feature vom Server neu laden
val queryParameters = QueryParameters().apply { val queryParameters = QueryParameters().apply {
objectIds.add(serverObjectId) objectIds.add(serverObjectId)
} }
// laden das Feature neu vom Server
serviceFeatureTable.queryFeatures(queryParameters).onSuccess { queryResult -> serviceFeatureTable.queryFeatures(queryParameters).onSuccess { queryResult ->
// Ein FeatureQueryResult ist ein Iterator, nehmen das erste Element
val fetchedFeature = queryResult.firstOrNull() as? ArcGISFeature val fetchedFeature = queryResult.firstOrNull() as? ArcGISFeature
if (fetchedFeature != null) { if (fetchedFeature != null) {
println("DEBUG: Feature neu geladen, füge Fotos hinzu...")
addPhotosToFeature(fetchedFeature, photos, serverObjectId) addPhotosToFeature(fetchedFeature, photos, serverObjectId)
} else { } else {
println("DEBUG: Feature nach Query nicht gefunden") println("DEBUG: Feature nach Query nicht gefunden")
snackBarMessage = "Fehler: Feature-ID $serverObjectId nicht gefunden." snackBarMessage = "Feature erstellt (ID: $serverObjectId), aber Fotos konnten nicht hinzugefügt werden."
} }
}.onFailure { }.onFailure { error ->
println("DEBUG: Query fehlgeschlagen: ${it.message}") println("DEBUG: Query fehlgeschlagen: ${error.message}")
snackBarMessage = "Fotos konnten nicht zugeordnet werden." snackBarMessage = "Feature erstellt, aber Fotos konnten nicht zugeordnet werden."
} }
} else { } else {
println("DEBUG: Keine Fotos, Feature erfolgreich erstellt!")
snackBarMessage = "Erfolgreich gemeldet! ID: $serverObjectId" snackBarMessage = "Erfolgreich gemeldet! ID: $serverObjectId"
} }
} else { } else {
println("DEBUG: Server-Fehler bei applyEdits: ${result?.error?.message}") println("DEBUG: Server-Fehler bei applyEdits: ${result?.error?.message}")
snackBarMessage = "Serverfehler: ${result?.error?.message}" snackBarMessage = "Serverfehler: ${result?.error?.message}"
} }
}.onFailure { }.onFailure { error ->
println("DEBUG: applyEdits total fehlgeschlagen: ${it.message}") println("DEBUG: applyEdits total fehlgeschlagen: ${error.message}")
snackBarMessage = "Senden fehlgeschlagen: ${it.message}" error.printStackTrace()
snackBarMessage = "Senden fehlgeschlagen: ${error.message}"
} }
} }
@@ -300,12 +312,16 @@ class MapViewModel(application: Application, context: Context) : AndroidViewMode
private fun pickReportLocation(screen: ScreenCoordinate) { private fun pickReportLocation(screen: ScreenCoordinate) {
val mapPoint = mapViewProxy.screenToLocationOrNull(screen) val mapPoint = mapViewProxy.screenToLocationOrNull(screen)
val p = mapPoint?.let { GeometryEngine.normalizeCentralMeridian(it) as? Point } val normalized = mapPoint?.let { GeometryEngine.normalizeCentralMeridian(it) as? Point }
if (p != null) { if (normalized != null) {
reportDraft = reportDraft.copy(point = p) // ===== KEIN Z hinzufügen! =====
pointGraphic.geometry = p reportDraft = reportDraft.copy(point = normalized)
pointGraphic.geometry = normalized
reopenReport = true reopenReport = true
println("DEBUG pickReportLocation: point = $normalized")
snackBarMessage = "Position gesetzt." snackBarMessage = "Position gesetzt."
} else { } else {
snackBarMessage = "Position konnte nicht gesetzt werden." snackBarMessage = "Position konnte nicht gesetzt werden."
@@ -343,26 +359,48 @@ class MapViewModel(application: Application, context: Context) : AndroidViewMode
viewModelScope.launch { viewModelScope.launch {
try { try {
// 1) Feature lokal erstellen println("DEBUG: Creating feature...")
// ===== FIX: Point OHNE Z erstellen =====
val point2D = if (draft.point != null) {
Point(
x = draft.point.x,
y = draft.point.y,
spatialReference = draft.point.spatialReference
)
} else {
null
}
println("DEBUG: Point 2D created: $point2D (hasZ = ${point2D?.hasZ})")
// 1) Feature lokal erstellen MIT 2D-Point
val feature = serviceFeatureTable.createFeature().apply { val feature = serviceFeatureTable.createFeature().apply {
geometry = draft.point geometry = point2D // ← OHNE Z!
attributes["Beschreibung"] = draft.beschreibung attributes["Beschreibung"] = draft.beschreibung
attributes["Typ"] = draft.typ attributes["Typ"] = draft.typ
attributes["status"] = draft.status attributes["status"] = draft.status
} }
// 2) Erst addFeature + applyEdits => Feature existiert am Server (ObjectID) println("DEBUG: Adding feature to table...")
// 2) Feature zur Tabelle hinzufügen
serviceFeatureTable.addFeature(feature).onSuccess { serviceFeatureTable.addFeature(feature).onSuccess {
// applyEditsWithPhotos macht bei dir: applyEdits -> ObjectID holen -> feature neu queryn -> attachments adden println("DEBUG: addFeature SUCCESS, calling applyEditsWithPhotos...")
// 3) JETZT ERST applyEdits mit Fotos
applyEditsWithPhotos(feature as ArcGISFeature, draft.photos) applyEditsWithPhotos(feature as ArcGISFeature, draft.photos)
// Draft/Preview zurücksetzen (am besten nach Erfolg; fürs Erste hier ok) // 4) Draft zurücksetzen
resetDraft() resetDraft()
}.onFailure { }.onFailure { error ->
snackBarMessage = "Fehler beim Hinzufügen: ${it.message}" println("DEBUG: addFeature FAILED: ${error.message}")
snackBarMessage = "Fehler beim Hinzufügen: ${error.message}"
} }
} catch (e: Exception) { } catch (e: Exception) {
println("DEBUG: Exception in submitDraftToLayer: ${e.message}")
e.printStackTrace()
snackBarMessage = "Fehler: ${e.message}" snackBarMessage = "Fehler: ${e.message}"
} }
} }

View File

@@ -42,7 +42,7 @@ class LocationHelper(private val context: Context) {
/** /**
* Composable zum Einrichten des Location Display * Composable zum Einrichten des Location Display
* * Beim Auslagern wird diese Funktion nicht mehr genutzt. Das sollte gefixt werden!!!!!!!!!!!!!!
*/ */
@Composable @Composable
fun setupLocationDisplay( fun setupLocationDisplay(

View File

@@ -98,15 +98,25 @@ fun ReportDialog(
text = { "Hinzufügen" }, text = { "Hinzufügen" },
style = AppButtonStyle.Outlined, style = AppButtonStyle.Outlined,
onClick = { onClick = {
// ===== DEBUG VOR DEM SUBMIT =====
println("DEBUG Button CLICKED: isValid = ${mapViewModel.reportDraft.isValid}")
println("DEBUG Button CLICKED: point = ${mapViewModel.reportDraft.point}")
println("DEBUG Button CLICKED: photos.size = ${mapViewModel.reportDraft.photos.size}")
println("DEBUG Button CLICKED: typ = '${mapViewModel.reportDraft.typ}'")
println("DEBUG Button CLICKED: beschreibung = '${mapViewModel.reportDraft.beschreibung}'")
scope.launch { scope.launch {
checking = true checking = true
showDuplicateDialog = mapViewModel.isDuplicateNearby(20.0) showDuplicateDialog = mapViewModel.isDuplicateNearby(20.0)
checking = false checking = false
if (!showDuplicateDialog) { if (!showDuplicateDialog) {
println("DEBUG: Calling submitDraftToLayer()...")
mapViewModel.submitDraftToLayer() mapViewModel.submitDraftToLayer()
viewModel.clearSelection() viewModel.clearSelection()
onCancel() onCancel()
} else {
println("DEBUG: Duplicate gefunden, zeige Dialog")
} }
} }
}, },

73
docs/README.md Normal file
View File

@@ -0,0 +1,73 @@
# Scan And Solve - Setup & Konfiguration
## Voraussetzungen
### ArcGIS Feature Layer
Die App benötigt einen ArcGIS Feature Layer mit spezifischen Attributen. Es gibt zwei Möglichkeiten:
---
## Option 1: Fertiger Demo-Layer (empfohlen)
Verwende den vorkonfigurierten Feature Layer:
```
https://services9.arcgis.com/UVxdrlZq3S3gqt7w/ArcGIS/rest/services/251120_StrassenSchaeden/FeatureServer/0
```
Dieser Layer enthält bereits alle erforderlichen Attribute und ist sofort einsatzbereit.
---
## Option 2: Eigenen Layer konfigurieren
Falls du einen eigenen Feature Layer verwenden möchtest, müssen folgende Attribute hinzugefügt werden:
### Erforderliche Attribute
| Attributname | Typ | Beschreibung | Standardwert |
|--------------|-----|--------------|--------------|
| **communitycounter** | Integer | Anzahl der Nutzerbestätigungen (Upvotes/Downvotes) | 0 |
| **status** | String | Bearbeitungsstatus des Schadens | "neu" |
### Status-Werte
Das `status`-Attribut akzeptiert folgende Werte (siehe `StatusSymbolRenderer.kt`):
| Wert | Beschreibung | Farbe |
|------|--------------|-------|
| `"neu"` | Neu gemeldeter Schaden | 🔴 Rot |
| `"in Bearbeitung"` | Schaden wird bearbeitet | 🟠 Orange |
| `"Schaden behoben"` | Schaden wurde behoben | 🟢 Grün |
---
## Berechtigungen & Benutzerrollen
### Nutzer-Rechte (App-Benutzer)
**Erlaubt:**
- Neue Schäden melden
- Fotos hochladen
- Community-Bewertungen abgeben (Upvote/Downvote)
- Schäden filtern und anzeigen
**Nicht erlaubt:**
- Status eines Schadens ändern
- Features löschen
### Mitarbeiter-Rechte (Internes Team)
**Erlaubt:**
- Alle Nutzer-Rechte
- Status ändern (neu → in Bearbeitung → behoben)
- Features löschen
> **Hinweis:** Der Status wird bei Erstellung automatisch auf `"neu"` gesetzt. Die Statusänderung erfolgt durch interne Mitarbeiter außerhalb der App (z.B. über ArcGIS Online).
---