验证Google地图中的某个点是Land还是Water

时间:2012-03-10 06:33:18

标签: javascript google-maps google-maps-api-3

..然后谷歌地图“划分水域的水”

嗯,不是出于圣经的意义,而是......

我想知道我有什么选择来验证[Lat,Lon]的点是土地还是水。

谷歌地图显然有这些数据(水体是蓝色的) - 但是我可以使用API​​中的某些东西吗?如果没有 - 他们是不是因为他们没有想到它而服务它?还是因为它太复杂了?

我没有找到任何有关此事的信息 - 除了这里有一些类似的问题(比如寻找地形类型或高程 - 但这不是我需要的)。

那有分离的层吗?一个选项?命令?或者我应该手动去做?

我能想到如何处理这个问题的唯一方法(我是否需要手动执行)是检查每个服务的磁贴的确切位置 - 然后检查该Google地图色调的RGB值。 这只是在理论上 - 因为在实践中 - 我不知道如何实现这一点,第一个障碍是我不知道如何将瓷砖上的像素位置转换为[LatLon]点,例如

现成的解决方案会更容易。

请注意,我不需要世界上所有的水(例如 - 我不关心溪流,小池塘,大多数河流或邻居的游泳池。我需要一个人可以在没有帮助的情况下冒险的地方浮动车辆)

编辑我

阅读评论后: 海拔方法不可靠,海平面以下有太多地方(你可以在这里看到“最深”10的清单http://geology.com/below-sea-level/)并且海平面以上有太多内陆水体(湖泊) )。 反向地理定位方法不可靠,因为它会多次返回地理政治实体,如城市或州 - 或ZERO。 我在问这个问题之前已经研究过那些伪解决方案 - 但是他们都没有回答过这个问题 - 这些方法充其量只是“猜测”。

18 个答案:

答案 0 :(得分:54)

您可以尝试以下两种方式:

  • 您可以使用Google Maps Reverse Geocoding。在结果集中,您可以通过选中types来确定它是否为水。在水域案例中,类型为natural_feature。请点击此链接http://code.google.com/apis/maps/documentation/geocoding/#Types了解更多信息。

    此外,您还需要检查要素的名称,如果它们包含Sea, Lake, Ocean以及其他与水相关的单词以获得更高的准确性。例如,沙漠也是natural_feature s。

    Prons - 所有检测过程都将在客户端的机器上完成。无需创建自己的服务器端服务。

    缺点 - 非常不准确,你在水域获得“无”的可能性非常高。

  • 您可以使用Google Static Maps按像素检测水域/土地。但为此您需要创建http服务。

    这些是您的服务必须执行的步骤:

    1. 从客户端接收latitudelongitudecurrent zoom
    2. 向Google静态地图服务发送http://maps.googleapis.com/maps/api/staticmap?center={纬度,经度}&zoom={当前缩放`}& size = 1x1& maptype = roadmap& sensor = false请求。
    3. 检测1x1静态图像的像素颜色。
    4. 回复有关检测的信息。
    5. 您无法在客户端检测像素的颜色。是的,您可以在客户端的计算机上加载静态图像并在canvas元素上绘制图像。但是你无法使用画布上下文的getImageData来获取像素的颜色。这受跨域策略的限制。

      Prons - 高度准确的检测

      缺点 - 使用自己的服务器资源进行检测

答案 1 :(得分:19)

目前任何Google服务似乎都无法实现。

但还有其他服务,例如 Koordinates Vector JSON Query service !您只需查询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

您必须注册并提供您的密钥和所选的图层编号。您可以搜索所有repository of available layers。大多数图层只是区域性的,但您也可以找到全局图,例如World Coastline

enter image description here

选择图层后,单击“服务”选项卡,即可获得示例请求URL。我相信你只需要注册即可!

现在最好的:

You can upload your layer!

它不能立即使用,嘿必须以某种方式处理它,但它应该工作!层存储库实际上看起来像人们根据需要上传它们。

答案 2 :(得分:8)

这是我使用的,它的工作并不太糟糕......如果你有更多的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;
}

答案 3 :(得分:7)

我觉得在本地进行这个查询更有意思,所以我可以更加自立:假设我想一次生成25000个随机陆地坐标,我宁愿避免调用可能代价高昂的外部API。这是我在python中使用TomSchober提到的python example的镜头。基本上它会查找包含所有陆地坐标的pre-made 350MB file上的坐标,如果坐标存在于那里,则打印它们。

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”文件here,这是对OpenStreetMaps的赞美。 (我自己使用了第一个WGS84下载链接,也许第二个也可以使用)

