Cut a geometry along a polyline.
Use case
You might cut a polygon representing a large parcel to subdivide it into smaller parcels.
How to use the sample
Tap the "Cut" button to cut the polygon with the polyline and see the resulting parts (shaded in different colors).
How it works
- Pass the geometry and polyline to
GeometryEngine.cutto cut the geometry along the polyline. - Loop through the returned list of part geometries. Some of these geometries may be multi-part.
Relevant API
- GeometryEngine
- Polygon
- Polyline
Additional information
This sample uses the GeoView-Compose Toolkit module to be able to implement a composable MapView.
Tags
cut, geometry, geoview-compose, split, toolkit
Sample Code
/* 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.cutgeometry.components import android.app.Application import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.arcgismaps.Color import com.arcgismaps.geometry.GeometryEngine import com.arcgismaps.geometry.Point import com.arcgismaps.geometry.Polygon import com.arcgismaps.geometry.PolygonBuilder import com.arcgismaps.geometry.Polyline import com.arcgismaps.geometry.PolylineBuilder import com.arcgismaps.geometry.SpatialReference import com.arcgismaps.mapping.ArcGISMap import com.arcgismaps.mapping.BasemapStyle import com.arcgismaps.mapping.Viewpoint import com.arcgismaps.mapping.symbology.SimpleFillSymbol import com.arcgismaps.mapping.symbology.SimpleFillSymbolStyle import com.arcgismaps.mapping.symbology.SimpleLineSymbol import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle import com.arcgismaps.mapping.view.Graphic import com.arcgismaps.mapping.view.GraphicsOverlay import com.arcgismaps.toolkit.geoviewcompose.MapViewProxy import com.esri.arcgismaps.sample.cutgeometry.R import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch class CutGeometryViewModel(application: Application) : AndroidViewModel(application) { // create a map with the topographic basemap style val arcGISMap by mutableStateOf( ArcGISMap(BasemapStyle.ArcGISTopographic).apply { initialViewpoint = Viewpoint( latitude = 39.8, longitude = -98.6, scale = 10e7 ) } ) // create a MapViewProxy to interact with the MapView val mapViewProxy = MapViewProxy() // get a polygon corresponding to Lake Superior private val lakeSuperiorPolygon = makeLakeSuperior() // get a polyline that divides Lake Superior into a Canada side and US side private val borderPolyline = makeBorderPolyline() // create a graphic for the polygon representing Lake Superior private val polygonGraphic = Graphic( geometry = lakeSuperiorPolygon, symbol = SimpleFillSymbol( style = SimpleFillSymbolStyle.Solid, color = Color(R.color.transparentBlue), outline = SimpleLineSymbol( style = SimpleLineSymbolStyle.Solid, color = Color.blue, width = 2F ) ) ) // create a graphic for the cut line private val polylineGraphic = Graphic( geometry = borderPolyline, symbol = SimpleLineSymbol( style = SimpleLineSymbolStyle.Dot, color = Color.red, width = 3F ) ) // create a state flow to handle the reset button private val _isResetButtonEnabled = MutableStateFlow(false) val isResetButtonEnabled = _isResetButtonEnabled.asStateFlow() // create a state flow to handle the cut button private val _isCutButtonEnabled = MutableStateFlow(false) val isCutButtonEnabled = _isCutButtonEnabled.asStateFlow() // create a graphic overlay val graphicsOverlay = GraphicsOverlay() // create a message dialog view model for handling error messages val messageDialogVM = MessageDialogViewModel() init { viewModelScope.launch { arcGISMap.load().onFailure { error -> messageDialogVM.showMessageDialog( title = "Failed to load map", description = error.message.toString() ) }.onSuccess { graphicsOverlay.graphics.add(polygonGraphic) graphicsOverlay.graphics.add(polylineGraphic) polygonGraphic.geometry?.let { polygonToCut -> mapViewProxy.setViewpoint(Viewpoint(polygonToCut)) } _isCutButtonEnabled.value = true } } } /** * Clear the current graphics, then re-add the graphics for Lake Superior and the cut polyline. */ fun resetGeometry() { graphicsOverlay.graphics.clear() graphicsOverlay.graphics.add(polygonGraphic) graphicsOverlay.graphics.add(polylineGraphic) polygonGraphic.geometry?.let { polygonToCut -> mapViewProxy.setViewpoint(Viewpoint(polygonToCut)) } _isResetButtonEnabled.value = false _isCutButtonEnabled.value = true } /** * Cut the Lake Superior graphic into a US side and Canada side using the cut polyline * and then add the resulting graphics to the graphics overlay. */ fun cutGeometry() { polygonGraphic.geometry?.let { graphicGeometry -> val parts = GeometryEngine.tryCut( geometry = graphicGeometry, cutter = polylineGraphic.geometry as Polyline ) // create graphics for the US and Canada sides val canadaSide = Graphic( geometry = parts[0], symbol = SimpleFillSymbol( style = SimpleFillSymbolStyle.BackwardDiagonal, color = Color.green, outline = SimpleLineSymbol( style = SimpleLineSymbolStyle.Null, color = Color.blue, width = 0F ) ) ) val usSide = Graphic( geometry = parts[1], symbol = SimpleFillSymbol( style = SimpleFillSymbolStyle.ForwardDiagonal, color = Color.yellow, outline = SimpleLineSymbol( style = SimpleLineSymbolStyle.Null, color = Color.blue, width = 0F ) ) ) // add the graphics to the graphics overlay graphicsOverlay.graphics.addAll(listOf(canadaSide, usSide)) // update button state _isCutButtonEnabled.value = false _isResetButtonEnabled.value = true } } /** * Define a blue color for polygon boundary. */ private val Color.Companion.blue: Color get() { return fromRgba( r = 0, g = 0, b = 255, a = 255 ) } /** * Create a polygon corresponding to Lake Superior. */ private fun makeLakeSuperior() : Polygon { return PolygonBuilder(SpatialReference.webMercator()) { addPoint(Point(x = -10254374.668616, y = 5908345.076380)) addPoint(Point(x = -10178382.525314, y = 5971402.386779)) addPoint(Point(x = -10118558.923141, y = 6034459.697178)) addPoint(Point(x = -9993252.729399, y = 6093474.872295)) addPoint(Point(x = -9882498.222673, y = 6209888.368416)) addPoint(Point(x = -9821057.766387, y = 6274562.532928)) addPoint(Point(x = -9690092.583250, y = 6241417.023616)) addPoint(Point(x = -9605207.742329, y = 6206654.660191)) addPoint(Point(x = -9564786.389509, y = 6108834.986367)) addPoint(Point(x = -9449989.747500, y = 6095091.726408)) addPoint(Point(x = -9462116.153346, y = 6044160.821855)) addPoint(Point(x = -9417652.665244, y = 5985145.646738)) addPoint(Point(x = -9438671.768711, y = 5946341.148031)) addPoint(Point(x = -9398250.415891, y = 5922088.336339)) addPoint(Point(x = -9419269.519357, y = 5855797.317714)) addPoint(Point(x = -9467775.142741, y = 5858222.598884)) addPoint(Point(x = -9462924.580403, y = 5902686.086985)) addPoint(Point(x = -9598740.325877, y = 5884092.264688)) addPoint(Point(x = -9643203.813979, y = 5845287.765981)) addPoint(Point(x = -9739406.633691, y = 5879241.702350)) addPoint(Point(x = -9783061.694736, y = 5922896.763395)) addPoint(Point(x = -9844502.151022, y = 5936640.023354)) addPoint(Point(x = -9773360.570059, y = 6019099.583107)) addPoint(Point(x = -9883306.649729, y = 5968977.105610)) addPoint(Point(x = -9957681.938918, y = 5912387.211662)) addPoint(Point(x = -10055501.612742, y = 5871965.858842)) addPoint(Point(x = -10116942.069028, y = 5884092.264688)) addPoint(Point(x = -10111283.079633, y = 5933406.315128)) addPoint(Point(x = -10214761.742852, y = 5888134.399970)) addPoint(Point(x = -10254374.668616, y = 5901877.659929)) }.toGeometry() } /** * Create a polyline corresponding to the US/Canada border over Lake Superior. */ private fun makeBorderPolyline() : Polyline { return PolylineBuilder(SpatialReference.webMercator()) { addPoint(Point(x = -9981328.687124, y = 6111053.281447)) addPoint(Point(x = -9946518.044066, y = 6102350.620682)) addPoint(Point(x = -9872545.427566, y = 6152390.920079)) addPoint(Point(x = -9838822.617103, y = 6157830.083057)) addPoint(Point(x = -9446115.050097, y = 5927209.572793)) addPoint(Point(x = -9430885.393759, y = 5876081.440801)) addPoint(Point(x = -9415655.737420, y = 5860851.784463)) }.toGeometry() } }