using System.Collections; using GeoAPI.Geometries; using GisSharpBlog.NetTopologySuite.Utilities; namespace GisSharpBlog.NetTopologySuite.Geometries.Utilities { /// /// Supports creating a new Geometry which is a modification of an existing one. /// Geometry objects are intended to be treated as immutable. /// This class allows you to "modify" a Geometry /// by traversing it and creating a new Geometry with the same overall structure but /// possibly modified components. /// The following kinds of modifications can be made: /// /// The values of the coordinates may be changed. /// Changing coordinate values may make the result Geometry invalid; /// this is not checked by the GeometryEditor. /// /// The coordinate lists may be changed /// (e.g. by adding or deleting coordinates). /// The modifed coordinate lists must be consistent with their original parent component /// (e.g. a LinearRing must always have at least 4 coordinates, and the first and last /// coordinate must be equal). /// /// Components of the original point may be deleted /// (e.g. holes may be removed from a Polygon, or LineStrings removed from a MultiLineString). /// Deletions will be propagated up the component tree appropriately. /// /// Note that all changes must be consistent with the original Geometry's structure /// (e.g. a Polygon cannot be collapsed into a LineString). /// The resulting Geometry is not checked for validity. /// If validity needs to be enforced, the new Geometry's IsValid should be checked. /// public class GeometryEditor { /// /// The factory used to create the modified Geometry. /// private IGeometryFactory factory = null; /// /// Creates a new GeometryEditor object which will create /// an edited Geometry with the same {GeometryFactory} as the input Geometry. /// public GeometryEditor() {} /// /// Creates a new GeometryEditor object which will create /// the edited Geometry with the given GeometryFactory. /// /// The GeometryFactory to create the edited Geometry with. public GeometryEditor(IGeometryFactory factory) { this.factory = factory; } /// /// Edit the input Geometry with the given edit operation. /// Clients will create subclasses of GeometryEditorOperation or /// CoordinateOperation to perform required modifications. /// /// The Geometry to edit. /// The edit operation to carry out. /// A new Geometry which is the result of the editing. public IGeometry Edit(IGeometry geometry, GeometryEditorOperation operation) { // if client did not supply a GeometryFactory, use the one from the input Geometry if (factory == null) { factory = geometry.Factory; } if (geometry is IGeometryCollection) { return EditGeometryCollection((IGeometryCollection) geometry, operation); } if (geometry is IPolygon) { return EditPolygon((IPolygon) geometry, operation); } if (geometry is IPoint) { return operation.Edit(geometry, factory); } if (geometry is ILineString) { return operation.Edit(geometry, factory); } Assert.ShouldNeverReachHere("Unsupported Geometry classes should be caught in the GeometryEditorOperation."); return null; } /// /// /// /// /// /// private IPolygon EditPolygon(IPolygon polygon, GeometryEditorOperation operation) { IPolygon newPolygon = (IPolygon) operation.Edit(polygon, factory); if (newPolygon.IsEmpty) { //RemoveSelectedPlugIn relies on this behaviour. [Jon Aquino] return newPolygon; } ILinearRing shell = (ILinearRing) Edit(newPolygon.ExteriorRing, operation); if (shell.IsEmpty) { //RemoveSelectedPlugIn relies on this behaviour. [Jon Aquino] return factory.CreatePolygon(null, null); } ArrayList holes = new ArrayList(); for (int i = 0; i < newPolygon.NumInteriorRings; i++) { ILinearRing hole = (ILinearRing) Edit(newPolygon.GetInteriorRingN(i), operation); if (hole.IsEmpty) { continue; } holes.Add(hole); } return factory.CreatePolygon(shell, (ILinearRing[]) holes.ToArray(typeof(ILinearRing))); } /// /// /// /// /// /// private IGeometryCollection EditGeometryCollection(IGeometryCollection collection, GeometryEditorOperation operation) { IGeometryCollection newCollection = (IGeometryCollection) operation.Edit(collection, factory); ArrayList geometries = new ArrayList(); for (int i = 0; i < newCollection.NumGeometries; i++) { IGeometry geometry = Edit(newCollection.GetGeometryN(i), operation); if (geometry.IsEmpty) { continue; } geometries.Add(geometry); } if (newCollection is IMultiPoint) { return factory.CreateMultiPoint((IPoint[]) geometries.ToArray(typeof(IPoint))); } if (newCollection is IMultiLineString) { return factory.CreateMultiLineString((ILineString[]) geometries.ToArray(typeof(ILineString))); } if (newCollection is IMultiPolygon) { return factory.CreateMultiPolygon((IPolygon[]) geometries.ToArray(typeof(IPolygon))); } return factory.CreateGeometryCollection((IGeometry[]) geometries.ToArray(typeof(IGeometry))); } /// /// A GeometryEditorOperation which modifies the coordinate list of a Geometry. /// Operates on Geometry subclasses which contains a single coordinate list. /// public abstract class CoordinateOperation : GeometryEditorOperation { /// /// Edits the array of Coordinates from a Geometry. /// /// The coordinate array to operate on. /// The point containing the coordinate list. /// An edited coordinate array (which may be the same as the input). public abstract ICoordinate[] Edit(ICoordinate[] coordinates, IGeometry geometry); /// /// /// /// /// /// public IGeometry Edit(IGeometry geometry, IGeometryFactory factory) { if (geometry is ILinearRing) { return factory.CreateLinearRing(Edit(geometry.Coordinates, geometry)); } if (geometry is ILineString) { return factory.CreateLineString(Edit(geometry.Coordinates, geometry)); } if (geometry is Point) { ICoordinate[] newCoordinates = Edit(geometry.Coordinates, geometry); return factory.CreatePoint((newCoordinates.Length > 0) ? newCoordinates[0] : null); } return geometry; } } /// /// A interface which specifies an edit operation for Geometries. /// public interface GeometryEditorOperation { /// /// Edits a Geometry by returning a new Geometry with a modification. /// The returned Geometry might be the same as the Geometry passed in. /// /// The Geometry to modify. /// /// The factory with which to construct the modified Geometry /// (may be different to the factory of the input point). /// /// A new Geometry which is a modification of the input Geometry. IGeometry Edit(IGeometry geometry, IGeometryFactory factory); } } }