随机地理坐标(在陆地上,避开海洋)
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&lng=10.851388&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 个城市。
【讨论】:
以上是关于随机地理坐标(在陆地上,避开海洋)的主要内容,如果未能解决你的问题,请参考以下文章