答案 4 :(得分:7)

查看这篇文章。它可以准确地检测水中是否有东西,而无需服务器。这是一个依赖谷歌地图中自定义样式功能的黑客。

http://tech.bellycard.com/blog/where-d-the-water-go-google-maps-water-pixel-detection-with-canvas/

答案 5 :(得分:6)

有一个免费的网络API可以解决这个名为onwater.io的问题。它不是内置于Google地图中的内容,但考虑到纬度和经度,它将通过get请求准确地返回true或false。

关于水的例子: https://api.onwater.io/api/v1/results/23.92323,-66.3

{
  lat: 23.92323,
  lon: -66.3,
  water: true
}

陆地上的例子: https://api.onwater.io/api/v1/results/42.35,-71.1

{
  lat: 42.35,
  lon: -71.1,
  water: false
}

完全披露我在Dockwa.com工作,这是一家名为onwater的公司。我们建立onwater以自己解决这个问题并帮助社区。它是免费使用(支付高额费用),我们想分享:)

答案 6 :(得分:5)

除了反向地理编码 - 正如Dr Molle指出的那样,它可能会返回ZERO_RESULTS - 您可以使用Elevation服务。如果通过反向地理编码获得零结果,请获取该位置的高程。由于海床低于海平面,海洋通常为负数。在http://www.daftlogic.com/sandbox-google-maps-find-altitude.htm处有一个完整的高程服务示例。

请记住,由于谷歌不提供此信息,任何其他方法只是猜测而猜测本质上是不准确的。但是,如果使用反向地理编码返回的type,或者type不可用,则会覆盖大多数可能性。

答案 7 :(得分:3)

如果所有其他方法都失败了,你总是可以尝试检查该点的高度和一些距离 - 除水以外的其他东西往往是完全平坦的。

答案 8 :(得分:3)

这种方法完全不可靠。 实际上,返回的数据完全取决于您正在使用的世界的哪个部分。 例如,我在法国工作。 如果我点击法国海岸的大海,谷歌将返回它可以“猜测”的最近的LAND位置。 当我向Google请求同一问题的信息时,他们回答说他们无法准确地返回水团所要求的点数。

我知道,这不是一个非常令人满意的答案。 这非常令人沮丧,特别是对于那些为用户提供点击地图以定义标记位置的人。

答案 9 :(得分:2)

以下是纯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");
    }
});

答案 10 :(得分:2)

See the answer I gave to a similar question - 它使用Earth Api中的“HIT_TEST_TERRAIN”来实现该功能。

我在这里提出了一个实例:http://www.msa.mmu.ac.uk/~fraser/ge/coord/

答案 11 :(得分:2)

我建议你自己滚动一下。您可以使用GDAL之类的工具来查询shapefile中某个点下的内容。您可以从许多来源获得美国地理的shapefile,包括US Census Bureau

这可以通过GDAL二进制文件,源代码C或Java,Python等swig来完成。

Census Maps

GDAL Information

Point Query Example in Python

答案 12 :(得分:2)

不幸的是,这个答案不在Google Maps API中,并且引用的资源不是免费的,但是DynamicGeometry提供的Web服务公开了一个接受纬度/经度对的操作GetWaterOrLand({{3 }})。

我对如何实现这一点的理解是使用水体形状文件。这些形状文件与Google Maps API的关系如何,但您可以从链接的演示中获得一些见解。

希望在某种程度上有所帮助。

答案 13 :(得分:2)

我设法通过使用Google Elevation API非常接近。这是结果的图像:

screenshot of results

你看到六边形几乎停留在陆地上,即使一个矩形周边定义为部分在水面上。在这种情况下,我从谷歌地图本身进行了快速检查,陆地上的最小高度约为8-9米,这是我的门槛。代码主要是从Google文档和Stack Overflow中复制/粘贴,这里有完整的要点:

https://gist.github.com/dvas0004/fd541a0502528ebfb825

答案 14 :(得分:1)

这是一个简单的解决方案

