128 votes

Comment calculer la boîte de délimitation pour une position lat/lng donnée ?

J'ai donné un lieu défini par la latitude et la longitude. Je veux maintenant calculer une boîte de délimitation dans un rayon de 10 kilomètres autour de ce point.

La boîte de délimitation doit être définie comme latmin, lngmin et latmax, lngmax.

J'ai besoin de ces éléments pour utiliser le API Panoramio .

Quelqu'un connaît-il la formule pour obtenir ces points ?

Editer : Je suis à la recherche d'une formule/fonction qui prend lat et lng en entrée et renvoie une boîte de délimitation en latmin & lngmin et latmax & latmin. Je ne sais pas si c'est une bonne idée, mais je ne sais pas si c'est une bonne idée, mais c'est une bonne idée.

Editer : Je ne cherche pas une solution qui m'indique la distance entre deux points.

0 votes

Si vous utilisez une géodatabase quelque part, il est certain qu'elle intègre un calcul de boîte de délimitation. Vous pouvez même vérifier la source de PostGIS/GEOS, par exemple.

78voto

Federico A. Ramponi Points 23106

Je suggère d'approximer localement la surface de la Terre comme une sphère dont le rayon est donné par l'ellipsoïde WGS84 à la latitude donnée. Je soupçonne que le calcul exact de latMin et latMax nécessiterait des fonctions elliptiques et n'apporterait pas un gain de précision appréciable (WGS84 est lui-même une approximation).

