예제 #1
0
        /**
         * Helper method to get area and optionally centroid.
         */

        private S2AreaCentroid GetAreaCentroid(bool doCentroid)
        {
            // Don't crash even if loop is not well-defined.
            if (NumVertices < 3)
            {
                return(new S2AreaCentroid(0D));
            }

            // The triangle area calculation becomes numerically unstable as the length
            // of any edge approaches 180 degrees. However, a loop may contain vertices
            // that are 180 degrees apart and still be valid, e.g. a loop that defines
            // the northern hemisphere using four points. We handle this case by using
            // triangles centered around an origin that is slightly displaced from the
            // first vertex. The amount of displacement is enough to get plenty of
            // accuracy for antipodal points, but small enough so that we still get
            // accurate areas for very tiny triangles.
            //
            // Of course, if the loop contains a point that is exactly antipodal from
            // our slightly displaced vertex, the area will still be unstable, but we
            // expect this case to be very unlikely (i.e. a polygon with two vertices on
            // opposite sides of the Earth with one of them displaced by about 2mm in
            // exactly the right direction). Note that the approximate point resolution
            // using the E7 or S2CellId representation is only about 1cm.

            var origin            = Vertex(0);
            var axis              = (origin.LargestAbsComponent + 1) % 3;
            var slightlyDisplaced = origin[axis] + S2.E * 1e-10;

            origin =
                new S2Point((axis == 0) ? slightlyDisplaced : origin.X,
                            (axis == 1) ? slightlyDisplaced : origin.Y, (axis == 2) ? slightlyDisplaced : origin.Z);
            origin = S2Point.Normalize(origin);

            double areaSum     = 0;
            var    centroidSum = new S2Point(0, 0, 0);

            for (var i = 1; i <= NumVertices; ++i)
            {
                areaSum += S2.SignedArea(origin, Vertex(i - 1), Vertex(i));
                if (doCentroid)
                {
                    // The true centroid is already premultiplied by the triangle area.
                    var trueCentroid = S2.TrueCentroid(origin, Vertex(i - 1), Vertex(i));
                    centroidSum = centroidSum + trueCentroid;
                }
            }
            // The calculated area at this point should be between -4*Pi and 4*Pi,
            // although it may be slightly larger or smaller than this due to
            // numerical errors.
            // assert (Math.abs(areaSum) <= 4 * S2.M_PI + 1e-12);

            if (areaSum < 0)
            {
                // If the area is negative, we have computed the area to the right of the
                // loop. The area to the left is 4*Pi - (-area). Amazingly, the centroid
                // does not need to be changed, since it is the negative of the integral
                // of position over the region to the right of the loop. This is the same
                // as the integral of position over the region to the left of the loop,
                // since the integral of position over the entire sphere is (0, 0, 0).
                areaSum += 4 * S2.Pi;
            }
            // The loop's sign() does not affect the return result and should be taken
            // into account by the caller.
            S2Point?centroid = null;

            if (doCentroid)
            {
                centroid = centroidSum;
            }
            return(new S2AreaCentroid(areaSum, centroid));
        }