¿Cómo funciona ST_Area en PostGIS?


8

Estoy realizando un cálculo simple en un polígono que se sabe que tiene aproximadamente 6226 km ^ 2 de área. Se almacena en una columna de Geografía (WGS84 SRID).

La consulta es:

select st_astext(col), st_area(col) area from table

y vuelve:

"POLYGON((-180 58.282525588539,-178.916399160189 57.4759784390599,-178.191728834624 58.5761461944577,-180 58.282525588539))" | 5807028547.33813

El área devuelta (5807028547.33813) parece ser mm ^ 2 y no km ^ 2? La documentación http://postgis.net/docs/ST_Area.html establece que "el área predeterminada se determina en un esferoide con unidades en metros cuadrados"

¿Es esto un error de documentación, o es correcto lo anterior y fundamentalmente estoy malinterpretando la funcionalidad?

Respuestas:


4

El cálculo da la salida correcta. Como mencionó, la documentación establece que "el área predeterminada se determina en un esferoide con unidades en metros cuadrados".

El resultado de su consulta es 5807028547.33813 m ^ 2 . Para obtener el área en km ^ 2, debes dividir el resultado en 1,000,000.

5807028547.33813 m^2 / 1,000,000 = 5807.02854733813 km^2

5807.02854733813 km ^ 2 corresponde aproximadamente a sus 6226 km ^ 2 esperados.


Esto es correcto. Para referencia: postgis.net/docs/ST_Area.html
alphabetasoup

Pero, ¿cuál es la base para dividir por 100,000? km ^ 2 = m ^ 2 / 1,000,000
J Tileson

Es un error tipográfico. El valor debería ser 1.0e + 006, pero el resultado fue correcto (5807 km ^ 2) mm ^ 2 habría sido 1.0e + 006 aún más grande.
Vince

@Vince Tu tienes razón. Corregí el error tipográfico.
yxcv

@ J Tilesone La pregunta sobre la base de esa división se puede preguntar en math.stackexchange.com .
yxcv

5

SELECCIONE
     st_astext (col)
    , st_area (col, false ) AS area
FROM table

ST_Area (geometría) calcula el área del polígono como WGS1984, SIN proyectar a igual área esfera / puntos suspensivos (si usa la Geometría de tipo sql en lugar de Geografía). El resultado se mide en la unidad en el SRID de la geometría.

ST_Area (geografía) calcula el área del polígono como WGS1984, CON proyección de área igual a la esfera / puntos suspensivos (si se utiliza el tipo SQL Geografía en lugar de geometría). El resultado se mide en metros cuadrados. Para pasar de m 2 a km 2 , debe dividir m 2 por 1000 2 (1000 metros en un kilómetro; se cuadra porque es un área, por lo que 1000 * 1000, también conocido como 1000 2 ).

ST_Area (geometría, verdadero / falso) calcula el área (en m 2 ) con coordenadas proyectadas en el sistema de coordenadas CylindricalEqualAreaworld (preservar el área, tiene sentido si desea calcular el área).

La diferencia entre verdadero / falso es la precisión.
ST_Area (geog, false) usa una esfera más rápida pero menos precisa.

Digamos, cuando uso este polígono:

var poly = [
    [47.3612503, 8.5351944],
    [47.3612252, 8.5342631],
    [47.3610145, 8.5342755],
    [47.3610212, 8.5345227],
    [47.3606405, 8.5345451],
    [47.3606350, 8.5343411],
    [47.3604067, 8.5343545],
    [47.3604120, 8.5345623],
    [47.3604308, 8.5352457],
    [47.3606508, 8.5352328],
    [47.3606413, 8.5348784],
    [47.3610383, 8.5348551],
    [47.3610477, 8.5352063],
    [47.3612503, 8.5351944]
];

Obtengo los siguientes resultados:

ST_Area(g) =             5.21556075001092E-07
ST_Area(g, false)     6379.25032051953
ST_Area(g, true)      6350.65051177517

Creo que la parte importante que debe extraerse de los documentos es esta:

Para la geometría , un área cartesiana 2D se determina con las unidades especificadas por el SRID.
Para geografía , el área predeterminada se determina en un esferoide con unidades en metros cuadrados .

