使用 PostgreSQL 数据库中的 ST_Distance 函数计算错误的距离?

Distance calculated wrongly with the ST_Distance function in PostgreSQL database?

我们正在开发一个应用程序,该应用程序涉及根据存储在 PostgreSQL 中的多边形计算给定点的最短距离 table。

我们正在使用来自 PostgreSQL 数据库的 ST_Distance 函数。

当我们将计算出的距离与Google地球进行比较时,两者之间存在巨大差异。

当然,Google 和 PostgreSQL 都不会错(或者它们是错的?),所以我们显然在这里遗漏了一些东西。

知道出了什么问题吗?

我在下面给出了我们使用 PostgreSQL 测试过的示例查询以及来自 Google Earth 的屏幕截图。

SELECT ST_Distance(Place1, Place2) As Place1ToPlace2
    , ST_Distance(Place1, Spot1) As Place1ToSpot1
    , ST_Distance(Place1, Spot2) As Place1ToSpot2
    , ST_Distance(Place2, Spot1) As Place2ToSpot1
    , ST_Distance(Place2, Spot2) As Place2ToSpot2
FROM (SELECT
    ST_PolygonFromText('SRID=4326;POLYGON((-74.0050636293915 40.75265123968514,-74.00500355126653 40.75268991743845,-74.00498169169283 40.75267084386348,-74.00503571044075 40.75263867886528,-74.0050636293915 40.75265123968514))') as Spot1
    ,ST_PolygonFromText('SRID=4326;POLYGON((-74.00503571044075 40.75263867886528,-74.00498225451273 40.75267084385684,-74.00495878551709 40.75265859837483,-74.00501023946696 40.75262521978885,-74.00503571044075 40.75263867886528))') as Spot2
    ,ST_GeogFromText('SRID=4326;POINT(-74.00489 40.752894)') As Place1
    ,ST_GeogFromText('SRID=4326;POINT(-74.004774 40.752846)') As Place2
    ) As foo  ;

它产生以下值:

place1toplace2 |place1tospot1 |place1tospot2 |place2tospot1 |place2tospot2 |
---------------|--------------|--------------|--------------|--------------|
11.152362504   |24.608417285  |25.977083731  |26.004190091  |26.011579435  |

以下是来自 Google 地球的屏幕截图:

  1. Place1ToPlace2
  2. Place1ToSpot1
  3. Place1ToSpot2
  4. Place2ToSpot1
  5. Place2ToSpot2

提前致谢!!

以下是从 Google 地球导出的 KML:

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<Document>
    <name>Spot 1.kml</name>
    <Style id="inline">
        <LineStyle>
            <color>ff0000ff</color>
            <width>2</width>
        </LineStyle>
        <PolyStyle>
            <fill>0</fill>
        </PolyStyle>
    </Style>
    <Style id="inline0">
        <LineStyle>
            <color>ff0000ff</color>
            <width>2</width>
        </LineStyle>
        <PolyStyle>
            <fill>0</fill>
        </PolyStyle>
    </Style>
    <StyleMap id="inline1">
        <Pair>
            <key>normal</key>
            <styleUrl>#inline</styleUrl>
        </Pair>
        <Pair>
            <key>highlight</key>
            <styleUrl>#inline0</styleUrl>
        </Pair>
    </StyleMap>
    <Placemark>
        <name>Spot 1</name>
        <styleUrl>#inline1</styleUrl>
        <Polygon>
            <tessellate>1</tessellate>
            <outerBoundaryIs>
                <LinearRing>
                    <coordinates>
                        -74.0050636293915,40.75265123968514,0 -74.00500355126653,40.75268991743845,0 -74.00498169169283,40.75267084386348,0 -74.00503571044075,40.75263867886528,0 -74.0050636293915,40.75265123968514,0 
                    </coordinates>
                </LinearRing>
            </outerBoundaryIs>
        </Polygon>
    </Placemark>
</Document>
</kml>


