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);
        }
Example #2
0
        /// <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);
        }
Example #6
0
        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);
        }
Example #13
0
        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));
        }
Example #14
0
        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));
        }
Example #18
0
        /// <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);
            }
        }
Example #19
0
        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));
        }
Example #20
0
        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));
        }
Example #21
0
        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);
        }
Example #24
0
        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;
            }
        }
Example #26
0
        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));
        }