FeatureInfo Widget hinzugefügt

This commit is contained in:
2026-01-19 21:40:57 +01:00
parent c9b2b262a8
commit 33e95641d0
3 changed files with 112 additions and 64 deletions

View File

@@ -4,6 +4,7 @@ import MapViewModel
import android.Manifest
import android.R.attr.enabled
import android.app.Application
import android.graphics.BitmapFactory
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
@@ -30,9 +31,12 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.arcgismaps.data.ArcGISFeature
// Hier holen wir die ArcGIS Klassen
import com.arcgismaps.mapping.ArcGISMap
import com.arcgismaps.mapping.BasemapStyle
@@ -88,6 +92,8 @@ fun MainScreen(modifier: Modifier = Modifier, application: Application) {
floatingActionButton = {
LargeFloatingActionButton(
onClick = {
mapViewModel.resetDraft()
mapViewModel.closeFeatureInfo()
openReport()
},
modifier = Modifier.offset(y = 64.dp),
@@ -136,6 +142,14 @@ fun ContentScreen(
)
}
if (mapViewModel.showFeatureInfo && mapViewModel.selectedFeature != null) {
FeatureInfoOverlay(
feature = mapViewModel.selectedFeature!!,
onClose = { mapViewModel.closeFeatureInfo() }
)
}
// Slider von Links
SideSlider(visible = sliderOpen) {
Text(
@@ -332,7 +346,11 @@ fun ReportOverlay(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Button(onClick = { mapViewModel.pickCurrentLocation() }) {
Button(
onClick = {
mapViewModel.pickCurrentLocation()
}
) {
Text(if (hasPoint) "Neue Position aus Standort" else "Position aus Standort")
}
Button(
@@ -346,3 +364,64 @@ fun ReportOverlay(
}
}
}
@Composable
fun FeatureInfoOverlay(
feature: ArcGISFeature,
onClose: () -> Unit
) {
val typ = feature.attributes["Typ"].toString()
val beschreibung = feature.attributes["Beschreibung"].toString()
val id = feature.attributes["OBJECTID"].toString()
var image by remember { mutableStateOf<ImageBitmap?>(null) }
LaunchedEffect(feature) {
image = loadFirstAttachmentBitmap(feature)
}
OverlayShell(
title = "Meldung $id",
footer = {
OutlinedButton(
onClick = {
onClose()
}
) { Text("Schließen", color = Color.Black) }
}
) {
image?.let {
Image(
bitmap = it,
contentDescription = "Feature Bild",
modifier = Modifier
.fillMaxWidth()
.height(200.dp),
contentScale = ContentScale.Crop
)
}
Text("Typ: $typ", color = Color.Black)
Text("Beschreibung:", color = Color.Black)
Text(beschreibung, color = Color.Black)
}
}
suspend fun loadFirstAttachmentBitmap(
feature: ArcGISFeature
): ImageBitmap? {
// Feature muss geladen sein
feature.load().getOrThrow()
// Attachments abrufen
val attachments = feature.fetchAttachments().getOrThrow()
val first = attachments.firstOrNull() ?: return null
// Attachment-Daten laden
val data = first.fetchData().getOrThrow()
val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
return bitmap.asImageBitmap()
}

View File

@@ -21,10 +21,8 @@ import androidx.core.content.ContextCompat
import com.arcgismaps.ApiKey
import com.arcgismaps.ArcGISEnvironment
import com.arcgismaps.Color
import com.arcgismaps.data.ArcGISFeature
import com.arcgismaps.location.LocationDisplayAutoPanMode
import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol
import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle
import com.arcgismaps.mapping.view.GraphicsOverlay
import com.arcgismaps.toolkit.geoviewcompose.MapView
import com.arcgismaps.toolkit.geoviewcompose.rememberLocationDisplay
import kotlinx.coroutines.launch

View File

@@ -40,7 +40,8 @@ class MapViewModel(application: Application) : AndroidViewModel(application) {
private set
var reopenReport by mutableStateOf(false)
private set
var showFeatureInfo by mutableStateOf(false)
private set
var selectedFeature: ArcGISFeature? by mutableStateOf(null)
val mapViewProxy = MapViewProxy()
var reportDraft by mutableStateOf(ReportDraft())
@@ -63,7 +64,7 @@ class MapViewModel(application: Application) : AndroidViewModel(application) {
init {
tempOverlay.graphics.add(pointGraphic)
viewModelScope.launch {
serviceFeatureTable = ServiceFeatureTable("https://services9.arcgis.com/UVxdrlZq3S3gqt7w/arcgis/rest/services/si_StrassenSchaeden/FeatureServer/0")
serviceFeatureTable = ServiceFeatureTable("https://services9.arcgis.com/UVxdrlZq3S3gqt7w/arcgis/rest/services/251120_StrassenSchaeden/FeatureServer/0")
serviceFeatureTable.load().onSuccess {
val typeDamageField = serviceFeatureTable.fields.firstOrNull { it.name == "Typ" }
val attributeDomain = typeDamageField?.domain as? CodedValueDomain
@@ -79,55 +80,6 @@ class MapViewModel(application: Application) : AndroidViewModel(application) {
}
}
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}"
}
}
}
fun pickCurrentLocation() {
// keine Coroutine nötig, das ist alles sync
val pos = locationDisplay?.location?.value?.position
@@ -146,7 +98,6 @@ class MapViewModel(application: Application) : AndroidViewModel(application) {
snackBarMessage = "Position aus GPS gesetzt."
}
private suspend fun applyEditsWithPhotos(feature: ArcGISFeature, photos: List<ImageBitmap>) {
serviceFeatureTable.applyEdits().onSuccess { editResults ->
val result = editResults.firstOrNull()
@@ -231,7 +182,7 @@ class MapViewModel(application: Application) : AndroidViewModel(application) {
}
when (selectedOperation) {
FeatureOperationType.DEFAULT -> selectFeatureForAttributeEditAt(singleTapConfirmedEvent.screenCoordinate)
FeatureOperationType.DEFAULT -> selectFeatureAt(singleTapConfirmedEvent.screenCoordinate)
FeatureOperationType.DELETE -> deleteFeatureAt(singleTapConfirmedEvent.screenCoordinate)
FeatureOperationType.UPDATE_ATTRIBUTE -> selectFeatureForAttributeEditAt(singleTapConfirmedEvent.screenCoordinate)
FeatureOperationType.UPDATE_GEOMETRY -> updateFeatureGeometryAt(singleTapConfirmedEvent.screenCoordinate)
@@ -282,7 +233,6 @@ class MapViewModel(application: Application) : AndroidViewModel(application) {
}
private fun updateFeatureGeometryAt(screenCoordinate: ScreenCoordinate) {
featureLayer?.let { featureLayer ->
when (selectedFeature) {
// When no feature is selected.
@@ -398,12 +348,33 @@ class MapViewModel(application: Application) : AndroidViewModel(application) {
}
}
fun selectFeatureAt(screenCoordinate: ScreenCoordinate) {
featureLayer?.let { featureLayer ->
// Clear any existing selection.
featureLayer.clearSelection()
selectedFeature = null
viewModelScope.launch {
// Determine if a user tapped on a feature.
mapViewProxy.identify(featureLayer, screenCoordinate, 10.dp).onSuccess {
identifyResult ->
// Get the identified feature.
val identifiedFeature = identifyResult.geoElements.firstOrNull() as? ArcGISFeature
identifiedFeature?.let {
selectedFeature = it.also {
featureLayer.selectFeature(it)
showFeatureInfo = true
}
}
}
}
}
}
fun closeFeatureInfo() {
showFeatureInfo = false
selectedFeature = null
featureLayer.clearSelection()
}
}
enum class FeatureOperationType(val operationName: String, val instruction: String) {