Clip a geometry with another geometry.

Use case
Create a new set of geometries for analysis (e.g. displaying buffer zones around abandoned coal mine shafts in an area of planned urban development) by clipping intersecting geometries.
How to use the sample
Tap the “Clip” button to clip the blue graphic with the red dashed envelopes.
How it works
- Use the static method
GeometryEngine.clip()to generate a clippedGeometry, passing in an existingGeometryand anEnvelopeas parameters. The existing geometry will be clipped where it intersects an envelope. - Create a new
Graphicfrom the clipped geometry and add it to aGraphicsOverlayon theMapView.
Relevant API
- Envelope
- Geometry
- GeometryEngine.clip
- Graphic
- GraphicsOverlay
Additional information
Note: the resulting geometry may be null if the envelope does not intersect the geometry being clipped.
Tags
analysis, clip, geometry
Sample Code
/* Copyright 2024 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.clipgeometry
import android.os.Bundleimport androidx.activity.ComponentActivityimport androidx.activity.compose.setContentimport androidx.activity.enableEdgeToEdgeimport androidx.compose.material3.MaterialThemeimport androidx.compose.material3.Surfaceimport androidx.compose.runtime.Composableimport androidx.compose.ui.res.stringResourceimport com.arcgismaps.ApiKeyimport com.arcgismaps.ArcGISEnvironmentimport com.esri.arcgismaps.sample.sampleslib.theme.SampleAppThemeimport com.esri.arcgismaps.sample.clipgeometry.screens.ClipGeometryScreen
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)
enableEdgeToEdge() setContent { SampleAppTheme { ClipGeometryApp() } } }
@Composable private fun ClipGeometryApp() { Surface(color = MaterialTheme.colorScheme.background) { ClipGeometryScreen( sampleName = stringResource(R.string.clip_geometry_app_name) ) } }}/* Copyright 2024 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.clipgeometry.components
import android.app.Applicationimport androidx.compose.runtime.mutableStateOfimport androidx.lifecycle.AndroidViewModelimport androidx.lifecycle.viewModelScopeimport com.arcgismaps.Colorimport com.arcgismaps.geometry.Envelopeimport com.arcgismaps.geometry.GeometryEngineimport com.arcgismaps.geometry.Pointimport com.arcgismaps.mapping.ArcGISMapimport com.arcgismaps.mapping.BasemapStyleimport com.arcgismaps.mapping.Viewpointimport com.arcgismaps.mapping.symbology.SimpleFillSymbolimport com.arcgismaps.mapping.symbology.SimpleFillSymbolStyleimport com.arcgismaps.mapping.symbology.SimpleLineSymbolimport com.arcgismaps.mapping.symbology.SimpleLineSymbolStyleimport com.arcgismaps.mapping.view.Graphicimport com.arcgismaps.mapping.view.GraphicsOverlayimport com.esri.arcgismaps.sample.clipgeometry.Rimport com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModelimport kotlinx.coroutines.launchimport androidx.compose.runtime.getValueimport androidx.compose.runtime.setValue
class ClipGeometryViewModel(application: Application) : AndroidViewModel(application) { // create a map with a topographic basemap style val arcGISMap = ArcGISMap(BasemapStyle.ArcGISTopographic).apply { initialViewpoint = Viewpoint(40.0, -106.0, 10000000.0) }
private var coloradoGraphic = createColoradoGraphic() private var coloradoFillSymbol = SimpleFillSymbol( SimpleFillSymbolStyle.Solid, Color(R.color.transparentDarkBlue), SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.green, 2f) ) private var envelopesGraphics = createEnvelopeGraphics()
// graphics overlay to display graphics val graphicsOverlay = GraphicsOverlay().apply { graphics.add(coloradoGraphic) graphics.addAll(envelopesGraphics) }
// Create a message dialog view model for handling error messages val messageDialogVM = MessageDialogViewModel()
var isClipButtonEnabled by mutableStateOf(false) var isResetButtonEnabled by mutableStateOf(false)
init { viewModelScope.launch { arcGISMap.load().onFailure { error -> messageDialogVM.showMessageDialog( "Failed to load map", error.message.toString() ) }.onSuccess { // set the viewpoint of the map isClipButtonEnabled = true } } }
/** * Clear the current graphics, then re-add the graphics for Colorado and the clip boxes. */ fun resetGeometry() {
graphicsOverlay.graphics.clear()
graphicsOverlay.graphics.add(coloradoGraphic) coloradoGraphic.isVisible = true
envelopesGraphics.forEach { graphic -> graphicsOverlay.graphics.add(graphic) }
// update the reset button and clip button isResetButtonEnabled = false isClipButtonEnabled = true }
/** * Clip the Colorado geometry using the clip boxes and then add the result to the graphics overlay. */ fun clipGeometry() {
coloradoGraphic.isVisible = false
// go through each envelope and clip the colorado geometry using it envelopesGraphics.forEach { graphic -> val geometry = coloradoGraphic.geometry?.let { coloradoGeometry -> GeometryEngine.clipOrNull(coloradoGeometry, graphic.geometry as Envelope) } val clippedGraphic = Graphic(geometry, coloradoFillSymbol) graphicsOverlay.graphics.add(clippedGraphic) }
// update the reset button and clip button isResetButtonEnabled = true isClipButtonEnabled = false }
/** * Create a blue rectangular graphic of the state of Colorado. */ private fun createColoradoGraphic() : Graphic { // create a blue graphic of Colorado val colorado = Envelope( Point(-11362327.128340, 5012861.290274), Point(-12138232.018408, 4441198.773776) ) val fillSymbol = SimpleFillSymbol( SimpleFillSymbolStyle.Solid, Color(R.color.transparentDarkBlue), SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.green, 2f) ) val coloradoGraphic = Graphic(colorado, fillSymbol)
return coloradoGraphic }
/** * Create three envelopes, outside, inside and intersecting Colorado graphic. */ private fun createEnvelopeGraphics() : List<Graphic> { // create a dotted red outline symbol val redOutline = SimpleLineSymbol(SimpleLineSymbolStyle.Dot, Color.red, 3f)
// create a envelope outside Colorado val outsideEnvelope = Envelope( Point(-11858344.321294, 5147942.225174), Point(-12201990.219681, 5297071.577304) ) val outside = Graphic(outsideEnvelope, redOutline)
// create a envelope intersecting Colorado val intersectingEnvelope = Envelope( Point(-11962086.479298, 4566553.881363), Point(-12260345.183558, 4332053.378376) ) val intersecting = Graphic(intersectingEnvelope, redOutline)
// create a envelope inside Colorado val insideEnvelope = Envelope( Point(-11655182.595204, 4741618.772994), Point(-11431488.567009, 4593570.068343) ) val inside = Graphic(insideEnvelope, redOutline)
return listOf(outside, intersecting, inside) }
}/* Copyright 2024 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.clipgeometry.screens
import androidx.compose.foundation.layout.Arrangementimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.Rowimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.material3.Buttonimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.Textimport androidx.compose.runtime.Composableimport androidx.compose.ui.Modifierimport androidx.compose.ui.res.stringResourceimport androidx.compose.ui.unit.dpimport androidx.lifecycle.viewmodel.compose.viewModelimport com.arcgismaps.toolkit.geoviewcompose.MapViewimport com.esri.arcgismaps.sample.clipgeometry.Rimport com.esri.arcgismaps.sample.clipgeometry.components.ClipGeometryViewModelimport com.esri.arcgismaps.sample.sampleslib.components.MessageDialogimport com.esri.arcgismaps.sample.sampleslib.components.SampleTopAppBar
/** * Main screen layout for the sample app */@Composablefun ClipGeometryScreen(sampleName: String) { // create a ViewModel to handle MapView interactions val mapViewModel: ClipGeometryViewModel = viewModel()
Scaffold( topBar = { SampleTopAppBar(title = sampleName) }, content = { Column( modifier = Modifier .fillMaxSize() .padding(it), ) { MapView( modifier = Modifier .fillMaxSize() .weight(1f), arcGISMap = mapViewModel.arcGISMap, graphicsOverlays = listOf(mapViewModel.graphicsOverlay) ) Row( modifier = Modifier. fillMaxWidth(), horizontalArrangement = Arrangement.Center ) { Button( modifier = Modifier.padding(12.dp), enabled = mapViewModel.isResetButtonEnabled, onClick = { mapViewModel.resetGeometry() } ) { Text( text = stringResource(R.string.reset_button_text) ) } Button( modifier = Modifier.padding(12.dp), enabled = mapViewModel.isClipButtonEnabled, onClick = { mapViewModel.clipGeometry() } ) { Text( text = stringResource(R.string.clip_geometry_button_text) ) } } }
mapViewModel.messageDialogVM.apply { if (dialogStatus) { MessageDialog( title = messageTitle, description = messageDescription, onDismissRequest = ::dismissDialog ) } } } )}