Verify if a point is Land or Water in Google Maps

然后谷歌地图“把水和水分开”

嗯,不是圣经意义上的,但是. 。

我想知道我有什么选择,以验证是否一点[拉特,伦]是土地或水。

谷歌地图显然有这些数据(水体是蓝色的)-但是在 API 中有什么我可以使用的东西吗?如果没有,难道他们没有服务,因为他们从来没有想过吗?还是因为它太复杂了?

我没有找到任何关于这个问题的信息-除了一些类似的问题在这里(如寻找类型的地形,或海拔-但它不是我真正需要的)。

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

我能想到的唯一方法是如何处理这个问题(如果我需要手动处理的话) ,就是检查每一个服务瓦片的确切位置,然后检查谷歌地图色调的 RGB 值。 This is only on theory - because in practice - I have no idea how to accomplish that, the first obstacle being that I do not know how I can convert a pixel location on a tile to [LatLon] point for example

现成的解决方案会容易得多。

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

编辑

阅读评论后: 高程计算方法不可靠,海平面以下的地方太多了(你可以在这里看到“最深”10个地方的列表 http://geology.com/below-sea-level/) ,海平面以上的内陆水体(湖泊)太多了。 反向地理定位方法是不可靠的,因为它会多次返回一个地理政治实体,如城市或国家——或零。 在问这个问题之前,我已经研究过这些假的解决方案,但是没有一个真正回答了这个问题,这些方法充其量只是糟糕的“猜测”。

48938 次浏览

These are 2 different ways, you may try:

  • 你可以使用 谷歌地图反座标化。在结果集中,您可以通过检查 types来确定它是否是水。在水域的情况下,类型是 natural_feature。在这个链接看到更多的 http://code.google.com/apis/maps/documentation/geocoding/#Types

    此外,你还需要检查的特征名称,如果他们包含 Sea, Lake, Ocean和一些其他字相关的水更准确。例如,沙漠也是 natural_feature

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

    缺点 -非常不准确,你在水域得到“无”的几率非常高。

  • 通过使用 Google Static Maps,您可以通过像素来检测水域/陆地。

    以下是您的服务必须执行的步骤:

    1. 从客户端接收 latitudelongitudecurrent zoom
    2. 向 Google 静态地图服务发送 http://maps.googleapis.com/maps/api/staticmap?center={纬度 ,longitude}&zoom={当前缩放‘} & size = 1x1 & maptype = road Map & Sensor = false request。
    3. 检测1x1静态图像的像素颜色。
    4. 回复有关检测的信息。

    在客户端无法检测像素的颜色。是的,您可以在客户端的机器上加载静态图像,并在 canvas元素上绘制图像。但是不能使用画布上下文的 getImageData来获得像素的颜色。这受到跨域策略的限制。

    Prons - Highly accurate detection

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

除了反座标化之外——正如 莫尔医生所指出的,它可能返回零结果——你还可以使用扬升服务。如果你的反座标化没有任何结果,那么就得到位置的海拔高度。一般来说,由于海床低于海平面,海洋得到的数字是负数。有一个全功能的 例子升高服务。

请记住,由于谷歌不提供这些信息,任何其他方法都只是猜测,而且猜测本身就是不准确的。然而,使用反座标化返回的 type,或者如果没有 type,则使用海拔高度,将覆盖大多数可能发生的情况。

这种方法是完全不可靠的。 In fact, the returned data will totally depend on what part of the world you are working with. 例如,我在法国工作。 如果我点击法国海岸的海域,谷歌将返回它能“猜测”到的最近的陆地位置。 当我为同样的问题向谷歌请求信息时,他们回答说,他们无法准确地返回在水团上请求的点。

我知道这不是个令人满意的答案。 这是相当令人沮丧的,尤其是对于我们这些为用户提供了点击地图来定义标记位置的能力的人来说。

遗憾的是,这个答案不在 Google Maps API 中,所引用的资源也不是免费的,但 DynamicGeology 提供了一个 Web 服务,它公开了一个接受纬度/经度对(你可以在这里看到一个演示)的操作 GetWaterOrLand

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

希望能有所帮助。

This what I use and it is working not too bad... you can improve the test if you have more cpu to waste by adding pixels.

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;
}

