diff --git a/identify-layer-features/README.md b/identify-layer-features/README.md index 816e71b8d..86e18d567 100644 --- a/identify-layer-features/README.md +++ b/identify-layer-features/README.md @@ -26,8 +26,9 @@ Tap to identify features. A bottom text banner will show all layers with feature ## Additional information +This sample uses the GeoCompose Toolkit module to be able to implement a Composable MapView. The GeoView supports two methods of identify: `identifyLayer`, which identifies features within a specific layer and `identifyLayers`, which identifies features for all layers in the current view. ## Tags -identify, recursion, recursive, sublayers +identify, geocompose, recursion, recursive, sublayers, toolkit diff --git a/identify-layer-features/README.metadata.json b/identify-layer-features/README.metadata.json index 8a0b8d3a1..78baccb42 100644 --- a/identify-layer-features/README.metadata.json +++ b/identify-layer-features/README.metadata.json @@ -8,9 +8,11 @@ ], "keywords": [ "identify", + "geocompose", "recursion", "recursive", "sublayers", + "toolkit", "IdentifyLayerResult", "IdentifyLayerResult.sublayerResults", "LayerContent" @@ -24,7 +26,6 @@ ], "snippets": [ "src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/MainActivity.kt", - "src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/ComposeMapView.kt", "src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/MapViewModel.kt", "src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/screens/MainScreen.kt" ], diff --git a/identify-layer-features/build.gradle b/identify-layer-features/build.gradle index 2a368331a..753cb6d08 100644 --- a/identify-layer-features/build.gradle +++ b/identify-layer-features/build.gradle @@ -45,4 +45,7 @@ dependencies { implementation "androidx.compose.ui:ui-tooling" implementation "androidx.compose.ui:ui-tooling-preview" implementation project(path: ':samples-lib') + // Toolkit dependencies + implementation(platform("com.esri:arcgis-maps-kotlin-toolkit-bom:$arcgisToolkitVersion")) + implementation('com.esri:arcgis-maps-kotlin-toolkit-geo-compose') } diff --git a/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/MainActivity.kt b/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/MainActivity.kt index 8b43d63dc..ac4844fa4 100644 --- a/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/MainActivity.kt +++ b/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/MainActivity.kt @@ -48,8 +48,7 @@ class MainActivity : ComponentActivity() { color = MaterialTheme.colorScheme.background ) { MainScreen( - sampleName = getString(R.string.app_name), - application = application + sampleName = getString(R.string.app_name) ) } } diff --git a/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/ComposeMapView.kt b/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/ComposeMapView.kt deleted file mode 100644 index 7bec430bb..000000000 --- a/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/ComposeMapView.kt +++ /dev/null @@ -1,89 +0,0 @@ -/* Copyright 2023 Esri - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package com.esri.arcgismaps.sample.identifylayerfeatures.components - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalLifecycleOwner -import androidx.compose.ui.viewinterop.AndroidView -import androidx.lifecycle.LifecycleOwner -import com.arcgismaps.mapping.view.MapView - -/** - * Wraps the MapView in a Composable function. - */ -@Composable -fun ComposeMapView( - modifier: Modifier = Modifier, - mapViewModel: MapViewModel -) { - // get an instance of the current lifecycle owner - val lifecycleOwner = LocalLifecycleOwner.current - // collect the latest state of the MapViewState - val mapViewState by mapViewModel.mapViewState.collectAsState() - // create and add MapView to the activity lifecycle - val mapView = createMapViewInstance(lifecycleOwner) - - // wrap the MapView as an AndroidView - AndroidView( - modifier = modifier, - factory = { mapView }, - // recomposes the MapView on changes in the MapViewState - update = { mapView -> - mapView.apply { - map = mapViewState.arcGISMap - setViewpoint(mapViewState.viewpoint) - } - } - ) - - // launch coroutine functions in the composition's CoroutineContext - LaunchedEffect(Unit) { - mapView.onSingleTapConfirmed.collect { - // call identifyLayers when a tap event occurs - val identifyResult = mapView.identifyLayers( - screenCoordinate = it.screenCoordinate, - tolerance = 12.0, - returnPopupsOnly = false, - maximumResults = 10 - ) - mapViewModel.handleIdentifyResult(identifyResult) - } - } -} - -/** - * Create the MapView instance and add it to the Activity lifecycle - */ -@Composable -fun createMapViewInstance(lifecycleOwner: LifecycleOwner): MapView { - // create the MapView - val mapView = MapView(LocalContext.current) - // add the side effects for MapView composition - DisposableEffect(lifecycleOwner) { - lifecycleOwner.lifecycle.addObserver(mapView) - onDispose { - lifecycleOwner.lifecycle.removeObserver(mapView) - } - } - return mapView -} diff --git a/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/MapViewModel.kt b/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/MapViewModel.kt index e1b216712..19a39a87a 100644 --- a/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/MapViewModel.kt +++ b/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/components/MapViewModel.kt @@ -31,15 +31,23 @@ import com.arcgismaps.mapping.view.IdentifyLayerResult import com.esri.arcgismaps.sample.identifylayerfeatures.R import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch class MapViewModel( application: Application, private val sampleCoroutineScope: CoroutineScope ) : AndroidViewModel(application) { - // set the MapView mutable stateflow - val mapViewState = MutableStateFlow(MapViewState()) + + // create an ArcGISMap and Viewpoint + var arcGISMap: ArcGISMap = ArcGISMap(BasemapStyle.ArcGISNavigationNight) + var viewpoint: Viewpoint = Viewpoint( + center = Point( + x = -10977012.785807, + y = 4514257.550369, + spatialReference = SpatialReference(wkid = 3857) + ), + scale = 68015210.0 + ) // create a ViewModel to handle dialog interactions val messageDialogVM: MessageDialogViewModel = MessageDialogViewModel() @@ -74,7 +82,7 @@ class MapViewModel( operationalLayers.add(featureLayer) } // assign the map to the map view - mapViewState.value.arcGISMap = map + arcGISMap = map } /** @@ -129,19 +137,4 @@ class MapViewModel( } return subLayerGeoElementCount + result.geoElements.size } -} - -/** - * Data class that represents the MapView state - */ -data class MapViewState( - var arcGISMap: ArcGISMap = ArcGISMap(BasemapStyle.ArcGISNavigationNight), - var viewpoint: Viewpoint = Viewpoint( - center = Point( - x = -10977012.785807, - y = 4514257.550369, - spatialReference = SpatialReference(wkid = 3857) - ), - scale = 68015210.0 - ) -) +} \ No newline at end of file diff --git a/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/screens/MainScreen.kt b/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/screens/MainScreen.kt index e81ad166d..8200b54c8 100644 --- a/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/screens/MainScreen.kt +++ b/identify-layer-features/src/main/java/com/esri/arcgismaps/sample/identifylayerfeatures/screens/MainScreen.kt @@ -29,21 +29,30 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp -import com.esri.arcgismaps.sample.identifylayerfeatures.components.ComposeMapView +import com.arcgismaps.toolkit.geocompose.MapView +import com.arcgismaps.toolkit.geocompose.MapViewProxy +import com.arcgismaps.toolkit.geocompose.MapViewpointOperation import com.esri.arcgismaps.sample.identifylayerfeatures.components.MapViewModel import com.esri.arcgismaps.sample.sampleslib.components.MessageDialog import com.esri.arcgismaps.sample.sampleslib.components.SampleTopAppBar +import kotlinx.coroutines.launch /** * Main screen layout for the sample app */ @Composable -fun MainScreen(sampleName: String, application: Application) { +fun MainScreen(sampleName: String) { // coroutineScope that will be cancelled when this call leaves the composition val sampleCoroutineScope = rememberCoroutineScope() + // get the application property that will be used to construct MapViewModel + val sampleApplication = LocalContext.current.applicationContext as Application // create a ViewModel to handle MapView interactions - val mapViewModel = remember { MapViewModel(application, sampleCoroutineScope) } + val mapViewModel = remember { MapViewModel(sampleApplication, sampleCoroutineScope) } + // create a mapViewProxy that will be used to identify features in the MapView + // should also be passed to the MapView composable this mapViewProxy is associated with + val mapViewProxy = MapViewProxy() Scaffold( topBar = { SampleTopAppBar(title = sampleName) }, @@ -53,13 +62,24 @@ fun MainScreen(sampleName: String, application: Application) { .fillMaxSize() .padding(it) ) { - // composable function that wraps the MapView - ComposeMapView( + MapView( modifier = Modifier .fillMaxSize() .weight(1f) .animateContentSize(), - mapViewModel = mapViewModel + arcGISMap = mapViewModel.arcGISMap, + viewpointOperation = MapViewpointOperation.Set(viewpoint = mapViewModel.viewpoint), + mapViewProxy = mapViewProxy, + onSingleTapConfirmed = { singleTapConfirmedEvent -> + sampleCoroutineScope.launch { + val identifyResult = mapViewProxy.identifyLayers( + screenCoordinate = singleTapConfirmedEvent.screenCoordinate, + tolerance = 12.dp, + maximumResults = 10 + ) + mapViewModel.handleIdentifyResult(identifyResult) + } + } ) // Bottom text to display the identify results Row( diff --git a/version.gradle b/version.gradle index 66a833761..87d5bde8a 100644 --- a/version.gradle +++ b/version.gradle @@ -2,7 +2,7 @@ ext { // ArcGIS Maps SDK for Kotlin version arcgisVersion = '200.4.0-4085' // ArcGIS Maps SDK for Kotlin Toolkit version - arcgisToolkitVersion = '200.3.0-1' + arcgisToolkitVersion = '200.4.0-1' // SDK versions compileSdkVersion = 34 minSdkVersion = 26