SQlite Obtenir les emplacements les plus proches (avec latitude et longitude)

J'ai des données avec la latitude et la longitude stockées dans ma base de données SQLite et je souhaite obtenir les emplacements les plus proches des paramètres que j'ai mis (ex. Mon emplacement actuel – lat / lng, etc.).

Je sais que cela est possible dans MySQL, et j'ai fait assez de recherches que SQLite a besoin d'une fonction externe personnalisée pour la formule Haversine (calcul de distance sur une sphère), mais je n'ai trouvé rien qui soit écrit en Java et fonctionne .

  • Quelle est la vitesse ou la rapidité de Qt mobile pour Android
  • Détecter le bouton de retour mais ne pas rejeter le dialogue de fragmentation
  • Comment puis-je passer des valeurs entre une boîte de dialogue et une activité?
  • Les éléments du menu doivent spécifier un titre
  • Impossible de créer une application Android (se référer à ActionBarSherlock & ViewPagerTabs) avec Ant
  • Erreur: Aucune chaîne d'outils trouvée dans le dossier des chaînes d'outils NDK pour ABI avec préfixe: llvm
  • En outre, si je veux ajouter des fonctions personnalisées, j'ai besoin de org.sqlite .jar (pour org.sqlite.Function ), et cela ajoute une taille inutile à l'application.

    L'autre côté de cela, j'ai besoin de l'ordre par fonction à partir de SQL, car l'affichage de la distance seule ne pose pas beaucoup de problème – je l'ai déjà fait dans mon CustomCursorAdapter personnalisé, mais je ne peux pas trier les données parce que je Je n'ai pas la colonne de distance dans ma base de données. Cela signifierait la mise à jour de la base de données chaque fois que l'emplacement change et c'est un gaspillage de batterie et de performance. Donc, si quelqu'un a une idée sur le tri du curseur avec une colonne qui n'est pas dans la base de données, je serais reconnaissant aussi!

    Je sais qu'il y a des tonnes d'applications Android là-bas qui utilisent cette fonction, mais quelqu'un peut-il expliquer la magie.

    En passant, j'ai trouvé cette alternative: Query pour obtenir des enregistrements basés sur Radius dans SQLite?

    Il est suggéré de créer 4 nouvelles colonnes pour les valeurs cos et sin de lat et lng, mais y a-t-il d'autres, pas trop redondants?

  • Google Play Game Services sur un jeu de bureau
  • Comment envoyer / recevoir des notifications push de l'application mobile ionique?
  • Comment étendre la zone tactile d'une vue
  • AdB Push / Pull avec barre de progression
  • Android 4.0.1 brise le stockage local WebView HTML 5?
  • Seize vibrateurs Bluetooth connectés à un téléphone Android
  • 7 Solutions collect form web for “SQlite Obtenir les emplacements les plus proches (avec latitude et longitude)”

    1) Filtrer d'abord vos données SQLite avec une bonne approximation et diminuer la quantité de données que vous devez évaluer dans votre code java. Utilisez la procédure suivante à cette fin:

    Pour avoir un seuil déterministe et un filtre plus précis sur les données, il est préférable de calculer 4 emplacements qui sont en mètre de radius du nord, de l'ouest, de l'est et du sud de votre point central dans votre code java , puis vérifiez facilement de moins en plus Que les opérateurs SQL (>, <) pour déterminer si vos points dans la base de données sont dans ce rectangle ou non.

    La méthode calculateDerivedPosition(...) calcule ces points pour vous (p1, p2, p3, p4 dans l'image).

    Entrez la description de l'image ici

     /** * Calculates the end-point from a given source at a given range (meters) * and bearing (degrees). This methods uses simple geometry equations to * calculate the end-point. * * @param point * Point of origin * @param range * Range in meters * @param bearing * Bearing in degrees * @return End-point from the source given the desired range and bearing. */ public static PointF calculateDerivedPosition(PointF point, double range, double bearing) { double EarthRadius = 6371000; // m double latA = Math.toRadians(point.x); double lonA = Math.toRadians(point.y); double angularDistance = range / EarthRadius; double trueCourse = Math.toRadians(bearing); double lat = Math.asin( Math.sin(latA) * Math.cos(angularDistance) + Math.cos(latA) * Math.sin(angularDistance) * Math.cos(trueCourse)); double dlon = Math.atan2( Math.sin(trueCourse) * Math.sin(angularDistance) * Math.cos(latA), Math.cos(angularDistance) - Math.sin(latA) * Math.sin(lat)); double lon = ((lonA + dlon + Math.PI) % (Math.PI * 2)) - Math.PI; lat = Math.toDegrees(lat); lon = Math.toDegrees(lon); PointF newPoint = new PointF((float) lat, (float) lon); return newPoint; } 

    Et créez maintenant votre requête:

     PointF center = new PointF(x, y); final double mult = 1; // mult = 1.1; is more reliable PointF p1 = calculateDerivedPosition(center, mult * radius, 0); PointF p2 = calculateDerivedPosition(center, mult * radius, 90); PointF p3 = calculateDerivedPosition(center, mult * radius, 180); PointF p4 = calculateDerivedPosition(center, mult * radius, 270); strWhere = " WHERE " + COL_X + " > " + String.valueOf(p3.x) + " AND " + COL_X + " < " + String.valueOf(p1.x) + " AND " + COL_Y + " < " + String.valueOf(p2.y) + " AND " + COL_Y + " > " + String.valueOf(p4.y) 

    COL_X est le nom de la colonne dans la base de données qui stocke les valeurs de latitude et COL_Y est pour la longitude.

    Donc, vous avez des données proches de votre point central avec une bonne approximation.

    2) Maintenant, vous pouvez faire une boucle sur ces données filtrées et déterminer si elles sont vraiment proches de votre point (dans le cercle) ou ne pas utiliser les méthodes suivantes:

     public static boolean pointIsInCircle(PointF pointForCheck, PointF center, double radius) { if (getDistanceBetweenTwoPoints(pointForCheck, center) <= radius) return true; else return false; } public static double getDistanceBetweenTwoPoints(PointF p1, PointF p2) { double R = 6371000; // m double dLat = Math.toRadians(p2.x - p1.x); double dLon = Math.toRadians(p2.y - p1.y); double lat1 = Math.toRadians(p1.x); double lat2 = Math.toRadians(p2.x); double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); double d = R * c; return d; } 

    Prendre plaisir!

    J'ai utilisé et personnalisé cette référence et l'ai complété.

    Je sais que cela a été répondu et accepté, mais je pensais ajouter mes expériences et ma solution.

    Alors que j'étais heureux de faire une fonction haversine sur l'appareil pour calculer la distance précise entre la position actuelle de l'utilisateur et n'importe quel emplacement de cible particulier, il fallait trier et limiter les résultats de la requête par ordre de distance.

    La solution moins satisfaisante est de renvoyer le lot et de trier et de filtrer après le fait, mais cela entraînerait un deuxième curseur et de nombreux résultats inutiles seraient renvoyés et éliminés.

    Ma solution préférée était de passer dans un ordre de tri des valeurs du delta carrées du long et des lats:

     ((<lat> - LAT_COLUMN) * (<lat> - LAT_COLUMN) + (<lng> - LNG_COLUMN) * (<lng> - LNG_COLUMN)) 

    Il n'y a pas besoin de faire la pleine vague pour un ordre de tri et il n'est pas nécessaire de mettre en ligne les résultats, donc SQLite peut gérer le calcul.

    MODIFIER:

    Cette réponse reçoit toujours de l'amour. Cela fonctionne bien dans la plupart des cas, mais si vous avez besoin d'un peu plus de précision, vérifiez la réponse par @Teasel ci-dessous, qui ajoute un facteur "fudge" qui corrige les inexactitudes qui augmentent à mesure que la latitude approche 90.

    La réponse de Chris est vraiment utile (merci!), Mais ne fonctionnera que si vous utilisez des coordonnées rectilignes (p. Ex. Références de grille UTM ou OS). Si vous utilisez des degrés pour lat / lng (p. Ex. WGS84), ce qui précède ne fonctionne que sur l'équateur. À d'autres latitudes, vous devez diminuer l'impact de la longitude sur l'ordre de tri. (Imaginez que vous êtes près du pôle nord … un degré de latitude est toujours le même que n'importe où, mais un degré de longitude ne peut être que de quelques pieds. Cela signifie que l'ordre de tri est incorrect).

    Si vous n'êtes pas à l'équateur, pré-calculer le fudge-factor, en fonction de votre latitude actuelle:

     <fudge> = Math.pow(Math.cos(Math.toRadians(<lat>)),2); 

    Ensuite, commandez par:

    ((<lat> - LAT_COLUMN) * (<lat> - LAT_COLUMN) + (<lng> - LNG_COLUMN) * (<lng> - LNG_COLUMN) * <fudge>)

    C'est toujours une approximation, mais beaucoup mieux que la première, donc les inexactitudes de tri seront beaucoup plus rares.

    Avez-vous considéré un tag / index Geohash pour vos entrées pour réduire la taille de votre jeu de résultats, puis appliquez la fonction appropriée.

    Une autre question de stackoverflow dans un domaine similaire: trouver-le-point le plus proche-à-un-point donné

    Afin d'augmenter les performances autant que possible, je suggère d'améliorer l'idée de @Chris Simpson avec la clause ORDER BY suivante:

     ORDER BY (<L> - <A> * LAT_COL - <B> * LON_COL + LAT_LON_SQ_SUM) 

    Dans ce cas, vous devez passer les valeurs suivantes du code:

     <L> = center_lat^2 + center_lon^2 <A> = 2 * center_lat <B> = 2 * center_lon 

    Et vous devriez également stocker LAT_LON_SQ_SUM = LAT_COL^2 + LON_COL^2 comme colonne supplémentaire dans la base de données. Prévoyez l'insertion de vos entités dans la base de données. Cela améliore légèrement les performances tout en extrayant une grande quantité de données.

    Jetez un oeil à cette publication:

    Fonction de distance pour sqlite

    Il semble vous permettre d'ajouter une fonction Custom Distance () à SQLite qui pourrait vous permettre d'éviter de sauter à travers tous les cercles dans les autres réponses.

    Essayez quelque chose comme ceci:

      //locations to calculate difference with Location me = new Location(""); Location dest = new Location(""); //set lat and long of comparison obj me.setLatitude(_mLat); me.setLongitude(_mLong); //init to circumference of the Earth float smallest = 40008000.0f; //m //var to hold id of db element we want Integer id = 0; //step through results while(_myCursor.moveToNext()){ //set lat and long of destination obj dest.setLatitude(_myCursor.getFloat(_myCursor.getColumnIndexOrThrow(DataBaseHelper._FIELD_LATITUDE))); dest.setLongitude(_myCursor.getFloat(_myCursor.getColumnIndexOrThrow(DataBaseHelper._FIELD_LONGITUDE))); //grab distance between me and the destination float dist = me.distanceTo(dest); //if this is the smallest dist so far if(dist < smallest){ //store it smallest = dist; //grab it's id id = _myCursor.getInt(_myCursor.getColumnIndexOrThrow(DataBaseHelper._FIELD_ID)); } } 

    Après cela, id contient l'élément que vous voulez de la base de données afin que vous puissiez l'extraire:

      //now we have traversed all the data, fetch the id of the closest event to us _myCursor = _myDBHelper.fetchID(id); _myCursor.moveToFirst(); //get lat and long of nearest location to user, used to push out to map view _mLatNearest = _myCursor.getFloat(_myCursor.getColumnIndexOrThrow(DataBaseHelper._FIELD_LATITUDE)); _mLongNearest = _myCursor.getFloat(_myCursor.getColumnIndexOrThrow(DataBaseHelper._FIELD_LONGITUDE)); 

    J'espère que cela pourra aider!

    coAndroid est un fan Android de Google, tout sur les téléphones Android, Android Wear, Android Dev et Android Games Apps.