ST_Area trong PostGIS hoạt động như thế nào?


8

Tôi đang thực hiện một phép tính đơn giản trên một đa giác được biết là có diện tích khoảng 6226 km ^ 2. Nó được lưu trữ trong cột Địa lý (WGS84 SRID).

Truy vấn là:

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

và trả lại:

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

Diện tích được trả về (5807028547.33813) dường như là mm ^ 2 chứ không phải km ^ 2? Tài liệu http://postgis.net/docs/ST_Area.html nêu rõ "theo khu vực mặc định được xác định trên một hình cầu có đơn vị tính bằng mét vuông"

Đây có phải là một lỗi tài liệu, hoặc là đúng ở trên và về cơ bản tôi đang hiểu sai chức năng?

Câu trả lời:


4

Tính toán cho đầu ra đúng. Như bạn đã trích dẫn các trạng thái tài liệu "theo khu vực mặc định được xác định trên một hình cầu có đơn vị tính bằng mét vuông".

Kết quả truy vấn của bạn là 5807028547.33813 m ^ 2 . Để có được diện tích trong km ^ 2, bạn phải chia kết quả cho 1.000.000.

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

5807.02854733813 km ^ 2 tương ứng với khoảng 6206 km ^ 2 dự kiến ​​của bạn.


Chính xác. Để tham khảo: postgis.net/docs/ST_Area.html
alphabetasoup

Nhưng cơ sở để chia cho 100.000 là gì? km ^ 2 = m ^ 2 / 1.000.000
J Tileson

Đó là một lỗi đánh máy. Giá trị phải là 1.0e + 006, nhưng kết quả là đúng (5807 km ^ 2) mm ^ 2 sẽ vẫn lớn hơn 1.0e + 006.
Vince

@Vince Bạn nói đúng. Tôi sửa lỗi đánh máy.
yxcv

@ J Tilesone Câu hỏi về cơ sở cho phân chia đó bạn có thể hỏi trên math.stackexchange.com .
yxcv

5

CHỌN
     st_astext (col)
    , st_area (col, false ) AS khu vực
TỪ bảng

ST_Area (hình học) tính diện tích đa giác là WGS1984, KHÔNG chiếu vào hình cầu / hình elip có diện tích bằng nhau (nếu bạn sử dụng Hình học loại sql thay vì Địa lý). Kết quả được đo bằng đơn vị trong SRID của hình học.

ST_Area (địa lý) tính diện tích đa giác là WGS1984, VỚI chiếu vào hình cầu / hình elip có diện tích bằng nhau (nếu bạn sử dụng Địa lý loại sql thay vì Hình học). Kết quả được đo bằng mét vuông. Để đi từ m 2 đến km 2 , bạn cần chia m 2 cho 1000 2 (1000 mét trong một km - nó vuông bởi vì đó là một khu vực, vì vậy 1000 * 1000 aka 1000 2 ).

ST_Area (hình học, đúng / sai) tính diện tích (tính bằng m 2 ) với tọa độ được chiếu vào hệ tọa độ hình trụ (hình trụ bảo tồn - có ý nghĩa nếu bạn muốn tính diện tích).

Sự khác biệt giữa đúng / sai là độ chính xác.
ST_Area (geog, false) sử dụng hình cầu nhanh hơn nhưng kém chính xác hơn.

Nói, khi tôi sử dụng đa giác này:

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

Tôi nhận được kết quả sau:

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

Tôi nghĩ phần quan trọng được lấy từ các tài liệu là đây:

Đối với metry địa lý , khu vực Cartesian 2D được xác định với các đơn vị được chỉ định bởi SRID.
Đối với geo graphy , theo khu vực mặc định được xác định trên một hình cầu với đơn vị tính bằng mét vuông .

Vì vậy, bạn cần cẩn thận để chọn địa lý , và KHÔNG hình học.
Nếu bạn sử dụng hình học, bạn CẦN sử dụng quá tải đúng / sai của ST_Area.

Trong C #, tôi nhận được ít nhiều giống như đúng với KnownCoordinateSystems.Projection.World.CylindricEqualAreaworld, và dường như là một thế giới bán kính có nghĩa là trái đất, một cái gì đó gần với WorldSpheroid.CylindumentEqualAreasphere hoặc WorldSpheroid tắt 2m 2 , vì vậy nó dường như làm việc riêng của mình.

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

ví dụ: bạn có được sự phù hợp gần với sai với bán kính trung bình:

// 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 là hệ thống tọa độ được sử dụng bởi Google-Maps.
Tên chính thức của hệ tọa độ này là EPSG: 3857.

Chính xác thì PostGIS làm gì, được ghi lại ở đây:
https://postgis.net/docs/ST_Area.html

Và chi tiết về mã nguồn có thể được tìm thấy ở đây:
http://postgis.net/docs/doxygen/2.2/dc/d52/geography__measousing_8c_a1a7c48d59bcf4ed56522ab26c142f61d.html

và ở đây:
http://postgis.net/docs/doxygen/2.2/d1/dc0/lwspheroid_8c_a29d141c632f6b46587dec3a1dbe3d176.html#a29d141c632f6b46587

Albers-Projection: Albers-Projection Albers-Projection 2

Hình trụ-Diện tích-Chiếu: Hình trụ Hình trụ 2


Stefan, điều này thật kỳ lạ. Nhận xét và kết quả tính toán thực tế của bạn cho trường hợp "ST_Area (địa lý)", do đó, không chỉ định boolean "use_spheroid", dường như mâu thuẫn với tài liệu chính thức của PostGIS. Trong tài liệu có ghi "Đối với địa lý, theo mặc định, diện tích được xác định trên một hình cầu có đơn vị tính bằng mét vuông.", Vì vậy, không chỉ định boolean, nên mặc định cho diện tích tính theo m2 dựa trên hình cầu. Đây cũng là kết quả được hiển thị trong ví dụ cuối cùng trong hộp màu xám trên trang Trợ giúp, trong đó rõ ràng không có boolean nào được sử dụng trong lệnh ST_Area, nhưng kết quả cho thấy "sqm_spheroid"? ..
Marco_B

1
Stefan, dữ liệu nguồn của bạn thực sự là trong địa lý, hoặc được chuyển đổi thành địa lý, hoặc chỉ là hình học xảy ra trong WGS1984 lat / long?
Marco_B

1
@Marco_B: Hình học chắc chắn. Mặt khác, 5.21556075001092E-07 không thể được giải thích. Tôi thấy, tôi nên sử dụng địa lý thay vì hình học. xem github.com/ststeiger/AnySqlWebAdmin/blob/master/AnySqlWebAdmin/ Kẻ
Stefan Steiger

Cảm ơn đã trả lời, điều đó giải thích nó sau đó. Có lẽ thật tốt khi chỉnh sửa bài đăng ban đầu của bạn, để thay đổi tuyên bố hiện tại khó hiểu và hơi không chính xác của "ST_Area (địa lý) tính diện tích đa giác là WGS1984, mà không chiếu vào hình cầu / dấu chấm lửng bằng nhau.", Và câu lệnh này chỉ áp dụng cho tình hình của các hình dạng trong Hình học, không phải lưu trữ Địa lý và trong trường hợp Địa lý, các hình dạng sẽ được sử dụng một hình cầu / hình cầu.
Marco_B

1
@Marco_B: Tôi đã bắt đầu làm điều đó khi bạn viết bình luận đó. Bây giờ đã xong.
Stefan Steiger
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.