web-dev-qa-db-fra.com

Vérifier si un point est terre ou eau dans Google Maps

..et Google-maps "séparer les eaux des eaux"

Eh bien, pas au sens biblique mais ..

J'aimerais connaître les options dont je dispose pour vérifier si un point de [Lat, Lon] est Terre ou Eau.

Il est évident que Google Maps dispose de ces données (les plans d'eau sont en bleu) - mais y a-t-il quelque chose dans l'API que je peux utiliser pour cela? Et sinon, ne le servent-ils pas parce qu'ils n'y ont jamais pensé? Ou parce que c'est trop compliqué?

Je n'ai trouvé aucune information à ce sujet - à part quelques questions similaires ici (comme le type de terrain ou l'élévation - mais ce n'est pas exactement ce dont j'ai besoin).

Y a-t-il une couche séparée pour ça? Une option? Commander? Ou devrais-je aller faire cela manuellement?

La seule façon de penser à cette approche (si je devais le faire manuellement) est de vérifier le point exact de chaque mosaïque servie - puis de vérifier la valeur RVB de cette teinte de carte Google . théorie - parce que dans la pratique - je ne sais pas comment y parvenir, le premier obstacle étant que je ne sais pas comment convertir un emplacement de pixel d'une tuile en point [LatLon], par exemple 

Une solution toute faite serait beaucoup plus facile.

Notez que je n'ai pas besoin de TOUTES les eaux du monde (par exemple, les ruisseaux, les petits étangs, la plupart des rivières ou la piscine de votre voisin ne m'intéressent pas. J'ai besoin des points où une personne peut s'aventurer sans l'aide d'un véhicule flottant. )

EDIT I ​​

Après avoir lu les commentaires: La méthode d’altitude n’est pas fiable, il y a trop de places AU-DESSOUS du niveau de la mer (vous pouvez voir une liste des "plus profonds" 10 ici http://geology.com/below-sea- niveau/ ) et il y a trop de masses d’eau enclavées AU-DESSUS du niveau de la mer (lacs) . La méthode de géolocalisation inversée n’est pas fiable car elle renverrait une entité géopolitique, comme une ville ou un État - ou ZÉRO plusieurs fois. J'ai déjà examiné ces pseudo-solutions avant de poser la question - mais aucune d’entre elles n’a répondu à la question - ces méthodes sont au mieux des "devinettes".

97
Obmerk Kronen

Ce sont 2 façons différentes, vous pouvez essayer:

  • Vous pouvez utiliser Google Maps Reverse Geocoding . Dans le jeu de résultats, vous pouvez déterminer s'il s'agit d'eau en cochant types. Dans les eaux, le type est natural_feature. Plus d'informations sur ce lien http://code.google.com/apis/maps/documentation/geocoding/#Types .

    Aussi, vous devez vérifier les noms des entités, si elles contiennent Sea, Lake, Ocean et quelques autres mots liés aux eaux pour plus de précision. Par exemple, les déserts sont aussi natural_features.

    Prons - Tout le processus de détection sera effectué sur la machine du client. Il n'est pas nécessaire de créer son propre service côté serveur.

    Inconvénients - Très imprécis et les chances que vous obteniez "aucun" aux eaux sont très élevées.

  • Vous pouvez détecter les eaux/terres en pixels, en utilisant Google Static Maps . Mais pour cela, vous devez créer un service http. 

    Voici les étapes à suivre par votre service:

    1. Recevez latitude, longitude et current zoom du client.
    2. Envoyez http://maps.googleapis.com/maps/api/staticmap?center={latitude,longitude}&zoom={current zoom`} & size = 1x1 & maptype = roadmap & sensor = false à la demande du service Google Static Map.
    3. Détecte la couleur de pixel de l'image statique 1x1.
    4. Répondez à une information sur la détection.

    Vous ne pouvez pas détecter la couleur du pixel côté client. Oui, vous pouvez charger une image statique sur la machine du client et dessiner une image sur l'élément canvas. Mais vous ne pouvez pas utiliser getImageData du contexte de la toile pour obtenir la couleur de pixel. Ceci est limité par la politique interdomaine. 

    Prons - Détection extrêmement précise

    Cons - Utilisation de ressources du serveur propres pour la détection

54
Engineer

C’est ce que j’utilise et cela ne fonctionne pas trop mal ... vous pouvez améliorer le test si vous avez plus de CPU à perdre en ajoutant des 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;
}
8
rebe100x

Commander cet article. Il détecte avec précision si quelque chose est sur l'eau sans avoir besoin d'un serveur. C'est un hack qui s'appuie sur la fonctionnalité de style personnalisé de Google Maps.

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

7
Saurav

