随机地理坐标(在陆地上,避开海洋)

Posted

技术标签:

【中文标题】随机地理坐标(在陆地上,避开海洋)【英文标题】:Random geographic coordinates (on land, avoid ocean) 【发布时间】:2012-02-24 16:03:59 【问题描述】:

关于如何生成地球上各个地点的随机坐标(纬度/经度)的任何巧妙想法?纬度/经度。精确到 5 点,避免水体。

    double minLat = -90.00;
    double maxLat = 90.00;      
    double latitude = minLat + (double)(Math.random() * ((maxLat - minLat) + 1));
    double minLon = 0.00;
    double maxLon = 180.00;     
    double longitude = minLon + (double)(Math.random() * ((maxLon - minLon) + 1));
    DecimalFormat df = new DecimalFormat("#.#####");        
    log.info("latitude:longitude --> " + df.format(latitude) + "," + df.format(longitude));

也许我生活在一个梦想的世界里,水的话题是不可避免的……但希望有更好、更清洁、更有效的方法来做到这一点?

编辑

一些很棒的答案/想法——但是,在规模上,假设我需要生成 25,000 个坐标。由于延迟、成本和其他一些因素,寻求外部服务提供商可能不是最佳选择。

【问题讨论】:

看看这个问题:***.com/q/990148/813951有些答案还不错。 可能会尝试 geohashing :) xkcd.com/426 这个 sn-p 不会在两极附近给出一个非常奇怪的分布吗? 【参考方案1】:

处理水体问题将主要是一个数据问题,例如你只想想念海洋还是想念小溪流。您需要使用具有所需数据质量的服务,或者您需要自己获取数据并在本地运行。根据您的编辑,听起来您想走本地数据路线,所以我将重点介绍一种方法。

一种方法是获取陆地区域或水域的 shapefile。然后,您可以生成一个随机点并确定它是否与陆地区域相交(或者,不与水域相交)。

首先,您可能会获得一些低分辨率数据here,然后获得更高分辨率数据here,以便在海岸线或湖泊/河流/等处获得更好的答案。您提到您希望将点精确到小数点后 5 位,即 1m 多一点。请注意,如果您获得与该精度匹配的数据,您将拥有一个庞大的数据集。而且,如果您想要真正好的数据,请准备好为此付费。

获得形状数据后,您需要一些工具来帮助您确定随机点的交点。 Geotools 是一个很好的起点,可能会满足您的需求。您最终还将查看 opengis 代码(geotools 站点下的文档 - 不确定他们是否使用了它们或什么)和 JTS 用于几何处理。使用它,您可以快速打开 shapefile 并开始执行一些交集查询。

    File f = new File ( "world.shp" );
    ShapefileDataStore dataStore = new ShapefileDataStore ( f.toURI ().toURL () );
    FeatureSource<SimpleFeatureType, SimpleFeature> featureSource = 
        dataStore.getFeatureSource ();
    String geomAttrName = featureSource.getSchema ()
        .getGeometryDescriptor ().getLocalName ();

    ResourceInfo resourceInfo = featureSource.getInfo ();
    CoordinateReferenceSystem crs = resourceInfo.getCRS ();
    Hints hints = GeoTools.getDefaultHints ();
    hints.put ( Hints.JTS_SRID, 4326 );
    hints.put ( Hints.CRS, crs );

    FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2 ( hints );
    GeometryFactory gf = JTSFactoryFinder.getGeometryFactory ( hints );

    Coordinate land = new Coordinate ( -122.0087, 47.54650 );
    Point pointLand = gf.createPoint ( land );
    Coordinate water = new Coordinate ( 0, 0 );
    Point pointWater = gf.createPoint ( water );

    Intersects filter = ff.intersects ( ff.property ( geomAttrName ), 
        ff.literal ( pointLand ) );
    FeatureCollection<SimpleFeatureType, SimpleFeature> features = featureSource
            .getFeatures ( filter );

    filter = ff.intersects ( ff.property ( geomAttrName ), 
        ff.literal ( pointWater ) );
    features = featureSource.getFeatures ( filter );

