ArcGIS Runtime SDK for iOS

Edit geometries

You edit geometries when you want to change existing geometries or create new ones. However, AGSGeometry objects are immutable. To edit geometries, use geometry builders.

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 AGSPolylineBuilder and AGSPolygonBuilder that share many members, both inherit from AGSMultipartBuilder, which in turn inherits from AGSGeometryBuilder. Other builders inherit directly from AGSGeometryBuilder.

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:

  1. 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 AGSPolylineBuilder and pass in the existing polyline to the builder.
  2. 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.
  3. Call toGeometry to return a new immutable geometry from the current state of the builder.
  4. 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 an AGSPolylineBuilder held as a member variable in a custom geometry editor class. The addPointToEnd method adds another vertex to the end of the polyline and can be called when a user taps at the required location. Each time an update is made, the drawLatestGeometry method can be used to update an AGSGraphicsOverlay with the new state of the geometry created so far.

@interface RoadGeometryEditor() {
 AGSPolylineBuilder *_polylineBuilder;
 AGSGraphic *_temporaryGraphic;
}
@end

@implementation RoadGeometryEditor

// builder held as member variable
-(void)roadGeometryEditor:(AGSGraphic*)roadFeature {
 // set initial state of the builder based on an existing geometry
 _polylineBuilder = [AGSPolylineBuilder polylineBuilderWithPolyline:(AGSPolyline*)roadFeature.geometry];
    
 // set up a temporary graphic to draw the geometry
 AGSSimpleLineSymbol *roadSymbol = [AGSSimpleLineSymbol simpleLineSymbolWithStyle:AGSSimpleLineSymbolStyleSolid color:[UIColor redColor] width:2];
 _temporaryGraphic = [AGSGraphic graphicWithGeometry:_polylineBuilder.toGeometry symbol:roadSymbol];
}

-(void)addPointToEnd:(AGSPoint*)newEndPoint {
 // change the geometry
 [_polylineBuilder addPoint:newEndPoint];
}

-(AGSPolyline*)currentGeometry {
 // get latest state of the geometry
 return _polylineBuilder.toGeometry;
}

-(void)drawLatestGeometry {
 // update the user's view of the geometry
 _temporaryGraphic.geometry = [self currentGeometry];
}

@end

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 isValidSketch to determine when a builder contains enough information to convert the builder’s state successfully to a geometry.

Tip:

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 AGSSpatialReference:

  • 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, and an exception will be thrown indicating a mismatch. 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 AGSPoint or AGSSegment returned from a geometry’s member will have the same spatial reference as the geometry it came from.
  • When creating a new AGSPolygon using the constructor, the API first sets the spatial reference parameter for the new AGSPolygon, then the points are added. If there is a mismatch between the spatial reference of the new AGSPolygon and the points being added, this is an error and the constructor will throw an exception.

Tip:

If required for your workflow, use the projectProject method on AGSGeometryEngine to project points to a different spatial reference.

Multipoint geometries

Use AGSMultipointBuilder to modify an existing AGSMultipoint. 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 AGSMultipoint from the current state of the builder.

AGSMultipointBuilder *builder = [AGSMultipointBuilder multipointBuilderWithSpatialReference:userAddedPoint1.spatialReference];
AGSMutablePointCollection *points = builder.points;
[points addPoint:userAddedPoint1];
[points addPoint:userAddedPoint1];
[...]
AGSMultipoint *multipoint = [builder toGeometry];

Multipart geometries

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, AGSPolylineBuilder and AGSPolygonBuilder are used more frequently than other builders such as AGSPointBuilder. Using the methods on these collections, you can build polygons and polylines by adding points. As with multipoint geometries, use the AGSPointCollection to modify multipart geometries. Or, you can edit the segments in multipart geometries.

AGSPolylineBuilder *polylineBuilder = [AGSPolylineBuilder polylineBuilderWithSpatialReference:[AGSSpatialReference WGS84]];
AGSMutablePart *firstPart = [AGSMutablePart partWithSpatialReference:polylineBuilder.spatialReference];
    
// a line segment (0,0) -> (0,7)
[firstPart addSegment:[AGSLineSegment lineSegmentWithXStart:0 yStart:0 xEnd:0 yEnd:7]];

