Code examples for this blog are written in C# using ArcGIS Maps SDK for .NET; but the Geometry Editor concepts all apply to Java, Kotlin, Qt, and Swift.
The 200.2 release of the ArcGIS Maps SDKs for Native Apps paves the way to a brand new GeometryEditor in .NET. Learning from developing the SketchEditor, we made significant improvements in its replacement. It is now built from the same core C++ to provide your applications a consistent, intuitive, easy to use, highly customizable, more reliable, and performant editor. This single core implementation will enable us to fulfill your most requested enhancements, like snapping and curve editing, more efficiently across all the Native SDKs.
In this article, I will cover the following topics to help you decide when and how to navigate the code migration process for .NET:
Functional equivalence
The SketchEditor is deprecated in the 200.2 release, replaced with an API that has a set of comparable functionalities.
To name a few, the GeometryEditor enables you to:
- interactively or programmatically create or update a point, polyline, polygon, and multipoint geometries
- edit vertices, draw in freehand mode, or start with predefined shapes
- transform geometries by inserting or deleting vertices, moving, rotating, or scaling the geometries
- undo and redo any edit made on the geometry and be notified when the geometry changes
- update the visibility and appearance of the tool handles used in vertex editing, rotation, and scale
You will notice that the start methods take remarkably similar parameters. There are two main groups: Draw, creates a new geometry based on type, and edit, updates a specified geometry. The GeometryEditor in support of these operations responds to the same map interaction using mouse, stylus, or touch devices; and similar routine calls for insert, move, delete, and replace.
The following table highlights the equivalent support for drawing and editing in the GeometryEditor.
Draw |
|
StartAsync(SketchCreationMode, bool) : Task<Geometry> | Start(GeometryType) |
StartAsync(SketchCreationMode, SketchEditConfiguration) : Task<Geometry> | |
Edit |
|
StartAsync(Geometry) : Task<Geometry> | Start(Geometry) |
StartAsync(Geometry, SketchCreationMode) : Task<Geometry> | |
StartAsync(Geometry, SketchCreationMode, SketchEditConfiguration) : Task<Geometry> |
You will notice that the SketchEditConfiguration parameter remains an accessible configuration from the Tool property in the GeometryEditor. The SketchEditor and its supporting classes have a counterpart in the GeometryEditor, and they are as follows:
Improvements
Given the migration table above, you might ask why the SketchCreationMode enum has three items replacing it. The GeometryEditor (through the Tool property) enables vertex editing, freehand drawing, or transforming shapes with VertexTool, FreehandTool or ShapeTool, respectively.
Previously, predefined shapes were only available when creating a polygon geometry.
Notice that while there is no circle type, the ShapeTool’s Configuration property has a ScaleMode that, when set to Uniform, will create a circle:
var tool = ShapeTool.Create(ShapeToolType.Ellipse); tool.Configuration.ScaleMode = GeometryEditorScaleMode.Uniform;
The animation below demonstrates interactively drawing geometries with the GeometryEditor.
With the Tool property, you can also configure allowable transformations for interaction and customize the style of these tool handles without interrupting the edit session.
The selected element reports which interaction options are available through its properties.
The animation below demonstrates interactively editing geometries while customizing tool handles without interrupting the GeometryEditor’s edit session.
With the GeometryEditor, you can do more than update a single vertex programmatically. The select, delete, and move methods act on a SelectedElement property. This element can be the entire geometry, a vertex, a mid-vertex, or a single part in a multipart geometry.
To add a new part, enable the Tool’s part creation and clear all selection before the next interaction or call to insert vertex. For transformation to apply on all parts, select the entire geometry before the next interaction or call to rotate/scale.
The animation below demonstrates drawing and editing a multipart polygon with the GeometryEditor.
.NET support
One noticeable difference .NET developers may find is the absence of ICommand properties and task-based asynchronous methods in the GeometryEditor. While data binding is supported with all GeometryEditor classes implementing INotifyPropertyChanged, ICommand properties that update the state of controls is usually coupled with business logic. Therefore, commands are best implemented in your own ViewModel.
Some examples of data binding are:
- A one-way binding to the GeometryEditor’s IsStarted (formerly, IsEnabled) property can change the visibility of your controls.
- A two-way binding to a slider’s value property can directly update the Opacity of the GeometryEditor’s tool handles.
Unlike these simple properties, commands tend to do more than update the View and perform operations on the Model. So, the GeometryEditor exposes methods and properties that will help you build commands instead. We realized that the presumptions we made in completing your draw or edit session in the SketchEditor should account for your application logic.
Commands use Methods/Properties |
|
AddCommand | InsertVertex(MapPoint) when IsStarted |
DeleteCommand | DeleteSelectedElement() when SelectedElement exists |
UndoCommand | Undo() when CanUndo |
RedoCommand | Redo() when CanRedo |
CancelCommand | Stop() when IsStarted |
CompleteCommand | Stop() when IsStarted and Geometry passes validation |
Previously, the StartAsync() method with drawAndEdit: false, when awaited, provided a convenient way to return a completed geometry using the following gestures for these creation modes:
- point: tap
- polyline/polygon: double tap
- freehand or shapes: pointer up or release
However, with this logic baked into the SketchEditor, you were unable to define the gesture, key combination, or state that determines the readiness of the geometry. With more control, you could include logic to do things like validate more than just the minimum number of vertices for this geometry, add multiple parts to this geometry, or perhaps even tie this draw or edit with a GeometryEngine method that cuts, reshapes, or auto completes with another geometry.
Asynchronous start task with geometry result
To create an asynchronous start task similar to what the SketchEditor provided, use a TaskCompletionSource<Geometry> to set the geometry result and handle its cancellation or any exception.
- To set the geometry result:
- To handle cancellation or an exception:
- Set canceled when IsStarted becomes false
- Set exception to propagate any error that occurs
The code example below assumes your ViewModel has created a GeometryEditor instance used by your MapView. For brevity of this snippet, I’m using the GeometryEditor’s PropertyChanged event. You can use the appropriate geometry builder’s IsSketchValid or be more creative about validating the geometry. The code awaiting the result of this start method should then be prepared to handle TaskCanceledException.
public Task<Geometry> StartAsync(GeometryType geometryType) { var tcs = new TaskCompletionSource<Geometry>(); PropertyChangedEventHandler? onPropertyChanged = null; onPropertyChanged = (s, e) => { if (e.PropertyName == nameof(GeometryEditor.Geometry) && IsSketchValid() && _geometryEditor.Stop() is Geometry geometry) { _geometryEditor.PropertyChanged -= onPropertyChanged; tcs.TrySetResult(geometry); } else if (e.PropertyName == nameof(GeometryEditor.IsStarted) && !_geometryEditor.IsStarted) { _geometryEditor.PropertyChanged -= onPropertyChanged; tcs.TrySetCanceled(); } }; _geometryEditor.PropertyChanged += onPropertyChanged; try { _geometryEditor.Start(geometryType); } catch (Exception ex) { tcs.TrySetException(ex); } return tcs.Task; }
You can also call Stop() when the ENTER or ESC keys are pressed or when certain conditions in your app are satisfied. You could enforce certain feature attribute values with the geometry change, snap updated geometry with another feature, or ensure that the edit abides by specific rules. For example, an update to a utility network feature should be backed by network rules.
ICommand properties
To create ICommand properties, it is important to know the following:
- what it needs to do (action),
- when it can do it (predicate), and
- when its ability to do it changes (raise predicate changed).
You can use the GeometryEditor methods and subscribe to a PropertyChanged event to be notified when a method can execute. Using .NET Community Toolkit’s RelayCommand, an undo command can be implemented this way:
private IRelayCommand? _undoCommand; public IRelayCommand UndoCommand => _undoCommand ??= new RelayCommand(() => _geometryEditor.Undo(), () => _geometryEditor.CanUndo); private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(GeometryEditor.CanUndo)) UndoCommand.NotifyCanExecuteChanged(); }
Conclusion
I hope this article has helped illustrate how the GeometryEditor now supersedes the legacy SketchEditor in capabilities. With:
- Better shape support, you can transform new shapes and create predefined shapes as line geometries.
- Enhanced configuration, you can refine allowable transformations to a specific geometry editor element.
- Complete multipart support, you can create, update, move, rotate, or scale individual parts.
- Improved programmatic support, you can now call rotate or scale; and have delete and move methods act on selected element.
By providing your applications greater flexibility, you can tailor the GeometryEditor to satisfy existing SketchEditor workflows. With more fine-grained control, you can incorporate your business requirements. For .NET developers migrating to GeometryEditor from SketchEditor, while data binding comes for free, ICommand and asynchronous tasks need to move to your ViewModel using equivalent GeometryEditor methods and properties.
I invite you to try out the new Create and edit geometries sample available in all platforms in the Native SDKs GitHub repos to see the GeometryEditor in action. You can also read more about capturing geometry edits for query and edit or spatial and data analysis.
In the future, you can expect enhancements like snapping, rule-based editing, curve support, and more in the GeometryEditor. The SketchEditor and related classes will be removed in the next major release. I therefore encourage you to migrate your code as soon as possible.
I look forward to hearing how you have enhanced your application with the GeometryEditor. Please send your feedback through the ArcGIS Maps SDKs for Native Apps Esri Community.
Commenting is not enabled for this article.