Por lo tanto, debe tener cuidado al elegir la geografía y NO la geometría.
Si usa geometría, NECESITA usar las sobrecargas verdaderas / falsas de ST_Area.

En C #, obtengo más o menos lo mismo que cierto con KnownCoordinateSystems.Projected.World.CylindricalEqualAreaworld, y false parece ser un mundo de radio medio-terrestre, algo cercano a WorldSpheroid.CylindricalEqualAreasphere o WorldSpheroid.EckertIVsphere, pero es apagado por 2 m 2 , por lo que parece hacer lo suyo.

using DotSpatial.Projections;
using DotSpatial.Topology;


namespace TestSpatial
{


    static class Program
    {

        // https://stackoverflow.com/questions/46159499/calculate-area-of-polygon-having-wgs-coordinates-using-dotspatial
        // pfff wrong...
        public static void TestPolygonArea()
        {
            // this feature can be see visually here http://www.allhx.ca/on/toronto/westmount-park-road/25/
            string feature = "-79.525542519049552,43.691278124243432 -79.525382520578987,43.691281097414787 -79.525228855617627,43.69124858593392 -79.525096151437353,43.691183664769774 -79.52472799258571,43.690927163079735 -79.525379447437814,43.690771996666641 -79.525602330675355,43.691267524226838 -79.525542519049552,43.691278124243432";
            feature = "47.3612503,8.5351944 47.3612252,8.5342631 47.3610145,8.5342755 47.3610212,8.5345227 47.3606405,8.5345451 47.3606350,8.5343411 47.3604067,8.5343545 47.3604120,8.5345623 47.3604308,8.5352457 47.3606508,8.5352328 47.3606413,8.5348784 47.3610383,8.5348551 47.3610477,8.5352063 47.3612503,8.5351944";

            string[] coordinates = feature.Split(' ');
            // System.Array.Reverse(coordinates);


            // dotspatial takes the x,y in a single array, and z in a separate array.  I'm sure there's a 
            // reason for this, but I don't know what it is.'
            double[] xy = new double[coordinates.Length * 2];
            double[] z = new double[coordinates.Length];
            for (int i = 0; i < coordinates.Length; i++)
            {
                double lon = double.Parse(coordinates[i].Split(',')[0]);
                double lat = double.Parse(coordinates[i].Split(',')[1]);
                xy[i * 2] = lon;
                xy[i * 2 + 1] = lat;
                z[i] = 0;
            }

            double area = CalculateArea(xy);
            System.Console.WriteLine(area);
        }