Je pensais que c'était plus intéressant de faire cette requête localement, donc je peux être plus autonome: disons que je veux générer 25000 coordonnées de terrain aléatoires à la fois, je préférerais éviter les appels à des API externes potentiellement coûteuses. Voici mon exemple en python, en utilisant l'exemple python mentionné par TomSchober. Fondamentalement, il recherche les coordonnées dans un fichier pré-créé de 350 Mo contenant toutes les coordonnées de terrain. Si ces coordonnées existent, il les affiche.

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)

J'ai essayé une dizaine de coordonnées, ça marche à merveille. Le fichier "land_polygons.shp" peut être téléchargé ici , avec les compliments d'OpenStreetMaps. (J'ai utilisé le premier lien de téléchargement WGS84 moi-même, le deuxième fonctionne également)

7
SylvainB

Il existe une API Web gratuite qui résout exactement ce problème appelé onwater.io . Ce n'est pas quelque chose de construit dans Google Maps, mais étant donné la latitude et la longitude, il retournera avec précision vrai ou faux via une requête get.

Exemple sur l’eau: https://api.onwater.io/api/v1/results/23.92323,-66.3

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

Exemple sur terre: https://api.onwater.io/api/v1/results/42.35,-71.1

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

Divulgation complète Je travaille à Dockwa.com , la société derrière onwater. Nous avons construit sur l'eau pour résoudre ce problème nous-mêmes et aider la communauté. Il est gratuit à utiliser (payé pour un volume élevé) et nous avons voulu partager :)

6
stuyam

En plus du géocodage inverse - comme l'a souligné le Dr Molle , il peut renvoyer ZERO_RESULTS - vous pouvez utiliser le service Elevation. Si vous n'obtenez aucun résultat en géocodage inversé, obtenez l'altitude de l'emplacement. Généralement, la mer reçoit un nombre négatif car le fond marin est en dessous du niveau de la mer. Vous trouverez un exemple complet du service d'élévation à l'adresse http://www.daftlogic.com/sandbox-google-maps-find-altitude.htm .

N'oubliez pas que, dans la mesure où Google ne met pas ces informations à disposition, aucune autre méthode n'est approximative et les suppositions sont intrinsèquement inexactes. Cependant, l'utilisation de type renvoyé par géocodage inverse, ou de l'élévation si type n'est pas disponible, couvrira la plupart des éventualités.

5
Andrew Leach

Si tout le reste échoue, vous pouvez toujours essayer de vérifier l'élévation au point et sur une certaine distance - pas beaucoup de choses autres que l'eau ont tendance à être complètement plat.

3
Steve Barnes

Cette méthode est totalement peu fiable . En fait, les données renvoyées dépendront totalement de la partie du monde avec laquelle vous travaillez . Par exemple, je travaille en France . Si je clique sur la mer sur la côte française, Google renverra l'emplacement de terrain le plus proche, il peut "deviner" à . Lorsque j'ai demandé des informations à Google pour cette même question, ils ont répondu qu'ils ne pouvaient pas renvoyer avec précision masse d'eau.

Ce n’est pas une réponse très satisfaisante, je sais… .. C’est assez frustrant, en particulier pour ceux qui offrent à l’utilisateur la possibilité de cliquer sur la carte pour définir une position de marqueur.

3
Simon

Voir la réponse que j'ai donnée à une question similaire - il utilise "HIT_TEST_TERRAIN" de l'API Terre pour réaliser la fonction. 

Il existe un exemple concret de l’idée que j’ai développée ici: http://www.msa.mmu.ac.uk/~fraser/ge/coord/

2
Fraser

Malheureusement, cette réponse ne figure pas dans l'API Google Maps et la ressource référencée n'est pas gratuite, mais il existe un service Web fourni par DynamicGeometry qui expose une opération GetWaterOrLand qui accepte une paire latitude/longitude ( vous pouvez voir une démonstration ici ).

Je comprends comment cela est mis en œuvre en utilisant des fichiers de forme de corps d’eau. Comment ces fichiers de formes sont-ils utilisés avec l'API Google Maps, mais vous pourrez peut-être obtenir des informations à partir de la démo liée.

J'espère que cela contribue de quelque façon.

2
sellmeadog

J'ai réussi à me rapprocher en utilisant l'API Google Elevation. Voici une image des résultats:

screenshot of results

Vous voyez que les hexagones restent à peu près à terre, même si un périmètre rectangulaire est défini, qui passe en partie sur l'eau. Dans ce cas, j'ai effectué une vérification rapide à partir de Google Maps et l'altitude minimale sur terre était d'environ 8 à 9 m, ce qui était mon seuil. Le code est principalement copié/collé à partir de la documentation Google et de Stack Overflow, voici l'intégralité de Gist:

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

2
dvas0004

Je recommanderais de rouler les vôtres ici. Vous pouvez utiliser des outils tels que GDAL pour interroger le contenu sous un point d'un fichier de formes. Vous pouvez obtenir des fichiers de formes pour la géographie des États-Unis auprès de nombreuses sources, notamment le US Census Bureau .

Cela peut être fait via les fichiers binaires GDAL, le C source, ou via swig en Java, Python, etc.

Cartes de recensement

Informations GDAL

Exemple de requête de point en Python

2
TomSchober

Voici un autre exemple en JavaScript pur: http://jsfiddle.net/eUwMf/

Comme vous pouvez le constater, l'idée est identique à rebe100x: obtenir l'image de l'API de carte statique Google et lire le premier pixel:

$("#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");
    }
});
2
IcanDivideBy0

