postgresql - PostgreSQL 数据库中的 ST_Distance 函数计算错误的距离?

标签 postgresql postgis

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

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

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

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

知道哪里出了问题吗?

我在下面给出了我们使用 PostgreSQL 测试过的示例查询以及来自 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.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 Earth 的屏幕截图:

  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。

当我们在 map 上看时,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   |

在 map 上时: 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>

最佳答案

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

Points and polygons

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

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

转到 distance calculation .在这里尽量简洁,但影响您计算的最大因素是基础投影。根据我的理解,地理类型更适合较大的区域——在适合您正在分析的区域的投影中使用几何图形,您会得到更准确的结果。 “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。

关于postgresql - PostgreSQL 数据库中的 ST_Distance 函数计算错误的距离?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40504193/

相关文章:

ruby-on-rails - Postgresql 模式和 Delayed::Job

sql - PostgreSQL:如何优化这个查询

sql - 如何为从多边形顶点生成的点创建 PostgreSQL 分区序列?

javascript - 为什么具有不同 basemap 的相同两个 geoJSON 特征会出现失真?

python - 将 Python 脚本连接到 PostgreSQL 数据库

python - django 唯一对象(不是唯一字段)?

django - 如何使地理领域独一无二?

c# - Npgsql 参数化查询输出与 PostGIS 不兼容

postgresql - 如何比较两个表的坐标并在第一个表的列中写入 true 或 false?

postgresql - 何时将经度和数据存储在支持空间的 DBMS 中以及何时存储为 Float?