在Google地图中确认一个点是Land还是Water
然后谷歌地图“划分水域”
那么,不是在圣经的意义上,但..
我想知道我有什么select来validation[Lat,Lon]的一个点是Land还是Water。
谷歌地图显然有这样的数据(水体是蓝色的) – 但是API中有什么东西可以使用吗? 如果不是 – 他们没有服务,因为他们从来没有想到它? 还是因为它太复杂了?
我还没有find任何有关这个问题的信息 – 除了这里的一些类似的问题(例如寻找地形types或海拔 – 但这不是我所需要的)。
有没有分开的层? 一个选项? 命令? 或者我应该去做手动?
我可以考虑如何处理这个问题的唯一方法(我需要手动进行)是检查每个服务的瓷砖的确切点 – 然后检查该RGB地图色调的RGB值。 这只是在理论上 – 因为在实践中 – 我不知道如何做到这一点,第一个障碍是,我不知道如何将瓦片上的像素位置转换为[LatLon]点
现成的解决scheme会容易得多。
请注意,我并不需要世界上所有的水(例如 – 我不关心溪stream,小池塘,大多数河stream或邻居的游泳池,我需要一个人可以在没有浮动车辆的帮助下冒险的点数)
编辑我
阅读后评论:海拔方法是不可靠的,有太多的地方以下海平面(你可以看到一个“最深”的地方清单http://geology.com/below-sea-level/ )在海平面(湖泊)以上的内陆水体太多。 反向地理定位方法是不可靠的,因为它会多次返回一个地理政治实体,比如城市,州或者ZERO。 在提出这个问题之前,我已经研究过这些伪解决scheme,但是他们中没有一个真的回答了这个问题 – 那些方法在最好的情况下是“坏的”猜测。
这是两种不同的方式,你可以试试:
-
您可以使用Google地图反向地理编码 。 在结果集中,您可以通过检查
types
来确定它是否为水。 在水域情况下,这种types是natural_feature
。 有关此链接的详细信息,请访问http://code.google.com/apis/maps/documentation/geocoding/#Types 。此外,您还需要检查function的名称,如果它们包含
Sea, Lake, Ocean
和其他一些与水有关的单词以获得更高的准确性。 例如,沙漠也是natural_feature
的。Prons – 所有的检测过程都将在客户端机器上完成。无需创build自己的服务器端服务。
缺点 – 非常不准确,在水域得到“无”的机会非常高。
-
您可以使用Google静态地图按像素检测水域/地块。 但为此,您需要创buildhttp服务。
这些是您的服务必须执行的步骤:
- 从客户端接收
latitude
,longitude
和current zoom
。 - 向Google静态地图服务发送
http://maps.googleapis.com/maps/api/staticmap?center={
纬度,
经度}&zoom={
当前缩放大小}和大小= 1×1&maptype =路线图和传感器=虚拟请求。 - 检测1×1静态图像的像素颜色。
- 回应有关检测的信息。
您无法检测客户端的像素颜色。 是的,您可以在客户机上加载静态图像,并在
canvas
元素上绘制图像。 但是您不能使用canvas的上下文的getImageData
获取像素的颜色。 这受到跨域策略的限制。Prons – 高度准确的检测
缺点 – 使用自己的服务器资源进行检测
- 从客户端接收
目前的Google服务似乎不可能。
但是还有其他的服务,比如Koordinates Vector JSON Query服务 ! 您只需查询URL中的数据,即可获得JSON / XML响应 。
示例请求: http : //api.koordinates.com/api/vectorQuery.json?key=YOUR_GEODATA_KEY&layer=1298&x=-159.9609375&y=13.239945499286312&max_results=3&radius=10000&geometry=true&with_field_names=true
您必须注册并提供您的密钥和选定的图层编号。 您可以search所有可用图层的存储库 。 大多数图层只是区域性的,但是你也可以find全球性的,比如世界海岸线 :
当你select一个图层,你点击“服务”标签,你会得到示例请求的URL。 我相信你只需要注册,就是这样!
而现在最好的:
你可以上传你的图层!
它不能马上得到,嘿必须以某种方式处理它,但它应该工作! 图层库实际上看起来像人们根据需要上传它们。
这是我用的,它工作不坏…你可以改善testing,如果你有更多的CPU浪费通过添加像素。
function isItWatter($lat,$lng) { $GMAPStaticUrl = "https://maps.googleapis.com/maps/api/staticmap?center=".$lat.",".$lng."&size=40x40&maptype=roadmap&sensor=false&zoom=12&key=YOURAPIKEY"; //echo $GMAPStaticUrl; $chuid = curl_init(); curl_setopt($chuid, CURLOPT_URL, $GMAPStaticUrl); curl_setopt($chuid, CURLOPT_RETURNTRANSFER, TRUE); curl_setopt($chuid, CURLOPT_SSL_VERIFYPEER, FALSE); $data = trim(curl_exec($chuid)); curl_close($chuid); $image = imagecreatefromstring($data); // this is for debug to print the image ob_start(); imagepng($image); $contents = ob_get_contents(); ob_end_clean(); echo "<img src='data:image/png;base64,".base64_encode($contents)."' />"; // here is the test : I only test 3 pixels ( enough to avoid rivers ... ) $hexaColor = imagecolorat($image,0,0); $color_tran = imagecolorsforindex($image, $hexaColor); $hexaColor2 = imagecolorat($image,0,1); $color_tran2 = imagecolorsforindex($image, $hexaColor2); $hexaColor3 = imagecolorat($image,0,2); $color_tran3 = imagecolorsforindex($image, $hexaColor3); $red = $color_tran['red'] + $color_tran2['red'] + $color_tran3['red']; $green = $color_tran['green'] + $color_tran2['green'] + $color_tran3['green']; $blue = $color_tran['blue'] + $color_tran2['blue'] + $color_tran3['blue']; imagedestroy($image); var_dump($red,$green,$blue); //int(492) int(570) int(660) if($red == 492 && $green == 570 && $blue == 660) return 1; else return 0; }
结帐这篇文章。 它准确地检测是否有东西在水面上,而不需要服务器。 这是一个依靠Google地图中的自定义样式function的黑客。
http://tech.bellycard.com/blog/where-d-the-water-go-google-maps-water-pixel-detection-with-canvas/
我认为在本地执行这个查询更有意思,所以我可以更加自力更生:比方说,我想要一次生成25000个随机地理坐标,我宁愿要避免调用可能昂贵的外部API。 这里是我在Python中使用TomSchober提到的python示例 。 基本上,它会查找包含所有陆地坐标的预制350MB文件上的坐标,如果坐标存在那里,则打印它们。
import ogr from IPython import embed import sys drv = ogr.GetDriverByName('ESRI Shapefile') #We will load a shape file ds_in = drv.Open("land_polygons.shp") #Get the contents of the shape file lyr_in = ds_in.GetLayer(0) #Get the shape file's first layer #Put the title of the field you are interested in here idx_reg = lyr_in.GetLayerDefn().GetFieldIndex("P_Loc_Nm") #If the latitude/longitude we're going to use is not in the projection #of the shapefile, then we will get erroneous results. #The following assumes that the latitude longitude is in WGS84 #This is identified by the number "4236", as in "EPSG:4326" #We will create a transformation between this and the shapefile's #project, whatever it may be geo_ref = lyr_in.GetSpatialRef() point_ref=ogr.osr.SpatialReference() point_ref.ImportFromEPSG(4326) ctran=ogr.osr.CoordinateTransformation(point_ref,geo_ref) def check(lon, lat): #Transform incoming longitude/latitude to the shapefile's projection [lon,lat,z]=ctran.TransformPoint(lon,lat) #Create a point pt = ogr.Geometry(ogr.wkbPoint) pt.SetPoint_2D(0, lon, lat) #Set up a spatial filter such that the only features we see when we #loop through "lyr_in" are those which overlap the point defined above lyr_in.SetSpatialFilter(pt) #Loop through the overlapped features and display the field of interest for feat_in in lyr_in: # success! print lon, lat check(-95,47)
我试了十几个坐标,奇妙地工作。 “land_polygons.shp”文件可以在这里下载,OpenStreetMaps恭维。 (我自己使用了第一个WGS84下载链接,也许第二个工作也是如此)
除了反向地理编码 – 正如Molle博士指出的那样,它可能会返回ZERO_RESULTS – 您可以使用Elevation服务。 如果通过反向地理编码获得零结果,请获取该位置的高程。 由于海底位于海平面以下,因此一般情况下,海水会得到负数。 在http://www.daftlogic.com/sandbox-google-maps-find-altitude.htm有一个完整的海拔服务示例。;
请记住,由于谷歌不提供这些信息,任何其他方法只是一个猜测,猜测本身就是不准确的。 但是,使用由反向地理编码返回的type
,或者如果type
不可用,则会覆盖大多数可能性。
如果一切都失败了,你总是可以尝试检查点的高程和一定的距离 – 除了水以外,其他的东西都不是完全平坦的。
这种方法是完全不可靠的。 实际上,返回的数据完全取决于你正在使用的世界的哪个部分。 例如,我在法国工作。 如果我点击法国海岸的海域,Google会返回它可以“猜测”的最近的LAND位置。 当我向Google询问同一个问题的信息时,他们回答说,他们无法准确地将所要求的内容返回给水质。
我知道,这不是一个很满意的答案。 这是非常令人沮丧的,特别是对于那些给用户提供点击地图以定义标记位置的能力的人。
通过使用Google Elevation API,我设法得到了相当接近。 以下是结果的图片:
你可以看到六边形几乎停留在陆地上,即使一个长方形的边界被定义为部分覆盖水面。 在这种情况下,我做了一个谷歌地图本身的快速检查,最低海拔高度约8-9米,这是我的门槛。 代码主要是从谷歌文档和堆栈溢出复制/粘贴,这里是完整的要点:
有一个免费的Web API,可以解决这个问题,叫做onwater.io 。 它不是内置于Google地图中的东西,但是经过一定的纬度和经度,它会通过获取请求准确地返回真或假。
水上示例: https : //api.onwater.io/23.92323,-66.3
{ lat: 23.92323, lon: -66.3, water: true }
土地示例: https : //api.onwater.io/42.35,-71.1
{ lat: 42.35, lon: -71.1, water: false }
充分披露我在Dockwater公司工作,这是落后的公司。 我们自己动手解决这个问题,帮助社区。 它总是免费使用,我们想分享:)
不幸的是,这个答案不在谷歌地图API中,被引用的资源不是免费的,但是有一个由DynamicGeometry提供的Web服务,它公开了一个接受纬度/经度对的操作GetWaterOrLand
( 你可以在这里看到一个演示 )。
我对这是如何实现的理解是通过使用水体形状文件。 Google Maps API如何使用这些形状文件,但是您可能能够从链接的演示中获得一些洞察。
希望有一些帮助。
这是纯JavaScript中的另一个例子: http : //jsfiddle.net/eUwMf/
正如你所看到的,ideia与rebe100x基本相同,从Google静态地图API中获取图像,并读取第一个像素:
$("#xGps, #yGps").change(function() { var img = document.getElementById('mapImg'); // Bypass the security issue : drawing a canvas from an external URL. img.crossOrigin='anonymous'; var xGps = $("#xGps").val(); var yGps = $("#yGps").val(); var mapUrl = "http://maps.googleapis.com/maps/api/staticmap?center=" + xGps + "," + yGps + "&zoom=14&size=20x20&maptype=roadmap&sensor=false"; // mapUrl += "&key=" + key; $(img).attr("src", mapUrl); var canvas = $('<canvas/>')[0]; canvas.width = img.width; canvas.height = img.height; canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height); var pixelData = canvas.getContext('2d').getImageData(1, 1, 1, 1).data; if (pixelData[0] == 164 && pixelData[1] == 190 && pixelData[2] == 220) { $("#result").html("Water"); } else { $("#result").html("Not water"); } });
看到我给类似的问题的答案 – 它使用Earth Api中的“HIT_TEST_TERRAIN”来实现这个function。
有一个我在这里放在一起的想法的工作示例: http : //www.msa.mmu.ac.uk/~fraser/ge/coord/
我会build议在这里滚动你自己。 您可以使用像GDAL这样的工具来查询shapefile中某个点下的内容。 你可以从美国人口普查局那里得到美国地理的形状文件。
这可以通过GDAL二进制文件,源代码C,或者通过Java,Python等的swig完成。
人口普查地图
GDAL信息
Python中的点查询示例
这是一个简单的解决scheme
由于Google不提供关于海洋或内陆水体坐标的可靠结果,因此您需要使用另一个备份服务(如Yandex)来帮助在缺失关键信息时提供这些信息。 您很可能不希望将Yandex作为您的主要地理编码器,因为Google在世界数据的可靠性和完整性方面远优于Yandex,但是Yandex对于检索数据的目的非常有用,所以同时使用。
Yandex文档: https : //api.yandex.com.tr/maps/doc/geocoder/desc/concepts/input_params.xml
检索海洋名称的步骤:
1.)首先使用Google来反转地理编码的坐标。
2.)如果Google返回零结果,坐标位于海洋上的可能性为99%。 现在使用相同的坐标向Yandex进行二级反向地理编码请求。 Yandex将返回一个JSON响应与确切的坐标,在这个响应内将是两个“关键”:“值”对的重要性
["GeoObject"]["metaDataProperty"]["GeocoderMetaData"]["kind"] and ["GeoObject"]["name"]
检查亲切的关键,如果它==“水电”,你知道你是一个水体,因为谷歌返回零结果99.99%可能这水体是一个海洋。 海洋的名字将是上面的“名字”键。
这里是我如何使用这个用Ruby编写的策略的例子
if result.data["GeoObject"]["metaDataProperty"]["GeocoderMetaData"]["kind"] == "hydro" ocean = result.data["GeoObject"]["name"] end
取回内陆水域名称的步骤:
对于这个例子,假设我们的坐标位于某处的湖泊:
1.)首先使用Google来反转地理编码的坐标。
2.)谷歌很可能会返回一个在附近的土地上突出的默认地址的结果。 在这个结果中,它提供了它返回的地址的坐标,这个坐标和你提供的不一致。 测量您提供的坐标与返回的坐标之间的距离,如果显着不同(例如100码),则使用Yandex执行辅助备份请求,并检查“kind”键的值是“水电”,那么你知道坐标位于水面上。 因为谷歌返回的结果与上面的例子相反,99.99%可能是内陆水域,所以现在你可以得到这个名字。 如果“kind”不是==“hydro”,那么使用Google geocoded对象。
["GeoObject"]["metaDataProperty"]["GeocoderMetaData"]["kind"] and ["GeoObject"]["name"]
这里是用Ruby编写的相同代码来获取inland_body_of_water
if result.data["GeoObject"]["metaDataProperty"]["GeocoderMetaData"]["kind"] == "hydro" inland_body_of_water = result.data["GeoObject"]["name"] end
关于授权的说明:据我所知,Google不允许您使用他们的数据在除Google以外的任何其他地图上显示。 然而,Yandex的授权非常灵活,您可以使用他们的数据在Google地图上显示。
此外,Yandex的免费费率高达每天50,000个请求,且不需要API密钥。
如果List<Address>
地址返回0,则可以将此位置假设为海洋或自然资源。只需在Google Places API响应的响应方法中添加下面的代码即可。
如上所述初始化下面的列表
List<Address> addresses = geocoder.getFromLocation(latLng.latitude, latLng.longitude, 1);
if (addresses.size()==0) { Toast.MakeText(getApplicationContext,"Ocean or Natural Resources selected",Toast.LENGTH_SHORT).show(); }else{ }
我在这里有不同的解决scheme。 在目前的谷歌地图的实施,它不计算方向/从水位到陆地位置的距离,反之亦然。 为什么我们不使用这个逻辑来确定这个点是土地还是水?
例如,让我们来看看这个例子
如果我们想确定,如果一个点是土地或水,那么
让我们检查点x
和已知点y
之间的方向,即土地。 如果确定了方向/距离,那么点x
就是土地,否则就是水。