Use the Geometry Editor to edit geometries using utility network connectivity rules.
Use case
A field worker can create new features in a utility network by editing and snapping the vertices of a geometry to existing features on a map. In a gas utility network, gas pipeline features can be represented with the polyline geometry type. Utility networks use geometric coincident-based connectivity to provide pathways for resources. Rule-based snapping uses utility network connectivity rules when editing features based on their asset type and asset group to help maintain network connectivity.
How to use the sample
To edit a geometry, tap a point geometry to be edited in the map to select it. Then edit the geometry by clicking the button to start the geometry editor.
Snap sources can be enabled and disabled. Snapping will not occur when SnapRuleBehavior.RulesPreventSnapping
even when the source is enabled.
To interactively snap a vertex to a feature or graphic, ensure that snapping is enabled for the relevant snap source, then move the mouse pointer or drag a vertex close to an existing feature or graphic. If the existing feature or graphic has valid utility network connectivity rules for the asset type that is being created or edited, the edit position will be adjusted to coincide with (or snap to) edges and vertices of its geometry. Click or release the touch pointer to place the vertex at the snapped location.
To discard changes and stop the geometry editor, press the discard button.
To save your edits, press the save button.
How it works
-
Create a map with
LoadSettings.FeatureTilingMode
set toEnabledWithFullResolutionWhenSupported
. -
Create a
Geodatabase
using the mobile geodatabase file location. -
Display
Geodatabase.FeatureTables
on the map using subtype feature layers. -
Create a
GeometryEditor
and connect it to the map view. -
When editing a feature:
a. Call
SnapRules.CreateAsync(UtilityNetwork, UtilityAssetType)
to get the snap rules associated with a givenUtilityAssetType
.b. Use
SyncSourceSettings(SnapRules, SnapSourceEnablingBehavior.SetFromRules)
to populate theSnapSettings.SourceSettings
withSnapSourceSettings
enabling the sources with rules. -
Start the geometry editor with an existing geometry or
GeometryType.Point
.
Relevant API
- FeatureLayer
- Geometry
- GeometryEditor
- GeometryEditorStyle
- GraphicsOverlay
- MapView
- SnapRuleBehavior
- SnapRules
- SnapSettings
- SnapSource
- SnapSourceEnablingBehavior
- SnapSourceSettings
- UtilityNetwork
Offline data
Read more about how to set up the sample's offline data here.
Link | Local Location |
---|---|
Naperville gas network | <userhome> /ArcGIS/Runtime/Data/raster/NapervilleGasUtilities.geodatabase |
About the data
The Naperville gas network mobile geodatabase contains a utility network with a set of connectivity rules that can be used to perform geometry edits with rules based snapping.
Tags
edit, feature, geometry editor, graphics, layers, map, snapping, utility network
Sample Code
// [Legal]
// 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.
// [Legal]
#ifndef SNAPGEOMETRYEDITSWITHRULES_H
#define SNAPGEOMETRYEDITSWITHRULES_H
// sample headers
#include "SnapSourcesListModel.h"
// Qt headers
#include <QImage>
#include <QObject>
template <typename T> class QFuture;
class QMouseEvent;
namespace Esri::ArcGISRuntime {
class ArcGISFeature;
class Geodatabase;
class GeometryEditor;
class IdentifyLayerResult;
class Map;
class MapQuickView;
class Renderer;
class SimpleLineSymbol;
class SnapSourceSettings;
class SubtypeFeatureLayer;
class SubtypeSublayer;
class UtilityAssetType;
}
Q_MOC_INCLUDE("MapQuickView.h");
struct LoadOperationalLayersReturnStruct
{
Esri::ArcGISRuntime::SubtypeFeatureLayer* m_pipeSubtypeLayer = nullptr;
Esri::ArcGISRuntime::SubtypeFeatureLayer* m_deviceSubtypeLayer = nullptr;
};
class SnapGeometryEditsWithRules : public QObject
{
Q_OBJECT
Q_PROPERTY(Esri::ArcGISRuntime::MapQuickView* mapView READ mapView WRITE setMapView NOTIFY mapViewChanged)
Q_PROPERTY(bool geometryEditorStarted READ geometryEditorStarted NOTIFY geometryEditorStartedChanged)
Q_PROPERTY(bool isElementSelected READ isElementSelected NOTIFY isElementSelectedChanged)
Q_PROPERTY(SnapSourcesListModel* snapSourcesListModel READ snapSourcesListModel NOTIFY snapSourceModelChanged)
public:
explicit SnapGeometryEditsWithRules(QObject* parent = nullptr);
~SnapGeometryEditsWithRules() override;
static void init();
Q_INVOKABLE void startEditor();
Q_INVOKABLE void stopEditing();
Q_INVOKABLE void discardEdits();
Esri::ArcGISRuntime::MapQuickView* mapView() const;
void setMapView(Esri::ArcGISRuntime::MapQuickView* mapView);
bool isElementSelected() const;
bool geometryEditorStarted() const;
SnapSourcesListModel* snapSourcesListModel() const;
signals:
void mapViewChanged();
void isElementSelectedChanged();
void geometryEditorStartedChanged();
void snapSourceModelChanged();
void assetGroupChanged(const QString& assetGroup);
void assetTypeChanged(const QString& assetType);
private slots:
void onMapViewClicked(const QMouseEvent& mouseEvent);
private:
void initializeMap();
QFuture<void> loadGeodatabase();
QFuture<LoadOperationalLayersReturnStruct> loadOperationalLayers();
QFuture<void> loadUtilityNetwork();
void modifyOperationalLayersVisibility(Esri::ArcGISRuntime::SubtypeFeatureLayer* pipeLayer, Esri::ArcGISRuntime::SubtypeFeatureLayer* deviceLayer);
void updateSnapSourceRenderers(const QList<Esri::ArcGISRuntime::SnapSourceSettings*>& snapSourceSettings);
Esri::ArcGISRuntime::ArcGISFeature* getFeatureFromResult(const QList<Esri::ArcGISRuntime::IdentifyLayerResult*>& identifyResult);
void setSnapSettings(Esri::ArcGISRuntime::UtilityAssetType* assetType);
void resetSelections();
Esri::ArcGISRuntime::Map* m_map = nullptr;
Esri::ArcGISRuntime::MapQuickView* m_mapView = nullptr;
Esri::ArcGISRuntime::Geodatabase* m_geodatabase = nullptr;
SnapSourcesListModel* m_snapSourcesListModel = nullptr;
Esri::ArcGISRuntime::Renderer* m_defaultDistributionRenderer = nullptr;
Esri::ArcGISRuntime::Renderer* m_defaultServiceRenderer = nullptr;
Esri::ArcGISRuntime::Renderer* m_defaultGraphicsOverlayRenderer = nullptr;
Esri::ArcGISRuntime::SubtypeSublayer* m_distributionPipeLayer = nullptr;
Esri::ArcGISRuntime::SubtypeSublayer* m_servicePipeLayer = nullptr;
Esri::ArcGISRuntime::SimpleLineSymbol* m_rulesLimitSymbol = nullptr;
Esri::ArcGISRuntime::SimpleLineSymbol* m_rulesPreventSymbol = nullptr;
Esri::ArcGISRuntime::SimpleLineSymbol* m_noneSymbol = nullptr;
Esri::ArcGISRuntime::ArcGISFeature* m_selectedFeature = nullptr;
};
#endif // SNAPGEOMETRYEDITSWITHRULES_H