ArcGIS Runtime SDK for Java

Edit geometries

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

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.

Class diagram of geometry builders

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 PolylineBuilder 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 PolylineBuilder 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 GraphicsOverlay with the new state of the geometry created so far.

class roadGeometryEditor {


  PolylineBuilder polylineBuilder = null;
  Graphic temporaryGraphic = null;


  // Builder held as member variable
  public roadGeometryEditor(Feature roadFeature) {
    // Set initial state of the builder based on an existing geometry
    polylineBuilder = new PolylineBuilder((Polyline) roadFeature.getGeometry());
    // Set up a temporary graphic to draw the geometry
    SimpleLineSymbol roadSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, 0xFFFF0000, 1f);
    temporaryGraphic = new Graphic(polylineBuilder.toGeometry(), roadSymbol);
    mapView.getGraphicsOverlays().get(0).getGraphics().add(temporaryGraphic);
  }


  public void addPointToEnd(Point newEndPoint) {
    // Change the geometry
    polylineBuilder.getParts().get(0).addPoint(newEndPoint);
  }


  public Polyline getCurrentGeometry() {
    // Get latest state of the geometry
    return polylineBuilder.toGeometry();
  }


  public void drawLatestGeometry(GraphicsOverlay editorOverlay) {
    // Update the user's view of the geometry
    temporaryGraphic.setGeometry(getCurrentGeometry());
  }
}

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 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, 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 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 using the constructor, 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 and the constructor will throw an exception.

Tip:

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

Multipoint geometries

Use MultipointBuilder to modify an existing Multipoint. Get and modify the mutable point collection from the builder's getPoints 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 builder = new MultipointBuilder((SpatialReference) null);
PointCollection points = builder.getPoints();
points.add(userAddedPoint1);
points.add(userAddedPoint2);
//...
Multipoint 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, 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.

// Create a builder, set the Spatial Reference
PolylineBuilder polylineBuilder = new PolylineBuilder(SpatialReferences.getWgs84());


Part firstPart = new Part(SpatialReferences.getWgs84());
firstPart.add(new LineSegment(0, 0, 0, 7));  // A LineSegment (0,0) -> (0,7)
firstPart.add(new LineSegment(new Point(0, 7), new Point(3, 6)));      // A LineSegment (0,7) -> (3, 6)


polylineBuilder.addPart(firstPart);
Polyline 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.

Here is an example of mixing pont-based and segment-based methods.

Part firstPart = new Part(SpatialReferences.getWgs84());
polygonBuilder.getParts().add(firstPart);
firstPart.addPoint(0, 0);      // Add an initial point
firstPart.addPoint(0, 7);      // Adjusts the Part so that it has one LineSegment (0,0) -> (0,7)
firstPart.add(new LineSegment(0, 7, 5, 0));  // Adds a new LineSegment (0,7) -> (5,0))

Editing parts

Use the PartCollection methods to add, remove, or insert Parts to a MultipartBuilder. A point-based helper method addPart(Iterable<Point>) is available for the common task of adding an existing set of Points to a PartCollection as a new Part.

PartCollection partCollection = new PartCollection(SpatialReferences.getWgs84());
Part part = new Part(SpatialReferences.getWgs84());
part.addPoint(new Point(78.75000000000003, 66.86936993741895));
part.addPoint(new Point(56.25000000000001, 67.22037237346096));
partCollection.add(part);
polygonBuilder.getParts().addAll(partCollection);

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.
    polygonBuilder.getParts().get(0).addPoint(0, new Point(56.7, 66.51326044311182));
  • 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.
    polygonBuilder.getParts().get(0).removePoint(0);
  • 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.

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 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 add(int, Segment) 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.
    LineSegment lineSegment =
      new LineSegment(new Point(78.75000000000003, 66.86936993741895), new Point(56.25000000000001, 67.22037237346096));
    polygonBuilder.getParts().get(0).add(lineSegment);
  • Use remove(int) 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.
    polygonBuilder.getParts().get(0).remove(0);
  • 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 set(int, Segment).
    LineSegment lineSegment =
      new LineSegment(new Point(78.75000000000003, 66.86936993741895), new Point(56.25000000000001, 67.22037237346096));
    polygonBuilder.getParts().get(0).set(0, lineSegment);
  • 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 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 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 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.

Tip:

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