- Featurelayer ArcGIS hinzugefügt

- Textbox jetzt nutzbar
- Schadenauswahl Dropdown Menü
- Featureerstellung nimmt eigene Position und wird hinzugefügt.
This commit is contained in:
2026-01-07 18:03:50 +01:00
parent 91b885f67c
commit f5ac96807c
4 changed files with 245 additions and 164 deletions

View File

@@ -1,64 +1,182 @@
package com.example.snapandsolve
import android.app.Application
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asAndroidBitmap
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.arcgismaps.LoadStatus
import com.arcgismaps.data.ArcGISFeature
import com.arcgismaps.data.CodedValueDomain
import com.arcgismaps.data.ServiceFeatureTable
import com.arcgismaps.geometry.GeometryEngine
import com.arcgismaps.geometry.Point
import com.arcgismaps.geometry.SpatialReference
import com.arcgismaps.mapping.ArcGISMap
import com.arcgismaps.mapping.BasemapStyle
import com.arcgismaps.mapping.Viewpoint
import com.arcgismaps.mapping.layers.FeatureLayer
import com.arcgismaps.mapping.view.LocationDisplay
import com.arcgismaps.toolkit.geoviewcompose.MapViewProxy
import kotlinx.coroutines.launch
import java.io.ByteArrayOutputStream
class MapViewModel(application: Application ): AndroidViewModel(application) {
class MapViewModel(application: Application) : AndroidViewModel(application) {
val map: ArcGISMap = ArcGISMap(BasemapStyle.OpenOsmStyle).apply {
initialViewpoint = Viewpoint(53.14, 8.20, 20000.0)
}
/*
ALLES UNTER DIESEM KOMMENTAR WIRD NICHT GENUTZT. Aber EVENTUELL nötig zum einbinden von
Layer und Features.
*/
// Hold a reference to the selected feature.
var selectedFeature: ArcGISFeature? by mutableStateOf(null)
val mapViewProxy = MapViewProxy()
//var currentFeatureOperation by mutableStateOf(FeatureOperationType.CREATE)
lateinit var featureLayer: FeatureLayer
// Create a snackbar message to display the result of feature operations.
var snackBarMessage: String by mutableStateOf("")
lateinit var serviceFeatureTable: ServiceFeatureTable
var currentDamageType by mutableStateOf("")
// The list of damage types to update the feature attribute.
var damageTypeList: List<String> = mutableListOf()
var locationDisplay: LocationDisplay? = null
init {
viewModelScope.launch {
serviceFeatureTable = ServiceFeatureTable("https://services9.arcgis.com/UVxdrlZq3S3gqt7w/ArcGIS/rest/services/StrassenSchaeden/FeatureServer/0")
serviceFeatureTable = ServiceFeatureTable("https://services9.arcgis.com/UVxdrlZq3S3gqt7w/arcgis/rest/services/si_StrassenSchaeden/FeatureServer/0")
serviceFeatureTable.load().onSuccess {
// Get the field from the feature table that will be updated.
val typeDamageField = serviceFeatureTable.fields.first { it.name == "Typ" }
// Get the coded value domain for the field.
val attributeDomain = typeDamageField.domain as CodedValueDomain
// Add the damage types to the list.
attributeDomain.codedValues.forEach {
val typeDamageField = serviceFeatureTable.fields.firstOrNull { it.name == "Typ" }
val attributeDomain = typeDamageField?.domain as? CodedValueDomain
attributeDomain?.codedValues?.forEach {
damageTypeList += it.name
}
println("DEBUG: ServiceFeatureTable erfolgreich geladen")
}.onFailure {
println("DEBUG: Fehler beim Laden der Tabelle: ${it.message}")
}
featureLayer = FeatureLayer.createWithFeatureTable(serviceFeatureTable)
map.operationalLayers.add(featureLayer)
}
}
fun createFeatureAtCurrentLocation(
beschreibung: String,
typ: String,
photos: List<ImageBitmap> = emptyList()
) {
viewModelScope.launch {
println("DEBUG: createFeature gestartet") // Erscheint das in der Logcat?
try {
// 1. Location prüfen
val locationData = locationDisplay?.location?.value
val gpsPoint = locationData?.position
if (gpsPoint == null) {
println("DEBUG: Standort ist NULL - GPS Signal fehlt!")
snackBarMessage = "Kein GPS Signal. Bitte kurz warten oder nach draußen gehen."
return@launch
}
println("DEBUG: GPS gefunden: x=${gpsPoint.x}, y=${gpsPoint.y}")
// 2. Feature erstellen
// Wichtig: explizit WGS84, falls es nicht schon hat
val pointWgs84 = Point(gpsPoint.x, gpsPoint.y, SpatialReference.wgs84())
val feature = serviceFeatureTable.createFeature().apply {
geometry = pointWgs84
attributes["Typ"] = typ
attributes["Beschreibung"] = beschreibung
}
println("DEBUG: Feature lokal erstellt. Sende an Server...")
// 3. Hochladen
serviceFeatureTable.addFeature(feature).onSuccess {
println("DEBUG: addFeature erfolgreich")
applyEditsWithPhotos(feature as ArcGISFeature, photos)
}.onFailure {
println("DEBUG: addFeature FEHLER: ${it.message}")
snackBarMessage = "Fehler: ${it.message}"
}
} catch (e: Exception) {
println("DEBUG: CRASH in createFeature: ${e.message}")
e.printStackTrace()
snackBarMessage = "Fehler: ${e.message}"
}
}
}
private suspend fun applyEditsWithPhotos(feature: ArcGISFeature, photos: List<ImageBitmap>) {
serviceFeatureTable.applyEdits().onSuccess { editResults ->
val result = editResults.firstOrNull()
if (result != null && result.error == null) {
val serverObjectId = result.objectId
println("DEBUG: Server-Erfolg! Echte ObjectID: $serverObjectId")
if (photos.isNotEmpty()) {
// Fix: erstellen eine Abfrage für die neue ID
val queryParameters = com.arcgismaps.data.QueryParameters().apply {
objectIds.add(serverObjectId)
}
// laden das Feature neu vom Server
serviceFeatureTable.queryFeatures(queryParameters).onSuccess { queryResult ->
// Ein FeatureQueryResult ist ein Iterator, nehmen das erste Element
val fetchedFeature = queryResult.firstOrNull() as? ArcGISFeature
if (fetchedFeature != null) {
addPhotosToFeature(fetchedFeature, photos, serverObjectId)
} else {
println("DEBUG: Feature nach Query nicht gefunden")
snackBarMessage = "Fehler: Feature-ID $serverObjectId nicht gefunden."
}
}.onFailure {
println("DEBUG: Query fehlgeschlagen: ${it.message}")
snackBarMessage = "Fotos konnten nicht zugeordnet werden."
}
} else {
snackBarMessage = "Erfolgreich gemeldet! ID: $serverObjectId"
}
} else {
println("DEBUG: Server-Fehler bei applyEdits: ${result?.error?.message}")
snackBarMessage = "Serverfehler: ${result?.error?.message}"
}
}.onFailure {
println("DEBUG: applyEdits total fehlgeschlagen: ${it.message}")
snackBarMessage = "Senden fehlgeschlagen: ${it.message}"
}
}
private suspend fun addPhotosToFeature(
feature: ArcGISFeature,
photos: List<ImageBitmap>,
objectId: Long?
) {
println("DEBUG: Füge ${photos.size} Fotos hinzu...")
photos.forEachIndexed { index, imageBitmap ->
try {
val byteArray = imageBitmapToByteArray(imageBitmap)
feature.addAttachment(
name = "photo_$index.jpg",
contentType = "image/jpeg",
data = byteArray
)
} catch (e: Exception) {
println("DEBUG: Attachment-Fehler bei Foto $index: ${e.message}")
}
}
serviceFeatureTable.updateFeature(feature).onSuccess {
println("DEBUG: Feature mit Anhängen aktualisiert. Finales applyEdits...")
serviceFeatureTable.applyEdits().onSuccess {
snackBarMessage = "Gespeichert mit ${photos.size} Fotos! ID: $objectId"
}
}.onFailure {
println("DEBUG: updateFeature für Anhänge fehlgeschlagen: ${it.message}")
}
}
private fun imageBitmapToByteArray(imageBitmap: ImageBitmap): ByteArray {
val stream = ByteArrayOutputStream()
imageBitmap.asAndroidBitmap().compress(android.graphics.Bitmap.CompressFormat.JPEG, 80, stream)
return stream.toByteArray()
}
}