这似乎不可能与任何现有的谷歌服务。

但是还有其他服务,比如 协调向量 JSON 查询服务! 您只需查询 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

您必须注册和提供您的关键和选定的层数。你可以搜索他们所有的 可用层储存库。大多数图层只是区域性的,但是你也可以找到全球性的,比如 世界海岸线:

enter image description here

当您选择一个图层,您点击“服务”选项卡,您得到的示例请求 URL。我相信你只需要注册,就是这样!

现在最棒的是:

你可以上传你的图层!

它不是马上可用,他们必须处理它以某种方式,但它应该工作!层存储库实际上看起来像是人们根据需要上传的。

我建议你在这里自己卷。您可以使用诸如 GDAL之类的工具来查询 shapefile 中某个点下的内容。您可以从包括 美国人口普查局在内的许多来源获得美国地理的 shapefiles。

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

人口普查地图

GDAL 信息

Python 中的点查询示例

下面是另一个纯 JavaScript 的例子: http://jsfiddle.net/eUwMf/

正如你所看到的,这个想法基本上和 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”来实现这个功能。

这里有一个关于这个想法的实例: http://www.msa.mmu.ac.uk/~fraser/ge/coord/

我有一个不同的解决方案。 在目前的谷歌地图实施,它没有计算方向/距离从水位置到土地位置,反之亦然。为什么我们不用这个逻辑来判断问题的关键是陆地还是水。

例如,让我们举这个例子

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

让我们检查点 x和一个已知点 y之间的方向,那就是陆地。如果它决定了方向/距离,那么点 x是陆地,或者它是水。

如果其他方法都失败了,你可以试着检查一下这个点的海拔高度和一段距离——除了水以外,没有多少东西是完全平坦的。

看看这个 文章。它不需要服务器就能准确地检测水面上是否有东西。这是一种依赖于谷歌地图自定义样式特性的黑客技术。

我认为在本地执行这个查询更有趣,因此我可以更加自力更生: 假设我想一次生成25000个随机的地面坐标,我宁愿避免调用可能代价高昂的外部 API。下面是我在 python 中使用 TomSchober 提到的 Python 示例的尝试。基本上,它在包含所有陆地坐标的 预制的350 MB 文件上查找坐标,如果这些坐标存在,它就会打印出来。

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)

我试了十几个坐标,效果非常好。土地多边形。可以下载“ shp”文件 给你,这是 OpenStreetMaps 的附加功能。(我自己使用了第一个 WGS84下载链接,也许第二个也可以)

这里有一个简单的解决方案

由于谷歌没有提供关于海洋或内陆水体坐标的可靠结果,你需要使用另一个备份服务,如 Yandex,以便在这些关键信息丢失时提供帮助。你很可能不想使用 Yandex 作为你的主要地理编码器,因为谷歌在世界数据的可靠性和完整性方面要优越得多,但是当 Yandex 与水体的坐标有关时,它对于检索数据非常有用,所以两者都要使用。


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


检索 Ocean 名称的步骤:

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

2)如果 Google 没有返回任何结果,那么99% 的可能性是坐标位于海洋之上。现在提出第二个反座标化请求坐标与 Yandex 相同。Yandex 将返回一个带有确切坐标的 JSON 响应,在这个响应中有两个“键”: 重要性的“值”对

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


and


["GeoObject"]["name"]

检查 kind 键,如果它 = = “ hydro”你知道你正在一片水域上方,因为谷歌没有返回任何结果,所以99.99% 的可能性这片水域是一片海洋。海洋的名称将是上面的“名称”键。

下面是我如何使用 Ruby 编写的这个策略的一个例子

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

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

这个例子假设我们的坐标在某个湖里:

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

