You edit geometries when you want to change existing geometries or create new ones. However, Geometry objects are immutable. To edit geometries, use geometry builders.
Geometry builders (or builders for short) create or change geometry. A geometry builder contains the same things a geometry contains—vertices, segments, and parts—allowing its state to be changed as needed. You can create new geometries at any point from the builder’s current state. A builder is available for each type of geometry. Each builder exposes the appropriate methods for modifying a specific type of geometry. In the case of PolylineBuilder and PolygonBuilder that share many members, both inherit from MultipartBuilder, which in turn inherits from GeometryBuilder. Other builders inherit directly from GeometryBuilder.
Because builders represent geometries under construction, they do not use the same immutable collection types as those returned from members of the geometry classes containing a part, part collection, or point collection. Instead, builder members use mutable collection types.
Modify existing geometry
To modify an existing geometry, complete the following steps:
- Create the appropriate type of builder and initialize its state by passing in the geometry to be edited to the constructor. For example, modify a polyline by creating an PolylineBuilder and pass in the existing polyline to the builder.
- Use the methods on the builder to change the builder’s state. For example, add a new part to a polyline or remove a point from a multipoint.
- Call toGeometry to return a new immutable geometry from the current state of the builder.
- Repeat steps 2 and 3 as many times as necessary. By keeping a reference to the builder, you can repeatedly modify the builder state as many times as required, for example, to update a temporary graphic to display the current state to the user.
The following code demonstrates how to use a polyline builder to update a polyline geometry in a graphic as more points are added to the polyline through mouse clicks. First, create the builder, initializing it with the geometry of the graphic you will append to. Connect to a mouse click signal to get the clicked map position, and call a function to append a point to the polyline at the clicked position. Here is the body of that function, which adds the point using the builder, gets the polyline from the builder, and applies the polyline as the geometry of an existing graphic.
m_polylineBuilder = new PolylineBuilder(map->spatialReference(),this);
connect(m_mv, &MapQuickView::mouseClicked, [this](QMouseEvent& mouseEvent)
Point newPoint = m_mv->screenToLocation(mouseEvent.x(), mouseEvent.y());
void EditPolylineTest::addPointToPolyline(double x, double y)
First, create the builder, initializing it with the geometry of the graphic you will append to.
Connect to a mouse click signal to get the clicked map position, and call a function to append a point to the polyline at the clicked position.
Here is the body of that function, which adds the point using the builder, gets the polyline from the builder, and applies the polyline as the geometry of an existing graphic.
You don't have to initialize a builder with an existing geometry—you can also use builders to create entirely new geometries.
When creating geometries, use isSketchValid to determine when a builder contains enough information to convert the builder’s state successfully to a geometry.
When editing geometries using locations from the screen, and the map is a wraparound map, you may need to normalize the geometry before you save it to a geodatabase or use it in tasks or other operations.
Spatial reference management
When working with geometries and builders, the following principles apply with regard to their SpatialReference:
- The spatial reference of a builder is always set during builder instantiation and cannot be changed. It can be set to null.
- You don't need to set the spatial reference of points or segments passed into methods on geometries, collections, or builders. If the spatial reference is not set, the input coordinates will be assumed to be in the same spatial reference as the geometry, collection, or builder.
- It is an error to add a point or segment having a different spatial reference than that of the geometry, collection, or builder you're adding it to. This includes the case where the spatial reference of the geometry, collection, or builder is null and that of the added point or segment is not null.
- A Point or Segment returned from a geometry’s member will have the same spatial reference as the geometry it came from.
- When creating a new Polygon, the API first sets the spatial reference parameter for the new Polygon, then the points are added. If there is a mismatch between the spatial reference of the new Polygon and the points being added, this is an error.
If required for your workflow, use the method on GeometryEngine to project points to a different spatial reference.
Use MultipointBuilder to modify an existing Multipoint. Get and modify the mutable point collection from the builder's points method, working directly with this collection to add, remove, or insert points. Call toGeometry to instantiate a new immutable instance of Multipoint from the current state of the builder.
MultipointBuilder* multiPointBuilder = new MultipointBuilder(SpatialReference(4326), this);
PointCollection* pc = new PointCollection(SpatialReference(4326), this);
pc->addPoint(-169, 34, 2);
pc->addPoint(Point(-175, 50, SpatialReference(4326)));
Polygons and polylines can have multiple parts. You can use a multipart polygon to define a group of islands, or a multipart polyline to represent a non-continuous linear feature. Due to the geometric complexity of polygons and polylines, PolylineBuilder and PolygonBuilder are used more frequently than other builders such as PointBuilder. Using the methods on these collections, you can build polygons and polylines by adding points. As with multipoint geometries, use the PointCollection to modify multipart geometries. Or, you can edit the segments in multipart geometries.
You can also edit using a mix of point-based and segment-based methods.
To edit the parts of a MultipartBuilder, use its parts method to retrieve the PartCollection, change the PartCollection, and apply it back to the MultipartBuilder using the setParts method. You can also add a new point to the end of a MultipartBuilder using the addPoint method.
Editing parts using points
The Part is a collection of segments. Working with the point-based method results in defining an ordered series of vertices and, as a consequence, creating straight LineSegments within the builder between those vertices.
The following principles apply when editing parts using points:
- Use addPoint(int, Point) to insert a vertex at a specific index into a part. This will increase the number of segments in the part by one. Connections between segments are preserved—adjacent segments touch at their starting and ending vertices.
- Use removePoint(int) to remove the vertex at a specific index from a part. The number of segments decreases by one, and connections between segments are preserved.
- Use setPoint to replace a vertex in the collection without the need for a remove followed by an add. The total number of vertices or segments in the collection remains unchanged.
- You can add a new vertex to the end of a MultipartBuilder using the addPoint method. This adds a vertex to the end of the last part in the builder. If there are no parts in the builder, this method will create a new part first, then add the new vertex.
- You do not need to close a part belonging to a polygon.
When working with point-based methods that take an index parameter, keep in mind that this is the index of the specific vertex, which is a different index than that used when working with segments. This is because where a segment’s ending vertex is at the same location as the starting vertex of the next segment, it is represented by a single Point in point-based methods.
Editing parts using segments
When you work with the segments in a part, you specify each segment’s starting and ending vertex. This differs from editing with points, where sequential vertices define a series of adjacent segments. Specifying starting and ending vertices for adjacent segments can result in gaps, where the end vertex of one segment does not have exactly the same location as the start vertex of the next adjacent segment. Gaps are not automatically corrected in the builder until you access the builder’s call toGeometry member to instantiate a new polyline or polygon. At that time, if any adjacent segments in the part do not touch, a short connecting segment will be added automatically to the new geometry to ensure the part forms a continuous edge in the new geometry. To avoid automatically adding segments, re-use the location of the end vertex of the previous segment as the start vertex of the subsequent segment, and use insert, remove, and add methods with care.
Similar to geometries, all segments are immutable and are created by using constructors or static factory methods. Unlike geometries, there are no builders for segments. Segments are fully defined when created, setting all properties at once.
The following principles apply when editing parts using segments:
- Use setSegment to insert a segment into a part at a specific index. This increases the number of segments in the part by one. Be careful: this will result in a gap at either end of the new segment if the start and end vertices do not share a location with the preceding and following adjacent segments, respectively.
- Use removeSegment to remove a segment from a part. If you remove anything other than the first or last segment in the part, this will result in a gap between the remaining segments. The number of segments decreases by one, and connections between segments are preserved.
- Because segments are immutable, existing segments in a part cannot be changed. However, you can replace the existing segment at a specified index with a new segment by calling setSegment.
- You do not need to close a part belonging to a polygon.
When working with segment-based methods that take an index parameter, keep in mind that this is the index of the segment within the part and is a different index than that used when working with point-based helper methods. If a segment's ending vertex is at the same location as the starting vertex of the next segment, this location is represented by a single Point in point-based methods. However, if there is a gap, separate Points are returned for the two locations that define the gap.
Topological simplicity in features and graphics
To be saved as a feature in a geodatabase, a geometry must be simplified—that is, be topologically consistent. The rules vary for different types of geometry but include requirements such as polygons not having overlapping parts. Pass a geometry to the GeometryEngine object's simplify method to return a simplified copy that can be saved in a geodatabase.
Note that in some cases, simplification can change the contents of the geometry, including resulting in an empty geometry. Topological simplicity is different than geometry generalization. For more information, see ITopologicalOperator.Simplify in the ArcObjects help.
The requirements for a geometry that will be used in a graphic are less rigorous than those used in a feature—there is no need to simplify geometries to display them in graphics.
Builders and isSketchValid
In some workflows, both graphics and features are used. In interactive editing scenarios, for example, a user sketches a geometry on the map. A graphic is used initially to display the state of the sketch during sketching. When the user decides that the sketch is complete, the geometry can be saved to a geodatabase feature. You can use isSketchValid on a geometry builder to determine when the builder contains enough state to be converted successfully to a geometry. This is a useful tool to enable or disable saving the sketch by the user. To be considered a valid sketch, a polygon builder must contain at least one part with three or more vertices in it, and a polyline builder must contain at least one part with at least two vertices. A multipoint builder must contain at least one point, and for a point or envelope builder, the x and y coordinates must not be NaN. If these conditions are satisfied, these cases should result in a geometry that could be visible if drawn.
Although isSketchValid may be a good first check when building a geometry, it should not be relied upon to indicate if the eventual geometry will be valid or empty after simplification. Before allowing a user to save a geometry, consider taking a further verification step of calling simplify. This approach can also be used for other situations that require geometries to be simple, for example, measurement or topological operations.
When importing or exporting geometries, for example, to JSON, if a multipart geometry has insufficient points, additional points can be added to ensure each segment has two endpoints to preserve the stability of other clients that may expect this.
Z-values and m-values
All types of geometry can have z-values. When building geometries, a simple rule determines whether or not the geometry stores z-values. If you call a method that has a z-value parameter, or add a point or segment that has z-values to a builder, the resulting geometry will store z-values—hasZ will return true.
Geometries can also have m-values, used in linear referencing workflows. Using m-values is generally less common than using z-values. Therefore, instead of using constructors, the Point and Envelope classes present static factory methods that allow the creation of geometries with m-values.
Pay special attention to z- and m-values in editing workflows. When syncing edits, sync errors may occur if the z- or m-awareness of the geometry does not match that of the service. Find out more in the ArcGIS REST API help topic Error handling with sync.
When saving geometries in geodatabase features, check that hasZ and hasM match the attribute awareness of the feature table being edited.