// a line segment (0,7) -> (3, 6)
[firstPart addSegment:[AGSLineSegment lineSegmentWithStartPoint:[AGSPoint pointWithX:0 y:7 
spatialReference:nil] endPoint:[AGSPoint pointWithX:3 y:6 spatialReference:nil]]]; 

[polylineBuilder.parts addPart:firstPart];

AGSPolyline *polyline = [polylineBuilder toGeometry]; 
// convert the current state of the builder to a polyline

Polygon created in the example

You can also edit using a mix of point-based and segment-based methods.

AGSMutablePart *firstPart = [AGSMutablePart partWithSpatialReference:[AGSSpatialReference WGS84]];
[polygonBuilder.parts addPart:firstPart];
    
// add an initial point
[firstPart addPoint:[AGSPoint pointWithX:0 y:0 spatialReference:nil]];
    
// adjusts the part so that it has one line segment (0,0) -> (0,7)
[firstPart addPointWithX:0 y:7];
    
// adds a new line segment (0,7) -> (5,0))
[firstPart addSegment:[AGSLineSegment lineSegmentWithXStart:0 yStart:7 xEnd:5 yEnd:0]];

Editing parts

Use the AGSMutablePartCollection methods to add, remove, or insert AGSMutableParts to a AGSMultipartBuilder.

//Define a mutable part using 3 points    
AGSMutablePart *part = [AGSMutablePart partWithSpatialReference:polygonBuilder.spatialReference];
        
[part addPointWithX:1 y:2];
[part addPointWithX:2 y:5];
[part addPointWithX:6 y:10];

//Instantiate a polygon builder (specifying the Spatial Reference)
AGSPolygonBuilder *polygonBuilder = [AGSPolygonBuilder polygonBuilderWithSpatialReference:[AGSSpatialReference WGS84]];

//Get the polygonbuilder's mutable part collection (parts) and add the part     
[polygonBuilder.parts addPart:part];

Editing parts using points

The AGSPart 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 AGSLineSegments within the builder between those vertices.

The following principles apply when editing parts using points:

  • Use insertPoint(AGSPoint, int) 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.
    [polygonBuilder.parts[0] insertPoint:AGSPointMake(112, 38, nil) atIndex:2];
  • Use removePointAtIndex(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.
    // Remove the first vertex (index position 0) from the first part
    // using AGSPolygonBuilder
    [polygonBuilder.parts[0] removePointAtIndex:0];
  • Use setPoint(AGSPoint, int) to replace a vertex in the collection. The total number of vertices or segments in the collection remains unchanged.
    // Replace point 3 in part 0 with a new point
    // using AGSPolygonBuilder   
    [polygonBuilder.parts[0] setPoint:AGSPointMake(111, 37, nil) atIndex:3];
  • You can add a new vertex to the end of a AGSMultipartBuilder 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.
    // Add a new point to the end of the last part
    // using AGSPolygonBuilder that inherits from AGSMultipartBuilder
    [polygonBuilder addPointWithX:110 y:36];
  • You do not need to close a part belonging to a polygon.

Caution:

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 AGSPoint 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 insertSegment(segment, int) 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.
    // create a simple line segment  
    AGSLineSegment *segment = [[AGSLineSegment alloc] initWithXStart:112 yStart:38 xEnd:111 yEnd:37];
    // insert it as the 2nd segment in the first part 
    [polygonBuilder.parts[0] insertSegment:segment atIndex: 1];
  • Use removeSegmentAtIndex 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.
    // remove the third segment from the first part
    [polygonBuilder.parts[0] removeSegmentAtIndex:2];
  • 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(AGSLineSegment, int).
    // Create a simple line segment
    AGSLineSegment *lineSegment = [[AGSLineSegment alloc] initWithXStart:112 yStart:38 xEnd:111 yEnd:37];
        
    // Replace the 3rd Segment in part zero 
    [polygonBuilder.parts[0] setSegment:lineSegment atIndex:3];
  • You do not need to close a part belonging to a polygon.

Caution:

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 AGSPoint in point-based methods. However, if there is a gap, separate AGSPoints 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 AGSGeometryEngine 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 isValidSketch

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 isValidSketch 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 isValidSketch 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 AGSPoint and AGSEnvelope 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.

Tip:

When saving geometries in geodatabase features, check that hasZ and hasM match the attribute awareness of the feature table being edited.