        public static double CalculateArea(double[] latLonPoints)
        {
            // source projection is WGS1984
            ProjectionInfo projFrom = KnownCoordinateSystems.Geographic.World.WGS1984;
            // most complicated problem - you have to find most suitable projection
            ProjectionInfo projTo = KnownCoordinateSystems.Projected.UtmWgs1984.WGS1984UTMZone37N;
            projTo = KnownCoordinateSystems.Projected.Europe.EuropeAlbersEqualAreaConic; // 6350.9772005155683
            // projTo= KnownCoordinateSystems.Geographic.World.WGS1984; // 5.215560750019806E-07
            projTo = KnownCoordinateSystems.Projected.WorldSpheroid.EckertIVsphere; // 6377.26664171461
            projTo = KnownCoordinateSystems.Projected.World.EckertIVworld; // 6391.5626849671826
            projTo = KnownCoordinateSystems.Projected.World.CylindricalEqualAreaworld; // 6350.6506013739854
            projTo = KnownCoordinateSystems.Projected.WorldSpheroid.CylindricalEqualAreasphere; // 6377.2695087222382
            projTo = KnownCoordinateSystems.Projected.WorldSpheroid.EquidistantCylindricalsphere; // 6448.6818862780929
            projTo = KnownCoordinateSystems.Projected.World.Polyconicworld; // 8483.7701716953889
            projTo = KnownCoordinateSystems.Projected.World.EquidistantCylindricalworld; // 6463.1380225215107
            projTo = KnownCoordinateSystems.Projected.World.EquidistantConicworld; // 8197.4427198320627
            projTo = KnownCoordinateSystems.Projected.World.VanderGrintenIworld; // 6537.3942984174937
            projTo = KnownCoordinateSystems.Projected.World.WebMercator; // 6535.5119516421109
            projTo = KnownCoordinateSystems.Projected.World.Mercatorworld; // 6492.7180733950809
            projTo = KnownCoordinateSystems.Projected.SpheroidBased.Lambert2; // 9422.0631835013628
            projTo = KnownCoordinateSystems.Projected.SpheroidBased.Lambert2Wide; // 9422.0614012926817
            projTo = KnownCoordinateSystems.Projected.TransverseMercator.WGS1984lo33; // 6760.01638841012
            projTo = KnownCoordinateSystems.Projected.Europe.EuropeAlbersEqualAreaConic; // 6350.9772005155683
            projTo = KnownCoordinateSystems.Projected.UtmOther.EuropeanDatum1950UTMZone37N; // 6480.7883094931021


            // ST_Area(g, false)     6379.25032051953
            // ST_Area(g, true)      6350.65051177517
            // ST_Area(g)            5.21556075001092E-07


            // prepare for ReprojectPoints (it's mutate array)
            double[] z = new double[latLonPoints.Length / 2];
            // double[] pointsArray = latLonPoints.ToArray();

            Reproject.ReprojectPoints(latLonPoints, z, projFrom, projTo, 0, latLonPoints.Length / 2);

            // assemblying new points array to create polygon
            System.Collections.Generic.List<Coordinate> points = 
                new System.Collections.Generic.List<Coordinate>(latLonPoints.Length / 2);

            for (int i = 0; i < latLonPoints.Length / 2; i++)
                points.Add(new Coordinate(latLonPoints[i * 2], latLonPoints[i * 2 + 1]));

            Polygon poly = new Polygon(points);
            return poly.Area;
        }


        [System.STAThread]
        static void Main(string[] args)
        {
            TestPolygonArea();

            System.Console.WriteLine(System.Environment.NewLine);
            System.Console.WriteLine(" --- Press any key to continue --- ");
            System.Console.ReadKey();
        }
    }
}

por ejemplo, obtienes un ajuste cercano a falso con el radio medio:

// https://gis.stackexchange.com/a/816/3997
function polygonArea()
{
    var poly = [
        [47.3612503, 8.5351944],
        [47.3612252, 8.5342631],
        [47.3610145, 8.5342755],
        [47.3610212, 8.5345227],
        [47.3606405, 8.5345451],
        [47.3606350, 8.5343411],
        [47.3604067, 8.5343545],
        [47.3604120, 8.5345623],
        [47.3604308, 8.5352457],
        [47.3606508, 8.5352328],
        [47.3606413, 8.5348784],
        [47.3610383, 8.5348551],
        [47.3610477, 8.5352063],
        [47.3612503, 8.5351944]
    ];


    var area = 0.0;
    var len = poly.length;

    if (len > 2)
    {

        var p1, p2;

        for (var i = 0; i < len - 1; i++)
        {

            p1 = poly[i];
            p2 = poly[i + 1];

            area += Math.radians(p2[0] - p1[0]) *
                (
                    2
                    + Math.sin(Math.radians(p1[1]))
                    + Math.sin(Math.radians(p2[1]))
                );
        }

        // https://en.wikipedia.org/wiki/Earth_radius#Equatorial_radius
        // https://en.wikipedia.org/wiki/Earth_ellipsoid
        // The radius you are using, 6378137.0 m corresponds to the equatorial radius of the Earth.
        var equatorial_radius = 6378137; // m
        var polar_radius = 6356752.3142; // m
        var mean_radius = 6371008.8; // m
        var authalic_radius = 6371007.2; // m (radius of perfect sphere with same surface as reference ellipsoid)
        var volumetric_radius = 6371000.8 // m (radius of a sphere of volume equal to the ellipsoid)
        // geodetic latitude φ
        var siteLatitude = Math.radians(poly[0][0]);


        // https://en.wikipedia.org/wiki/Semi-major_and_semi-minor_axes
        // https://en.wikipedia.org/wiki/World_Geodetic_System
        var a = 6378137; // m
        var b = 6356752.3142; // m
        // where a and b are, respectively, the equatorial radius and the polar radius.

        var R1 = Math.pow(a * a * Math.cos(siteLatitude), 2) + Math.pow(b * b * Math.sin(siteLatitude), 2)
        var R2 = Math.pow(a * Math.cos(siteLatitude), 2) + Math.pow(b * Math.sin(siteLatitude), 2);

        // https://en.wikipedia.org/wiki/Earth_radius#Radius_at_a_given_geodetic_latitude
        // Geocentric radius
        var R = Math.sqrt(R1 / R2);
        // var merid_radius = ((a * a) * (b * b)) / Math.pow(Math.pow(a * Math.cos(siteLatitude), 2) + Math.pow(b * Math.sin(siteLatitude), 2), 3/2)



        // console.log(R);
        // var hrad = polar_radius + (90 - Math.abs(siteLatitude)) / 90 * (equatorial_radius - polar_radius);
        var radius = mean_radius;

        area = area * radius * radius / 2.0;
    } // End if len > 0

    // equatorial_radius: 6391.565558418869 m2
    // mean_radius:       6377.287126172337m2
    // authalic_radius:   6377.283923019292 m2
    // volumetric_radius: 6377.271110415153 m2
    // merid_radius:      6375.314923754325 m2
    // polar_radius:      6348.777989748668 m2
    // R:                 6368.48180842528 m2
    // hrad:              6391.171919886588 m2

    // http://postgis.net/docs/doxygen/2.2/dc/d52/geography__measurement_8c_a1a7c48d59bcf4ed56522ab26c142f61d.html
    // ST_Area(false)     6379.25032051953
    // ST_Area(true)      6350.65051177517

    // return area;
    return area.toFixed(2);
}