快速解释:

    假设你得到的 shapefile 是多边形数据。线或点的交点不会给你想要的。 第一部分打开 shapefile - 没什么有趣的 您必须获取给定文件的几何属性名称 坐标系统的东西 - 您在帖子中指定了纬度/经度,但 GIS 可能要复杂得多。一般来说,我指给你的数据是geographic, wgs84,这就是我在这里设置的。但是,如果您不是这种情况,那么您需要确保您在正确的坐标系中处理数据。如果这一切听起来像是胡言乱语,请在谷歌周围搜索有关 GIS/坐标系/基准面/椭球的教程。 生成坐标几何图形和过滤器是不言自明的。生成的要素集要么是空的,这意味着如果您的数据是土地覆盖,则坐标在水中,或者不是空的,这意味着相反。

注意:如果你用一组非常随机的点来做这个,你会经常碰到水,你可能需要一段时间才能达到 25k 点。您可能想尝试比真正随机的更好地确定您的点生成范围(例如删除大西洋/太平洋/印度洋的大块区域)。

另外,您可能会发现您的交叉点查询太慢了。如果是这样,您可能需要考虑使用 GDAL 之类的工具创建四叉树索引 (qix)。不过,我不记得 geotools 支持哪些索引类型。

【讨论】:

我可以在这里提一下my solution in python,因为这个问题/答案确实很鼓舞人心,但对于我这样的非Java开发人员来说却很沮丧?【参考方案2】:

很久以前就有人问过这个问题,我现在也有类似的需求。我正在研究两种可能性:

1.定义随机发生器的表面范围。

在此确定您要达到的精度水平很重要。最简单的方法是采用非常轻松和近似的方法。在这种情况下,您可以将世界地图分成“盒子”:

每个盒子都有自己的经度范围。然后你首先随机化以获得一个随机框,然后你随机化以获得该框边界内的随机纬度和随机长度。

这里的精度当然不是最好的......虽然这取决于:) 如果你做好功课并定义了许多覆盖最复杂表面形状的框 - 你可能对精度非常满意。

2。列表项

一些从坐标或地址或国家或地区返回大陆名称的 API = WATER 没有的东西。 Google Maps API 可以在这里提供帮助。我没有对此进行更深入的研究,但我认为这是可能的,尽管您必须对每对生成的坐标进行检查并在错误时重新运行。所以如果随机生成器一直把你扔到海里,你可能会有点卡住。

另外 - 一些水确实属于国家、地区......所以是的,不是很精确。

出于我的需要 - 我使用“盒子”,因为我还想控制获取随机坐标的确切区域,并且不介意它是否落在湖泊或河流上,而不是开阔的海洋:)

【讨论】:

【参考方案3】:
    下载一卡车包含陆地位置的 KML 文件。 从中提取所有坐标this might help here。 随机挑选。

【讨论】:

【参考方案4】:

当然,您应该将地图作为资源。你可以在这里拿:http://www.naturalearthdata.com/

然后我会准备1bit黑白位图资源,1s标记土地,0x标记水。

位图的大小取决于您所需的精度。如果您需要 5 度,那么您的位图将是 360/5 x 180/5 = 72x36 像素 = 2592 位。

然后我会在 Java 中加载这个位图,在上面的范围内生成随机整数,读取位,如果为零则重新生成。

附:您也可以在这里挖掘http://geotools.org/ 以获得一些现成的解决方案。

【讨论】:

他要求小数点后 5 位的精度,即 1,603,314,989,500,000 位,所以不确定这是否适用于这种精度。【参考方案5】:

还有另一种方法可以使用 Google 地球 Api 来解决此问题。我知道它是 javascript,但我认为这是一种解决问题的新方法。

无论如何,我已经在这里整理了一个完整的工作解决方案 - 请注意它也适用于河流:http://www.msa.mmu.ac.uk/~fraser/ge/coord/

我使用的基本思想是在Google Earth Api中实现GEView object的hiTest method。

看看以下来自 Google 的 hitest 示例。 http://earth-api-samples.googlecode.com/svn/trunk/examples/hittest.html

hitTest 方法提供屏幕上的一个随机点(像素坐标),它返回一个 GEHitTestResult 对象,该对象包含有关与该点对应的地理位置的信息。如果使用 GEPlugin.HIT_TEST_TERRAIN 模式,只要我们将结果筛选到高度 > 1m 的点,就可以将结果限制为仅着陆(地形)

这是我使用的实现 hitTest 的函数:

var hitTestTerrain = function()

    var x = getRandomInt(0, 200); // same pixel size as the map3d div height
    var y = getRandomInt(0, 200); // ditto for width
    var result = ge.getView().hitTest(x, ge.UNITS_PIXELS, y, ge.UNITS_PIXELS, ge.HIT_TEST_TERRAIN);
    var success = result && (result.getAltitude() > 1);
    return  success: success, result: result ;
