Wie funktioniert ST_Area in PostGIS?

8

Ich führe eine einfache Berechnung für ein Polygon durch, von dem bekannt ist, dass es eine Fläche von ungefähr 6226 km ^ 2 hat. Es wird in einer Spalte Geografie (WGS84 SRID) gespeichert.

Die Abfrage lautet:

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

und kehrt zurück:

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

Die zurückgegebene Fläche (5807028547.33813) scheint mm ^ 2 und nicht km ^ 2 zu sein. In der Dokumentation http://postgis.net/docs/ST_Area.html heißt es: "Standardmäßig wird die Fläche auf einem Sphäroid mit Einheiten in Quadratmetern bestimmt."

Handelt es sich um einen Dokumentationsfehler oder ist der obige Fehler korrekt und ich verstehe die Funktionalität grundlegend falsch?

J Tileson
quelle

Antworten:

4

Die Berechnung liefert die richtige Ausgabe. Wie Sie zitiert haben, heißt es in der Dokumentation: "Standardmäßig wird die Fläche auf einem Sphäroid mit Einheiten in Quadratmetern bestimmt."

Das Ergebnis Ihrer Abfrage ist 5807028547.33813 m ^ 2 . Um die Fläche in km ^ 2 zu erhalten, müssen Sie das Ergebnis um 1.000.000 teilen.

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

5807.02854733813 km ^ 2 entspricht ungefähr Ihren erwarteten 6226 km ^ 2.

yxcv
quelle
Das ist richtig. Als Referenz: postgis.net/docs/ST_Area.html
alphabetasoup
Aber was ist die Basis für die Division durch 100.000? km ^ 2 = m ^ 2 / 1.000.000
J Tileson
Es ist ein Tippfehler. Der Wert sollte 1,0e + 006 sein, aber das Ergebnis war richtig (5807 km ^ 2) mm ^ 2 wäre 1,0e + 006 noch größer gewesen.
Vince
@Vince Du hast recht. Ich habe den Tippfehler korrigiert.
yxcv
@ J Tilesone Die Frage nach der Basis für diese Abteilung können Sie auf math.stackexchange.com stellen .
yxcv
5

SELECT
     st_astext (col)
    , st_area (col, false ) AS-Bereich
FROM-Tabelle

ST_Area (Geometrie) berechnet die Polygonfläche als WGS1984, OHNE auf eine Kugel / Ellipse mit gleicher Fläche zu projizieren (wenn Sie die Geometrie vom SQL-Typ anstelle von Geographie verwenden). Das Ergebnis wird in der Einheit in der SRID der Geometrie gemessen.

ST_Area (Geografie) berechnet die Polygonfläche als WGS1984, mit Kugel / Ellipsen auf gleiche Fläche vorsteht (wenn Sie den SQL-Typen Geografie statt Geometrie verwenden). Das Ergebnis wird in Quadratmetern gemessen. Um von m 2 auf km 2 zu gelangen , müssen Sie m 2 durch 1000 2 teilen (1000 Meter in einem Kilometer - es ist ein Quadrat, weil es eine Fläche ist, also 1000 * 1000 oder 1000 2 ).

ST_Area (Geometrie, wahr / falsch) berechnet die Fläche (in m 2 ) mit Koordinaten, die in das Koordinatensystem CylindricalEqualAreaworld projiziert werden (Fläche beibehalten - sinnvoll, wenn Sie die Fläche berechnen möchten).

Der Unterschied zwischen wahr / falsch ist die Genauigkeit.
ST_Area (geog, false) verwendet eine schnellere, aber weniger genaue Kugel.

Sagen Sie, wenn ich dieses Polygon verwende:

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]
];

Ich erhalte folgende Ergebnisse:

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

Ich denke, der wichtige Teil, der aus den Dokumenten entnommen werden muss, ist folgender:

Für die Geometrie wird ein kartesisches 2D-Gebiet mit Einheiten bestimmt, die von der SRID angegeben werden.
Für die Geografie wird standardmäßig die Fläche auf einem Sphäroid mit Einheiten in Quadratmetern bestimmt .

