-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create and edit geometries sample basic implementation (#306)
- Loading branch information
Showing
10 changed files
with
461 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Create and edit geometries |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
"category": "Edit and Manage Data", | ||
"description": "TODO", | ||
"formal_name": "CreateAndEditGeometries", | ||
"ignore": false, | ||
"images": [ | ||
"create-and-edit-geometries.png" | ||
], | ||
"keywords": [ ], | ||
"language": "kotlin", | ||
"redirect_from": "", | ||
"relevant_apis": [ ], | ||
"snippets": [ | ||
"src/main/java/com/esri/arcgismaps/sample/createandeditgeometries/CreateAndEditGeometriesViewModel.kt", | ||
"src/main/java/com/esri/arcgismaps/sample/createandeditgeometries/CreateAndEditGeometriesScreen.kt", | ||
"src/main/java/com/esri/arcgismaps/sample/createandeditgeometries/MainActivity.kt" | ||
], | ||
"title": "Create and edit geometries" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
plugins { | ||
alias(libs.plugins.arcgismaps.android.library) | ||
alias(libs.plugins.arcgismaps.android.library.compose) | ||
alias(libs.plugins.arcgismaps.kotlin.sample) | ||
alias(libs.plugins.gradle.secrets) | ||
} | ||
|
||
secrets { | ||
// this file doesn't contain secrets, it just provides defaults which can be committed into git. | ||
defaultPropertiesFileName = "secrets.defaults.properties" | ||
} | ||
|
||
android { | ||
namespace = "com.esri.arcgismaps.sample.createandeditgeometries" | ||
buildFeatures { | ||
buildConfig = true | ||
} | ||
} | ||
|
||
dependencies { | ||
// Only module specific dependencies needed here | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions
14
samples/create-and-edit-geometries/src/main/AndroidManifest.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||
|
||
<uses-permission android:name="android.permission.INTERNET" /> | ||
|
||
<application><activity | ||
android:exported="true" | ||
android:name=".MainActivity" | ||
android:label="@string/display_composable_map_view_app_name"> | ||
|
||
</activity> | ||
</application> | ||
|
||
</manifest> |
53 changes: 53 additions & 0 deletions
53
...ometries/src/main/java/com/esri/arcgismaps/sample/createandeditgeometries/MainActivity.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/* Copyright 2025 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.createandeditgeometries | ||
|
||
import android.os.Bundle | ||
import androidx.activity.ComponentActivity | ||
import androidx.activity.compose.setContent | ||
import androidx.compose.material3.MaterialTheme | ||
import androidx.compose.material3.Surface | ||
import androidx.compose.runtime.Composable | ||
import com.arcgismaps.ApiKey | ||
import com.arcgismaps.ArcGISEnvironment | ||
import com.esri.arcgismaps.sample.sampleslib.theme.SampleAppTheme | ||
import com.esri.arcgismaps.sample.createandeditgeometries.screens.CreateAndEditGeometriesScreen | ||
|
||
class MainActivity : ComponentActivity() { | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
// authentication with an API key or named user is | ||
// required to access basemaps and other location services | ||
ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.ACCESS_TOKEN) | ||
|
||
setContent { | ||
SampleAppTheme { | ||
CreateAndEditGeometriesApp() | ||
} | ||
} | ||
} | ||
|
||
@Composable | ||
private fun CreateAndEditGeometriesApp() { | ||
Surface(color = MaterialTheme.colorScheme.background) { | ||
CreateAndEditGeometriesScreen( | ||
sampleName = getString(R.string.create_and_edit_geometries_app_name) | ||
) | ||
} | ||
} | ||
} |
167 changes: 167 additions & 0 deletions
167
.../arcgismaps/sample/createandeditgeometries/components/CreateAndEditGeometriesViewModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
/* Copyright 2025 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.createandeditgeometries.components | ||
|
||
import android.app.Application | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.ui.unit.dp | ||
import androidx.lifecycle.AndroidViewModel | ||
import androidx.lifecycle.viewModelScope | ||
import com.arcgismaps.geometry.GeometryType | ||
import com.arcgismaps.geometry.Multipoint | ||
import com.arcgismaps.geometry.Point | ||
import com.arcgismaps.geometry.Polygon | ||
import com.arcgismaps.geometry.Polyline | ||
import com.arcgismaps.mapping.ArcGISMap | ||
import com.arcgismaps.mapping.BasemapStyle | ||
import com.arcgismaps.mapping.Viewpoint | ||
import com.arcgismaps.mapping.view.Graphic | ||
import com.arcgismaps.mapping.view.GraphicsOverlay | ||
import com.arcgismaps.mapping.view.SingleTapConfirmedEvent | ||
import com.arcgismaps.mapping.view.geometryeditor.GeometryEditor | ||
import com.arcgismaps.mapping.view.geometryeditor.GeometryEditorStyle | ||
import com.arcgismaps.toolkit.geoviewcompose.MapViewProxy | ||
import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel | ||
import kotlinx.coroutines.launch | ||
|
||
class CreateAndEditGeometriesViewModel(application: Application) : AndroidViewModel(application) { | ||
// create a map with the imagery basemap style | ||
val arcGISMap by mutableStateOf( | ||
ArcGISMap(BasemapStyle.ArcGISImagery).apply { | ||
// a viewpoint centered at the island of Inis Meáin (Aran Islands) in Ireland | ||
initialViewpoint = Viewpoint( | ||
latitude = 53.08230, | ||
longitude = -9.5920, | ||
scale = 5000.0 | ||
) | ||
} | ||
) | ||
|
||
// create a message dialog view model for handling error messages | ||
val messageDialogVM = MessageDialogViewModel() | ||
|
||
// create a MapViewProxy that will be used to identify features in the MapView and set the viewpoint | ||
val mapViewProxy = MapViewProxy() | ||
|
||
// create a geometryEditorStyle | ||
private val geometryEditorStyle = GeometryEditorStyle() | ||
// create a graphic to hold graphics identified on tap | ||
private var identifiedGraphic = Graphic() | ||
// create a graphics overlay | ||
val graphicsOverlay = GraphicsOverlay() | ||
// create a geometry editor | ||
val geometryEditor = GeometryEditor() | ||
|
||
init { | ||
viewModelScope.launch { | ||
// load the map | ||
arcGISMap.load().onFailure { error -> | ||
messageDialogVM.showMessageDialog( | ||
title = "Failed to load map", | ||
description = error.message.toString() | ||
) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Starts the GeometryEditor using the selected [GeometryType]. | ||
*/ | ||
fun startEditor(selectedGeometry: GeometryType) { | ||
if (!geometryEditor.isStarted.value) { | ||
geometryEditor.start(selectedGeometry) | ||
} | ||
} | ||
|
||
/** | ||
* Stops the GeometryEditor and updates the identified graphic or calls [createGraphic]. | ||
*/ | ||
fun stopEditor() { | ||
// check if there was a previously identified graphic | ||
if (identifiedGraphic.geometry != null) { | ||
// update the identified graphic | ||
identifiedGraphic.geometry = geometryEditor.stop() | ||
// deselect the identified graphic | ||
identifiedGraphic.isSelected = false | ||
} else if (geometryEditor.isStarted.value) { | ||
// create a graphic from the geometry that was being edited | ||
createGraphic() | ||
} | ||
} | ||
|
||
/** | ||
* Creates a graphic from the geometry and adds it to the GraphicsOverlay. | ||
*/ | ||
private fun createGraphic() { | ||
// stop the geometry editor and get its final geometry state | ||
val geometry = geometryEditor.stop() | ||
?: return messageDialogVM.showMessageDialog( | ||
title = "Error!", | ||
description = "Error stopping editing session" | ||
) | ||
|
||
// create a graphic to represent the new geometry | ||
val graphic = Graphic(geometry) | ||
|
||
// give the graphic an appropriate fill based on the geometry type | ||
when (geometry) { | ||
is Point, is Multipoint -> graphic.symbol = geometryEditorStyle.vertexSymbol | ||
is Polyline -> graphic.symbol = geometryEditorStyle.lineSymbol | ||
is Polygon -> graphic.symbol = geometryEditorStyle.fillSymbol | ||
else -> {} | ||
} | ||
// add the graphic to the graphics overlay | ||
graphicsOverlay.graphics.add(graphic) | ||
// deselect the graphic | ||
graphic.isSelected = false | ||
} | ||
|
||
/** | ||
* Identifies the graphic at the tapped screen coordinate in the provided [singleTapConfirmedEvent] | ||
* and starts the GeometryEditor using the identified graphic's geometry. Hide the BottomSheet on | ||
* [singleTapConfirmedEvent]. | ||
*/ | ||
fun identify(singleTapConfirmedEvent: SingleTapConfirmedEvent) { | ||
viewModelScope.launch { | ||
// attempt to identify a graphic at the location the user tapped | ||
val graphicsResult = mapViewProxy.identifyGraphicsOverlays( | ||
screenCoordinate = singleTapConfirmedEvent.screenCoordinate, | ||
tolerance = 10.0.dp, | ||
returnPopupsOnly = false | ||
).getOrNull() | ||
|
||
if (!geometryEditor.isStarted.value) { | ||
if (graphicsResult != null) { | ||
if (graphicsResult.isNotEmpty()) { | ||
// get the tapped graphic | ||
identifiedGraphic = graphicsResult.first().graphics.first() | ||
// select the graphic | ||
identifiedGraphic.isSelected = true | ||
// start the geometry editor with the identified graphic | ||
identifiedGraphic.geometry?.let { | ||
geometryEditor.start(it) | ||
} | ||
} | ||
} | ||
// reset the identified graphic back to null | ||
identifiedGraphic.geometry = null | ||
} | ||
} | ||
} | ||
|
||
} |
107 changes: 107 additions & 0 deletions
107
...es/src/main/java/com/esri/arcgismaps/sample/createandeditgeometries/screens/ButtonMenu.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
/* Copyright 2025 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.createandeditgeometries.screens | ||
|
||
import androidx.compose.foundation.layout.Box | ||
import androidx.compose.foundation.layout.Row | ||
import androidx.compose.foundation.layout.fillMaxWidth | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.material.icons.Icons | ||
import androidx.compose.material.icons.filled.Check | ||
import androidx.compose.material.icons.filled.Create | ||
import androidx.compose.material3.DropdownMenu | ||
import androidx.compose.material3.DropdownMenuItem | ||
import androidx.compose.material3.Icon | ||
import androidx.compose.material3.IconButton | ||
import androidx.compose.material3.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.runtime.setValue | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.unit.dp | ||
import com.arcgismaps.geometry.GeometryType | ||
|
||
/** | ||
* Composable component to display the menu buttons. | ||
*/ | ||
@Composable | ||
fun ButtonMenu( | ||
isGeometryEditorStarted: Boolean, | ||
onStartEditingButtonClick: (GeometryType) -> Unit, | ||
onStopEditingButtonClick: () -> Unit | ||
) { | ||
val rowModifier = Modifier | ||
.padding(12.dp) | ||
.fillMaxWidth() | ||
|
||
Row( | ||
modifier = rowModifier | ||
) { | ||
var expanded by remember { mutableStateOf(false) } | ||
Box( | ||
modifier = Modifier | ||
) { | ||
IconButton( | ||
enabled = !isGeometryEditorStarted, | ||
onClick = { expanded = !expanded } | ||
) { | ||
Icon(imageVector = Icons.Default.Create, contentDescription = "Start") | ||
} | ||
DropdownMenu( | ||
expanded = expanded, | ||
onDismissRequest = { expanded = false } | ||
) { | ||
DropdownMenuItem( | ||
text = { Text("Point") }, | ||
onClick = { | ||
onStartEditingButtonClick(GeometryType.Point) | ||
expanded = false | ||
} | ||
) | ||
DropdownMenuItem( | ||
text = { Text("Multipoint") }, | ||
onClick = { | ||
onStartEditingButtonClick(GeometryType.Multipoint) | ||
expanded = false | ||
} | ||
) | ||
DropdownMenuItem( | ||
text = { Text("Polyline") }, | ||
onClick = { | ||
onStartEditingButtonClick(GeometryType.Polyline) | ||
expanded = false | ||
} | ||
) | ||
DropdownMenuItem( | ||
text = { Text("Polygon") }, | ||
onClick = { | ||
onStartEditingButtonClick(GeometryType.Polygon) | ||
expanded = false | ||
} | ||
) | ||
} | ||
} | ||
IconButton( | ||
enabled = isGeometryEditorStarted, | ||
onClick = { onStopEditingButtonClick() } | ||
) { | ||
Icon(imageVector = Icons.Default.Check, contentDescription = "Save Edits") | ||
} | ||
} | ||
} |
Oops, something went wrong.