;

显然,您还希望获得来自全球任何地方的随机结果(不仅仅是从单个视点可见的随机点)。为此,我在每次成功的hitTestTerrain 呼叫后移动地球视图。这是使用一个小的辅助函数来实现的。

var flyTo = function(lat, lng, rng)

    lookAt.setLatitude(lat);
    lookAt.setLongitude(lng);
    lookAt.setRange(rng);
    ge.getView().setAbstractView(lookAt);
;

最后是调用这两个方法的主代码块的精简版。

var getRandomLandCoordinates = function()

    var test = hitTestTerrain();
    if (test.success)
    
        coords[coords.length] =  lat: test.result.getLatitude(), lng: test.result.getLongitude() ;
    

    if (coords.length <= number)
    
       getRandomLandCoordinates();
    
    else
    
       displayResults();
    
;

所以,地球随机移动到一个位置

其中的其他函数只是生成随机 x,y 和随机 lat,lng 数字、输出结果以及切换控件等的助手。

我已经对代码进行了相当多的测试,结果并不是 100% 完美,将 altitude 调整到更高的值,比如 50m 可以解决这个问题,但显然它会减少可能选择的坐标区域。

显然,您可以根据自己的需要调整这个想法。也许多次运行代码以填充数据库或其他东西。

【讨论】:

“我认为这是一种解决问题的新方法。” ..是的+1【参考方案6】:

要在纬度和经度上获得良好的均匀分布,您应该执行以下操作来获得正确的角度:

double longitude = Math.random() * Math.PI * 2;
double latitude = Math.acos(Math.random() * 2 - 1);

至于避开水体,你有关于水在哪里的数据吗?好吧,只需重新采样,直到您成功!如果您还没有这些数据,那么似乎其他人有比我更好的建议...

希望这会有所帮助,干杯。

【讨论】:

在球体上均匀随机分布点的正确方法在mathworld.wolfram.com/SpherePointPicking.html 但是请注意,您的坐标以弧度为单位,并且您的经度在 [0, 2*PI) 范围内,这是不正确的。它需要在 [-PI, PI) 弧度或 [-180, 180) 度范围内。【参考方案7】:

作为 B 计划,也许你可以选择一个随机国家,然后在这个国家内部随机选择一个坐标。为了公平起见,选择一个国家时,您可以使用其面积作为权重。

【讨论】:

【参考方案8】:

有一个库here,您可以使用它的 .random() 方法获取随机坐标。然后您可以使用GeoNames WebServices 来确定它是否在陆地上。他们有一个网络服务列表,你只需要使用正确的。 GeoNames 是免费且可靠的。

【讨论】:

【参考方案9】:
    去那里http://wiki.openstreetmap.org/ 尝试使用API​​:http://wiki.openstreetmap.org/wiki/Databases_and_data_access_APIs

【讨论】:

【参考方案10】:

我想您可以使用世界地图,在其上定义几个点以按您所说的划分大部分水体,然后使用 polygon.contains 方法来验证坐标。

更快的算法是使用这张地图,随机取一些点并检查下面的颜色,如果是蓝色,然后是水......当你有坐标时,你将它们转换为纬度/经度。

【讨论】:

为了阐述第二段,我们可以通过创建一个纯蓝色的蒙版贴图来映射水体来改进这一点。【参考方案11】:

你也可以做蓝绿色的事情,然后存储所有的绿色点供以后查找。这样做的好处是可以“逐步”精炼。当您找到一种更好的方法来制作点列表时,您可以将随机抓取器指向越来越准确的点组。

也许服务提供商已经回答了您的问题:例如https://www.google.com/enterprise/marketplace/viewListing?productListingId=3030+17310026046429031496&pli=1

海拔API? http://code.google.com/apis/maps/documentation/elevation/海平面以上还是以下? (没有荷兰分给你!)

【讨论】:

【参考方案12】:

生成很容易,问题是它们不应该在水上。例如,我将在这里导入“打开街道地图”http://ftp.ecki-netz.de/osm/ 并将其导入数据库(非常简单的数据结构)。我建议使用 PostgreSQL,它带有一些地理功能 http://www.postgresql.org/docs/8.2/static/functions-geometry.html 。为此,您必须将点保存在“多边形”列中,然后您可以使用“&&”运算符检查它是否在水多边形中。对于 OpenStreetmap Way-Entry 的属性,您应该查看http://wiki.openstreetmap.org/wiki/Category:En:Keys

【讨论】:

