web-dev-qa-db-fra.com

Coordonnées géographiques aléatoires (sur terre, éviter les océans)

Des idées intelligentes sur la façon de générer des coordonnées aléatoires (latitude/longitude) de lieux sur Terre? Latitude Longitude. Précision à 5 points et éviter les masses d'eau. 

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

Peut-être que je vis dans un monde de rêve et que le sujet de l'eau est inévitable ... mais j'espère qu'il existe un moyen plus agréable, plus propre et plus efficace de le faire?

MODIFIER

Quelques réponses/idées fantastiques - cependant, à l’échelle, disons que j’ai besoin de générer 25 000 coordonnées. Faire appel à un fournisseur de services externe peut ne pas être la meilleure option en raison de la latence, du coût et de quelques autres facteurs. 

29
sdolgy

Traiter le problème de l'étendue de l'eau va être en grande partie une question de données, par exemple. voulez-vous simplement manquer les océans ou devez-vous également manquer de petits ruisseaux. Soit vous devez utiliser un service avec la qualité de données dont vous avez besoin, soit vous devez obtenir les données vous-même et les exécuter localement. À partir de votre édition, il semble que vous souhaitiez utiliser la route de données locale, je vais donc me concentrer sur un moyen de le faire.

Une méthode consiste à obtenir un fichier de formes pour les zones terrestres ou les zones aquatiques. Vous pouvez ensuite générer un point aléatoire et déterminer s'il intersecte une zone terrestre (ou alternativement, ne coupe pas une zone d'eau).

Pour commencer, vous pouvez obtenir des données à basse résolution ici puis des données à plus haute résolution ici lorsque vous souhaitez obtenir de meilleures réponses sur les côtes ou avec des lacs/rivières/etc. Vous avez indiqué que vous souhaitiez une précision dans vos points jusqu'à 5 décimales, ce qui représente un peu plus de 1 m. Sachez que si vous obtenez des données correspondant à cette précision, vous aurez un ensemble de données géant. Et si vous voulez de très bonnes données, préparez-vous à les payer.

Une fois que vous avez vos données de forme, vous avez besoin d’outils pour vous aider à déterminer l’intersection de vos points aléatoires. Geotools est un excellent endroit pour commencer et travaillera probablement pour vos besoins. Vous allez également finir par regarder le code opengis (docs sur le site de geotools - ne savez pas s’ils les ont consommés ou quoi) et JTS pour la gestion de la géométrie. En utilisant cela, vous pouvez ouvrir rapidement le fichier de formes et commencer à faire des requêtes sur les intersections.

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

Explications rapides:

  1. Cela suppose que le fichier de formes que vous avez obtenu est constitué de données surfaciques. Une intersection sur des lignes ou des points ne vous donnera pas ce que vous voulez.
  2. La première section ouvre le fichier de formes - rien d’intéressant
  3. vous devez récupérer le nom de la propriété de géométrie pour le fichier donné
  4. système de coordonnées - vous avez spécifié lat/long dans votre message, mais les SIG peuvent être un peu plus compliqués. En général, les données sur lesquelles je vous ai indiqué sont geographique, wgs84 , et c’est ce que j’ai configuré ici. Cependant, si ce n'est pas le cas pour vous, vous devez vous assurer que vous traitez vos données dans le système de coordonnées correct. Si tout cela ressemble à du charabia, allez sur Google pour un tutoriel sur les SIG/systèmes de coordonnées/datum/ellipsoïde.
  5. générer les géométries de coordonnées et les filtres s’expliquent assez bien. L'ensemble d'entités résultant sera soit vide, ce qui signifie que la coordonnée est dans l'eau si vos données sont en couverture du sol, ou non vide, ce qui signifie le contraire.

Remarque: si vous faites cela avec un ensemble de points vraiment aléatoire, vous allez toucher l'eau assez souvent et cela peut vous prendre un certain temps pour atteindre 25k points. Vous voudrez peut-être essayer de mieux cibler votre génération de points que véritablement aléatoire (comme supprimer de gros morceaux des océans Atlantique/Pacifique/Indien).

En outre, vous pouvez trouver que vos requêtes d'intersection sont trop lentes. Si tel est le cas, vous pouvez envisager de créer un index sur quatre arbres (qix) avec un outil tel que GDAL . Je ne me souviens pas quels types d'index sont pris en charge par les outils géographiques.

14
philwb

Cela a été demandé il y a longtemps et j'ai maintenant le même besoin. Je cherche deux possibilités:

1. Définissez les plages de surface pour le générateur aléatoire.

Ici, il est important d’identifier le niveau de précision recherché. Le plus simple serait d’avoir une approche très détendue et approximative. Dans ce cas, vous pouvez diviser la carte du monde en "cases":

 enter image description here