<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<Document>
    <name>Spot 2.kml</name>
    <Style id="inline">
        <LineStyle>
            <color>ff0000ff</color>
            <width>2</width>
        </LineStyle>
        <PolyStyle>
            <fill>0</fill>
        </PolyStyle>
    </Style>
    <StyleMap id="inline0">
        <Pair>
            <key>normal</key>
            <styleUrl>#inline1</styleUrl>
        </Pair>
        <Pair>
            <key>highlight</key>
            <styleUrl>#inline</styleUrl>
        </Pair>
    </StyleMap>
    <Style id="inline1">
        <LineStyle>
            <color>ff0000ff</color>
            <width>2</width>
        </LineStyle>
        <PolyStyle>
            <fill>0</fill>
        </PolyStyle>
    </Style>
    <Placemark>
        <name>Spot 2</name>
        <styleUrl>#inline0</styleUrl>
        <Polygon>
            <tessellate>1</tessellate>
            <outerBoundaryIs>
                <LinearRing>
                    <coordinates>
                        -74.00503571044075,40.75263867886528,0 -74.00498225451273,40.75267084385684,0 -74.00495878551709,40.75265859837483,0 -74.00501023946696,40.75262521978885,0 -74.00503571044075,40.75263867886528,0 
                    </coordinates>
                </LinearRing>
            </outerBoundaryIs>
        </Polygon>
    </Placemark>
</Document>
</kml>

!编辑 2!

我又尝试了一件事情。我从 Spot1 中提取了视觉上看起来最接近 Place1 的点并将其标记为 Place3。

当我们在地图上看时,Place3 似乎比 Place2 更接近 Place1,但在查询时,到 Place3 的距离给出了更高的值。

我已经检查了以下查询:

SELECT ST_Distance(Place1, Place2) As Place1ToPlace2
,ST_Distance(Place1, Place3) As Place1ToPlace3
FROM (SELECT
    ST_GeogFromText('SRID=4326;POINT(-74.00489 40.752894)') As Place1
    ,ST_GeogFromText('SRID=4326;POINT(-74.004774 40.752846)') As Place2
    ,ST_GeogFromText('SRID=4326;POINT(-74.00500355126653 40.75268991743845)') As Place3
    ) As foo;

结果如下:

place1toplace2 |place1toplace3 |
---------------|---------------|
11.152362504   |24.608417285   |

在地图上时: Place1ToPlace3

以下是 Place1、Place2 和 Place3 的 KML(我已经从 KML 中删除了与样式相关的标签)

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<Document>
    <name>Distance Comparison.kml</name>
    <Folder>
        <name>Distance Comparison</name>
        <open>1</open>
        <Style>
            <ListStyle>
                <listItemType>check</listItemType>
                <bgColor>00ffffff</bgColor>
                <maxSnippetLines>2</maxSnippetLines>
            </ListStyle>
        </Style>
        <Placemark>
            <name>Place 1 -  40°45&apos;9.78&quot;N  74° 0&apos;18.07&quot;W (40.752894, -74.00489)</name>
            <open>1</open>
            <LookAt>
                <longitude>-74.00500758183839</longitude>
                <latitude>40.75269419172616</latitude>
                <altitude>0</altitude>
                <heading>-0.0008536233435993688</heading>
                <tilt>29.8433509629012</tilt>
                <range>47.16429940085073</range>
                <gx:altitudeMode>relativeToSeaFloor</gx:altitudeMode>
            </LookAt>
            <styleUrl>#msn_1</styleUrl>
            <Point>
                <gx:drawOrder>1</gx:drawOrder>
                <coordinates>-74.00501944444444,40.75271666666666,0</coordinates>
            </Point>
        </Placemark>
        <Placemark>
            <name>Place 2 - 40°45&apos;9.54&quot;N 74° 0&apos;17.70&quot;W (40.752846, -74.004774)</name>
            <open>1</open>
            <LookAt>
                <longitude>-74.00500758183839</longitude>
                <latitude>40.75269419172616</latitude>
                <altitude>0</altitude>
                <heading>-0.0008536233435993688</heading>
                <tilt>29.8433509629012</tilt>
                <range>47.16429940085073</range>
                <gx:altitudeMode>relativeToSeaFloor</gx:altitudeMode>
            </LookAt>
            <styleUrl>#msn_2</styleUrl>
            <Point>
                <gx:drawOrder>1</gx:drawOrder>
                <coordinates>-74.00491666666667,40.75265,0</coordinates>
            </Point>
        </Placemark>
        <Placemark>
            <name>Place 3 -  40°45&apos;9.68&quot;N  74° 0&apos;18.01&quot;W (40.75268991743845 -74.00500355126653)</name>
            <open>1</open>
            <styleUrl>#msn_3</styleUrl>
            <Point>
                <coordinates>-74.00500277777778,40.75268888888889,0</coordinates>
            </Point>
        </Placemark>
    </Folder>
