From bd66f298e0bc1bf8874e20464783f29581766abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Ahlers?= Date: Thu, 4 Dec 2025 11:02:45 +0100 Subject: [PATCH] Add GPS and map-based feature creation to `CreatePage` with attribute validation, `MapViewModel` enhancements for feature handling, and dependency updates. --- app/build.gradle.kts | 2 + .../strassenschadenpro2/MapViewModel.kt | 51 +++++++++++++++++++ .../strassenschadenpro2/pages/CreatePage.kt | 35 +++++++++++-- gradle/libs.versions.toml | 2 +- 4 files changed, 85 insertions(+), 5 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a607ca5..33b715e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -66,6 +66,8 @@ dependencies { androidTestImplementation(libs.androidx.compose.ui.test.junit4) debugImplementation(libs.androidx.compose.ui.tooling) debugImplementation(libs.androidx.compose.ui.test.manifest) + implementation("androidx.compose.material:material-icons-extended:1.7.8") + implementation("com.google.android.gms:play-services-location:21.3.0") // ArcGIS Maps for Kotlin - SDK dependency implementation(libs.arcgis.maps.kotlin) diff --git a/app/src/main/java/de/jadehs/strassenschadenpro2/MapViewModel.kt b/app/src/main/java/de/jadehs/strassenschadenpro2/MapViewModel.kt index 6284faf..98a8bd9 100644 --- a/app/src/main/java/de/jadehs/strassenschadenpro2/MapViewModel.kt +++ b/app/src/main/java/de/jadehs/strassenschadenpro2/MapViewModel.kt @@ -12,6 +12,7 @@ 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.mapping.ArcGISMap import com.arcgismaps.mapping.BasemapStyle import com.arcgismaps.mapping.Viewpoint @@ -118,6 +119,56 @@ class MapViewModel(application: Application): AndroidViewModel(application) { } } + fun createFeatureWithAttributesAt(screenCoordinate: ScreenCoordinate, beschreibung: String, typ: String) { + // Create the feature. + val feature = serviceFeatureTable?.createFeature()?.apply { + // Get the normalized geometry for the tapped location and use it as the feature's geometry. + mapViewProxy.screenToLocationOrNull(screenCoordinate)?.let { mapPoint -> + geometry = GeometryEngine.normalizeCentralMeridian(mapPoint) + // Set feature attributes. + attributes["Typ"] = typ + attributes["Beschreibung"] = beschreibung + } + } + feature?.let { + viewModelScope.launch { + // Add the feature to the table. + serviceFeatureTable?.addFeature(it) + // Apply the edits to the service on the service geodatabase. + serviceFeatureTable?.applyEdits() + // Update the feature to get the updated objectid - a temporary ID is used before the feature is added. + it.refresh() + // Confirm feature addition. + snackBarMessage = "Created feature ${it.attributes["objectid"]}" + } + } + } + + fun createFeatureWithAttributesAtPoint(point: Point, beschreibung: String, typ: String) { + // Create the feature. + val feature = serviceFeatureTable?.createFeature()?.apply { + // Get the normalized geometry for the tapped location and use it as the feature's geometry. + + geometry = GeometryEngine.normalizeCentralMeridian(point) + // Set feature attributes. + attributes["Typ"] = typ + attributes["Beschreibung"] = beschreibung + + } + feature?.let { + viewModelScope.launch { + // Add the feature to the table. + serviceFeatureTable?.addFeature(it) + // Apply the edits to the service on the service geodatabase. + serviceFeatureTable?.applyEdits() + // Update the feature to get the updated objectid - a temporary ID is used before the feature is added. + it.refresh() + // Confirm feature addition. + snackBarMessage = "Created feature ${it.attributes["objectid"]}" + } + } + } + /** * Selects a feature at the tapped location in preparation for deletion. */ diff --git a/app/src/main/java/de/jadehs/strassenschadenpro2/pages/CreatePage.kt b/app/src/main/java/de/jadehs/strassenschadenpro2/pages/CreatePage.kt index f46c5a8..574180a 100644 --- a/app/src/main/java/de/jadehs/strassenschadenpro2/pages/CreatePage.kt +++ b/app/src/main/java/de/jadehs/strassenschadenpro2/pages/CreatePage.kt @@ -22,9 +22,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp +import com.arcgismaps.geometry.Point +import com.arcgismaps.geometry.SpatialReference import com.arcgismaps.location.LocationDisplayAutoPanMode import com.arcgismaps.toolkit.geoviewcompose.MapView import com.arcgismaps.toolkit.geoviewcompose.rememberLocationDisplay +import com.google.android.gms.location.LocationServices import de.jadehs.strassenschadenpro2.MapViewModel import de.jadehs.strassenschadenpro2.components.DropDownMenuBox import kotlinx.coroutines.launch @@ -38,6 +41,8 @@ fun CreatePage(modifier: Modifier = Modifier, mapViewModel: MapViewModel) { var showModalBottomSheet = remember { mutableStateOf(false) } + var enableSaveButton = remember { mutableStateOf(false) } + val locationDisplay = rememberLocationDisplay().apply { setAutoPanMode(LocationDisplayAutoPanMode.Off) } @@ -60,6 +65,8 @@ fun CreatePage(modifier: Modifier = Modifier, mapViewModel: MapViewModel) { ) } + val fuesedLocationClient = remember { LocationServices.getFusedLocationProviderClient(context) } + Column(modifier = modifier.fillMaxSize() .padding(top = 16.dp, start = 16.dp, end = 16.dp, bottom = 16.dp), verticalArrangement = Arrangement.Top, @@ -69,6 +76,7 @@ fun CreatePage(modifier: Modifier = Modifier, mapViewModel: MapViewModel) { value = beschreibungTextFieldValue.value, onValueChange = { beschreibungTextFieldValue.value = it + enableSaveButton.value = beschreibungTextFieldValue.value.text.isNotEmpty() && currentDamageType.value.isNotEmpty() }, minLines = 5, label = { Text("Beschreibung")} @@ -81,15 +89,28 @@ fun CreatePage(modifier: Modifier = Modifier, mapViewModel: MapViewModel) { dropDownItemList = mapViewModel.damageTypeList, onIndexSelected = { index -> currentDamageType.value = mapViewModel.damageTypeList[index] - Log.d("CreatePage", "Selected index: $index") + enableSaveButton.value = beschreibungTextFieldValue.value.text.isNotEmpty() && currentDamageType.value.isNotEmpty() } ) Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly){ - Button(onClick = {}){ + Button(enabled = enableSaveButton.value, + onClick = { + fuesedLocationClient.lastLocation.addOnSuccessListener { location -> + location?.let { location-> + val beschreibung = beschreibungTextFieldValue.value.text + val typ = currentDamageType.value + var point = Point(location.longitude, + location.latitude, + SpatialReference.wgs84()) + mapViewModel.createFeatureWithAttributesAtPoint(point,beschreibung,typ) + } + } + }){ Text("GPS") } - Button(onClick = { + Button(enabled = enableSaveButton.value, + onClick = { showModalBottomSheet.value = !showModalBottomSheet.value }){ Text("Karte") @@ -98,6 +119,7 @@ fun CreatePage(modifier: Modifier = Modifier, mapViewModel: MapViewModel) { } if(showModalBottomSheet.value) { ModalBottomSheet( + sheetGesturesEnabled = false, onDismissRequest = { showModalBottomSheet.value = false } @@ -107,7 +129,12 @@ fun CreatePage(modifier: Modifier = Modifier, mapViewModel: MapViewModel) { arcGISMap = mapViewModel.map, locationDisplay = locationDisplay, mapViewProxy = mapViewModel.mapViewProxy, - onSingleTapConfirmed = mapViewModel::onTap, + onSingleTapConfirmed = { singleTapConfirmedEvent -> + val screenCoordinate = singleTapConfirmedEvent.screenCoordinate + val beschreibung = beschreibungTextFieldValue.value.text + val typ = currentDamageType.value + mapViewModel.createFeatureWithAttributesAt(screenCoordinate,beschreibung,typ) + }, ) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ab4236b..04132ab 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ junitVersion = "1.3.0" espressoCore = "3.7.0" lifecycleRuntimeKtx = "2.9.4" activityCompose = "1.11.0" -composeBom = "2024.09.00" +composeBom = "2025.12.00" arcgisMapsKotlin = "200.8.0" [libraries]