理解CLLocation是否代表陆地还是海洋。

11

我正在尝试构建一个API,可以判断CLLocation是否代表陆地。由于我预计大多数用户无法连接网络,因此需要支持离线工作。我使用MapBox作为瓦片服务器,但这仍然是一个MapKit问题,因为我没有使用MapBox SDK。

我已经尝试过几种方法来确定给定坐标是否代表陆地或海洋位置:

  • 离线数据库包含大致构成世界海岸线的坐标。仍然需要解决如何确定给定点是内部还是外部的问题。
  • 对png瓦片资源进行颜色分析(一定有更好的方法!也需要大量离线数据才能有效)

此外,在处理完上述问题后,是否有有效的方法可以根据瓦片坐标(x,y,z)来确定它是陆地/海洋/海岸线瓦片?

如果有人曾经遇到过这个问题,我将感激不尽。


为什么确定一个点是在封闭曲线内还是外面是一个问题?标准的方法是从该点引出一条无限长的射线,计算射线与曲线的交点数量。如果该数量为奇数,则该点在曲线内。 - Reinhard Männer
谢谢@ReinhardMänner,我目前在创建具有一组单个点的多边形方面遇到了一些困难。另外,你是否有一个能够演示你所建议的工作示例? - Stavash
我没有可用的代码,但也许你可以在http://en.wikipedia.org/wiki/Point_in_polygon找到更多信息。 - Reinhard Männer
2
我提供了一个希望有所帮助的答案,尽管我手头没有源代码,抱歉。不过我想问一下 - 你不觉得需要一个应用程序来告诉他们是否在海上的人,可能会有比应用程序能解决的更严重的问题吗? :-) - Benjohn
为什么要尝试复杂的方法,当苹果已经给我们提供了工具呢?(检查我的答案) - Jerome Diaz
显示剩余5条评论
4个回答

13

别担心,似乎苹果已经想到了这个问题!

如果你查看 CLGeocoder 类(CoreLocation),会发现有一个 reverseGeocodeLocation:completionHandler: 方法。

在完成处理程序中,您可以检索 CLPlacemark 对象数组。

CLPlacemark 有两个有趣的属性:

[placemark inlandWater]

对于位于内陆水域上的坐标,此属性包含该水域的名称 - 湖泊、小溪、河流或其他水道的名称。

[placemark ocean]

对于位于海洋上的坐标,此属性包含海洋的名称。

因此,您只需要对您的位置进行逆地理编码,然后检查生成的 CLPlacemark 对象是否设置了 ocean 属性即可。


太好了,这是一个整洁的解决方案。比我写的算法好多了!遗憾的是,CLPlacemark没有包括本地时区偏移量 - 那将是很棒的。 - Benjohn
谢谢Jerome,我能在离线的情况下实现这个吗? - Stavash
这个答案中提到的CLGeocoderAPI仅适用于在线。 - Benjohn
对于离线模式,您需要使用GLGeocoder方法创建自己的数据库。让我们从纬度x = -90开始,尝试经度y = -180到经度y = +180,每次增加0.5个经度。然后增加纬度并重新开始。这将为您提供一个庞大的数据库,但不用担心。对于每个尝试的纬度,我们将尝试定义“海洋上方”的经度“范围”。例如:对于纬度-40,您可能有3个范围[-180; -100] [-60; +40] [+100;+180]。 - Jerome Diaz
这些范围应该保存在数据库中。在您的应用程序中,当测试坐标时,将纬度四舍五入为数据库中存在的一个值(使用此示例四舍五入到0.5),并测试您的经度是否在该纬度的范围内。 - Jerome Diaz
显示剩余2条评论

6
我花了一段时间寻找一个可靠的算法,在球体上进行时区查找,但是我甚至没有找到好的伪代码,更不用说c/c++了。我将讲述我所发现的内容,并提供可以相对容易地组合在一起的资源。
这个问题被称为"点在多边形内"
一个经常使用的简单POP算法是"射线投射"。在二维平面上的POP依赖于一个无限远点。在平面上,这非常容易。有无数个无限远点。随便选一个!但是在球体上却没有这样的点。
如果你有一个已知的点位于任何给定查询多边形的内部或外部,那么你就可以摆脱这个问题。考虑到你的使用情况,这并不是一个繁琐的要求:你可以轻松地选择海洋中的任何一个单独的点,这将位于所有陆地多边形之外。

我认为"绕数"POP算法也会失败,因为在球面上你可以从两个方向接近任何边缘。

一个算法

我想要一种不需要辅助点和启发式方法(用于从边缘数据生成辅助点)的方法。如果我很诚实,我之所以想要这样做,是因为我相信这应该是可能的,而不是因为我真正需要它。

对于您的用例,您可以使用通常的射线投射算法和已知在海洋中的单个点,因此您不需要依赖于启发式方法,尽管它们可能仍然有效。

我提出的方法如下...

  • 您需要自己循环遍历多边形。对于每个多边形...
  • 找到一个通过查询点并穿过多边形的至少一条边缘的大圆(两个角之间的中点即可)。
  • 将大圆与多边形的边缘相交。
  • 当您按顺序沿其边缘行走时,多边形内部在您的右侧(如果您喜欢,则在左侧)。这使得每个交点都具有足够的信息,以知道内部或外部在哪一侧。
  • 从最近的交点,您可以确定查询点是内部还是外部。

实现提示

如果您打算实现此算法(或任何其他POP算法),请不要尝试使用正弦或余弦。

将您的点(多边形角和查询点)表示为单位向量。将您的大圆(多边形边缘和查询点所在的圆)表示为垂直于大圆所在平面的单位向量。使用点积和叉积。不要考虑角度。请考虑向量。

这应该不会太难 - 我没有完全需要实现它。如果您希望进行自由职业编写,请联系我!

可用于构建解决方案的链接

c++ boost库有一个POP实现,但我不是很喜欢,这主要是因为我是个完美主义者——我想它在几乎所有情况下都能满足需求。

tz_world数据库包含陆地多边形,还有GeoJSON变体。您可以使用内置的NSJSONSerialization类来很好地解析它。

这里有一些NASA的点和球的算法(虽然我不喜欢他们的POP)。


0

使用:

mapView.visibleFeatures(at: CGPoint, styleLayerIdentifiers: Set<String>)

请参考这个问题


0

你可以了解一下Mapbox的UTFGrid交互性,它可以离线工作(通过缓存网格瓦片,这些瓦片本质上是文本,或将它们预先捆绑成MBTiles文件)。

查看Mapbox iOS Example的第三个选项卡,在README中有图片,它基本上对被点击的国家进行编码和检索信息。这是在像素分辨率级别上完成的,基本上是预光栅化的,因此您不需要大量数据 - 它不会以更高的分辨率存储,因为您无法以每像素分辨率以上的精度触摸。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接