【参考方案13】:

对bsimic 所说的关于挖掘 GeoNames 的 Web 服务的补充,这里有一个捷径: 他们有一个用于请求海洋名称的专用 WebService

(由于请求的数量,我知道 OP 对 使用公共 Web 服务的限制。尽管如此,我偶然发现了同样的基本问题,并认为这很有帮助。)

转到http://www.geonames.org/export/web-services.html#astergdem 并查看“海洋/反向地理编码”。它以 XML 和 JSON 的形式提供。创建免费用户帐户以防止模拟帐户的每日限制。

海洋区域的请求示例(波罗的海,JSON-URL):

http://api.geonames.org/oceanJSON?lat=54.049889&amp;lng=10.851388&amp;username=demo

结果


  "ocean": 
    "distance": "0",
    "name": "Baltic Sea"
  

虽然陆地上的一些坐标会导致


  "status": 
    "message": "we are afraid we could not find an ocean for latitude and longitude :53.0,9.0",
    "value": 15
  

【讨论】:

【参考方案14】:

随机点必须均匀分布在世界各地吗?如果您可以满足看似均匀分布,您可以这样做:

打开您最喜欢的地图服务,在美国、俄罗斯、中国、西欧以及绝对是非洲北部绘制一个矩形 - 确保矩形内没有大湖或里海。取每个矩形的角坐标,然后随机选择这些矩形内部的坐标。

保证这些点都不在任何海或湖上。您可能会偶尔发现一条河流,但我不确定有多少地理服务能够准确地做到这一点。

【讨论】:

【参考方案15】:

从理论和实践的角度来看,这是一个非常有趣的问题。最合适的解决方案在很大程度上取决于您的确切要求。你需要考虑每一个水域,还是只考虑主要的海洋?准确性和正确性有多重要;将海洋识别为陆地或反之亦然会是灾难性的失败吗?

我认为机器学习技术将是解决此问题的绝佳方法,前提是您不介意某个水点被错误分类为陆地的(希望很小)概率。如果这不是问题,那么与其他技术相比,这种方法应该具有许多优势。

使用位图是一个不错的解决方案,简单而优雅。它可以以指定的精度生成,并且保证分类是正确的(或者至少与您制作位图一样正确)。但它的实用性取决于您需要解决方案的准确性。您提到您希望坐标精度精确到小数点后 5 位(这相当于将地球的整个表面映射到最近的米)。每个元素使用 1 位,位图的重量约为 73.6 TB!

不过,我们不需要存储所有这些数据;我们只需要知道海岸线在哪里。只要知道一个点相对于海岸的位置,我们就可以确定它是在陆地上还是在海上。粗略估计,CIA 世界概况报告称地球上有 22498 公里的海岸线。如果我们要存储每米海岸线的坐标,每个纬度和经度使用一个 32 位字,那么存储空间将不到 1.35GB。如果这是一个微不足道的应用程序,它仍然很多,但比使用位图少几个数量级。但是,如果不需要如此高的准确性,那么这些数字将大大下降。将映射减少到最近的公里将使位图只有约 75GB,并且世界海岸线的坐标可以放在软盘上。

我的建议是使用聚类算法来确定一个点是否在陆地上。我们首先需要大量的坐标,这些坐标我们已经知道在陆地或海上。现有的 GIS 数据库将适用于此。然后我们可以分析这些点来确定陆地和海洋的集群。聚类间的决策边界应落在海岸线上,所有未确定决策边界的点都可以去掉。可以重复此过程以逐步提供更准确的边界。

只需要存储确定决策边界/海岸线的点,通过使用简单的距离度量,我们可以快速轻松地确定一组坐标是在陆地上还是在海上。训练系统需要大量资源,但一旦完成,分类器将需要很少的空间或时间。

【讨论】:

【参考方案16】:

假设数据库中没有亚特兰蒂斯,您可以随机选择城市。如果您打算模仿人类活动,这也提供了更真实的点分布: https://simplemaps.com/data/world-cities

免费版只有 7,300 个城市。

【讨论】:

以上是关于随机地理坐标(在陆地上,避开海洋)的主要内容,如果未能解决你的问题,请参考以下文章

根据距离生成随机地理坐标

海边到陆地多少米海洋管理范围?

luogu P1325 雷达安装

MapTiler 用透明海洋设置土地多边形填充

在区域(国家/城市)内生成随机经纬度值

在给定地理点的 50 英尺范围内随机创建一个地理点(空间 SQL)