由于Google无法提供有关海洋或内陆水域坐标的可靠结果,因此您需要使用其他备用服务(如Yandex)来帮助提供缺失时的重要信息。您很可能不希望使用Yandex作为主要的地理编码器,因为Google在世界数据的可靠性和完整性方面要优越得多,但是Yandex在与水体坐标相关时检索数据非常有用,所以两者兼用。

Yandex文档:https://api.yandex.com.tr/maps/doc/geocoder/desc/concepts/input_params.xml

检索海洋名称的步骤:

1。)首先使用Google来反转地理编码坐标。

2。)如果Google返回零结果,则坐标位于海洋上的可能性为99%。现在使用与Yandex相同的坐标进行二次反向地理编码请求。 Yandex将返回一个精确坐标的JSON响应,在此响应中将是两个&#34; key&#34;:&#34; value&#34;对重要性

["GeoObject"]["metaDataProperty"]["GeocoderMetaData"]["kind"]

and

["GeoObject"]["name"]

检查种类密钥,如果它==&#34; hydro&#34;你知道你已经超过了一块水,而且由于谷歌的结果为零,因此这个水体可能是海洋的99.99%。海洋的名称将是以上&#34;名称&#34;键。

以下是我如何使用Ruby编写的策略

的示例
if result.data["GeoObject"]["metaDataProperty"]["GeocoderMetaData"]["kind"] == "hydro"
     ocean = result.data["GeoObject"]["name"] 
end

检索内陆水体名称的步骤:

对于这个例子,假设我们的坐标位于一个湖泊的某个地方:

1。)首先使用Google来反转地理编码坐标。

2。)Google很可能会返回附近土地上显着默认地址的结果。在此结果中,它提供了它返回的地址的坐标,该坐标与您提供的坐标不匹配。测量您提供的坐标与返回的坐标与结果之间的距离,如果它显着不同(例如100码),则使用Yandex执行辅助备份请求并检查&#34; kind&#34的值;关键,如果是&#34; hydro&#34;然后你知道坐标位于水面上。因为谷歌返回了一个结果而不是上面的例子,所以这是一个内陆水域的99.99%,所以现在你可以得到这个名字。如果&#34; kind&#34;没有==&#34; hydro&#34;然后使用Google地理编码对象。

["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

关于许可的说明:据我所知,谷歌不允许您使用他们的数据显示在谷歌提供的任何其他地图上。然而,Yandex拥有非常灵活的许可,您可以使用他们的数据在Google地图上显示。

此外,Yandex的免费使用率高达50,000请求,且无需API密钥。

答案 15 :(得分:1)

如果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{ }

答案 16 :(得分:0)

作为Python的新手,我无法获得SylvainB的解决方案来与检查坐标是否在陆地上的python脚本一起使用。但是,我设法下载了OSGeo4W(https://trac.osgeo.org/osgeo4w/),然后安装了我需要的所有东西pip,Ipython,并检查了所有指定的导入项,从而弄清楚了这一点。我将以下代码另存为.py文件。

用于检查坐标是否在陆地上的代码

###make sure you check these are there and working separately before using the .py file 

import ogr
from IPython import embed
from osgeo import osr
import osgeo

import random
#####generate a 1000 random coordinates
ran1= [random.uniform(-180,180) for x in range(1,1001)]
ran2= [random.uniform(-180,180) for x in range(1,1001)]


drv = ogr.GetDriverByName('ESRI Shapefile') #We will load a shape file
ds_in = drv.Open("D:\Downloads\land-polygons-complete-4326\land-polygons-complete-4326\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=osgeo.osr.SpatialReference()
point_ref.ImportFromEPSG(4326)
ctran=osgeo.osr.CoordinateTransformation(point_ref,geo_ref)
###check if the random coordinates are on land
def check(runs):
    lon=ran1[runs]
    lat=ran2[runs]
    #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:
        return(lon, lat)

###give it a try
result = [check(x) for x in range(1,11)] ###checks first 10 coordinates

我试图让它在R中工作,但是我梦a以求地获取了需要安装的所有软件包,以致于无法使用python。

答案 17 :(得分:-2)

我在这里有一个不同的解决方案。 在当前的谷歌地图实施中,它不计算从水位到陆地位置的方向/距离,反之亦然。为什么我们不使用这个逻辑来确定这个点是土地还是水。

例如,让我们举个例子

如果我们想确定点x是土地还是水,那么

让我们检查点x和已知点y之间的方向。如果确定方向/距离,则点x为陆地,否则为水。