Voici une solution simple

Étant donné que Google ne fournit pas de résultats fiables en ce qui concerne les coordonnées situées sur des étendues d'eau océaniques ou intérieures, vous devez utiliser un autre service de sauvegarde, tel que Yandex, pour vous aider à fournir ces informations critiques en cas d'absence. Il est fort probable que vous ne voudriez pas utiliser Yandex comme géocodeur principal, car Google est de loin plus fiable et plus complet que les données du monde. Cependant, Yandex peut être très utile pour récupérer des données s’il s’agit de coordonnées sur des plans alors utilisez les deux.


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


Les étapes pour récupérer le nom de l'océan:

1.) Utilisez d'abord Google pour inverser le géocodage de la coordonnée.

2.) Si Google ne renvoie aucun résultat, il est probable que les coordonnées se situent sur un océan à 99%. Faites maintenant une demande de géocodage inversé secondaire avec les mêmes coordonnées en Yandex. Yandex renverra une réponse JSON avec les coordonnées exactes. Dans cette réponse, il y aura deux paires "clé": "valeur".

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

and

["GeoObject"]["name"]

Vérifiez la clé, si elle == "hydro", vous savez que vous êtes au-dessus d’un plan d’eau, et comme Google n’a renvoyé aucun résultat, il est probable à 99,99% que ce plan d’eau est un océan. Le nom de l'océan sera la clé "nom" ci-dessus.

Voici un exemple d'utilisation de cette stratégie en Ruby

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

Les étapes pour récupérer un nom de masse d'eau intérieure:

Pour cet exemple, supposons que nos coordonnées se trouvent dans un lac quelque part:

1.) Utilisez d'abord Google pour inverser le géocodage de la coordonnée. 

2.) Google renverra très probablement un résultat correspondant à une adresse par défaut bien visible sur un terrain à proximité. Dans ce résultat, il fournit les coordonnées de l'adresse renvoyée. Ces coordonnées ne correspondent pas à celles que vous avez fournies. Mesurez la distance entre la coordonnée que vous avez fournie et celle renvoyée avec le résultat, si elle est sensiblement différente (par exemple 100 mètres), puis effectuez une demande de sauvegarde secondaire avec Yandex et vérifiez si la valeur de la clé "kind" est vérifiée. est "hydro" alors vous savez que la coordonnée se trouve sur l’eau. Comme Google a renvoyé un résultat contrairement à l'exemple ci-dessus, il est probable à 99,99% qu'il s'agit d'un plan d'eau intérieur. Vous pouvez donc maintenant obtenir le nom. Si "type" ne le fait pas == "hydro", utilisez l'objet géocodé de Google. 

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

and

["GeoObject"]["name"]

Voici le même code écrit en Ruby pour obtenir inland_body_of_water

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

Remarque sur les licences: Google ne vous permet pas, à ma connaissance, d'utiliser leurs données pour les afficher sur des cartes autres que celles proposées par Google. Yandex dispose cependant de licences très souples et vous pouvez utiliser leurs données pour les afficher sur Google Maps. 

De plus, Yandex a une limite de débit élevée de 50 000 requêtes/jour gratuitement et sans clé API requise.

1

En tant que novice complet de Python, je ne pouvais pas utiliser la solution de SylvainB avec le script python qui vérifie si les coordonnées sont sur terre. J'ai toutefois réussi à le comprendre en téléchargeant OSGeo4W ( https://trac.osgeo.org/osgeo4w/ ), puis en installant tout ce dont j'avais besoin pip, Ipython, et en vérifiant que toutes les importations spécifiées étaient présentes. J'ai enregistré le code suivant dans un fichier .py.

Code pour vérifier si les coordonnées sont à terre

###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

J'ai essayé de le faire fonctionner en R mais j'ai eu un cauchemar en essayant d'obtenir tous les paquets que vous devez installer afin de rester bloqués en python.

0
Alec Christie