Chaque boîte a sa propre plage de lat lon. Ensuite, vous commencez par randomiser pour obtenir une boîte aléatoire, puis vous alignez pour obtenir une latitude et une longueur aléatoires dans les limites de cette zone. 

Les précisions ne sont bien sûr pas les meilleures du tout ici ... Bien que cela dépende :) Si vous faites bien vos devoirs et définissez un grand nombre de cases couvrant la plupart des formes de surface complexes, vous serez peut-être satisfait de la précision.

2. Elément de liste

Certaines API retournent le nom du continent à partir des coordonnées OR adresse OR pays OR district = quelque chose que WATER n'a pas. Les API Google Maps peuvent aider ici. Je n'ai pas fait de recherches plus approfondies à ce sujet, mais je pense que c'est possible, bien que vous deviez vérifier chaque paire de coordonnées générées et réexécuter SI c'est faux. Vous pouvez donc rester un peu coincé si un générateur aléatoire continue à vous jeter dans l'océan.

De plus, une partie de l'eau appartient à des pays, des districts ... alors oui, pas très précise. 

Pour mes besoins - je vais avec des "boîtes" parce que je veux aussi contrôler les zones exactes à partir desquelles les coordonnées aléatoires sont prises, et cela ne me dérange pas si elles atterrissent sur un lac ou une rivière, mais pas en pleine mer :)

4
GeekSince1982
  1. Téléchargez un chargement de fichiers KML contenant des emplacements réservés à la terre.
  2. Extrait toutes les coordonnées cela pourrait aider ici .
  3. Choisissez-les au hasard.
4
OldCurmudgeon

Vous devriez certainement avoir une carte comme ressource. Vous pouvez le prendre ici: http://www.naturalearthdata.com/

Ensuite, je préparais une ressource bitmap en noir et blanc de 1 bit avec 1s marquant la terre et 0x marquant l’eau.

La taille du bitmap dépend de la précision requise. Si vous avez besoin de 5 degrés, votre bitmap sera de 360/5 x 180/5 = 72x36 pixels = 2592 bits. 

Ensuite, je charge ce bitmap en Java, génère un nombre entier aléatoire dans la plage ci-dessus, lit le bit et le régénère s'il est à zéro.

P.S. Aussi, vous pouvez creuser ici http://geotools.org/ pour des solutions toutes faites.

3
Suzan Cioc

Il existe une autre façon de procéder à l'aide de l'API Google Earth. Je sais que c'est du javascript, mais je pensais que c'était une nouvelle façon de résoudre le problème.

Quoi qu’il en soit, j’ai mis au point une solution de travail complète ici - remarquez que cela fonctionne aussi pour les rivières: http://www.msa.mmu.ac.uk/~fraser/ge/coord/

L’idée de base que j’ai utilisée est d’implémenter la méthode hiTest de l’objet GEView dans le Google Earth Api

Jetez un coup d'œil à l'exemple suivant du plus gros problème de Google. http://earth-api-samples.googlecode.com/svn/trunk/examples/hittest.html

La méthode hitTest reçoit un point aléatoire sur l'écran en (coordonnées en pixels) pour lequel elle renvoie un objet GEHitTestResult contenant des informations sur l'emplacement géographique correspondant au point. Si on utilise le mode GEPlugin.HIT_TEST_TERRAIN avec la méthode, on peut limiter les résultats uniquement à la terre (terrain) à condition de filtrer les résultats sur des points d'une altitude> 1m.

C’est la fonction que j’utilise qui implémente le 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 };
};

Évidemment, vous souhaitez également obtenir des résultats aléatoires à partir de n’importe où sur le globe (pas seulement des points aléatoires visibles d’un seul point de vue). Pour ce faire, je déplace la vue de la Terre après chaque appel hitTestTerrain réussi. Ceci est réalisé en utilisant une petite fonction d'aide.

var flyTo = function(lat, lng, rng)
{
    lookAt.setLatitude(lat);
    lookAt.setLongitude(lng);
    lookAt.setRange(rng);
    ge.getView().setAbstractView(lookAt);
};

Enfin, voici une version simplifiée du bloc de code principal qui appelle ces deux méthodes.

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

Ainsi, la terre se déplace au hasard vers une position

Les autres fonctions ne sont que des aides pour générer les nombres aléatoires x, y et lat, lng, pour afficher les résultats et pour basculer les commandes, etc.

J'ai testé le code un peu et les résultats ne sont pas parfaits à 100%, en ajustant altitude à quelque chose de plus élevé, comme si 50m le résolvait, mais évidemment, cela diminue la surface des coordonnées sélectionnées possibles. 