</Document>
</kml>

我看到的潜在主要问题是,您在调用 ST_Distance. From a glance at Postgres' API, the ST_Distance 函数时混合了几何图形和地理图形,但不能同时使用几何图形或地理图形。

请尝试以下查询。在这里,我已将您对 ST_GeogFromText, which returns a geography, with ST_GeometryFromText 的调用替换为 returns 几何。

SELECT ST_Distance(Place1, Place2) AS Place1ToPlace2,
       ST_Distance(Place1, Spot1)  AS Place1ToSpot1
       ST_Distance(Place1, Spot2)  AS Place1ToSpot2
       ST_Distance(Place2, Spot1)  AS Place2ToSpot1
       ST_Distance(Place2, Spot2)  AS Place2ToSpot2
FROM
(
    SELECT ST_PolygonFromText('SRID=4326;POLYGON((-74.0050636293915 40.75265123968514,-74.00500355126653 40.75268991743845,-74.00498169169283 40.75267084386348,-74.00503571044075 40.75263867886528,-74.0050636293915 40.75265123968514))') AS Spot1,
           ST_PolygonFromText('SRID=4326;POLYGON((-74.00503571044075 40.75263867886528,-74.00498225451273 40.75267084385684,-74.00495878551709 40.75265859837483,-74.00501023946696 40.75262521978885,-74.00503571044075 40.75263867886528))') AS Spot2,
           ST_GeometryFromText('SRID=4326;POINT(-74.00489 40.752894)') AS Place1
           ST_GeometryFromText('SRID=4326;POINT(-74.004774 40.752846)') AS Place2
) AS foo;

您在 PostGIS 中的功能似乎与它们在 Google 中的位置不一样...这是我通过可视化得到的结果。

现在还有两个问题。一、geometry and geography的区别。二、距离计算

st_distance geographies returns 值(以米为单位)。这就是为什么你的第一个查询的输出基本上是你所期望的,给定特征的位置(因为你的点是地理,Postgres 为你将多边形转换为地理)。但是,st_distance 在 geometries returns 上的值以基础投影的单位表示,在本例中为度数。这解释了你的第二个查询的输出。

distance calculation. Trying to be concise here, but the biggest thing affecting your calculation is the underlying projection. From my understanding, the geography type is better for larger areas - you'd get more accurate results using geometries in a suitable projection for the area you're analysing. The "suitable projection”取决于您正在观察世界的哪些地方以及您的目的是什么。

此外,不要假设 Google 的数字是正确的 - 我进行了快速搜索,但没有找到任何关于他们如何计算距离的信息。如果他们使用不同的方法,你会得到不同的答案...

编辑 这是带有显示在屏幕截图中的坐标的查询。注意坐标值的差异!另外请注意,您屏幕截图中显示的度分秒与括号中的十进制度值不匹配!

SELECT ST_Distance(Place1, Place2) As Place1ToPlace2
    , ST_Distance(Place1, Spot1) As Place1ToSpot1
    , ST_Distance(Place1, Spot2) As Place1ToSpot2
    , ST_Distance(Place2, Spot1) As Place2ToSpot1
    , ST_Distance(Place2, Spot2) As Place2ToSpot2
FROM (SELECT
    ST_PolygonFromText('SRID=4326;POLYGON((-74.0050636293915 40.75265123968514,-74.00500355126653 40.75268991743845,-74.00498169169283 40.75267084386348,-74.00503571044075 40.75263867886528,-74.0050636293915 40.75265123968514))') as Spot1
    ,ST_PolygonFromText('SRID=4326;POLYGON((-74.00503571044075 40.75263867886528,-74.00498225451273 40.75267084385684,-74.00495878551709 40.75265859837483,-74.00501023946696 40.75262521978885,-74.00503571044075 40.75263867886528))') as Spot2
    ,ST_GeogFromText('SRID=4326;POINT( -74.005019 40.752717)') As Place1
    ,ST_GeogFromText('SRID=4326;POINT(-74.004917 40.752650)') As Place2
    ) As foo  ;

返回值为11.38223433、3.27827391、5.99175215、5.93327383、3.65564537。它们与 Google.

的结果相差不到 cm