Mon implémentation est la suivante (elle est écrite en Python ; je ne l'ai pas testée) :

# degrees to radians
def deg2rad(degrees):
    return math.pi*degrees/180.0
# radians to degrees
def rad2deg(radians):
    return 180.0*radians/math.pi

# Semi-axes of WGS-84 geoidal reference
WGS84_a = 6378137.0  # Major semiaxis [m]
WGS84_b = 6356752.3  # Minor semiaxis [m]

# Earth radius at a given latitude, according to the WGS-84 ellipsoid [m]
def WGS84EarthRadius(lat):
    # http://en.wikipedia.org/wiki/Earth_radius
    An = WGS84_a*WGS84_a * math.cos(lat)
    Bn = WGS84_b*WGS84_b * math.sin(lat)
    Ad = WGS84_a * math.cos(lat)
    Bd = WGS84_b * math.sin(lat)
    return math.sqrt( (An*An + Bn*Bn)/(Ad*Ad + Bd*Bd) )

# Bounding box surrounding the point at given coordinates,
# assuming local approximation of Earth surface as a sphere
# of radius given by WGS84
def boundingBox(latitudeInDegrees, longitudeInDegrees, halfSideInKm):
    lat = deg2rad(latitudeInDegrees)
    lon = deg2rad(longitudeInDegrees)
    halfSide = 1000*halfSideInKm

    # Radius of Earth at given latitude
    radius = WGS84EarthRadius(lat)
    # Radius of the parallel at given latitude
    pradius = radius*math.cos(lat)

    latMin = lat - halfSide/radius
    latMax = lat + halfSide/radius
    lonMin = lon - halfSide/pradius
    lonMax = lon + halfSide/pradius

    return (rad2deg(latMin), rad2deg(lonMin), rad2deg(latMax), rad2deg(lonMax))

EDIT : Le code suivant convertit (degrés, nombres premiers, secondes) en degrés + fractions de degré, et vice versa (non testé) :

def dps2deg(degrees, primes, seconds):
    return degrees + primes/60.0 + seconds/3600.0

def deg2dps(degrees):
    intdeg = math.floor(degrees)
    primes = (degrees - intdeg)*60.0
    intpri = math.floor(primes)
    seconds = (primes - intpri)*60.0
    intsec = round(seconds)
    return (int(intdeg), int(intpri), int(intsec))

4 votes

Comme indiqué dans la documentation de la bibliothèque CPAN proposée, cela n'a de sens que pour halfSide <= 10km.

1 votes

Cela fonctionne-t-il près des pôles ? Il ne semble pas que ce soit le cas, car il semble que l'on aboutisse à latMin < -pi (pour le pôle sud) ou latMax > pi (pour le pôle nord) ? Je pense que lorsque l'on est à moins de halfSide d'un pôle, il faut renvoyer une boîte englobante qui inclut toutes les longitudes et les latitudes calculées normalement pour le côté éloigné du pôle et au pôle sur le côté proche du pôle.

0 votes

Cela ne fonctionne pas non plus autour des pôles. JanMatuschek.de/LatitudeLongitudeCoordonnées de délimitation donne le "bon algorithme"

65voto

J'ai écrit un article sur la recherche des coordonnées de délimitation :

http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates

L'article explique les formules et fournit également une implémentation Java. (Il montre également pourquoi la formule de Federico pour la longitude min/max est inexacte).

4 votes

J'ai réalisé un portage PHP de votre classe GeoLocation. Il se trouve ici : pastie.org/5416584

1 votes

Je l'ai téléchargé sur github : github.com/anthonymartin/GeoLocation.class.php

1 votes

Est-ce que cela répond à la question ? Si nous n'avons qu'un seul point de départ, nous ne pouvons pas calculer la distance du grand cercle comme le fait ce code, qui nécessite deux positions lat et long.

39voto

Ε Г И І И О Points 1582

J'ai converti la réponse de Federico A. Ramponi en C# pour les personnes intéressées :

public class MapPoint
{
    public double Longitude { get; set; } // In Degrees
    public double Latitude { get; set; } // In Degrees
}

public class BoundingBox
{
    public MapPoint MinPoint { get; set; }
    public MapPoint MaxPoint { get; set; }
}        

// Semi-axes of WGS-84 geoidal reference
private const double WGS84_a = 6378137.0; // Major semiaxis [m]
private const double WGS84_b = 6356752.3; // Minor semiaxis [m]

// 'halfSideInKm' is the half length of the bounding box you want in kilometers.
public static BoundingBox GetBoundingBox(MapPoint point, double halfSideInKm)
{            
    // Bounding box surrounding the point at given coordinates,
    // assuming local approximation of Earth surface as a sphere
    // of radius given by WGS84
    var lat = Deg2rad(point.Latitude);
    var lon = Deg2rad(point.Longitude);
    var halfSide = 1000 * halfSideInKm;

    // Radius of Earth at given latitude
    var radius = WGS84EarthRadius(lat);
    // Radius of the parallel at given latitude
    var pradius = radius * Math.Cos(lat);

    var latMin = lat - halfSide / radius;
    var latMax = lat + halfSide / radius;
    var lonMin = lon - halfSide / pradius;
    var lonMax = lon + halfSide / pradius;

    return new BoundingBox { 
        MinPoint = new MapPoint { Latitude = Rad2deg(latMin), Longitude = Rad2deg(lonMin) },
        MaxPoint = new MapPoint { Latitude = Rad2deg(latMax), Longitude = Rad2deg(lonMax) }
    };            
}

// degrees to radians
private static double Deg2rad(double degrees)
{
    return Math.PI * degrees / 180.0;
}

// radians to degrees
private static double Rad2deg(double radians)
{
    return 180.0 * radians / Math.PI;
}

// Earth radius at a given latitude, according to the WGS-84 ellipsoid [m]
private static double WGS84EarthRadius(double lat)
{
    // http://en.wikipedia.org/wiki/Earth_radius
    var An = WGS84_a * WGS84_a * Math.Cos(lat);
    var Bn = WGS84_b * WGS84_b * Math.Sin(lat);
    var Ad = WGS84_a * Math.Cos(lat);
    var Bd = WGS84_b * Math.Sin(lat);
    return Math.Sqrt((An*An + Bn*Bn) / (Ad*Ad + Bd*Bd));
}

1 votes

Merci, cela fonctionne pour moi. J'ai dû tester le code à la main, je ne savais pas comment écrire un test unitaire pour cela, mais il génère des résultats exacts au degré de précision dont j'ai besoin.

0 votes

Qu'est-ce que le haldSideinKm ici ? je ne comprends pas... que dois-je passer dans ces arguments, le rayon entre deux points sur la carte ou quoi ?

1 votes

@GeloVolro : C'est la moitié de la longueur de la boîte de délimitation que vous voulez.

13voto

asalisbury Points 1

J'ai écrit une fonction JavaScript qui renvoie les quatre coordonnées d'une boîte de délimitation carrée, compte tenu d'une distance et d'une paire de coordonnées :

'use strict';

/**
 * @param {number} distance - distance (km) from the point represented by centerPoint
 * @param {array} centerPoint - two-dimensional array containing center coords [latitude, longitude]
 * @description
 *   Computes the bounding coordinates of all points on the surface of a sphere
 *   that has a great circle distance to the point represented by the centerPoint
 *   argument that is less or equal to the distance argument.
 *   Technique from: Jan Matuschek <http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates>
 * @author Alex Salisbury
*/

getBoundingBox = function (centerPoint, distance) {
  var MIN_LAT, MAX_LAT, MIN_LON, MAX_LON, R, radDist, degLat, degLon, radLat, radLon, minLat, maxLat, minLon, maxLon, deltaLon;
  if (distance < 0) {
    return 'Illegal arguments';
  }
  // helper functions (degrees<–>radians)
  Number.prototype.degToRad = function () {
    return this * (Math.PI / 180);
  };
  Number.prototype.radToDeg = function () {
    return (180 * this) / Math.PI;
  };
  // coordinate limits
  MIN_LAT = (-90).degToRad();
  MAX_LAT = (90).degToRad();
  MIN_LON = (-180).degToRad();
  MAX_LON = (180).degToRad();
  // Earth's radius (km)
  R = 6378.1;
  // angular distance in radians on a great circle
  radDist = distance / R;
  // center point coordinates (deg)
  degLat = centerPoint[0];
  degLon = centerPoint[1];
  // center point coordinates (rad)
  radLat = degLat.degToRad();
  radLon = degLon.degToRad();
  // minimum and maximum latitudes for given distance
  minLat = radLat - radDist;
  maxLat = radLat + radDist;
  // minimum and maximum longitudes for given distance
  minLon = void 0;
  maxLon = void 0;
  // define deltaLon to help determine min and max longitudes
  deltaLon = Math.asin(Math.sin(radDist) / Math.cos(radLat));
  if (minLat > MIN_LAT && maxLat < MAX_LAT) {
    minLon = radLon - deltaLon;
    maxLon = radLon + deltaLon;
    if (minLon < MIN_LON) {
      minLon = minLon + 2 * Math.PI;
    }
    if (maxLon > MAX_LON) {
      maxLon = maxLon - 2 * Math.PI;
    }
  }
  // a pole is within the given distance
  else {
    minLat = Math.max(minLat, MIN_LAT);
    maxLat = Math.min(maxLat, MAX_LAT);
    minLon = MIN_LON;
    maxLon = MAX_LON;
  }
  return [
    minLon.radToDeg(),
    minLat.radToDeg(),
    maxLon.radToDeg(),
    maxLat.radToDeg()
  ];
};

0 votes

Ce code ne fonctionne pas du tout. Je veux dire, même après avoir corrigé les erreurs évidentes comme minLon = void 0; y maxLon = MAX_LON; il encore ne fonctionne pas.

1 votes

@aroth, je viens de le tester et je n'ai eu aucun problème. Rappelez-vous que centerPoint est un tableau composé de deux coordonnées. Par exemple, getBoundingBox([42.2, 34.5], 50) - void 0 est la sortie CoffeeScript pour "undefined" et n'affectera pas la capacité du code à s'exécuter.

0 votes

Ce code ne fonctionne pas. degLat.degToRad n'est pas une fonction

6voto

jcoby Points 2389

Vous recherchez une formule d'ellipsoïde.

Le meilleur endroit que j'ai trouvé pour commencer à coder est la bibliothèque Geo::Ellipsoid du CPAN. Elle vous donne une base sur laquelle vous pouvez créer vos tests et comparer vos résultats avec les siens. J'ai utilisé cette bibliothèque comme base pour une bibliothèque similaire pour PHP chez mon ancien employeur.

Geo::Ellipsoid

Jetez un coup d'œil à la location méthode. Appelez-la deux fois et vous aurez votre bbox.

Vous n'avez pas indiqué la langue que vous utilisiez. Il se peut qu'une bibliothèque de géocodage soit déjà disponible pour vous.

Oh, et si vous ne l'avez pas encore compris, Google maps utilise l'ellipsoïde WGS84.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X