/// <summary> /// Takes an array of simple polygons and combines them into one multi-part shape. /// </summary> /// <param name="parts">The array of polygons.</param> /// <param name="resultShp">The resulting multi-part shape.</param> public static void CombineParts(ref IFeature[] parts, ref IFeature resultShp) { int numParts = parts.Length; Polygon po = new Polygon(parts[0].Coordinates); Polygon poly; for (int i = 0; i <= numParts - 1; i++) { poly = new Polygon(parts[i].Coordinates); po = poly.Union(po) as Polygon; } resultShp = new Feature(po); }
/// <summary> /// /// </summary> /// <param name="polygon"></param> /// <param name="writer"></param> protected virtual void Write(Polygon polygon, XmlTextWriter writer) { writer.WriteStartElement("Polygon"); writer.WriteStartElement("outerBoundaryIs"); Write(polygon.ExteriorRing as LinearRing, writer); writer.WriteEndElement(); for(int i = 0; i < polygon.NumHoles; i++) { writer.WriteStartElement("innerBoundaryIs"); Write(polygon.Holes[i] as LinearRing, writer); writer.WriteEndElement(); } writer.WriteEndElement(); }
/// <summary> /// /// </summary> /// <param name="polygon"></param> /// <returns></returns> protected virtual int SetByteStreamLength(Polygon polygon) { int count = InitValue; count += polygon.NumPoints * CoordSize; return count; }
/// <summary> /// Create a new contains computer for two geometries. /// </summary> /// <param name="rectangle">A rectangular geometry.</param> public RectangleContains(Polygon rectangle) { _rectangle = rectangle; _rectEnv = rectangle.EnvelopeInternal; }
/// <summary> /// If the FeatureType is polygon, this is the code for converting the vertex array /// into a feature. /// </summary> /// <param name="index"></param> /// <returns></returns> protected IFeature GetPolygon(int index) { Feature feature = new Feature(); feature.Envelope = ShapeIndices[index].Extent.ToEnvelope(); ShapeRange shape = ShapeIndices[index]; List<ILinearRing> shells = new List<ILinearRing>(); List<ILinearRing> holes = new List<ILinearRing>(); foreach (PartRange part in shape.Parts) { List<Coordinate> coords = new List<Coordinate>(); int i = part.StartIndex; foreach (Vertex d in part) { Coordinate c = new Coordinate(d); if (M != null && M.Length > 0) c.M = M[i]; if (Z != null && Z.Length > 0) c.Z = Z[i]; i++; coords.Add(c); } LinearRing ring = new LinearRing(coords); if (shape.Parts.Count == 1) { shells.Add(ring); } else { if (CGAlgorithms.IsCounterClockwise(ring.Coordinates)) { holes.Add(ring); } else { shells.Add(ring); } //if(part.IsHole()) //{ // holes.Add(ring); //} //else //{ // shells.Add(ring); //} } } //// Now we have a list of all shells and all holes List<ILinearRing>[] holesForShells = new List<ILinearRing>[shells.Count]; for (int i = 0; i < shells.Count; i++) { holesForShells[i] = new List<ILinearRing>(); } // Find holes for (int i = 0; i < holes.Count; i++) { ILinearRing testRing = holes[i]; ILinearRing minShell = null; IEnvelope minEnv = null; IEnvelope testEnv = testRing.EnvelopeInternal; Coordinate testPt = testRing.Coordinates[0]; ILinearRing tryRing; for (int j = 0; j < shells.Count; j++) { tryRing = shells[j]; IEnvelope tryEnv = tryRing.EnvelopeInternal; if (minShell != null) minEnv = minShell.EnvelopeInternal; bool isContained = false; if (tryEnv.Contains(testEnv) && (CGAlgorithms.IsPointInRing(testPt, tryRing.Coordinates) || (PointInList(testPt, tryRing.Coordinates)))) { isContained = true; } // Check if this new containing ring is smaller than the current minimum ring if (isContained) { if (minShell == null || minEnv.Contains(tryEnv)) { minShell = tryRing; } holesForShells[j].Add(holes[i]); } } } IPolygon[] polygons = new Polygon[shells.Count]; for (int i = 0; i < shells.Count; i++) { polygons[i] = new Polygon(shells[i], holesForShells[i].ToArray()); } if (polygons.Length == 1) { feature.BasicGeometry = polygons[0]; } else { // It's a multi part feature.BasicGeometry = new MultiPolygon(polygons); } // feature.FID = feature.RecordNumber; FID is now dynamic feature.ParentFeatureSet = this; feature.ShapeIndex = shape; feature.RecordNumber = shape.RecordNumber; feature.ContentLength = shape.ContentLength; feature.ShapeType = shape.ShapeType; //Attributes handled in the overridden case return feature; }
/// <summary> /// /// </summary> /// <param name="p"></param> private void AddPolygon(Polygon p) { AddPolygonRing(p.ExteriorRing, Locations.Exterior, Locations.Interior); for (int i = 0; i < p.NumHoles; i++) // Holes are topologically labelled opposite to the shell, since // the interior of the polygon lies on their opposite side // (on the left, if the hole is oriented CW) AddPolygonRing(p.GetInteriorRingN(i), Locations.Interior, Locations.Exterior); }
/// <summary> /// /// </summary> /// <param name="rectangle"></param> /// <param name="b"></param> /// <returns></returns> public static bool Contains(Polygon rectangle, IGeometry b) { RectangleContains rc = new RectangleContains(rectangle); return rc.Contains(b); }
/// <summary> /// If the input geometry is a singular basic geometry, this will become a collection of 1 geometry. /// If the input geometry is a multi- basic geometry, this will simply ensure that each member /// is upgraded to a full geometry. /// </summary> /// <param name="inGeometry"></param> /// <param name="inFactory"></param> public GeometryCollection(IBasicGeometry inGeometry, IGeometryFactory inFactory) : base(inFactory) { if (inGeometry == null) { _geometries = new IGeometry[] { }; return; } IBasicPolygon pg = inGeometry.GetBasicGeometryN(0) as IBasicPolygon; if (pg != null) { _geometries = new IGeometry[inGeometry.NumGeometries]; for (int iGeom = 0; iGeom < inGeometry.NumGeometries; iGeom++) { pg = inGeometry.GetBasicGeometryN(iGeom) as IBasicPolygon; _geometries[iGeom] = new Polygon(pg); } return; } IBasicPoint pt = inGeometry.GetBasicGeometryN(0) as IBasicPoint; if (pt != null) { _geometries = new IGeometry[inGeometry.NumGeometries]; for (int iGeom = 0; iGeom < inGeometry.NumGeometries; iGeom++) { pt = inGeometry.GetBasicGeometryN(iGeom) as IBasicPoint; _geometries[iGeom] = new Point(pt); } return; } IBasicLineString ls = inGeometry.GetBasicGeometryN(0) as IBasicLineString; if (ls != null) { _geometries = new IGeometry[inGeometry.NumGeometries]; for (int iGeom = 0; iGeom < inGeometry.NumGeometries; iGeom++) { ls = inGeometry.GetBasicGeometryN(iGeom) as IBasicLineString; _geometries[iGeom] = new LineString(ls); } return; } }
/// <summary> /// Presuming that the specified basic geometry describes a MultiPolygon, this will perform the necessary /// casting in order to create a MultiPolygon. If, in fact, it is only a BasicMultiPolygon, this will /// create a new, fully functional MultiPolygon based on the same coordinates. /// </summary> /// <param name="inGeometry">The IBasicGeometry to turn into a MultiPolygon. </param> public static new IMultiPolygon FromBasicGeometry(IBasicGeometry inGeometry) { // Multipolygons cast directly IMultiPolygon result = inGeometry as IMultiPolygon; if (result != null) return result; // Polygons are just wrapped in a Multipolygon with the one polygon as an element IPolygon p = (IPolygon)inGeometry; if (p != null) { return new MultiPolygon(new[] { p }); } IBasicPolygon bp = (IBasicPolygon)inGeometry; if (bp != null) { return new MultiPolygon(new[] { bp }); } IPolygon[] polygonArray = new IPolygon[inGeometry.NumGeometries]; // assume that we have some kind of MultiGeometry of IBasicPolygon objects for (int i = 0; i < inGeometry.NumGeometries; i++) { IBasicPolygon ibp = (IBasicPolygon)inGeometry.GetBasicGeometryN(i); polygonArray[i] = new Polygon(ibp); } return new MultiPolygon(polygonArray); }
/// <summary> /// The Voronoi Graph calculation creates the lines that form a voronoi diagram /// </summary> /// <param name="points">The points to use for creating the tesselation</param> /// <param name="cropToExtent">The normal polygons have sharp angles that extend like stars. /// Cropping will ensure that the original featureset extent plus a small extra buffer amount /// is the outer extent of the polygons. Errors seem to occur if the exact extent is used.</param> /// <param name="result">The output featureset</param> /// <param name="progHandler">A progress handler for updating progress information</param> /// <returns></returns> public static void VoronoiPolygons(IFeatureSet points, IFeatureSet result, bool cropToExtent, IProgressHandler progHandler) { double[] vertices = points.Vertex; VoronoiGraph gp = Fortune.ComputeVoronoiGraph(vertices); IEnvelope env = points.Envelope; env.ExpandBy(env.Width / 100, env.Height / 100); IPolygon bounds = env.ToPolygon(); // Convert undefined coordinates to a defined coordinate. HandleBoundaries(gp, env); for (int i = 0; i < vertices.Length/2; i++) { List<VoronoiEdge> myEdges = new List<VoronoiEdge>(); Vector2 v = new Vector2(vertices, i*2); foreach (VoronoiEdge edge in gp.Edges) { if (!v.Equals(edge.RightData) && !v.Equals(edge.LeftData)) continue; myEdges.Add(edge); } List<Coordinate> coords = new List<Coordinate>(); VoronoiEdge firstEdge = myEdges[0]; coords.Add(firstEdge.VVertexA.ToCoordinate()); coords.Add(firstEdge.VVertexB.ToCoordinate()); Vector2 previous = firstEdge.VVertexB; myEdges.Remove(myEdges[0]); Vector2 start = firstEdge.VVertexA; while (myEdges.Count > 0) { for (int j = 0; j < myEdges.Count; j++) { VoronoiEdge edge = myEdges[j]; if (edge.VVertexA.Equals(previous)) { previous = edge.VVertexB; Coordinate c = previous.ToCoordinate(); coords.Add(c); myEdges.Remove(edge); break; } // couldn't match by adding to the end, so try adding to the beginning if (edge.VVertexB.Equals(start)) { start = edge.VVertexA; coords.Insert(0, start.ToCoordinate()); myEdges.Remove(edge); break; } // I don't like the reverse situation, but it seems necessary. if (edge.VVertexB.Equals(previous)) { previous = edge.VVertexA; Coordinate c = previous.ToCoordinate(); coords.Add(c); myEdges.Remove(edge); break; } if (edge.VVertexA.Equals(start)) { start = edge.VVertexB; coords.Insert(0, start.ToCoordinate()); myEdges.Remove(edge); break; } } } for (int j = 0; j < coords.Count; j++) { Coordinate cA = coords[j]; // Remove NAN values if (double.IsNaN(cA.X) || double.IsNaN(cA.Y)) { coords.Remove(cA); } // Remove duplicate coordinates for (int k = j + 1; k < coords.Count; k++) { Coordinate cB = coords[k]; if (cA.Equals2D(cB)) coords.Remove(cB); } } foreach (Coordinate coord in coords) { if (double.IsNaN(coord.X) || double.IsNaN(coord.Y)) { coords.Remove(coord); } } if (coords.Count <= 2) continue; Polygon pg = new Polygon(coords); if (cropToExtent) { try { IGeometry g = pg.Intersection(bounds); IPolygon p = g as IPolygon; if (p != null) { Feature f = new Feature(p, result); f.CopyAttributes(points.Features[i]); } } catch (Exception) { Feature f = new Feature(pg, result); f.CopyAttributes(points.Features[i]); } } else { Feature f = new Feature(pg, result); f.CopyAttributes(points.Features[i]); } } return; }
/// <summary> /// Check if a shell is incorrectly nested within a polygon. This is the case /// if the shell is inside the polygon shell, but not inside a polygon hole. /// (If the shell is inside a polygon hole, the nesting is valid.) /// The algorithm used relies on the fact that the rings must be properly contained. /// E.g. they cannot partially overlap (this has been previously checked by /// <c>CheckRelateConsistency</c>). /// </summary> private void CheckShellNotNested(LinearRing shell, Polygon p, GeometryGraph graph) { IList<Coordinate> shellPts = shell.Coordinates; // test if shell is inside polygon shell LinearRing polyShell = (LinearRing)p.ExteriorRing; IList<Coordinate> polyPts = polyShell.Coordinates; Coordinate shellPt = FindPointNotNode(shellPts, polyShell, graph); // if no point could be found, we can assume that the shell is outside the polygon if (shellPt == null) return; bool insidePolyShell = CGAlgorithms.IsPointInRing(shellPt, polyPts); if (!insidePolyShell) return; // if no holes, this is an error! if (p.NumHoles <= 0) { _validErr = new TopologyValidationError(TopologyValidationErrors.NestedShells, shellPt); return; } /* * Check if the shell is inside one of the holes. * This is the case if one of the calls to checkShellInsideHole * returns a null coordinate. * Otherwise, the shell is not properly contained in a hole, which is an error. */ Coordinate badNestedPt = null; for (int i = 0; i < p.NumHoles; i++) { LinearRing hole = (LinearRing)p.GetInteriorRingN(i); badNestedPt = CheckShellInsideHole(shell, hole, graph); if (badNestedPt == null) return; } _validErr = new TopologyValidationError(TopologyValidationErrors.NestedShells, badNestedPt); }
private static IFeatureSet CreatePolygonFeatureSet() { Random rnd = new Random(DateTime.Now.Millisecond); FeatureSet fs = new FeatureSet(FeatureTypes.Point); fs.DataTable.Columns.Add("Number", typeof(int)); fs.DataTable.Columns.Add("Letter", typeof(string)); fs.Name = "Test"; for (int i = 0; i < 10; i++) { Coordinate center = NextCoordinate(rnd); List<Coordinate> shell = new List<Coordinate>(); for (int j = 0; j < 10; j++) { Coordinate c = new Coordinate(); double dx = 5*Math.Sin(Math.PI*(double) j/(double) 5); double dy = 5*Math.Cos(Math.PI*(double) j/(double) 5); c.X = center.X + dx; c.Y = center.Y + dy; shell.Add(c); } Polygon p = new Polygon(shell); Feature f = new Feature(p); fs.Features.Add(f); f.DataRow["Number"] = i; f.DataRow["Letter"] = "Shape " + i; } return fs; }
/// <summary> /// Reads a stream and converts the shapefile record to an equilivent geometry object. /// </summary> /// <param name="file">The stream to read.</param> /// <param name="geometryFactory">The geometry factory to use when making the object.</param> /// <returns>The Geometry object that represents the shape file record.</returns> public override IGeometry Read(BigEndianBinaryReader file, IGeometryFactory geometryFactory) { int shapeTypeNum = file.ReadInt32(); ShapeGeometryTypes shapeType = (ShapeGeometryTypes)Enum.Parse(typeof(ShapeGeometryTypes), shapeTypeNum.ToString()); if ( ! ( shapeType == ShapeGeometryTypes.Polygon || shapeType == ShapeGeometryTypes.PolygonM || shapeType == ShapeGeometryTypes.PolygonZ || shapeType == ShapeGeometryTypes.PolygonZM)) throw new ShapefileException("Attempting to load a non-polygon as polygon."); // Read and for now ignore bounds. double[] box = new double[4]; for (int i = 0; i < 4; i++) box[i] = file.ReadDouble(); int numParts = file.ReadInt32(); int numPoints = file.ReadInt32(); int[] partOffsets = new int[numParts]; for (int i = 0; i < numParts; i++) partOffsets[i] = file.ReadInt32(); ArrayList shells = new ArrayList(); ArrayList holes = new ArrayList(); for (int part = 0; part < numParts; part++) { int start = partOffsets[part]; int finish; if (part == numParts - 1) finish = numPoints; else finish = partOffsets[part + 1]; int length = finish - start; CoordinateList points = new CoordinateList(); for (int i = 0; i < length; i++) { Coordinate external = new Coordinate(file.ReadDouble(), file.ReadDouble() ); new PrecisionModel(geometryFactory.PrecisionModel).MakePrecise(external); Coordinate internalCoord = external; points.Add(internalCoord); } ILinearRing ring = geometryFactory.CreateLinearRing(points.ToArray()); // If shape have only a part, jump orientation check and add to shells if (numParts == 1) shells.Add(ring); else { // Orientation check if (CGAlgorithms.IsCounterClockwise(points.ToArray())) holes.Add(ring); else shells.Add(ring); } } // Now we have a list of all shells and all holes ArrayList holesForShells = new ArrayList(shells.Count); for (int i = 0; i < shells.Count; i++) holesForShells.Add(new ArrayList()); // Find holes for (int i = 0; i < holes.Count; i++) { LinearRing testRing = (LinearRing) holes[i]; LinearRing minShell = null; IEnvelope minEnv = null; IEnvelope testEnv = testRing.EnvelopeInternal; Coordinate testPt = testRing.GetCoordinateN(0); LinearRing tryRing; for (int j = 0; j < shells.Count; j++) { tryRing = (LinearRing) shells[j]; IEnvelope tryEnv = tryRing.EnvelopeInternal; if (minShell != null) minEnv = minShell.EnvelopeInternal; bool isContained = false; CoordinateList coordList = new CoordinateList(tryRing.Coordinates); if (tryEnv.Contains(testEnv) && (CGAlgorithms.IsPointInRing(testPt, coordList.ToArray()) || (PointInList(testPt, coordList)))) isContained = true; // Check if this new containing ring is smaller than the current minimum ring if (isContained) { if (minShell == null || minEnv.Contains(tryEnv)) minShell = tryRing; // Suggested by Brian Macomber and added 3/28/2006: // holes were being found but never added to the holesForShells array // so when converted to geometry by the factory, the inner rings were never created. ArrayList holesForThisShell = (ArrayList)holesForShells[j]; holesForThisShell.Add(holes[i]); } } } IPolygon[] polygons = new Polygon[shells.Count]; for (int i = 0; i < shells.Count; i++) polygons[i] = geometryFactory.CreatePolygon((LinearRing) shells[i], (LinearRing[])((ArrayList) holesForShells[i]).ToArray(typeof(LinearRing))); if (polygons.Length == 1) return polygons[0]; // It's a multi part return geometryFactory.CreateMultiPolygon(polygons); }
/// <summary> /// updates the auto-filling X and Y coordinates /// </summary> /// <param name="e"></param> protected override void OnMouseMove(GeoMouseArgs e) { if (_standBy) return; if (_coordinates == null || _coordinates.Count == 0) return; Coordinate c1 = e.GeographicLocation; if (_measureDialog.MeasureMode == MeasureModes.Distance) { Coordinate c2 = _coordinates[_coordinates.Count-1]; double dx = Math.Abs(c2.X - c1.X); double dy = Math.Abs(c2.Y - c1.Y); double dist; if(Map.Projection != null) { if(Map.Projection.IsLatLon) { double y = (c2.Y + c1.Y) / 2; double factor = Math.Cos(y * Math.PI / 180); dx *= factor; dist = Math.Sqrt(dx*dx + dy*dy); dist = dist * 111319.5; } else { dist = Math.Sqrt(dx * dx + dy * dy); dist *= Map.Projection.Unit.Meters; } } else { dist = Math.Sqrt(dx * dx + dy * dy); } _measureDialog.Distance = dist; _measureDialog.TotalDistance = _previousDistance + dist; } else { List<Coordinate> tempPolygon = _coordinates.ToList(); tempPolygon.Add(c1); if (tempPolygon.Count < 3) { _measureDialog.Area = 0; _measureDialog.TotalArea = 0; if(tempPolygon.Count == 2) { Rectangle r = Map.ProjToPixel(new LineString(tempPolygon).Envelope); r.Inflate(20,20); Map.Invalidate(r); } _mousePosition = e.Location; return; } Polygon pg = new Polygon(new LinearRing(tempPolygon)); double area = pg.Area; if (Map.Projection != null) { if (Map.Projection.IsLatLon) { area = area * 111319.5 * 111319.5; } else { area *= Map.Projection.Unit.Meters * Map.Projection.Unit.Meters; } } _measureDialog.Area = area; _measureDialog.TotalArea = area; Rectangle rr = Map.ProjToPixel(pg.Envelope); rr.Inflate(20, 20); Map.Invalidate(rr); _mousePosition = e.Location; } if (_coordinates != null && _coordinates.Count > 0) { List<System.Drawing.Point> points = new List<System.Drawing.Point>(); foreach (Coordinate coord in _coordinates) { points.Add(Map.ProjToPixel(coord)); } Rectangle oldRect = Global.GetRectangle(_mousePosition, points[points.Count - 1]); Rectangle newRect = Global.GetRectangle(e.Location, points[points.Count - 1]); Rectangle invalid = Rectangle.Union(newRect, oldRect); invalid.Inflate(20, 20); Map.Invalidate(invalid); } _mousePosition = e.Location; base.OnMouseMove(e); }
/// <summary> /// Executes the ClipPolygonWithLine Operation tool programaticaly. /// Ping deleted static for external testing 01/2010 /// </summary> /// <param name="input1">The input Polygon FeatureSet.</param> /// <param name="input2">The input Polyline FeatureSet.</param> /// <param name="output">The output Polygon FeatureSet.</param> /// <param name="cancelProgressHandler">The progress handler.</param> /// <returns></returns> public bool Execute(IFeatureSet input1, IFeatureSet input2, IFeatureSet output, ICancelProgressHandler cancelProgressHandler) { //Validates the input and output data if (input1 == null || input2 == null || output == null) { return false; } if (cancelProgressHandler.Cancel) return false; IFeature polygon = input1.Features[0]; IFeature line = input2.Features[0]; IFeatureSet resultFS = new FeatureSet(FeatureTypes.Polygon); int previous = 0; if (DoClipPolygonWithLine(ref polygon, ref line, ref output) == false) { throw new SystemException(TextStrings.Exceptioninclipin); } int intFeature = output.Features.Count; for (int i = 0; i < intFeature; i++) { Polygon poly = new Polygon(output.Features[i].Coordinates); resultFS.AddFeature(poly); int current = Convert.ToInt32(Math.Round(i * 100D / intFeature)); //only update when increment in percentage if (current > previous) cancelProgressHandler.Progress("", current, current + TextStrings.progresscompleted); previous = current; } cancelProgressHandler.Progress("", 100, 100 + TextStrings.progresscompleted); resultFS.SaveAs(output.Filename, true); return true; }
/// <summary> /// Constructs a <c>MultiPolygon</c>. /// </summary> /// <param name="polygons"> /// The <c>Polygon</c>s for this <c>MultiPolygon</c> /// , or <c>null</c> or an empty array to create the empty point. /// Elements may be empty <c>Polygon</c>s, but not <c>null</c> /// s. The polygons must conform to the assertions specified in the /// <see href="http://www.opengis.org/techno/specs.htm"/> OpenGIS Simple Features /// Specification for SQL. /// </param> /// <remarks> /// For create this <see cref="Geometry"/> is used a standard <see cref="GeometryFactory"/> /// with <see cref="PrecisionModel" /> <c> == </c> <see cref="PrecisionModels.Floating"/>. /// </remarks> public MultiPolygon(Polygon[] polygons) : this(polygons, DefaultFactory) { }
/// <summary> /// /// </summary> /// <param name="reader"></param> /// <returns></returns> protected virtual IGeometry ReadMultiPolygon(BinaryReader reader) { int numGeometries = reader.ReadInt32(); Polygon[] polygons = new Polygon[numGeometries]; for (int i = 0; i < numGeometries; i++) { ReadByteOrder(reader); WkbGeometryTypes geometryType = (WkbGeometryTypes)reader.ReadInt32(); if (geometryType != WkbGeometryTypes.Polygon) throw new ArgumentException("Polygon feature expected"); polygons[i] = ReadPolygon(reader) as Polygon; } return Factory.CreateMultiPolygon(polygons); }
/// <summary> /// /// </summary> /// <param name="rectangle"></param> /// <param name="b"></param> /// <returns></returns> public static bool Intersects(Polygon rectangle, IGeometry b) { RectangleIntersects rp = new RectangleIntersects(rectangle); return rp.Intersects(b); }