WebMercator es el sistema de coordenadas utilizado por Google-Maps.
El nombre oficial de este sistema de coordenadas es EPSG: 3857.

Lo que hace exactamente PostGIS está documentado aquí:
https://postgis.net/docs/ST_Area.html

Y los detalles en el código fuente se pueden encontrar aquí:
http://postgis.net/docs/doxygen/2.2/dc/d52/geography__measurement_8c_a1a7c48d59bcf4ed56522ab26c142f61d.html

y aquí:
http://postgis.net/docs/doxygen/2.2/d1/dc0/lwspheroid_8c_a29d141c632f6b46587dec3a1dbe3d176.html#a29d141c632f6b46587dec3a1dbe3d176

Proyección Albers: Proyección Albers Albers-Proyección 2

Proyección cilíndrica de igual área: Cilíndrico Cilíndrico 2


Stefan, esto es raro. Su comentario y resultados de cálculo reales para el caso "ST_Area (geografía)", por lo tanto, sin especificar el booleano "use_spheroid", parecen contradecir la documentación oficial de PostGIS. En la documentación, dice "Para la geografía, el área predeterminada se determina en un esferoide con unidades en metros cuadrados", por lo que sin especificar el valor booleano, se debe dar de forma predeterminada el área en m2 basada en el esferoide. Este también es el resultado que se muestra en el último ejemplo en el cuadro gris en la página de Ayuda, donde claramente no se usa booleano en el comando ST_Area, pero el resultado muestra "sqm_spheroid"? ..
Marco_B

1
Stefan, ¿sus datos de origen estaban realmente en geografía, o convertidos a geografía, o simplemente la geometría estaba en WGS1984 lat / long?
Marco_B

1
@Marco_B: Definitivamente Geometría. De lo contrario, no se puede explicar el 5.21556075001092E-07. Ya veo, debería haber usado la geografía en lugar de la geometría. ver github.com/ststeiger/AnySqlWebAdmin/blob/master/AnySqlWebAdmin/…
Stefan Steiger

Gracias por la respuesta, eso lo explica entonces. Tal vez sea bueno editar su publicación inicial, cambiar la declaración ahora confusa y algo imprecisa de "ST_Area (geografía) calcula el área del polígono como WGS1984, sin proyectar a elipsis / esfera de área igual", y que esta declaración solo se aplica a la situación de las formas en Geometría, no en el almacenamiento de Geografía, y que en el caso de Geografía, las formas usarán un esferoide / esfera.
Marco_B

1
@Marco_B: ya había comenzado a hacer eso cuando escribiste ese comentario. Ahora hecho
Stefan Steiger
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.