Sie müssen also vorsichtig sein, um die Geografie und NICHT die Geometrie auszuwählen .
Wenn Sie Geometrie verwenden, MÜSSEN Sie die True / False-Überladungen von ST_Area verwenden.

In C # erhalte ich mit KnownCoordinateSystems.Projected.World.CylindricalEqualAreaworld mehr oder weniger das Gleiche wie true, und false scheint eine Welt mit einem mittleren Erdradius zu sein, etwas, das WorldSpheroid.CylindricalEqualAreasphere oder WorldSpheroid.EckertIVsphere nahe kommt, aber es ist um 2m 2 ab , also scheint es sein eigenes Ding zu machen.

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();
        }
    }
}

zB erhalten Sie eine enge Anpassung an false mit dem mittleren Radius:

// 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 ist das von Google-Maps verwendete Koordinatensystem.
Der offizielle Name für dieses Koordinatensystem lautet EPSG: 3857.

Was genau PostGIS tut, ist hier dokumentiert:
https://postgis.net/docs/ST_Area.html

Details zum Quellcode finden Sie hier:
http://postgis.net/docs/doxygen/2.2/dc/d52/geography__measurement_8c_a1a7c48d59bcf4ed56522ab26c142f61d.html

und hier:
http://postgis.net/docs/doxygen/2.2/d1/dc0/lwspheroid_8c_a29d141c632f6b46587dec3a1dbe3d176.html#a29d141c632f6b46587dec3a1dbe3d176

Albers-Projektion: Albers-Projektion Albers-Projektion 2

Zylindrische flächengleiche Projektion: Zylindrisch Zylinder 2

Stefan Steiger
quelle
Stefan, das ist komisch. Ihre Bemerkung und die tatsächlichen Berechnungsergebnisse für den Fall "ST_Area (Geografie)", also ohne Angabe des Booleschen Werts "use_spheroid", scheinen der offiziellen PostGIS-Dokumentation zu widersprechen. In der Dokumentation heißt es: "Für die Geografie wird standardmäßig die Fläche auf einem Sphäroid mit Einheiten in Quadratmetern bestimmt." Ohne Angabe des Booleschen Werts sollte daher standardmäßig die Fläche in m2 basierend auf dem Sphäroid angegeben werden. Dies ist auch das Ergebnis, das im letzten Beispiel im grauen Feld auf der Hilfeseite angezeigt wird, wo im Befehl ST_Area eindeutig kein Boolescher Wert verwendet wird, das Ergebnis jedoch "sqm_spheroid" anzeigt.
Marco_B
1
Stefan, waren deine Quelldaten tatsächlich geografisch oder in geografisch konvertiert oder war Geometrie zufällig in WGS1984 lat / long?
Marco_B
1
@Marco_B: Auf jeden Fall Geometrie. Andernfalls kann der 5.21556075001092E-07 nicht erklärt werden. Ich sehe, ich hätte Geographie anstelle von Geometrie verwenden sollen. siehe github.com/ststeiger/AnySqlWebAdmin/blob/master/AnySqlWebAdmin/…
Stefan Steiger
Danke für die Antwort, das erklärt es dann. Vielleicht ist es gut, Ihren ersten Beitrag zu bearbeiten, um die jetzt verwirrende und etwas ungenaue Aussage von "ST_Area (Geografie) berechnet die Polygonfläche als WGS1984, ohne auf eine Fläche / Ellipse mit gleicher Fläche zu projizieren." Zu ändern, und diese Aussage gilt nur für Die Situation der Formen befindet sich in der Geometrie, nicht in der Geografiespeicherung, und im Fall der Geografie verwenden die Formen einen Sphäroid / eine Kugel.
Marco_B
1
@Marco_B: Ich hatte bereits damit begonnen, als Sie diesen Kommentar geschrieben haben. Jetzt fertig.
Stefan Steiger