2)谷歌最有可能返回的结果是附近土地上一个显著的默认地址。在这个结果中,它提供了它返回的地址的坐标,这个坐标将与您提供的坐标不匹配。测量你提供的坐标和返回结果的坐标之间的距离,如果有显著差异(例如100码) ,然后用 Yandex 执行二次备份请求,检查“ kind”键的值,如果是“ hydro”,那么你就知道坐标位于水上。因为谷歌返回的结果与上面的例子相反,99.99% 的可能性是内陆水体,所以现在你可以得到这个名字。如果“ kind”不 = = “ hydro”,那么使用 Google 地理编码对象。

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


and


["GeoObject"]["name"]

下面是用 Ruby 编写的获取内陆水体的相同代码

if result.data["GeoObject"]["metaDataProperty"]["GeocoderMetaData"]["kind"] == "hydro"
inland_body_of_water = result.data["GeoObject"]["name"]
end

关于授权的注意事项: 据我所知,谷歌不允许你使用他们的数据在谷歌提供的地图之外的任何其他地图上显示。不过 Yandex 有非常灵活的许可,你可以使用他们的数据在谷歌地图上显示。

Yandex 还有一个免费的高速率限制,每天有50,000个请求,并且没有必要的 API 密钥。

通过使用谷歌高地 API,我已经很接近了。下面是结果的图片:

screenshot of results

你可以看到六边形几乎停留在陆地上,即使一个长方形的边界被定义为部分越过水面。在这种情况下,我从谷歌地图本身做了一个快速检查,陆地上的最低海拔大约是8-9米,所以这是我的门槛。代码大部分是从 Google 文档和 Stack Overflow 中复制/粘贴过来的,以下是完整的要点:

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

如果 List<Address>地址返回0,您可以假定这个位置为海洋或自然资源。只需在 Google Places API 响应的响应方法中添加以下代码即可。

如上所述,初始化以下列表

List<Address> addresses = geocoder.getFromLocation(latLng.latitude, latLng.longitude, 1);

If (addresses.size () = = 0) { (getApplicationContext,“选择海洋或自然资源”,Toast.LENGTH _ SHORT) . show () ; { }

有一个免费的 web API 可以精确地解决这个叫做 在水上的问题。它不是内置在谷歌地图中的东西,但给定一个经纬度,它会通过 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工作,公司背后的水。我们依靠水来解决这个问题,并帮助社区。它是免费使用的(大容量付费) ,我们想分享:)

作为 Python 的新手,我无法使用 Python 脚本检查 SylvainB 的解决方案是否在陆地上。然而,通过下载 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 中工作,但我有一个恶梦,试图得到所有的软件包,你需要安装所以坚持 Python。

下面是一个类型化的异步函数,它返回 true 或 false,如果 lat/lng 是水或不是水。无需支付外部应用程序接口的费用。你必须启用谷歌云静态地图。

async function isLatLngWater(lat: number, lng: number) {
return new Promise<boolean>((resolve) => {
const img = new Image();
img.crossOrigin = "Anonymous";
img.onload = () => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");


ctx!.drawImage(img, 0, 0);


const { data } = ctx!.getImageData(10, 10, 1, 1);


if (data[0] == 156 && data[1] == 192 && data[2] == 249) {
canvas.remove();
resolve(true);
} else {
canvas.remove();
resolve(false);
}
};


img.src =
"https://maps.googleapis.com/maps/api/staticmap?center=" +
lat +
"," +
lng +
"&size=40x40&maptype=roadmap&sensor=false&zoom=20&key=" +
import.meta.env.VITE_GM_MSTAT;
});
}

有一个名为 IsItWater.com的 API 服务可以让你检查:

请求

curl 'https://isitwater-com.p.rapidapi.com/?latitude=41.9029192&longitude=-70.2652276&rapidapi-key=YOUR-X-RAPIDAPI-KEY'

回应

{
"water": true,
"latitude": 41.9029192,
"longitude": -70.2652276
}