Exemplo n.º 1
0
    // For shapes of dimension 2, returns the area of the shape on the unit
    // sphere.  The result is between 0 and 4*Pi steradians.  Otherwise returns
    // zero.  This method has good relative accuracy for both very large and very
    // small regions.
    //
    // All edges are modeled as spherical geodesics.  The result can be converted
    // to an area on the Earth's surface (with a worst-case error of 0.900% near
    // the poles) using the functions in s2earth.h.
    public static double GetArea(S2Shape shape)
    {
        if (shape.Dimension() != 2)
        {
            return(0.0);
        }

        // Since S2Shape uses the convention that the interior of the shape is to
        // the left of all edges, in theory we could compute the area of the polygon
        // by simply adding up all the loop areas modulo 4*Pi.  The problem with
        // this approach is that polygons holes typically have areas near 4*Pi,
        // which can create large cancellation errors when computing the area of
        // small polygons with holes.  For example, a shell with an area of 4 square
        // meters (1e-13 steradians) surrounding a hole with an area of 3 square
        // meters (7.5e-14 sterians) would lose almost all of its accuracy if the
        // area of the hole was computed as 12.566370614359098.
        //
        // So instead we use S2.GetSignedArea() to ensure that all loops have areas
        // in the range [-2*Pi, 2*Pi].
        //
        // TODO(ericv): Rarely, this function returns the area of the complementary
        // region (4*Pi - area).  This can only happen when the true area is very
        // close to zero or 4*Pi and the polygon has multiple loops.  To make this
        // function completely robust requires checking whether the signed area sum is
        // ambiguous, and if so, determining the loop nesting structure.  This allows
        // the sum to be evaluated in a way that is guaranteed to have the correct
        // sign.
        double area       = 0;
        double max_error  = 0;
        int    num_chains = shape.NumChains();

        for (int chain_id = 0; chain_id < num_chains; ++chain_id)
        {
            GetChainVertices(shape, chain_id, out var vertices);
            area += S2.GetSignedArea(vertices.ToList());
#if DEBUG
            max_error += S2.GetCurvatureMaxError(new(vertices));
#endif
        }
        // Note that S2.GetSignedArea() guarantees that the full loop (containing
        // all points on the sphere) has a very small negative area.
        System.Diagnostics.Debug.Assert(Math.Abs(area) <= S2.M_4_PI + max_error);
        if (area < 0.0)
        {
            area += S2.M_4_PI;
        }
        return(area);
    }
Exemplo n.º 2
0
    public void Test_GetSignedArea_Underflow()
    {
        var loop = ParsePointsOrDie("0:0, 0:1e-88, 1e-88:1e-88, 1e-88:0");

        Assert.True(S2.GetSignedArea(loop) > 0);
    }