使用 cartopy 和 Natural Earth 10m 数据检查地理坐标点是陆地还是海洋

B. *_*eas 4 python shapely cartopy

上一个问题“使用 cartopy 检查地理坐标点是陆地还是海洋”的答案请参阅https://stackoverflow.com/questions/asktitle=Checking%20if%20a%20geocooperative%20point%20is%20land%20or%20ocean% 20using%20cartopy%20and%20Natural%20Earth%2010m%20data建议使用以下代码来确定地理坐标是否为“is_land”:

    import cartopy.io.shapereader as shpreader
    import shapely.geometry as sgeom
    from shapely.ops import unary_union
    from shapely.prepared import prep

    land_shp_fname = shpreader.natural_earth(resolution='50m',
                                   category='physical', name='land')

    land_geom = 
    unary_union(list(shpreader.Reader(land_shp_fname).geometries()))
    land = prep(land_geom)

    def is_land(x, y):
       return land.contains(sgeom.Point(x, y))
Run Code Online (Sandbox Code Playgroud)

当自然地球“物理”“土地”形状文件的分辨率更改为“10m”时,此代码返回地理坐标 (0,0) 的意外结果“True”。

    >>> print(is_land(0, 0))
    True
Run Code Online (Sandbox Code Playgroud)

这是 Natural Earth shapefile 数据或 shapely 实用程序代码的问题吗?

    print shapely.__version__
    1.6.4.post1
Run Code Online (Sandbox Code Playgroud)

pel*_*son 6

好奇的。我确实见过产生无效几何形状的情况unary_union。在这种情况下运行land_geom.is_valid需要花费大量时间,但实际上表明它是有效的几何图形。如果有疑问,GEOS/Shapely 的常见技巧是缓冲 0,这通常会导致改进的几何图形,以改进的形式表示我们之前拥有的几何图形。这样做还声称可以产生有效的几何图形。

不幸的是,结果仍然是...查询继续报告在 0, 0 处有土地...

此时,我有点不知所措。如果有疑问,总是值得查看数据。为了理智起见,我检查了谷歌地图,确认 0, 0 处肯定没有陆地。接下来,我查看了使用以下代码生成的几何图形:

import cartopy.crs as ccrs
import matplotlib.pyplot as plt

ax = plt.subplot(1, 2, 1, projection=ccrs.PlateCarree())
ax.add_geometries([b], ccrs.PlateCarree(), facecolor='green', edgecolor='none')

ax = plt.subplot(1, 2, 2, projection=ccrs.PlateCarree())
ax.add_geometries([b], ccrs.PlateCarree(), facecolor='green', edgecolor='none')
box_size = 5e-2
ax.set_extent([-box_size, box_size, -box_size, box_size])
ax.xaxis.set_visible(True)
ax.yaxis.set_visible(True)
Run Code Online (Sandbox Code Playgroud)

0, 0 处有陆地吗?

看哪,有一块大小约为 1 英里^2 的完美正方形土地正好位于 0, 0 处!阴谋论者会很高兴看到这可能是一块被主流媒体抹去的真正的土地,用于外星人军事研究和猫王的家,但我怀疑更平凡的答案是数据中存在错误(或者也许在读取数据的工具中)。

接下来,我使用fiona进行了快速调查,看看它是否也加载给定区域的几何图形:

import fiona
c = fiona.open(land_shp_fname)
hits = list(c.items(bbox=(-0.01, -0.01, 0.01, 0.01)))
len(hits)
hits
Run Code Online (Sandbox Code Playgroud)

结果是确定的......这里确实有一个几何图形,而且它甚至比绘图显示的还要小(可能是由于缓冲区的浮点容差?):

[(9,
  {'type': 'Feature',
   'id': '9',
   'geometry': {'type': 'Polygon',
    'coordinates': [[(-0.004789435546374336, -0.004389928165484299),
      (-0.004789435546374336, 0.00481690381926197),
      (0.004328009720073429, 0.00481690381926197),
      (0.004328009720073429, -0.004389928165484299),
      (-0.004789435546374336, -0.004389928165484299)]]},
   'properties': OrderedDict([('featurecla', 'Null island'),
                ('scalerank', 100),
                ('min_zoom', 100.0)])})]
Run Code Online (Sandbox Code Playgroud)

令我恐惧的是,快速搜索这个地方的名称“Null Island”就会发现,这是数据的故意怪癖...... https://en.wikipedia.org/wiki/Null_Islandhttps://blogs .loc.gov/maps/2016/04/the-geographical-oddity-of-null-island/详细介绍了 Null Island 从深处升起的情况(事实上,海拔近 5000m)。

那么,除了接受这个怪癖并承认 0, 0 处的土地之外,我们还能做什么呢?好吧,我们可以尝试过滤掉它......

因此,使用您的代码并进行一些调整:

land_shp_fname = shpreader.natural_earth(
    resolution='10m', category='physical', name='land')

land_geom = unary_union(
    [record.geometry
     for record in shpreader.Reader(land_shp_fname).records()
     if record.attributes.get('featurecla') != "Null island"])

land = prep(land_geom)

def is_land(x, y):
   return land.contains(sgeom.Point(x, y))
Run Code Online (Sandbox Code Playgroud)

我们最终得到了一个以 1:10,000,000 比例 (10m) 评估自然地球数据集的函数,以确定一个点是海洋还是陆地,而不考虑来自自然地球数据集的空岛奇数。

>>> print(is_land(0, 0))
False
Run Code Online (Sandbox Code Playgroud)