Évidemment, vous pouvez adapter l’idée à vos besoins. Peut-être que vous exécutez le code plusieurs fois pour remplir une base de données ou quelque chose de ce genre. 

3
Fraser

Pour obtenir une distribution égale de Nice sur les latitudes et les longitudes, vous devriez faire quelque chose comme ceci pour obtenir les bons angles:

double longitude = Math.random() * Math.PI * 2;
double latitude = Math.acos(Math.random() * 2 - 1);

Pour ce qui est d'éviter les masses d'eau, avez-vous les données pour savoir où se trouve déjà l'eau? Eh bien, il suffit de ré-échantillonner jusqu'à ce que vous obteniez un coup! Si vous ne disposez pas déjà de ces données, il semblerait que d'autres personnes aient de meilleures suggestions que moi pour cela ...

J'espère que cela aide, à la vôtre.

2
Elias Vasylenko

En tant que plan B, vous pouvez peut-être choisir un pays aléatoire, puis une coordonnée aléatoire à l'intérieur de ce pays. Pour être juste lorsque vous choisissez un pays, vous pouvez utiliser sa superficie en poids.

2
cassioso

Il existe une bibliothèque ici et vous pouvez utiliser sa méthode .random () pour obtenir une coordonnée aléatoire. Ensuite, vous pouvez utiliser WebServices GeoNames pour déterminer s’il est terrestre ou non. Ils ont une liste de webservices et il ne vous reste plus qu'à utiliser le bon. GeoNames est gratuit et fiable. 

1
bsimic
1
Kamil

En complément de ce que bsimic a dit à propos de la fouille dans les Webservices de GeoNames, voici un raccourci:
Ils ont un WebService dédié pour demander un nom d’océan .

(Je suis conscient de la contrainte imposée par OP à pas utiliser des services Web publics en raison du nombre de demandes. Néanmoins, je suis tombé sur cette question avec la même question de base et le trouve utile.)

Allez à http://www.geonames.org/export/web-services.html#astergdem et regardez "océan/géocodage inverse". Il est disponible en XML et JSON. Créez un compte utilisateur gratuit pour empêcher les limites quotidiennes sur le compte de démonstration.

Exemple de demande sur la zone océanique (mer Baltique, URL JSON):

http://api.geonames.org/oceanJSON?lat=54.049889&lng=10.851388&username=demo

résulte en

{
  "ocean": {
    "distance": "0",
    "name": "Baltic Sea"
  }
}

alors que certaines coordonnées sur la terre résultent en

{
  "status": {
    "message": "we are afraid we could not find an ocean for latitude and longitude :53.0,9.0",
    "value": 15
  }
}
0
hennzen

Vous pouvez également faire la chose verte bleue, puis stocker tous les points verts pour une recherche ultérieure. Cela a l'avantage d'être "progressif". Au fur et à mesure que vous trouvez une meilleure façon de faire votre liste de points, vous pouvez simplement indiquer à votre graber aléatoire un groupe de points de plus en plus précis.

Peut-être qu'un fournisseur de services a déjà une réponse à votre question: par exemple. https://www.google.com/enterprise/marketplace/viewListing?productListingId=3030+17310026046429031496&pli=1

Élévation api? http://code.google.com/apis/maps/documentation/elevation/ au-dessus du niveau de la mer ou au-dessous? (pas de points néerlandais pour vous!)

0
David Thornton

Générer est facile, le problème est qu'ils ne doivent pas être sur l'eau. Je voudrais importer le "Open Streetmap" par exemple ici http://ftp.ecki-netz.de/osm/ et l’importer dans une base de données (très facile structure de données). Je suggérerais PostgreSQL, il est livré avec des fonctions géographiques http://www.postgresql.org/docs/8.2/static/functions-geometry.html . Pour cela, vous devez enregistrer les points dans une colonne "polygone". Vous pouvez ensuite vérifier avec l'opérateur "&&" s'il s'agit d'un polygone d'eau. Pour les attributs d’une entrée de voie OpenStreetmap, vous devriez jeter un coup d’œil sur http://wiki.openstreetmap.org/wiki/Category:En:Keys

0
wutzebaer

J'imagine que vous pourriez utiliser une carte du monde, définir quelques points dessus pour délimiter la plupart des masses d'eau, comme vous le dites, et utiliser une méthode polygone.contenant pour valider les coordonnées.

Un algorithme plus rapide consisterait à utiliser cette carte, à prendre un point quelconque et à vérifier la couleur ci-dessous, si elle est bleue, puis eau ... lorsque vous avez les coordonnées, vous les convertissez en lat/long.

0
Snicolas