public void TestFindMutuallyVisibleVertex() { List <Cartesian> outerRing = new List <Cartesian> { Ellipsoid.Wgs84.ToCartesian(new Cartographic(-122.0, 37.0, 0.0)), Ellipsoid.Wgs84.ToCartesian(new Cartographic(-122.0, 37.1, 0.0)), Ellipsoid.Wgs84.ToCartesian(new Cartographic(-121.9, 37.1, 0.0)), Ellipsoid.Wgs84.ToCartesian(new Cartographic(-121.9, 37.0, 0.0)), Ellipsoid.Wgs84.ToCartesian(new Cartographic(-122.0, 37.0, 0.0)) }; List <Cartesian> innerRing = new List <Cartesian> { Ellipsoid.Wgs84.ToCartesian(new Cartographic(-121.99, 37.01, 0.0)), Ellipsoid.Wgs84.ToCartesian(new Cartographic(-121.96, 37.01, 0.0)), Ellipsoid.Wgs84.ToCartesian(new Cartographic(-121.96, 37.04, 0.0)), Ellipsoid.Wgs84.ToCartesian(new Cartographic(-121.99, 37.04, 0.0)), Ellipsoid.Wgs84.ToCartesian(new Cartographic(-121.99, 37.01, 0.0)) }; List <List <Cartesian> > innerRings = new List <List <Cartesian> >(); innerRings.Add(innerRing); int expectedResult = 1; int result = PolygonAlgorithms.GetMutuallyVisibleVertexIndex(outerRing, innerRings); Assert.AreEqual(expectedResult, result); }
/// <inheritdoc /> protected override void Write() { XElement coordinatesElement = m_element.Element(Document.Namespace + "outerBoundaryIs").Element(Document.Namespace + "LinearRing").Element(Document.Namespace + "coordinates"); IEnumerable <XElement> innerElements = m_element.Elements(Document.Namespace + "innerBoundaryIs"); List <List <Cartographic> > innerRings = new List <List <Cartographic> >(); foreach (XElement innerElement in innerElements) { string innerCoords = innerElement.Element(Document.Namespace + "LinearRing").Element(Document.Namespace + "coordinates").Value.Trim(); List <Cartographic> innerCoordinates = ParseCoordinates(innerCoords); innerRings.Add(innerCoordinates); } //outerboundary/linearRing string coordinates = coordinatesElement.Value.Trim(); var outerRing = ParseCoordinates(coordinates); List <Cartographic> outerPositions = outerRing; while (innerRings.Count > 0) { outerPositions = PolygonAlgorithms.EliminateHole(outerPositions, innerRings); } using (var positions = this.PacketWriter.OpenVertexPositionsProperty()) { positions.WriteCartographicRadians(outerPositions); } }
/// <summary> /// Initializes a new instance of the <see cref="MinkowskiSumAlgorithm" /> class. /// </summary> /// <param name="source">The source.</param> /// <param name="buffer">The buffer.</param> /// <exception cref="System.ArgumentNullException">The source is null.</exception> /// <exception cref="System.ArgumentNullException">The buffer is null.</exception> /// <exception cref="System.ArgumentException">The orientation of the source is not suitable.</exception> /// <exception cref="System.ArgumentException">The orientation of the buffer is not suitable.</exception> public MinkowskiSumAlgorithm(IList <Coordinate> source, IBasicPolygon buffer) { if (source == null) { throw new ArgumentNullException(nameof(source), "The source is null."); } if (buffer == null) { throw new ArgumentNullException(nameof(buffer), "The buffer is null."); } _sourceShellCoordinates = source; if (_sourceShellCoordinates.Count > 1 && _sourceShellCoordinates[_sourceShellCoordinates.Count - 1] != _sourceShellCoordinates[0]) { _sourceShellCoordinates.Add(_sourceShellCoordinates[0]); } if (_sourceShellCoordinates.Count > 3 && PolygonAlgorithms.Orientation(_sourceShellCoordinates) != Orientation.CounterClockwise) { throw new ArgumentException("The orientation of the source is not suitable.", nameof(source)); } _bufferCoordinates = new List <Coordinate>(); foreach (Coordinate coordinate in buffer.Shell.Coordinates) { _bufferCoordinates.Add(coordinate); } if (PolygonAlgorithms.Orientation(_bufferCoordinates) != Orientation.CounterClockwise) { throw new ArgumentException("The orientation of the buffer is not suitable.", nameof(buffer)); } _hasResult = false; }
public void TestEliminateHole() { List <Cartographic> outerRing = new List <Cartographic> { new Cartographic(-122.0, 37.0, 0.0), new Cartographic(-122.0, 37.1, 0.0), new Cartographic(-121.9, 37.1, 0.0), new Cartographic(-121.9, 37.0, 0.0), new Cartographic(-122.0, 37.0, 0.0) }; List <Cartographic> innerRing = new List <Cartographic> { new Cartographic(-121.99, 37.01, 0.0), new Cartographic(-121.96, 37.01, 0.0), new Cartographic(-121.96, 37.04, 0.0), new Cartographic(-121.99, 37.04, 0.0), new Cartographic(-121.99, 37.01, 0.0) }; List <List <Cartographic> > innerRings = new List <List <Cartographic> >(); innerRings.Add(innerRing); List <Cartographic> result = PolygonAlgorithms.EliminateHole(outerRing, innerRings); Assert.AreEqual(0, innerRings.Count); }
public void TestGetRightmostRingIndex() { List <Cartesian> ring0 = new List <Cartesian> { new Cartesian(0.0, 1.0, 0.0), new Cartesian(1.0, 2.0, 0.0), new Cartesian(2.0, 2.0, 0.0), new Cartesian(3.0, 1.0, 0.0), new Cartesian(2.0, 0.0, 0.0), new Cartesian(1.0, 1.0, 0.0) }; List <Cartesian> ring1 = new List <Cartesian> { new Cartesian(4.0, 1.0, 0.0), new Cartesian(5.0, 2.0, 0.0), new Cartesian(6.0, 2.0, 0.0), new Cartesian(7.0, 1.0, 0.0), new Cartesian(6.0, 0.0, 0.0), new Cartesian(5.0, 1.0, 0.0) }; List <List <Cartesian> > rings = new List <List <Cartesian> >(); rings.Add(ring0); rings.Add(ring1); int expectedResult = 1; int result = PolygonAlgorithms.GetRightmostRingIndex(rings); Assert.AreEqual(expectedResult, result); }
public void PolygonAlgorithmsOrientationTest() { // counter clockwise orientation convex polygon List <Coordinate> coordinates = new List <Coordinate> { new Coordinate(0, 0), new Coordinate(10, 0), new Coordinate(10, 10), new Coordinate(0, 10), new Coordinate(0, 0) }; Assert.AreEqual(Orientation.CounterClockwise, PolygonAlgorithms.Orientation(coordinates)); // clockwise orientation convex polygon coordinates = new List <Coordinate> { new Coordinate(0, 0), new Coordinate(0, 10), new Coordinate(10, 10), new Coordinate(10, 0), new Coordinate(0, 0) }; Assert.AreEqual(Orientation.Clockwise, PolygonAlgorithms.Orientation(coordinates)); // collinear orientation convex polygon coordinates = new List <Coordinate> { new Coordinate(0, 0), new Coordinate(0, 10), new Coordinate(0, 20), new Coordinate(0, 30), new Coordinate(0, 0) }; Assert.AreEqual(Orientation.Collinear, PolygonAlgorithms.Orientation(coordinates)); // clockwise concave polygon coordinates = new List <Coordinate> { new Coordinate(0, 0), new Coordinate(0, 10), new Coordinate(10, 10), new Coordinate(10, 0), new Coordinate(10, 10), new Coordinate(0, 10), new Coordinate(0, 0) }; Assert.AreEqual(Orientation.Clockwise, PolygonAlgorithms.Orientation(coordinates)); // counter clockwise concave polygon coordinates = new List <Coordinate> { new Coordinate(0, 0), new Coordinate(2, 1), new Coordinate(8, 1), new Coordinate(10, 0), new Coordinate(9, 2), new Coordinate(9, 8), new Coordinate(10, 10), new Coordinate(8, 9), new Coordinate(2, 9), new Coordinate(0, 10), new Coordinate(1, 8), new Coordinate(1, 2), new Coordinate(0, 0) }; Assert.AreEqual(Orientation.CounterClockwise, PolygonAlgorithms.Orientation(coordinates)); }
public void TestIsVertex() { List <Cartesian> ring = new List <Cartesian> { new Cartesian(0.0, 0.0, 0.0), new Cartesian(0.0, 1.0, 0.0), new Cartesian(1.0, 1.0, 0.0), new Cartesian(1.0, 0.0, 0.0), }; Assert.That(PolygonAlgorithms.IsVertex(ring, ring[0])); Assert.That(!PolygonAlgorithms.IsVertex(ring, new Cartesian(1.0, 1.0, 1.0))); }
public void TestIntersectPointWithRing() { List <Cartesian> ring = new List <Cartesian> { new Cartesian(0.0, 0.0, 0.0), new Cartesian(0.0, 1.0, 0.0), new Cartesian(1.0, 1.0, 0.0), new Cartesian(1.0, 0.0, 0.0), }; Cartesian point = new Cartesian(0.5, 0.5, 0.0); Cartesian expectedResult = new Cartesian(1.0, 0.5, 0.0); Cartesian result = PolygonAlgorithms.IntersectPointWithRing(point, ring); Assert.AreEqual(expectedResult, result); }
public void TestComputeArea2DCounterClockWise() { List <Cartesian> outerRing = new List <Cartesian> { new Cartesian(0.0, 5.0, 0.0), new Cartesian(5.0, 0.0, 0.0), new Cartesian(10.0, 5.0, 0.0), new Cartesian(5.0, 10.0, 0.0), new Cartesian(0.0, 5.0, 0.0) }; var result = PolygonAlgorithms.GetWindingOrder(outerRing); Assert.That(result == WindingOrder.CounterClockWise); }
/// <summary> /// Initializes a new instance of the <see cref="MinkowskiSumAlgorithm"/> class. /// </summary> /// <param name="source">The source.</param> /// <param name="buffer">The buffer.</param> /// <exception cref="System.ArgumentNullException">The source is null.</exception> /// <exception cref="System.ArgumentNullException">The buffer is null.</exception> /// <exception cref="System.ArgumentException">The orientation of the source is not suitable.</exception> /// <exception cref="System.ArgumentException">The orientation of the buffer is not suitable.</exception> public MinkowskiSumAlgorithm(IBasicPolygon source, IBasicPolygon buffer) { if (source == null) { throw new ArgumentNullException(nameof(source), "The source is null."); } if (buffer == null) { throw new ArgumentNullException(nameof(buffer), "The buffer is null."); } _sourceShellCoordinates = new List <Coordinate>(); foreach (Coordinate coordinate in source.Shell.Coordinates) { _sourceShellCoordinates.Add(coordinate); } if (PolygonAlgorithms.Orientation(_sourceShellCoordinates) != Orientation.CounterClockwise) { throw new ArgumentException("The orientation of the source is not suitable.", nameof(source)); } _sourceHoleCoordinates = new List <IList <Coordinate> >(); for (Int32 holeIndex = 0; holeIndex < source.HoleCount; holeIndex++) { _sourceHoleCoordinates.Add(new List <Coordinate>()); foreach (Coordinate coordinate in source.Holes[holeIndex].Coordinates) { _sourceHoleCoordinates[holeIndex].Add(coordinate); } if (PolygonAlgorithms.Orientation(_sourceHoleCoordinates[holeIndex]) != Orientation.Clockwise) { throw new ArgumentException("The orientation of the source is not suitable.", nameof(source)); } } _bufferCoordinates = new List <Coordinate>(); foreach (Coordinate coordinate in buffer.Shell.Coordinates) { _bufferCoordinates.Add(coordinate); } if (PolygonAlgorithms.Orientation(_bufferCoordinates) != Orientation.CounterClockwise) { throw new ArgumentException("The orientation of the buffer is not suitable.", nameof(buffer)); } _hasResult = false; }
public void TestGetRightmostVertexIndex() { List <Cartesian> ring = new List <Cartesian> { new Cartesian(0.0, 1.0, 0.0), new Cartesian(1.0, 2.0, 0.0), new Cartesian(2.0, 2.0, 0.0), new Cartesian(3.0, 1.0, 0.0), new Cartesian(2.0, 0.0, 0.0), new Cartesian(1.0, 1.0, 0.0) }; int expectedResult = 3; int result = PolygonAlgorithms.GetRightmostVertexIndex(ring); Assert.AreEqual(expectedResult, result); }
public void TestIsPointInTriangle() { Cartesian a = new Cartesian(0, 0, 0); Cartesian b = new Cartesian(0, 1, 0); Cartesian c = new Cartesian(1, 0, 0); // p is on the boundary of the triangle Cartesian p = new Cartesian(0, 0, 0); Assert.That(PolygonAlgorithms.IsPointInTriangle(a, b, c, p)); // p is inside the triangle p = new Cartesian(0.5, 0.25, 0); Assert.That(PolygonAlgorithms.IsPointInTriangle(a, b, c, p)); // p is outside the triangle p = new Cartesian(1, 1, 0); Assert.That(PolygonAlgorithms.IsPointInTriangle(a, b, c, p) == false); }
public void RandomPolygonGeneratorCreateRandomPolygonTest() { // check properties of generated polygons Int32 coordinateNumber = 10; Coordinate minCoordinate = new Coordinate(10, 10); Coordinate maxCoordinate = new Coordinate(20, 20); Double convexityRatio = 0.1; for (Int32 polygonNumber = 1; polygonNumber < 100; polygonNumber++) { IBasicPolygon randomPolygon = RandomPolygonGenerator.CreateRandomPolygon(coordinateNumber * polygonNumber, minCoordinate, maxCoordinate); Assert.AreEqual(coordinateNumber * polygonNumber + 1, randomPolygon.Shell.Count); // number of coordinates Assert.AreEqual(Orientation.CounterClockwise, PolygonAlgorithms.Orientation(randomPolygon)); // orientation foreach (Coordinate coordinate in randomPolygon.Shell) { Assert.True(coordinate.X > minCoordinate.X && coordinate.X <maxCoordinate.X && coordinate.Y> minCoordinate.Y && coordinate.Y < maxCoordinate.Y); // all coordinates are located in the rectangle } Assert.IsFalse(ShamosHoeyAlgorithm.Intersects(randomPolygon.Shell)); // no intersection } // check convexity for (Int32 polygonNumber = 1; polygonNumber < 100; polygonNumber++) { Assert.IsTrue(PolygonAlgorithms.IsConvex(RandomPolygonGenerator.CreateRandomPolygon(coordinateNumber * polygonNumber, minCoordinate, maxCoordinate, 1))); } for (Int32 polygonNumber = 1; polygonNumber < 100; polygonNumber++) { Assert.IsFalse(PolygonAlgorithms.IsConvex(RandomPolygonGenerator.CreateRandomPolygon(coordinateNumber * polygonNumber, minCoordinate, maxCoordinate, 0))); } // check exceptions Assert.Throws <ArgumentOutOfRangeException>(() => RandomPolygonGenerator.CreateRandomPolygon(1, minCoordinate, maxCoordinate, convexityRatio)); Assert.Throws <ArgumentOutOfRangeException>(() => RandomPolygonGenerator.CreateRandomPolygon(coordinateNumber, maxCoordinate, minCoordinate, convexityRatio)); Assert.Throws <ArgumentOutOfRangeException>(() => RandomPolygonGenerator.CreateRandomPolygon(coordinateNumber, minCoordinate, maxCoordinate, -1)); }
public void PolygonAlgorithmsSingedAreaTest() { // signed area of a rectangle with coordinates given in counterclockwise order List <Coordinate> shell = new List <Coordinate> { new Coordinate(5.5, 5.5), new Coordinate(130.5, 5.5), new Coordinate(130.5, 100.5), new Coordinate(5.5, 100.5) }; BasicPolygon polygon = new BasicPolygon(shell, null); Assert.AreEqual(-11875, PolygonAlgorithms.SignedArea(polygon)); // signed area of a triangle with coordinates given in clockwise order shell = new List <Coordinate> { new Coordinate(3, 3), new Coordinate(6, 1), new Coordinate(2, 1), new Coordinate(3, 3) }; Assert.AreEqual(Math.Round(3.98), Math.Round(PolygonAlgorithms.SignedArea(shell))); // exceptions Assert.Throws <ArgumentNullException>(() => PolygonAlgorithms.SignedArea((List <Coordinate>)null)); Assert.Throws <ArgumentNullException>(() => PolygonAlgorithms.SignedArea((IBasicPolygon)null)); }
public void TestGetReflexVertices() { List <Cartesian> polygon = new List <Cartesian> { new Cartesian(0.0, 2.0, 0.0), new Cartesian(1.6, 2.0, 0.0), // reflex new Cartesian(2.0, 3.0, 0.0), new Cartesian(2.3, 2.0, 0.0), // reflex new Cartesian(4.0, 2.0, 0.0), new Cartesian(2.6, 1.0, 0.0), // reflex new Cartesian(3.0, 0.0, 0.0), new Cartesian(2.0, 1.0, 0.0), // reflex new Cartesian(1.0, 0.0, 0.0), new Cartesian(1.3, 1.0, 0.0) // reflex }; List <Cartesian> reflexVertices = PolygonAlgorithms.GetReflexVertices(polygon); Assert.That(reflexVertices.Count == 5); Assert.That(reflexVertices.Contains(polygon[1])); Assert.That(reflexVertices.Contains(polygon[3])); Assert.That(reflexVertices.Contains(polygon[5])); Assert.That(reflexVertices.Contains(polygon[7])); Assert.That(reflexVertices.Contains(polygon[9])); }
public void PolygonAlgorithmsSingedAreaTest() { // signed area of a rectangle with coordinates given in counterclockwise order Coordinate[] shell = new[] { new Coordinate(5.5, 5.5), new Coordinate(130.5, 5.5), new Coordinate(130.5, 100.5), new Coordinate(5.5, 100.5) }; BasicPolygon polygon = new BasicPolygon(shell, null); PolygonAlgorithms.SignedArea(polygon).ShouldBe(-11875); // signed area of a triangle with coordinates given in clockwise order shell = new[] { new Coordinate(3, 3), new Coordinate(6, 1), new Coordinate(2, 1), new Coordinate(3, 3) }; PolygonAlgorithms.SignedArea(shell).ShouldBe(4); // exceptions Should.Throw <ArgumentNullException>(() => PolygonAlgorithms.SignedArea((List <Coordinate>)null)); Should.Throw <ArgumentNullException>(() => PolygonAlgorithms.SignedArea((IBasicPolygon)null)); }
public void PolygonAlgorithmsIsConvexTest() { // rectangle with counterclockwise orientation Coordinate[] shell = new[] { new Coordinate(5, 5), new Coordinate(130, 5), new Coordinate(130, 100), new Coordinate(5, 100) }; IBasicPolygon polygon = new BasicPolygon(shell, null); PolygonAlgorithms.IsConvex(polygon).ShouldBeTrue(); // rectangle with a hole shell = new[] { new Coordinate(5, 5), new Coordinate(130, 5), new Coordinate(130, 100), new Coordinate(5, 100) }; Coordinate[] hole = new[] { new Coordinate(0, 0), new Coordinate(0, 2), new Coordinate(2, 2), new Coordinate(2, 0), }; polygon = new BasicPolygon(shell, new[] { hole }); PolygonAlgorithms.IsConvex(polygon).ShouldBeFalse(); // non-convex polygon with counterclockwise orientation shell = new[] { new Coordinate(5, 5), new Coordinate(130, 5), new Coordinate(130, 100), new Coordinate(70, 70), new Coordinate(5, 100) }; polygon = new BasicPolygon(shell, null); PolygonAlgorithms.IsConvex(polygon).ShouldBeFalse(); // convex polygon with counterclockwise orientation shell = new[] { new Coordinate(6, 7), new Coordinate(7, 6), new Coordinate(9, 6), new Coordinate(9, 8), new Coordinate(8, 10), new Coordinate(7, 9) }; PolygonAlgorithms.IsConvex(shell).ShouldBeTrue(); // convex polygon with clockwise orientation shell = new[] { new Coordinate(0, 2), new Coordinate(2, 0), new Coordinate(3, -2), new Coordinate(1, -4), new Coordinate(-1, -4), new Coordinate(-3, -2), new Coordinate(-2, 0) }; PolygonAlgorithms.IsConvex(shell).ShouldBeTrue(); // convex polygon that contains collinear edges shell = new[] { new Coordinate(0, 2), new Coordinate(2, 0), new Coordinate(3, -2), new Coordinate(1, -4), new Coordinate(0, -4), new Coordinate(-1, -4), new Coordinate(-3, -2), new Coordinate(-2, 0) }; PolygonAlgorithms.IsConvex(shell).ShouldBeTrue(); // non-simple polygon shell = new[] { new Coordinate(1, 1), new Coordinate(1, 3), new Coordinate(5, 3), new Coordinate(7, 5), new Coordinate(7, 3), new Coordinate(5, 3), new Coordinate(5, 1) }; polygon = new BasicPolygon(shell, null); PolygonAlgorithms.IsConvex(polygon).ShouldBeFalse(); // exceptions Should.Throw <ArgumentNullException>(() => PolygonAlgorithms.IsConvex((IBasicPolygon)null)); Should.Throw <ArgumentNullException>(() => PolygonAlgorithms.IsConvex((List <Coordinate>)null)); }
/// <summary> /// Transform the data to a geometry representation. /// </summary> /// <returns>The geometry representation of the data.</returns> /// <exception cref="System.NotSupportedException">Multipatch conversion is not supported.</exception> public IGeometry ToGeometry() { Coordinate[] partCoordinates = null; List <IPolygon> polygons = null; switch (Type) { case ShapeType.Point: case ShapeType.PointM: case ShapeType.PointZ: return(_factory.CreatePoint(Coordinates[0].X, Coordinates[0].Y, Coordinates[0].Z, _metadata)); case ShapeType.PolyLine: case ShapeType.PolyLineM: case ShapeType.PolyLineZ: if (PartCount > 1) { Coordinate[][] lines = new Coordinate[PartCount][]; for (Int32 i = 0; i < PartCount - 1; i++) { lines[i] = new Coordinate[_parts[i + 1] - _parts[i]]; Array.Copy(_coordinates, _parts[i], lines[i], 0, _parts[i + 1] - _parts[i]); } lines[_parts.Length - 1] = new Coordinate[_coordinates.Length - _parts[_parts.Length - 1]]; Array.Copy(_coordinates, _parts[_parts.Length - 1], lines[_parts.Length - 1], 0, _coordinates.Length - _parts[_parts.Length - 1]); List <ILineString> lineStringList = new List <ILineString>(); foreach (Coordinate[] line in lines) { lineStringList.Add(_factory.CreateLineString(line)); } return(_factory.CreateMultiLineString(lineStringList, _metadata)); } else { return(_factory.CreateLineString(_coordinates, _metadata)); } case ShapeType.Polygon: case ShapeType.PolygonM: case ShapeType.PolygonZ: if (PartCount > 1) // if there are more parts (interior rings or multiple polygons are added) { partCoordinates = null; polygons = new List <IPolygon>(); // all parts except final for (Int32 i = 0; i < PartCount - 1; i++) { if (_parts[i + 1] == _parts[i]) // in case the part is empty (should never happen) { continue; } if (_coordinates[_parts[i]] != _coordinates[_parts[i + 1] - 1]) { partCoordinates = new Coordinate[_parts[i + 1] - _parts[i]]; Array.Copy(_coordinates, _parts[i], partCoordinates, 0, _parts[i + 1] - _parts[i]); partCoordinates[partCoordinates.Length - 1] = partCoordinates[0]; } else { partCoordinates = new Coordinate[_parts[i + 1] - _parts[i]]; Array.Copy(_coordinates, _parts[i], partCoordinates, 0, _parts[i + 1] - _parts[i]); } // check whether the current part is an outer or inner ring switch (PolygonAlgorithms.Orientation(partCoordinates)) { case Orientation.Clockwise: // in case of outer ring a new polygon is created polygons.Add(_factory.CreatePolygon(partCoordinates.Reverse(), _metadata)); break; case Orientation.CounterClockwise: // inner rings are simply added to the last polygon polygons[polygons.Count - 1].AddHole(_factory.CreateLinearRing(partCoordinates.Reverse())); break; } } // final part if (_coordinates[_parts[_parts.Length - 1]] != _coordinates[_coordinates.Length - 1]) { partCoordinates = new Coordinate[_coordinates.Length - _parts[_parts.Length - 1] + 1]; Array.Copy(_coordinates, _parts[_parts.Length - 1], partCoordinates, 0, _coordinates.Length - _parts[_parts.Length - 1]); partCoordinates[partCoordinates.Length - 1] = partCoordinates[0]; } else { partCoordinates = new Coordinate[_coordinates.Length - _parts[_parts.Length - 1]]; Array.Copy(_coordinates, _parts[_parts.Length - 1], partCoordinates, 0, _coordinates.Length - _parts[_parts.Length - 1]); } // check whether the current part is an outer or inner ring switch (PolygonAlgorithms.Orientation(partCoordinates)) { case Orientation.Clockwise: // in case of outer ring a new polygon is created polygons.Add(_factory.CreatePolygon(partCoordinates.Reverse(), _metadata)); break; case Orientation.CounterClockwise: // inner rings are simply added to the last polygon polygons[polygons.Count - 1].AddHole(_factory.CreateLinearRing(partCoordinates.Reverse())); break; } // if there are more than one outer rings a multipolygon will be created if (polygons.Count == 1) { return(polygons[0]); } else { return(_factory.CreateMultiPolygon(polygons, _metadata)); } } else { if (_coordinates[0] != _coordinates[_coordinates.Length - 1]) { partCoordinates = new Coordinate[_coordinates.Length + 1]; Array.Copy(_coordinates, 0, partCoordinates, 0, _coordinates.Length); partCoordinates[partCoordinates.Length - 1] = partCoordinates[0]; return(_factory.CreatePolygon(partCoordinates.Reverse(), _metadata)); } else { return(_factory.CreatePolygon(_coordinates.Reverse(), _metadata)); } } case ShapeType.MultiPoint: case ShapeType.MultiPointM: case ShapeType.MultiPointZ: return(_factory.CreateMultiPoint(_coordinates, _metadata)); case ShapeType.MultiPatch: Int32 partIndex = 0; polygons = new List <IPolygon>(); while (partIndex < _parts.Length) { if (partIndex == _parts.Length - 1) { partCoordinates = new Coordinate[_coordinates.Length - _parts[partIndex]]; } else { partCoordinates = new Coordinate[_parts[partIndex + 1] - _parts[partIndex]]; } Array.Copy(_coordinates, _parts[_parts.Length - 1], partCoordinates, 0, _coordinates.Length - _parts[_parts.Length - 1]); switch (PartTypes[partIndex]) { case ShapePartType.OuterRing: polygons.Add(_factory.CreatePolygon(partCoordinates)); break; case ShapePartType.InnerRing: if (polygons.Count == 0) { continue; } polygons[polygons.Count - 1].AddHole(_factory.CreateLinearRing(partCoordinates)); break; default: throw new InvalidOperationException("Multipatch conversion is not supported."); } } if (polygons.Count == 1) { return(polygons[0]); } else { return(_factory.CreateMultiPolygon(polygons, _metadata)); } default: return(null); } }
public void PolygonAlgorithmsIsSimpleTest() { // simple convex polygon without holes in clockwise orientation, containing collinear edges List <Coordinate> shell = new List <Coordinate> { new Coordinate(0, 2), new Coordinate(2, 0), new Coordinate(3, -2), new Coordinate(1, -4), new Coordinate(0, -4), new Coordinate(-1, -4), new Coordinate(-3, -2), new Coordinate(-2, 0) }; IBasicPolygon polygon = new BasicPolygon(shell, null); Assert.IsTrue(PolygonAlgorithms.IsSimple(polygon)); // simple concave polygon without holes in clockwise orientation shell = new List <Coordinate> { new Coordinate(2, 2), new Coordinate(2, 5), new Coordinate(4, 5), new Coordinate(3, 3), new Coordinate(6, 3), new Coordinate(5, 5), new Coordinate(7, 5), new Coordinate(7, 2) }; polygon = new BasicPolygon(shell, null); Assert.IsTrue(PolygonAlgorithms.IsSimple(polygon)); // non-simple polygon without holes, with two intersecting edges shell = new List <Coordinate> { new Coordinate(2, 2), new Coordinate(7, 5), new Coordinate(2, 5), new Coordinate(7, 2) }; polygon = new BasicPolygon(shell, null); Assert.IsFalse(PolygonAlgorithms.IsSimple(polygon)); // non-simple polygon with a hole shell = new List <Coordinate> { new Coordinate(2, 2), new Coordinate(7, 5), new Coordinate(2, 5), new Coordinate(7, 2) }; List <Coordinate> hole = new List <Coordinate> { new Coordinate(4, 2), new Coordinate(5, 2), new Coordinate(5, 3), new Coordinate(4, 3) }; polygon = new BasicPolygon(shell, new[] { hole }); Assert.IsFalse(PolygonAlgorithms.IsSimple(polygon)); // convex polygon with a hole shell = new List <Coordinate> { new Coordinate(10, 5), new Coordinate(11, 4), new Coordinate(16, 4), new Coordinate(17, 5), new Coordinate(17, 8), new Coordinate(16, 9), new Coordinate(11, 9), new Coordinate(10, 8) }; hole = new List <Coordinate> { new Coordinate(13, 7), new Coordinate(13, 8), new Coordinate(14, 8), new Coordinate(14, 7) }; polygon = new BasicPolygon(shell, new[] { hole }); Assert.IsFalse(PolygonAlgorithms.IsSimple(polygon)); // non-simple polygon with two intersecting edges shell = new List <Coordinate> { new Coordinate(5, 5), new Coordinate(3, 2), new Coordinate(6, 9), new Coordinate(8, 2), new Coordinate(10, 5) }; polygon = new BasicPolygon(shell, null); Assert.IsFalse(PolygonAlgorithms.IsSimple(polygon)); // non- simple polygon with three intersecting edges shell = new List <Coordinate> { new Coordinate(4, 2), new Coordinate(7, 1), new Coordinate(4, 6), new Coordinate(2, 4), new Coordinate(8, 5), new Coordinate(9, 4), new Coordinate(4, 2) }; Assert.IsFalse(PolygonAlgorithms.IsSimple(shell)); // exceptions Assert.Throws <ArgumentNullException>(() => PolygonAlgorithms.IsSimple((IBasicPolygon)null)); Assert.Throws <ArgumentNullException>(() => PolygonAlgorithms.IsSimple((List <Coordinate>)null)); }
public void PolygonAlgorithmsAreaTest() { // area of a rectangle in counterclockwise orientation List <Coordinate> shell = new List <Coordinate> { new Coordinate(5, 5), new Coordinate(130, 5), new Coordinate(130, 100), new Coordinate(5, 100), new Coordinate(5, 5) }; Assert.AreEqual(11875, PolygonAlgorithms.Area(shell)); // area of a rectangle in counterclockwise orientation and double precision coordinates shell = new List <Coordinate> { new Coordinate(5.5, 5.5), new Coordinate(130.5, 5.5), new Coordinate(130.5, 100.5), new Coordinate(5.5, 100.5) }; IBasicPolygon polygon = new BasicPolygon(shell, null); Assert.AreEqual(11875, PolygonAlgorithms.Area(polygon)); // area of a square with a hole shell = new List <Coordinate> { new Coordinate(-5, 5), new Coordinate(5, 5), new Coordinate(5, 15), new Coordinate(-5, 15) }; List <Coordinate> hole = new List <Coordinate> { new Coordinate(0, 0), new Coordinate(0, 2), new Coordinate(2, 2), new Coordinate(2, 0), }; polygon = new BasicPolygon(shell, new[] { hole }); Assert.AreEqual(96, PolygonAlgorithms.Area(polygon)); // area of a regular hexagon in counterclockwise orientation shell = new List <Coordinate> { new Coordinate(-3, -5), new Coordinate(3, -5), new Coordinate(6, 0), new Coordinate(3, 5), new Coordinate(-3, 5), new Coordinate(-6, 0) }; polygon = new BasicPolygon(shell, null); Assert.AreEqual(90, PolygonAlgorithms.Area(polygon)); // area of a concave polygon shell = new List <Coordinate> { new Coordinate(1, 1), new Coordinate(1, 3), new Coordinate(5, 3), new Coordinate(7, 5), new Coordinate(7, 1) }; polygon = new BasicPolygon(shell, null); Assert.AreEqual(14, PolygonAlgorithms.Area(polygon)); // area of a non-simple polygon shell = new List <Coordinate> { new Coordinate(1, 1), new Coordinate(1, 3), new Coordinate(5, 3), new Coordinate(7, 5), new Coordinate(7, 3), new Coordinate(5, 3), new Coordinate(5, 1) }; polygon = new BasicPolygon(shell, null); Assert.AreEqual(10, PolygonAlgorithms.Area(polygon)); // exceptions Assert.Throws <ArgumentNullException>(() => PolygonAlgorithms.Area((IBasicPolygon)null)); Assert.Throws <ArgumentNullException>(() => PolygonAlgorithms.Area((List <Coordinate>)null)); }
private void JoinInnerRings() { PolygonShape polygon = (PolygonShape)m_shape; List <int> outerRingIndices = new List <int>(); if (polygon.Count > 1) { List <CartographicExtent> ringExtents = new List <CartographicExtent>(); for (int i = 0; i < polygon.Count; i++) { double south, west, east, north; south = north = polygon[i][0].Latitude; west = east = polygon[i][0].Longitude; foreach (Cartographic point in polygon[i]) { if (point.Longitude > east) { east = point.Longitude; } if (point.Longitude < west) { east = point.Longitude; } if (point.Latitude > north) { north = point.Latitude; } if (point.Latitude < south) { south = point.Latitude; } } ringExtents.Add(new CartographicExtent(west, south, east, north)); } for (int i = 0; i < ringExtents.Count; i++) { for (int j = 0; j < ringExtents.Count; j++) { if (i != j && ringExtents[j].WestLongitude >= ringExtents[i].WestLongitude && ringExtents[j].EastLongitude <= ringExtents[i].EastLongitude && ringExtents[j].NorthLatitude <= ringExtents[i].NorthLatitude && ringExtents[j].SouthLatitude >= ringExtents[i].SouthLatitude) { outerRingIndices.Add(i); break; } } } for (int i = 0; i < outerRingIndices.Count; i++) { int outerRingIndex = outerRingIndices[i]; List <List <Cartographic> > innerRings = new List <List <Cartographic> >(); for (int j = 0; j < ringExtents.Count; j++) { if (outerRingIndex != j && ringExtents[j].WestLongitude >= ringExtents[outerRingIndex].WestLongitude && ringExtents[j].EastLongitude <= ringExtents[outerRingIndex].EastLongitude && ringExtents[j].NorthLatitude <= ringExtents[outerRingIndex].NorthLatitude && ringExtents[j].SouthLatitude >= ringExtents[outerRingIndex].SouthLatitude) { List <Cartographic> innerRing = new List <Cartographic>(); foreach (Cartographic point in polygon[j]) { innerRing.Add(point); } innerRings.Add(innerRing); } } List <Cartographic> outerRing = new List <Cartographic>(); foreach (Cartographic point in polygon[outerRingIndex]) { outerRing.Add(point); } while (innerRings.Count > 0) { outerRing = PolygonAlgorithms.EliminateHole(outerRing, innerRings); } simplifiedRings.Add(new ShapePart(outerRing.ToArray(), 0, outerRing.Count)); } } else { simplifiedRings.Add(polygon[0]); } }
/// <summary> /// Creates the result polygon with separated shell and hole coordinates. /// </summary> /// <param name="coordinates">The list of coordinates.</param> /// <param name="isHole">if set to <c>true</c> [is hole].</param> private void ComputeResultPolygon(IList <Coordinate> coordinates, Boolean isHole) { BentleyOttmannAlgorithm algorithm = new BentleyOttmannAlgorithm(coordinates); List <Coordinate> intersections = new List <Coordinate>(algorithm.Intersections); List <Tuple <Int32, Int32> > edgeIndices = new List <Tuple <Int32, Int32> >(algorithm.EdgeIndices); // add edge intersection coordinates to the list of coordinates in counterclockwise order List <Coordinate> coordinatesWithIntersections = new List <Coordinate>(); coordinatesWithIntersections = GetCoordinatesWithIntersections(coordinates, intersections, edgeIndices); // remove unnecessary internal coordinates and create holes List <List <Coordinate> > internalPolygons = new List <List <Coordinate> >(); List <List <Coordinate> > holes = new List <List <Coordinate> >(); List <Int32> nonShellIndexes = new List <Int32>(); Int32 internalPolygonIndex = -1; for (Int32 coordinateIndex = 0; coordinateIndex < coordinatesWithIntersections.Count; coordinateIndex++) { if (internalPolygonIndex != -1) { internalPolygons[internalPolygonIndex].Add(coordinatesWithIntersections[coordinateIndex]); nonShellIndexes.Add(coordinateIndex); } if (intersections.Any(x => x.Equals(coordinatesWithIntersections[coordinateIndex]))) { Int32 matchingPolygonIndex = internalPolygons.FindIndex(x => x[0].Equals(coordinatesWithIntersections[coordinateIndex])); if (internalPolygonIndex != -1 && internalPolygonIndex < internalPolygons.Count && matchingPolygonIndex != -1) { if (PolygonAlgorithms.Orientation(internalPolygons[matchingPolygonIndex]) == Orientation.Clockwise) { holes.Add(internalPolygons[matchingPolygonIndex]); } for (Int32 polygonIndex = internalPolygons.Count - 1; polygonIndex >= matchingPolygonIndex; polygonIndex--) { internalPolygons.RemoveAt(polygonIndex); } internalPolygonIndex = matchingPolygonIndex - 1; } else { internalPolygonIndex++; internalPolygons.Add(new List <Coordinate>() { coordinatesWithIntersections[coordinateIndex] }); } } } for (Int32 i = 0; i < nonShellIndexes.Count; i++) { coordinatesWithIntersections.RemoveAt(nonShellIndexes[nonShellIndexes.Count - 1 - i]); } // add shell and hole coordinates to the result if (isHole) { _resultHoleCoordinates.Add(new List <Coordinate>(coordinatesWithIntersections)); } else { _resultShellCoordinates = coordinatesWithIntersections; foreach (List <Coordinate> hole in holes) { _resultHoleCoordinates.Add(hole); } } }
/// <summary> /// Gets the buffer edges that lie between the corresponding source edges. /// </summary> /// <param name="coordinates">The list of coordinates.</param> /// <param name="sourceIndex">Index of the source coordinate.</param> /// <returns> /// The list of edges. /// </returns> private List <Tuple <Coordinate, Coordinate> > GetConvolutionEdges(IList <Coordinate> coordinates, Int32 sourceIndex) { List <Tuple <Coordinate, Coordinate> > edges = new List <Tuple <Coordinate, Coordinate> >(); Double previousElementX = sourceIndex != 0 ? coordinates[sourceIndex - 1].X : coordinates[coordinates.Count - 2].X; Double previousElementY = sourceIndex != 0 ? coordinates[sourceIndex - 1].Y : coordinates[coordinates.Count - 2].Y; Double firstAngle = Math.Atan2(coordinates[sourceIndex].Y - previousElementY, coordinates[sourceIndex].X - previousElementX); Double secondAngle = Math.Atan2(coordinates[sourceIndex + 1].Y - coordinates[sourceIndex].Y, coordinates[sourceIndex + 1].X - coordinates[sourceIndex].X); if (firstAngle < 0 && secondAngle == 0) { secondAngle = 2 * Math.PI; } if (firstAngle < 0) { firstAngle = 2 * Math.PI + firstAngle; } if (secondAngle < 0) { secondAngle = 2 * Math.PI + secondAngle; } for (Int32 bufferIndex = 0; bufferIndex < _bufferCoordinates.Count - 1; bufferIndex++) { Double middleAngle = Math.Atan2(_bufferCoordinates[bufferIndex + 1].Y - _bufferCoordinates[bufferIndex].Y, _bufferCoordinates[bufferIndex + 1].X - _bufferCoordinates[bufferIndex].X); if (middleAngle < 0) { middleAngle = 2 * Math.PI + middleAngle; } Boolean addCondition = false; if (PolygonAlgorithms.Orientation(coordinates) == Orientation.Clockwise) { if (firstAngle >= secondAngle) { addCondition = (middleAngle <= firstAngle) && (middleAngle >= secondAngle); } else { addCondition = middleAngle <= firstAngle || middleAngle >= secondAngle; } } else { if (firstAngle <= secondAngle) { addCondition = middleAngle >= firstAngle && middleAngle <= secondAngle; } else { addCondition = (middleAngle > firstAngle) || (middleAngle < secondAngle); } } if (addCondition) { edges.Add( Tuple.Create( new Coordinate( coordinates[sourceIndex].X + _bufferCoordinates[bufferIndex].X, coordinates[sourceIndex].Y + _bufferCoordinates[bufferIndex].Y), new Coordinate( coordinates[sourceIndex].X + _bufferCoordinates[bufferIndex + 1].X, coordinates[sourceIndex].Y + _bufferCoordinates[bufferIndex + 1].Y))); } } return(edges); }
public void MinkowskiSumAlgorithmBufferTest() { // two triangles Coordinate[] buffer = new[] { new Coordinate(5, 5), new Coordinate(5, 7), new Coordinate(4, 6) }; Coordinate[] sourceShell = new[] { new Coordinate(2, 1), new Coordinate(4, 1), new Coordinate(3, 3), new Coordinate(2, 1) }; Coordinate[] expectedShell = new[] { new Coordinate(6, 7), new Coordinate(7, 6), new Coordinate(9, 6), new Coordinate(9, 8), new Coordinate(8, 10), new Coordinate(7, 9), }; IBasicPolygon expected = new BasicPolygon(expectedShell); IBasicPolygon actual = MinkowskiSumAlgorithm.Buffer(sourceShell, buffer); PolygonAlgorithms.IsConvex(actual).ShouldBeTrue(); actual.Shell.ShouldBe(expected.Shell); // triangle and a square buffer = new[] { new Coordinate(8, 6), new Coordinate(9, 6), new Coordinate(9, 7), new Coordinate(8, 7) }; sourceShell = new[] { new Coordinate(3, 4), new Coordinate(5, 4), new Coordinate(4, 6), new Coordinate(3, 4) }; expectedShell = new[] { new Coordinate(11, 11), new Coordinate(11, 10), new Coordinate(13, 10), new Coordinate(14, 10), new Coordinate(14, 11), new Coordinate(13, 13), new Coordinate(12, 13), new Coordinate(11, 11) }; expected = new BasicPolygon(expectedShell); actual = MinkowskiSumAlgorithm.Buffer(sourceShell, buffer); PolygonAlgorithms.IsConvex(actual).ShouldBeTrue(); actual.Shell.ShouldBe(expected.Shell); // square and circle of 4 points (square) buffer = new[] { new Coordinate(8, 6), new Coordinate(9, 6), new Coordinate(9, 7), new Coordinate(8, 7), new Coordinate(8, 6) }; expectedShell = new[] { new Coordinate(6, 6), new Coordinate(8, 4), new Coordinate(9, 4), new Coordinate(11, 6), new Coordinate(11, 7), new Coordinate(9, 9), new Coordinate(8, 9), new Coordinate(6, 7), new Coordinate(6, 6) }; expected = new BasicPolygon(expectedShell); actual = MinkowskiSumAlgorithm.Buffer(buffer, 2, 4); PolygonAlgorithms.IsConvex(actual).ShouldBeTrue(); actual.Shell.Count.ShouldBe(expected.Shell.Count); for (Int32 coordIndex = 0; coordIndex < expected.Shell.Count; coordIndex++) { actual.Shell[coordIndex].X.ShouldBe(expected.Shell[coordIndex].X, 0.01); actual.Shell[coordIndex].Y.ShouldBe(expected.Shell[coordIndex].Y, 0.01); } // point and circle (of 4 points) IBasicPoint point = new BasicPoint(new Coordinate(1, 1, 0)); actual = MinkowskiSumAlgorithm.Buffer(point, 2, 4); expectedShell = new[] { new Coordinate(3, 1), new Coordinate(1, 3), new Coordinate(-1, 1), new Coordinate(1, -1) }; expected = new BasicPolygon(expectedShell); PolygonAlgorithms.IsConvex(actual).ShouldBeTrue(); actual.Shell.Count.ShouldBe(expected.Shell.Count); for (Int32 coordIndex = 0; coordIndex < expected.Shell.Count; coordIndex++) { actual.Shell[coordIndex].X.ShouldBe(expected.Shell[coordIndex].X, 0.01); actual.Shell[coordIndex].Y.ShouldBe(expected.Shell[coordIndex].Y, 0.01); } // square with a square hole and circle (of 8 points) sourceShell = new[] { new Coordinate(1, 1), new Coordinate(9, 1), new Coordinate(9, 9), new Coordinate(1, 9), new Coordinate(1, 1) }; Coordinate[][] sourceHoles = new[] { new[] { new Coordinate(3, 3), new Coordinate(3, 7), new Coordinate(7, 7), new Coordinate(7, 3), new Coordinate(3, 3) } }; actual = MinkowskiSumAlgorithm.Buffer(new BasicPolygon(sourceShell, sourceHoles), 1, 8); expectedShell = new[] { new Coordinate(0, 1), new Coordinate(0.3, 0.3), new Coordinate(1, 0), new Coordinate(9, 0), new Coordinate(9.7, 0.3), new Coordinate(10, 1), new Coordinate(10, 9), new Coordinate(9.7, 9.7), new Coordinate(9, 10), new Coordinate(1, 10), new Coordinate(0.3, 9.7), new Coordinate(0, 9) }; Coordinate[][] expectedHoles = new[] { new[] { new Coordinate(4, 3), new Coordinate(3.7, 3.7), new Coordinate(3, 4), new Coordinate(3, 6), new Coordinate(3.7, 6.3), new Coordinate(4, 7), new Coordinate(6, 7), new Coordinate(6.3, 6.3), new Coordinate(7, 6), new Coordinate(7, 4), new Coordinate(6.3, 3.7), new Coordinate(6, 3) } }; expected = new BasicPolygon(expectedShell, expectedHoles); actual.Shell.Count.ShouldBe(expected.Shell.Count); for (Int32 coordIndex = 0; coordIndex < expected.Shell.Count; coordIndex++) { actual.Shell[coordIndex].X.ShouldBe(expected.Shell[coordIndex].X, 0.01); actual.Shell[coordIndex].Y.ShouldBe(expected.Shell[coordIndex].Y, 0.01); } actual.HoleCount.ShouldBe(expected.HoleCount); for (Int32 holeIndex = 0; holeIndex < expected.HoleCount; holeIndex++) { actual.Holes[holeIndex].Count.ShouldBe(expected.Holes[holeIndex].Count); for (Int32 coordIndex = 0; coordIndex < expected.Holes[holeIndex].Count; coordIndex++) { actual.Holes[holeIndex][coordIndex].X.ShouldBe(expected.Holes[holeIndex][coordIndex].X, 0.01); actual.Holes[holeIndex][coordIndex].Y.ShouldBe(expected.Holes[holeIndex][coordIndex].Y, 0.01); } } // line string (2 points) and square buffer = new[] { new Coordinate(0, 2), new Coordinate(0, 0), new Coordinate(2, 0), new Coordinate(2, 2) }; actual = MinkowskiSumAlgorithm.Buffer(new[] { new Coordinate(0, 4), new Coordinate(2, 4) }, buffer); expectedShell = new[] { new Coordinate(0, 6), new Coordinate(0, 4), new Coordinate(2, 4), new Coordinate(4, 4), new Coordinate(4, 6), new Coordinate(2, 6) }; expected = new BasicPolygon(expectedShell); actual.Shell.ShouldBe(expected.Shell); // line string (4 points) and square buffer = new[] { new Coordinate(0, 2), new Coordinate(0, 0), new Coordinate(2, 0), new Coordinate(2, 2) }; actual = MinkowskiSumAlgorithm.Buffer(new[] { new Coordinate(1, 1), new Coordinate(3, 3), new Coordinate(5, 6), new Coordinate(3, 8) }, buffer); expectedShell = new[] { new Coordinate(1, 3), new Coordinate(1, 1), new Coordinate(3, 1), new Coordinate(5, 3), new Coordinate(7, 6), new Coordinate(7, 8), new Coordinate(5, 10), new Coordinate(3, 10) }; expected = new BasicPolygon(expectedShell); actual.Shell.ShouldBe(expected.Shell); // concave and convex with no hole in the result buffer = new[] { new Coordinate(13, 2), new Coordinate(14, 3), new Coordinate(13, 4), new Coordinate(12, 3) }; sourceShell = new[] { new Coordinate(1, 1), new Coordinate(11, 1), new Coordinate(11, 6), new Coordinate(8, 6), new Coordinate(9, 3), new Coordinate(3, 3), new Coordinate(4, 6), new Coordinate(1, 6) }; actual = MinkowskiSumAlgorithm.Buffer(sourceShell, buffer); expectedShell = new[] { new Coordinate(13, 4), new Coordinate(14, 3), new Coordinate(24, 3), new Coordinate(25, 4), new Coordinate(25, 9), new Coordinate(24, 10), new Coordinate(21, 10), new Coordinate(20, 9), new Coordinate(20.67, 7), new Coordinate(17.33, 7), new Coordinate(18, 9), new Coordinate(17, 10), new Coordinate(14, 10), new Coordinate(13, 9) }; expected = new BasicPolygon(expectedShell); actual.Shell.Count.ShouldBe(expected.Shell.Count); for (Int32 coordIndex = 0; coordIndex < expected.Shell.Count; coordIndex++) { actual.Shell[coordIndex].X.ShouldBe(expected.Shell[coordIndex].X, 0.01); actual.Shell[coordIndex].Y.ShouldBe(expected.Shell[coordIndex].Y, 0.01); } // convex and concave with no hole in the result buffer = new[] { new Coordinate(0, 3), new Coordinate(-3, 0), new Coordinate(0, -3), new Coordinate(3, 0) }; sourceShell = new[] { new Coordinate(1, 1), new Coordinate(11, 1), new Coordinate(11, 6), new Coordinate(8, 6), new Coordinate(9, 3), new Coordinate(3, 3), new Coordinate(4, 6), new Coordinate(1, 6) }; actual = MinkowskiSumAlgorithm.Buffer(sourceShell, buffer); expectedShell = new[] { new Coordinate(-2, 1), new Coordinate(1, -2), new Coordinate(11, -2), new Coordinate(14, 1), new Coordinate(14, 6), new Coordinate(11, 9), new Coordinate(8, 9), new Coordinate(6, 7), new Coordinate(4, 9), new Coordinate(1, 9), new Coordinate(-2, 6) }; expected = new BasicPolygon(expectedShell); actual.Shell.Count.ShouldBe(expected.Shell.Count); for (Int32 coordIndex = 0; coordIndex < expected.Shell.Count; coordIndex++) { actual.Shell[coordIndex].X.ShouldBe(expected.Shell[coordIndex].X, 0.01); actual.Shell[coordIndex].Y.ShouldBe(expected.Shell[coordIndex].Y, 0.01); } // convex and concave with hole in the result buffer = new[] { new Coordinate(13, 4), new Coordinate(12, 3), new Coordinate(13, 2), new Coordinate(14, 3) }; sourceShell = new[] { new Coordinate(1, 1), new Coordinate(11, 1), new Coordinate(11, 6), new Coordinate(7, 6), new Coordinate(9, 3), new Coordinate(4, 3), new Coordinate(6, 6), new Coordinate(1, 6) }; actual = MinkowskiSumAlgorithm.Buffer(sourceShell, buffer); expectedShell = new[] { new Coordinate(13, 4), new Coordinate(14, 3), new Coordinate(24, 3), new Coordinate(25, 4), new Coordinate(25, 9), new Coordinate(24, 10), new Coordinate(20, 10), new Coordinate(19.5, 9.5), new Coordinate(19, 10), new Coordinate(14, 10), new Coordinate(13, 9) }; expectedHoles = new[] { new[] { new Coordinate(19.5, 8.25), new Coordinate(20.3, 7), new Coordinate(18.6, 7) } }; expected = new BasicPolygon(expectedShell, expectedHoles); actual.Shell.Count.ShouldBe(expected.Shell.Count); for (Int32 coordIndex = 0; coordIndex < expected.Shell.Count; coordIndex++) { actual.Shell[coordIndex].X.ShouldBe(expected.Shell[coordIndex].X, 0.01); actual.Shell[coordIndex].Y.ShouldBe(expected.Shell[coordIndex].Y, 0.01); } actual.HoleCount.ShouldBe(expected.HoleCount); for (Int32 holeIndex = 0; holeIndex < expected.HoleCount; holeIndex++) { actual.Holes[holeIndex].Count.ShouldBe(expected.Holes[holeIndex].Count); for (Int32 coordIndex = 0; coordIndex < expected.Holes[holeIndex].Count; coordIndex++) { actual.Holes[holeIndex][coordIndex].X.ShouldBe(expected.Holes[holeIndex][coordIndex].X, 0.1); actual.Holes[holeIndex][coordIndex].Y.ShouldBe(expected.Holes[holeIndex][coordIndex].Y, 0.1); } } // convex source polygon with hole and a rhombus buffer = new[] { new Coordinate(10, 2), new Coordinate(11, 3), new Coordinate(10, 4), new Coordinate(9, 3) }; sourceShell = new[] { new Coordinate(1, 1), new Coordinate(9, 1), new Coordinate(9, 9), new Coordinate(1, 9), new Coordinate(1, 1) }; sourceHoles = new[] { new[] { new Coordinate(3, 3), new Coordinate(3, 7), new Coordinate(7, 7), new Coordinate(7, 3), new Coordinate(3, 3) } }; actual = MinkowskiSumAlgorithm.Buffer(new BasicPolygon(sourceShell, sourceHoles), new BasicPolygon(buffer)); expectedShell = new[] { new Coordinate(10, 4), new Coordinate(11, 3), new Coordinate(19, 3), new Coordinate(20, 4), new Coordinate(20, 12), new Coordinate(19, 13), new Coordinate(11, 13), new Coordinate(10, 12) }; expectedHoles = new[] { new[] { new Coordinate(14, 6), new Coordinate(13, 7), new Coordinate(13, 9), new Coordinate(14, 10), new Coordinate(16, 10), new Coordinate(17, 9), new Coordinate(17, 7), new Coordinate(16, 6) } }; expected = new BasicPolygon(expectedShell, expectedHoles); actual.Shell.Count.ShouldBe(expected.Shell.Count); for (Int32 coordIndex = 0; coordIndex < expected.Shell.Count; coordIndex++) { actual.Shell[coordIndex].X.ShouldBe(expected.Shell[coordIndex].X, 0.01); actual.Shell[coordIndex].Y.ShouldBe(expected.Shell[coordIndex].Y, 0.01); } actual.HoleCount.ShouldBe(expected.HoleCount); for (Int32 holeIndex = 0; holeIndex < expected.HoleCount; holeIndex++) { actual.Shell.Count.ShouldBe(expected.Holes[holeIndex].Count); for (Int32 coordIndex = 0; coordIndex < expected.Holes[holeIndex].Count; coordIndex++) { actual.Holes[holeIndex][coordIndex].X.ShouldBe(expected.Holes[holeIndex][coordIndex].X, 0.01); actual.Holes[holeIndex][coordIndex].Y.ShouldBe(expected.Holes[holeIndex][coordIndex].Y, 0.01); } } // exceptions Should.Throw <ArgumentNullException>(() => MinkowskiSumAlgorithm.Buffer((IBasicPolygon)null, new BasicPolygon(buffer))); Should.Throw <ArgumentNullException>(() => MinkowskiSumAlgorithm.Buffer(new BasicPolygon(sourceShell, sourceHoles), null)); }
/// <summary> /// Computes the result of the operation. /// </summary> protected override void ComputeResult() { switch (_dimension) { case 0: foreach (IGraphVertex vertex in Source.Vertices) { _points.Add(_factory.CreatePoint(vertex.Coordinate, _metadataPreservation ? vertex.Metadata : null)); } break; case 1: foreach (IGraphVertex vertex in Source.Vertices) { _points.Add(_factory.CreatePoint(vertex.Coordinate, _metadataPreservation ? vertex.Metadata : null)); } foreach (IGraphVertex vertex in Source.Vertices) { foreach (IGraphEdge edge in Source.OutEdges(vertex)) { if (edge.Source.Coordinate.Equals(edge.Target.Coordinate)) { continue; } _points.Remove(_factory.CreatePoint(edge.Source.Coordinate)); _points.Remove(_factory.CreatePoint(edge.Target.Coordinate)); _lines.Add(_factory.CreateLine(edge.Source.Coordinate, edge.Target.Coordinate, _metadataPreservation ? edge.Metadata : null)); } } break; case 2: // source: Jiang, X. Y., Bunke, H.: An optimal algorithm for extracting the regions of a plane graph (1993) List <Coordinate> coordinates = new List <Coordinate>(); // find all wedges List <AngularEdge> edges = new List <AngularEdge>(); foreach (IGraphVertex vertex in Source.Vertices) { Coordinate sourceCoordinate = vertex.Coordinate; Coordinate baseCoordinate = vertex.Coordinate - new CoordinateVector(1, 0); IEnumerable <IGraphEdge> outEdges = Source.OutEdges(vertex); if (outEdges.Count() == 0) { coordinates.Add(vertex.Coordinate); continue; } foreach (IGraphEdge edge in outEdges) { Coordinate target = edge.Target.Coordinate; edges.Add(new AngularEdge(sourceCoordinate, target, Coordinate.Orientation(sourceCoordinate, baseCoordinate, target) == Orientation.CounterClockwise ? Coordinate.Angle(sourceCoordinate, baseCoordinate, target) : Math.PI - Coordinate.Angle(sourceCoordinate, baseCoordinate, target))); } } edges.Sort(new AngularEdgeComparer()); IEnumerable <IGrouping <Coordinate, AngularEdge> > groups = edges.GroupBy(angularEdge => angularEdge.Source); List <Wedge> wedges = new List <Wedge>(); foreach (IGrouping <Coordinate, AngularEdge> group in groups) { AngularEdge[] groupEdges = group.ToArray(); for (Int32 i = 0; i < groupEdges.Length - 1; i++) { wedges.Add(new Wedge(groupEdges[i + 1].Target, groupEdges[i].Source, groupEdges[i].Target)); } wedges.Add(new Wedge(groupEdges[0].Target, groupEdges[0].Source, groupEdges[groupEdges.Length - 1].Target)); } // grouping wedges to regions List <List <Wedge> > faces = new List <List <Wedge> >(); List <List <Wedge> > leftOverEdges = new List <List <Wedge> >(); for (Int32 i = 0; i < wedges.Count; i++) { List <Wedge> wedgeSeries = new List <Wedge>(); Wedge currentWedge = wedges[i]; do { wedges.Remove(currentWedge); wedgeSeries.Add(currentWedge); currentWedge = wedges.FirstOrDefault(wedge => wedge.First.Equals(currentWedge.Second) && wedge.Second.Equals(currentWedge.Third)); }while (currentWedge != null); if (wedgeSeries[0].First.Equals(wedgeSeries[wedgeSeries.Count - 1].Second) && wedgeSeries[0].Second.Equals(wedgeSeries[wedgeSeries.Count - 1].Third)) { faces.Add(wedgeSeries); } else { leftOverEdges.Add(wedgeSeries); } } for (Int32 i = 0; i < faces.Count; i++) { List <Coordinate> shell = faces[i].Select(wedge => wedge.First).ToList(); shell.Add(shell[0]); if (PolygonAlgorithms.Orientation(shell) == Orientation.CounterClockwise) { Dictionary <String, Object> metadata = null; if (_metadataPreservation) { metadata = new Dictionary <String, Object>(); foreach (IMetadataCollection collection in shell.Select(coordinate => Source.GetVertex(coordinate).Metadata)) { foreach (String key in collection.Keys) { metadata[key] = collection[key]; } } } _polygons.Add(_factory.CreatePolygon(shell, metadata)); } } for (Int32 i = 0; i < leftOverEdges.Count; i++) { for (Int32 j = 0; j < leftOverEdges[i].Count; j++) { _lines.Add(_factory.CreateLine(leftOverEdges[i][j].First, leftOverEdges[i][j].Second, _metadataPreservation ? Source.GetEdge(leftOverEdges[i][j].First, leftOverEdges[i][j].Second).Metadata : null)); } } foreach (Coordinate coordinate in coordinates) { _points.Add(_factory.CreatePoint(coordinate, _metadataPreservation ? Source.GetVertex(coordinate).Metadata : null)); } break; } }
public void MinkowskiSumAlgorithmBufferTest() { // two triangles IBasicPolygon buffer = new BasicPolygon( new Coordinate[] { new Coordinate(5, 5), new Coordinate(5, 7), new Coordinate(4, 6) }); List <Coordinate> sourceShellCoordinates = new List <Coordinate> { new Coordinate(2, 1), new Coordinate(4, 1), new Coordinate(3, 3) }; List <Coordinate> expectedCoordinates = new List <Coordinate> { new Coordinate(6, 7), new Coordinate(7, 6), new Coordinate(9, 6), new Coordinate(9, 8), new Coordinate(8, 10), new Coordinate(7, 9), }; IBasicPolygon expectedResult = new BasicPolygon(expectedCoordinates); IBasicPolygon actualResult = MinkowskiSumAlgorithm.Buffer(sourceShellCoordinates, buffer); Assert.IsTrue(PolygonAlgorithms.IsConvex(actualResult)); Assert.AreEqual(expectedResult.Shell.Count, actualResult.Shell.Count); for (Int32 i = 0; i < expectedResult.Shell.Count; i++) { Assert.AreEqual(expectedResult.Shell.Coordinates[i], actualResult.Shell.Coordinates[i]); } // triangle and a square buffer = new BasicPolygon( new Coordinate[] { new Coordinate(8, 6), new Coordinate(9, 6), new Coordinate(9, 7), new Coordinate(8, 7) }); sourceShellCoordinates = new List <Coordinate> { new Coordinate(3, 4), new Coordinate(5, 4), new Coordinate(4, 6) }; expectedCoordinates = new List <Coordinate> { new Coordinate(11, 11), new Coordinate(11, 10), new Coordinate(13, 10), new Coordinate(14, 10), new Coordinate(14, 11), new Coordinate(13, 13), new Coordinate(12, 13), new Coordinate(11, 11) }; expectedResult = new BasicPolygon(expectedCoordinates); actualResult = MinkowskiSumAlgorithm.Buffer(sourceShellCoordinates, buffer); Assert.IsTrue(PolygonAlgorithms.IsConvex(actualResult)); Assert.AreEqual(expectedResult.Shell.Count, actualResult.Shell.Count); for (Int32 i = 0; i < expectedResult.Shell.Count; i++) { Assert.AreEqual(expectedResult.Shell.Coordinates[i], actualResult.Shell.Coordinates[i]); } // square and circle of 4 points (square) buffer = new BasicPolygon( new Coordinate[] { new Coordinate(8, 6), new Coordinate(9, 6), new Coordinate(9, 7), new Coordinate(8, 7) }); expectedCoordinates = new List <Coordinate> { new Coordinate(6, 6), new Coordinate(8, 4), new Coordinate(9, 4), new Coordinate(11, 6), new Coordinate(11, 7), new Coordinate(9, 9), new Coordinate(8, 9), new Coordinate(6, 7), new Coordinate(6, 6) }; expectedResult = new BasicPolygon(expectedCoordinates); actualResult = MinkowskiSumAlgorithm.Buffer(buffer, 2, 4); Assert.IsTrue(PolygonAlgorithms.IsConvex(actualResult)); Assert.AreEqual(expectedResult.Shell.Count, actualResult.Shell.Count); for (Int32 i = 0; i < expectedResult.Shell.Count; i++) { Assert.AreEqual(expectedResult.Shell.Coordinates[i].X, Math.Round(actualResult.Shell.Coordinates[i].X, 2)); Assert.AreEqual(expectedResult.Shell.Coordinates[i].Y, Math.Round(actualResult.Shell.Coordinates[i].Y, 2)); } // point and circle (of 4 points) IBasicPoint point = _factory.CreatePoint(1, 1, 0); actualResult = MinkowskiSumAlgorithm.Buffer(point, 2, 4); expectedCoordinates = new List <Coordinate> { new Coordinate(3, 1), new Coordinate(1, 3), new Coordinate(-1, 1), new Coordinate(1, -1) }; expectedResult = new BasicPolygon(expectedCoordinates); Assert.AreEqual(expectedResult.Shell.Count, actualResult.Shell.Count); for (Int32 i = 0; i < expectedResult.Shell.Count; i++) { Assert.AreEqual(expectedResult.Shell.Coordinates[i].X, Math.Round(actualResult.Shell.Coordinates[i].X, 2)); Assert.AreEqual(expectedResult.Shell.Coordinates[i].Y, Math.Round(actualResult.Shell.Coordinates[i].Y, 2)); } // square with a square hole and circle (of 8 points) sourceShellCoordinates = new List <Coordinate> { new Coordinate(1, 1), new Coordinate(9, 1), new Coordinate(9, 9), new Coordinate(1, 9), new Coordinate(1, 1) }; List <List <Coordinate> > holes = new List <List <Coordinate> >(); holes.Add(new List <Coordinate>()); holes[0] = new List <Coordinate>(); holes[0].Add(new Coordinate(3, 3)); holes[0].Add(new Coordinate(3, 7)); holes[0].Add(new Coordinate(7, 7)); holes[0].Add(new Coordinate(7, 3)); holes[0].Add(new Coordinate(3, 3)); BasicPolygon source = new BasicPolygon(sourceShellCoordinates, holes); actualResult = MinkowskiSumAlgorithm.Buffer(source, 1, 8); expectedCoordinates = new List <Coordinate> { new Coordinate(0, 1), new Coordinate(0.3, 0.3), new Coordinate(1, 0), new Coordinate(9, 0), new Coordinate(9.7, 0.3), new Coordinate(10, 1), new Coordinate(10, 9), new Coordinate(9.7, 9.7), new Coordinate(9, 10), new Coordinate(1, 10), new Coordinate(0.3, 9.7), new Coordinate(0, 9) }; List <List <Coordinate> > expectedHoleCoordinates = new List <List <Coordinate> >(); expectedHoleCoordinates.Add(new List <Coordinate>() { new Coordinate(4, 3), new Coordinate(3.7, 3.7), new Coordinate(3, 4), new Coordinate(3, 6), new Coordinate(3.7, 6.3), new Coordinate(4, 7), new Coordinate(6, 7), new Coordinate(6.3, 6.3), new Coordinate(7, 6), new Coordinate(7, 4), new Coordinate(6.3, 3.7), new Coordinate(6, 3) }); expectedResult = new BasicPolygon(expectedCoordinates); Assert.AreEqual(expectedResult.Shell.Count, actualResult.Shell.Count); Assert.AreEqual(expectedResult.Shell.Count, actualResult.Shell.Count); for (Int32 i = 0; i < expectedResult.Shell.Count; i++) { Assert.AreEqual(expectedResult.Shell.Coordinates[i].X, Math.Round(actualResult.Shell.Coordinates[i].X, 1)); Assert.AreEqual(expectedResult.Shell.Coordinates[i].Y, Math.Round(actualResult.Shell.Coordinates[i].Y, 1)); } Assert.AreEqual(expectedHoleCoordinates.Count, actualResult.HoleCount); for (Int32 i = 0; i < expectedHoleCoordinates.Count; i++) { for (Int32 j = 0; j < expectedHoleCoordinates[i].Count; j++) { Assert.AreEqual(expectedHoleCoordinates[i][j].X, Math.Round(actualResult.Holes[i].Coordinates[j].X, 1)); Assert.AreEqual(expectedHoleCoordinates[i][j].Y, Math.Round(actualResult.Holes[i].Coordinates[j].Y, 1)); } } // line string (2 points) and square buffer = new BasicPolygon( new Coordinate[] { new Coordinate(0, 2), new Coordinate(0, 0), new Coordinate(2, 0), new Coordinate(2, 2) }); actualResult = MinkowskiSumAlgorithm.Buffer(new BasicLineString(new List <Coordinate>() { new Coordinate(0, 4), new Coordinate(2, 4) }), buffer); expectedCoordinates = new List <Coordinate> { new Coordinate(0, 6), new Coordinate(0, 4), new Coordinate(2, 4), new Coordinate(4, 4), new Coordinate(4, 6), new Coordinate(2, 6) }; expectedResult = new BasicPolygon(expectedCoordinates); Assert.AreEqual(expectedResult.Shell.Count, actualResult.Shell.Count); for (Int32 i = 0; i < expectedResult.Shell.Count; i++) { Assert.AreEqual(expectedResult.Shell.Coordinates[i], actualResult.Shell.Coordinates[i]); } // line string (4 points) and square buffer = new BasicPolygon( new Coordinate[] { new Coordinate(0, 2), new Coordinate(0, 0), new Coordinate(2, 0), new Coordinate(2, 2) }); actualResult = MinkowskiSumAlgorithm.Buffer(new BasicLineString(new List <Coordinate>() { new Coordinate(1, 1), new Coordinate(3, 3), new Coordinate(5, 6), new Coordinate(3, 8) }), buffer); expectedCoordinates = new List <Coordinate> { new Coordinate(1, 3), new Coordinate(1, 1), new Coordinate(3, 1), new Coordinate(5, 3), new Coordinate(7, 6), new Coordinate(7, 8), new Coordinate(5, 10), new Coordinate(3, 10) }; expectedResult = new BasicPolygon(expectedCoordinates); Assert.AreEqual(expectedResult.Shell.Count, actualResult.Shell.Count); for (Int32 i = 0; i < expectedResult.Shell.Count; i++) { Assert.AreEqual(expectedResult.Shell.Coordinates[i], actualResult.Shell.Coordinates[i]); } // concave and convex with no hole in the result buffer = new BasicPolygon( new Coordinate[] { new Coordinate(13, 2), new Coordinate(14, 3), new Coordinate(13, 4), new Coordinate(12, 3) }); sourceShellCoordinates = new List <Coordinate> { new Coordinate(1, 1), new Coordinate(11, 1), new Coordinate(11, 6), new Coordinate(8, 6), new Coordinate(9, 3), new Coordinate(3, 3), new Coordinate(4, 6), new Coordinate(1, 6) }; actualResult = MinkowskiSumAlgorithm.Buffer(sourceShellCoordinates, buffer); expectedCoordinates = new List <Coordinate> { new Coordinate(13, 4), new Coordinate(14, 3), new Coordinate(24, 3), new Coordinate(25, 4), new Coordinate(25, 9), new Coordinate(24, 10), new Coordinate(21, 10), new Coordinate(20, 9), new Coordinate(20.67, 7), new Coordinate(17.33, 7), new Coordinate(18, 9), new Coordinate(17, 10), new Coordinate(14, 10), new Coordinate(13, 9) }; expectedResult = new BasicPolygon(expectedCoordinates); Assert.AreEqual(expectedResult.Shell.Count, actualResult.Shell.Count); for (Int32 i = 0; i < expectedResult.Shell.Count; i++) { Assert.AreEqual(expectedResult.Shell.Coordinates[i].X, Math.Round(actualResult.Shell.Coordinates[i].X, 2)); Assert.AreEqual(expectedResult.Shell.Coordinates[i].Y, Math.Round(actualResult.Shell.Coordinates[i].Y, 2)); } // convex and concave with no hole in the result buffer = new BasicPolygon( new Coordinate[] { new Coordinate(0, 3), new Coordinate(-3, 0), new Coordinate(0, -3), new Coordinate(3, 0) }); sourceShellCoordinates = new List <Coordinate> { new Coordinate(1, 1), new Coordinate(11, 1), new Coordinate(11, 6), new Coordinate(8, 6), new Coordinate(9, 3), new Coordinate(3, 3), new Coordinate(4, 6), new Coordinate(1, 6) }; actualResult = MinkowskiSumAlgorithm.Buffer(sourceShellCoordinates, buffer); expectedCoordinates = new List <Coordinate> { new Coordinate(-2, 1), new Coordinate(1, -2), new Coordinate(11, -2), new Coordinate(14, 1), new Coordinate(14, 6), new Coordinate(11, 9), new Coordinate(8, 9), new Coordinate(6, 7), new Coordinate(4, 9), new Coordinate(1, 9), new Coordinate(-2, 6) }; expectedResult = new BasicPolygon(expectedCoordinates); Assert.AreEqual(expectedResult.Shell.Count, actualResult.Shell.Count); for (Int32 i = 0; i < expectedResult.Shell.Count; i++) { Assert.AreEqual(expectedResult.Shell.Coordinates[i].X, Math.Round(actualResult.Shell.Coordinates[i].X, 2)); Assert.AreEqual(expectedResult.Shell.Coordinates[i].Y, Math.Round(actualResult.Shell.Coordinates[i].Y, 2)); } // convex and concave with hole in the result buffer = new BasicPolygon( new Coordinate[] { new Coordinate(13, 4), new Coordinate(12, 3), new Coordinate(13, 2), new Coordinate(14, 3) }); sourceShellCoordinates = new List <Coordinate> { new Coordinate(1, 1), new Coordinate(11, 1), new Coordinate(11, 6), new Coordinate(7, 6), new Coordinate(9, 3), new Coordinate(4, 3), new Coordinate(6, 6), new Coordinate(1, 6) }; actualResult = MinkowskiSumAlgorithm.Buffer(sourceShellCoordinates, buffer); expectedCoordinates = new List <Coordinate> { new Coordinate(13, 4), new Coordinate(14, 3), new Coordinate(24, 3), new Coordinate(25, 4), new Coordinate(25, 9), new Coordinate(24, 10), new Coordinate(20, 10), new Coordinate(19.5, 9.5), new Coordinate(19, 10), new Coordinate(14, 10), new Coordinate(13, 9) }; expectedHoleCoordinates = new List <List <Coordinate> >(); expectedHoleCoordinates.Add(new List <Coordinate>() { new Coordinate(19.5, 8.25), new Coordinate(20.3, 7), new Coordinate(18.6, 7) }); expectedResult = new BasicPolygon(expectedCoordinates); Assert.AreEqual(expectedResult.Shell.Count, actualResult.Shell.Count); for (Int32 i = 0; i < expectedResult.Shell.Count; i++) { Assert.AreEqual(expectedResult.Shell.Coordinates[i], actualResult.Shell.Coordinates[i]); } Assert.AreEqual(expectedHoleCoordinates.Count, actualResult.HoleCount); for (Int32 i = 0; i < expectedHoleCoordinates.Count; i++) { for (Int32 j = 0; j < expectedHoleCoordinates[i].Count; j++) { Assert.AreEqual(expectedHoleCoordinates[i][j].X, Math.Round(actualResult.Holes[i].Coordinates[j].X), 2); Assert.AreEqual(expectedHoleCoordinates[i][j].Y, Math.Round(actualResult.Holes[i].Coordinates[j].Y), 2); } } // convex source polygon with hole and a rhombus buffer = _factory.CreatePolygon( _factory.CreatePoint(10, 2), _factory.CreatePoint(11, 3), _factory.CreatePoint(10, 4), _factory.CreatePoint(9, 3)); sourceShellCoordinates = new List <Coordinate> { new Coordinate(1, 1), new Coordinate(9, 1), new Coordinate(9, 9), new Coordinate(1, 9), new Coordinate(1, 1) }; holes = new List <List <Coordinate> >(); holes.Add(new List <Coordinate>()); holes[0] = new List <Coordinate>(); holes[0].Add(new Coordinate(3, 3)); holes[0].Add(new Coordinate(3, 7)); holes[0].Add(new Coordinate(7, 7)); holes[0].Add(new Coordinate(7, 3)); holes[0].Add(new Coordinate(3, 3)); source = new BasicPolygon(sourceShellCoordinates, holes); actualResult = MinkowskiSumAlgorithm.Buffer(source, buffer); expectedCoordinates = new List <Coordinate> { new Coordinate(10, 4), new Coordinate(11, 3), new Coordinate(19, 3), new Coordinate(20, 4), new Coordinate(20, 12), new Coordinate(19, 13), new Coordinate(11, 13), new Coordinate(10, 12) }; expectedHoleCoordinates = new List <List <Coordinate> >(); expectedHoleCoordinates.Add(new List <Coordinate>() { new Coordinate(14, 6), new Coordinate(13, 7), new Coordinate(13, 9), new Coordinate(14, 10), new Coordinate(16, 10), new Coordinate(17, 9), new Coordinate(17, 7), new Coordinate(16, 6) }); expectedResult = new BasicPolygon(expectedCoordinates); Assert.AreEqual(expectedResult.Shell.Count, actualResult.Shell.Count); for (Int32 i = 0; i < expectedResult.Shell.Count; i++) { Assert.AreEqual(expectedResult.Shell.Coordinates[i], actualResult.Shell.Coordinates[i]); } Assert.AreEqual(expectedHoleCoordinates.Count, actualResult.HoleCount); for (Int32 i = 0; i < expectedHoleCoordinates.Count; i++) { for (Int32 j = 0; j < expectedHoleCoordinates[i].Count; j++) { Assert.AreEqual(expectedHoleCoordinates[i][j], actualResult.Holes[i].Coordinates[j]); } } // ArgumentNullException: null source Assert.Throws <ArgumentNullException>(() => MinkowskiSumAlgorithm.Buffer((IBasicPolygon)null, buffer)); // ArgumentNullException: null buffer polygon Assert.Throws <ArgumentNullException>(() => MinkowskiSumAlgorithm.Buffer(source, null)); // ArgumentException: source polygon with clockwise orientation sourceShellCoordinates = new List <Coordinate> { new Coordinate(1, 1), new Coordinate(1, 9), new Coordinate(9, 9), new Coordinate(9, 1), new Coordinate(1, 1) }; source = new BasicPolygon(sourceShellCoordinates); Assert.Throws <ArgumentException>(() => MinkowskiSumAlgorithm.Buffer(source, buffer)); }
public void PolygonAlgorithmsOrientationTest() { // counter clockwise orientation convex polygon Coordinate[] coordinates = new[] { new Coordinate(0, 0), new Coordinate(10, 0), new Coordinate(10, 10), new Coordinate(0, 10), new Coordinate(0, 0) }; PolygonAlgorithms.Orientation(coordinates).ShouldBe(Orientation.Counterclockwise); coordinates = new[] { new Coordinate(2, 1, 0), new Coordinate(4, 1, 0), new Coordinate(3, 3, 0), new Coordinate(2, 1, 0) }; PolygonAlgorithms.Orientation(coordinates).ShouldBe(Orientation.Counterclockwise); coordinates = new[] { new Coordinate(2, 1, 0), new Coordinate(4, 1, 0), new Coordinate(3, 3, 0) }; PolygonAlgorithms.Orientation(coordinates).ShouldBe(Orientation.Counterclockwise); // clockwise orientation convex polygon coordinates = new[] { new Coordinate(0, 0), new Coordinate(0, 10), new Coordinate(10, 10), new Coordinate(10, 0), new Coordinate(0, 0) }; PolygonAlgorithms.Orientation(coordinates).ShouldBe(Orientation.Clockwise); // collinear orientation convex polygon coordinates = new[] { new Coordinate(0, 0), new Coordinate(0, 10), new Coordinate(0, 20), new Coordinate(0, 30), new Coordinate(0, 0) }; PolygonAlgorithms.Orientation(coordinates).ShouldBe(Orientation.Collinear); // clockwise concave polygon coordinates = new[] { new Coordinate(0, 0), new Coordinate(0, 10), new Coordinate(10, 10), new Coordinate(10, 0), new Coordinate(5, 5), new Coordinate(0, 0) }; PolygonAlgorithms.Orientation(coordinates).ShouldBe(Orientation.Clockwise); // counter clockwise concave polygon coordinates = new[] { new Coordinate(0, 0), new Coordinate(2, 1), new Coordinate(8, 1), new Coordinate(10, 0), new Coordinate(9, 2), new Coordinate(9, 8), new Coordinate(10, 10), new Coordinate(8, 9), new Coordinate(2, 9), new Coordinate(0, 10), new Coordinate(1, 8), new Coordinate(1, 2), new Coordinate(0, 0) }; PolygonAlgorithms.Orientation(coordinates).ShouldBe(Orientation.Counterclockwise); }
public void PolygonAlgorithmsAreaTest() { // area of a rectangle in counterclockwise orientation Coordinate[] shell = new[] { new Coordinate(5, 5), new Coordinate(130, 5), new Coordinate(130, 100), new Coordinate(5, 100), new Coordinate(5, 5) }; PolygonAlgorithms.Area(shell).ShouldBe(11875); // area of a rectangle in counterclockwise orientation and double precision coordinates shell = new[] { new Coordinate(5.5, 5.5), new Coordinate(130.5, 5.5), new Coordinate(130.5, 100.5), new Coordinate(5.5, 100.5) }; IBasicPolygon polygon = new BasicPolygon(shell, null); PolygonAlgorithms.Area(polygon).ShouldBe(11875); // area of a square with a hole shell = new[] { new Coordinate(-5, 5), new Coordinate(5, 5), new Coordinate(5, 15), new Coordinate(-5, 15) }; Coordinate[] hole = new[] { new Coordinate(0, 0), new Coordinate(0, 2), new Coordinate(2, 2), new Coordinate(2, 0), }; polygon = new BasicPolygon(shell, new[] { hole }); PolygonAlgorithms.Area(polygon).ShouldBe(96); // area of a regular hexagon in counterclockwise orientation shell = new[] { new Coordinate(-3, -5), new Coordinate(3, -5), new Coordinate(6, 0), new Coordinate(3, 5), new Coordinate(-3, 5), new Coordinate(-6, 0) }; polygon = new BasicPolygon(shell, null); PolygonAlgorithms.Area(polygon).ShouldBe(90); // area of a concave polygon shell = new[] { new Coordinate(1, 1), new Coordinate(1, 3), new Coordinate(5, 3), new Coordinate(7, 5), new Coordinate(7, 1) }; polygon = new BasicPolygon(shell, null); PolygonAlgorithms.Area(polygon).ShouldBe(14); // area of a non-simple polygon shell = new[] { new Coordinate(1, 1), new Coordinate(1, 3), new Coordinate(5, 3), new Coordinate(7, 5), new Coordinate(7, 3), new Coordinate(5, 3), new Coordinate(5, 1) }; polygon = new BasicPolygon(shell, null); PolygonAlgorithms.Area(polygon).ShouldBe(10); // exceptions Should.Throw <ArgumentNullException>(() => PolygonAlgorithms.Area((IBasicPolygon)null)); Should.Throw <ArgumentNullException>(() => PolygonAlgorithms.Area((List <Coordinate>)null)); }
public void PolygonAlgorithmsIsValidTest() { // simple convex polygon without holes in clockwise orientation, containing collinear edges Coordinate[] shell = new[] { new Coordinate(0, 2), new Coordinate(2, 0), new Coordinate(3, -2), new Coordinate(1, -4), new Coordinate(0, -4), new Coordinate(-1, -4), new Coordinate(-3, -2), new Coordinate(-2, 0) }; IBasicPolygon polygon = new BasicPolygon(shell, null); PolygonAlgorithms.IsValid(polygon).ShouldBeTrue(); // rectangle in counterclockwise orientation shell = new[] { new Coordinate(5, 5), new Coordinate(130, 5), new Coordinate(130, 100), new Coordinate(5, 100), new Coordinate(5, 5) }; PolygonAlgorithms.IsValid(shell).ShouldBeTrue(); // square with a hole shell = new[] { new Coordinate(-5, 5), new Coordinate(5, 5), new Coordinate(5, 15), new Coordinate(-5, 15) }; Coordinate[] hole = new[] { new Coordinate(0, 0), new Coordinate(0, 2), new Coordinate(2, 2), new Coordinate(2, 0), }; polygon = new BasicPolygon(shell, new[] { hole }); PolygonAlgorithms.IsValid(polygon).ShouldBeTrue(); // two coordinates shell = new[] { new Coordinate(-5, 5), new Coordinate(5, 5) }; PolygonAlgorithms.IsValid(shell).ShouldBeFalse(); // simple concave polygon in counterclockwise orientation shell = new[] { new Coordinate(5, 5), new Coordinate(130, 5), new Coordinate(130, 100), new Coordinate(70, 70), new Coordinate(5, 100) }; polygon = new BasicPolygon(shell, null); PolygonAlgorithms.IsValid(polygon).ShouldBeTrue(); // simple concave polygon in clockwise orientation shell = new[] { new Coordinate(2, 2), new Coordinate(2, 5), new Coordinate(4, 5), new Coordinate(3, 3), new Coordinate(6, 3), new Coordinate(5, 5), new Coordinate(7, 5), new Coordinate(7, 2) }; polygon = new BasicPolygon(shell, null); PolygonAlgorithms.IsValid(polygon).ShouldBeTrue(); // triangle with a line inside shell = new[] { new Coordinate(1, 1), new Coordinate(6, 1), new Coordinate(2, 6) }; hole = new[] { new Coordinate(2, 2), new Coordinate(4, 2) }; polygon = new BasicPolygon(shell, new[] { hole }); PolygonAlgorithms.IsValid(polygon).ShouldBeFalse(); // non-simple polygon shell = new[] { new Coordinate(2, 2), new Coordinate(7, 5), new Coordinate(2, 5), new Coordinate(7, 2) }; polygon = new BasicPolygon(shell, null); PolygonAlgorithms.IsValid(polygon).ShouldBeFalse(); // coordinate present more than once shell = new[] { new Coordinate(2, 2), new Coordinate(2, 5), new Coordinate(4, 5), new Coordinate(3, 3), new Coordinate(6, 3), new Coordinate(5, 5), new Coordinate(7, 5), new Coordinate(7, 2), new Coordinate(7, 2) }; polygon = new BasicPolygon(shell, null); PolygonAlgorithms.IsValid(polygon).ShouldBeFalse(); // not ordered coordinates shell = new[] { new Coordinate(2, 2), new Coordinate(2, 5), new Coordinate(3, 3), new Coordinate(7, 5), new Coordinate(6, 3), new Coordinate(5, 5), new Coordinate(7, 2), new Coordinate(4, 5), }; polygon = new BasicPolygon(shell, null); PolygonAlgorithms.IsValid(polygon).ShouldBeFalse(); // different Z coordinates shell = new[] { new Coordinate(5, 5, 1), new Coordinate(130, 5), new Coordinate(130, 100), new Coordinate(70, 70), new Coordinate(5, 100) }; polygon = new BasicPolygon(shell, null); PolygonAlgorithms.IsValid(polygon).ShouldBeFalse(); // exceptions Should.Throw <ArgumentNullException>(() => PolygonAlgorithms.IsValid((IBasicPolygon)null)); Should.Throw <ArgumentNullException>(() => PolygonAlgorithms.IsValid((List <Coordinate>)null)); }