How to calculate distance from different markers in a map and then pick up the least one

后端 未结 9 992
野趣味
野趣味 2020-12-05 08:53

I have to get distance from different markers on the map to the current location of the device and the pick up the shortest one. I have the lat and long for the markers and

相关标签:
9条回答
  • 2020-12-05 09:15

    from your comment I see that you expect a maximum of 70-80 locations. This is not much.

    You can simply do a brute force search over all markers and take the minimum.

    Iterate over all markers, and search min distance:

        List<Marker> markers = createMarkers(); // returns an ArrayList<Markers> from your data source
        int minIndex = -1;
        double minDist = 1E38; // initialize with a huge value that will be overwritten
        int size = markers.size();
        for (int i = 0; i < size; i++) {
            Marker marker = markers.get(i);
            double curDistance = calcDistance(curLatitude, curLongitude, marker.latitude, marker.longitude);
          if (curDistance < minDist) {
             minDist = curDistance;  // update neares
             minIndex = i;           // store index of nearest marker in minIndex
          }
        }
    
        if (minIndex >= 0) {
           // now nearest maker found:
           Marker nearestMarker = markers.get(minIndex);
           // TODO do something with nearesr marker
        } else {
          // list of markers was empty
        }
    

    For calcDistance, use the distance calculation method provided by android. (e.g Location.distanceTo() )
    For 70-80 markers there is no need to make it faster and much more complex. If you have some thousands points then it is worth to invest in a faster solution (using a spatial index, and an own distance calculation which avoids the sqrt calc).

    Just print out the current time in milli seconds at the begin and at the end of the nearest maker search, and you will see, that it is fast enough.

    0 讨论(0)
  • 2020-12-05 09:16

    An efficient way to search for the smallest distance between a single point (that may change frequently), and a large set of points, in two dimensions is to use a QuadTree. There is a cost to initially build the QuadTree (i.e., add your marker locations to the data structure), so you only want to do this once (or as infrequently as possible). But, once constructed, searches for the closest point will typically be faster than a brute force comparison against all points in the large set.

    BBN's OpenMap project has an open-source QuadTree Java implementation that I believe should work on Android that has a get(float lat, float lon) method to return the closest point.

    Google's android-maps-utils library also has an open-source implementation of a QuadTree intended to run on Android, but as it is currently written it only supports a search(Bounds bounds) operation to return a set of points in a given bounding box, and not the point closest to an input point. But, it could be modified to perform the closest point search.

    If you have a relatively small number of points (70-80 may be sufficiently small), then in real-world performance a brute-force comparison may execute in a similar amount of time to the QuadTree solution. But, it also depends on how frequently you intended on re-calculating the closest point - if frequent, a QuadTree may be a better choice.

    0 讨论(0)
  • 2020-12-05 09:20

    Here, I got a way to do that Using databases. This is a calculate distance function:

    public void calculateDistance() {
    
      if (latitude != 0.0 && longitude != 0.0) {
        for(int i=0;i<97;i++) { 
          Location myTargetLocation=new Location("");
          myTargetLocation.setLatitude(targetLatitude[i]);
          myTargetLocation.setLongitude(targetLongitude[i]);
          distance[i]=myCurrentLocation.distanceTo(myTargetLocation);
          distance[i]=distance[i]/1000;
          mdb.insertDetails(name[i],targetLatitude[i], targetLongitude[i], distance[i]);
        }
    
        Cursor c1= mdb.getallDetail();
        while (c1.moveToNext()) {
          String station_name=c1.getString(1);
          double latitude=c1.getDouble(2);
          double longitude=c1.getDouble(3);
          double dis=c1.getDouble(4);
          //Toast.makeText(getApplicationContext(),station_name+" & "+latitude+" &  "+longitude+" &  "+dis,1).show();
        }
        Arrays.sort(distance);
        double nearest_distance=distance[0];
        Cursor c2=mdb.getNearestStationName();
        {
            while (c2.moveToNext()) {
    
                double min_dis=c2.getDouble(4);
                if(min_dis==nearest_distance)
                {
                    String nearest_stationName=c2.getString(1);
                    if(btn_clicked.equals("source"))
                    {
                    source.setText(nearest_stationName);
                    break;
                    }
                    else if(btn_clicked.equals("dest"))
                    {
                        destination.setText(nearest_stationName);
                        break;
                    }
                    else
                    {
    
                    }
                }
            }
        }
         }
         else
         {
             Toast.makeText(this, "GPS is Not Working Properly,, please check Gps and  Wait for few second", 1).show(); 
         }
    }
    

    All we have to do is Create an array named targetLatitude[i] and targetLongitude[i] containing Lats and Longs of all the places you want to calculate distance from. Then create a database as shown below:

    public class MyDataBase {
        SQLiteDatabase sdb;
        MyHelper mh;
        MyDataBase(Context con)
        {
        mh = new MyHelper(con, "Metro",null, 1);
        }
    
    public void open() {
    try
    {
    sdb=mh.getWritableDatabase();
    }
    catch(Exception e)
    {
    
    }
    
    
    }
    
    public void insertDetails(String name,double latitude,double longitude,double distance) {
    
    ContentValues cv=new ContentValues();
    cv.put("name", name);
    cv.put("latitude", latitude);
    cv.put("longitude",longitude);
    cv.put("distance", distance);
    sdb.insert("stations", null, cv);
    }
    
    public void insertStops(String stop,double latitude,double logitude)
    {
        ContentValues cv=new ContentValues();
        cv.put("stop", stop);
        cv.put("latitude", latitude);
        cv.put("logitude", logitude);
        sdb.insert("stops", null, cv);
    
    }
    
    
    
    public Cursor getallDetail()
    {   
        Cursor c=sdb.query("stations",null,null,null,null,null,null);
        return c;
    }
    public Cursor getNearestStationName() {
        Cursor c=sdb.query("stations",null,null,null,null,null,null);
        return c;
    }
    
    
    public Cursor getStops(String stop)
    {
        Cursor c;
        c=sdb.query("stops",null,"stop=?",new String[]{stop},null, null, null);
        return c;
    }
    
    class MyHelper extends SQLiteOpenHelper
    {
    
        public MyHelper(Context context, String name, CursorFactory factory,
                int version) {
            super(context, name, factory, version);
            // TODO Auto-generated constructor stub
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            // TODO Auto-generated method stub
            db.execSQL("Create table stations(_id integer primary key,name text," +
                    " latitude double, longitude double, distance double );");
            db.execSQL("Create table stops(_id integer primary key,stop text," +
                    "latitude double,logitude double);");
    
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            // TODO Auto-generated method stub
    
        }
    
    }
    public void deleteDetail() {
        sdb.delete("stations",null,null);
        sdb.delete("stops",null,null);
    
    }
    
    public void close() {
        sdb.close();
    
    }
    
    }
    

    Then execute the CalculateDistance function wherever you want and you can get the nearest station name.

    0 讨论(0)
  • 2020-12-05 09:25

    This code could help you getting the distances: https://github.com/BeyondAR/beyondar/blob/master/android/BeyondAR_Framework/src/com/beyondar/android/util/math/Distance.java

    0 讨论(0)
  • 2020-12-05 09:27

    How about looping over all markers and checking the distance using Location.distanceBetween? There is no magic involved ;)

    List<Marker> markers;
    LatLng currentPosition;
    
    float minDistance = Float.MAX_VALUE;
    Marker closest = null;
    float[] currentDistance = new float[1];
    for (Marker marker : markers) {
        LatLng markerPosition = marker.getPosition();
        Location.distanceBetween(currentPosition.latitude, currentPosition.longitude, markerPosition.latitude, markerPosition.longitude, currentDistance);
        if (minDistance > currentDistance[0]) {
            minDistance = currentDistance[0];
            closest = marker;
        }
    }
    
    0 讨论(0)
  • 2020-12-05 09:34

    Here is my implementation of a so called KDTree, consisting of 3 classes: KDTree, KDTNode and KDTResult. What you finally need is to create the KDTree using KDTree.createTree(), which returns the rootNode of the tree and gets all your fixed points passed in. Then use KDTree.findNearestWp() to find the nearest Waypoint to the given location.

    KDTree:

    public class KDTree {
    
    private Comparator<LatLng> latComparator = new LatLonComparator(true);
    private Comparator<LatLng> lonComparator = new LatLonComparator(false);;
    
    /**
     * Create a KDTree from a list of Destinations. Returns the root-node of the
     * tree.
     */
    public KDTNode createTree(List<LatLng> recList) {
        return createTreeRecursive(0, recList);
    }
    
    /**
     * Traverse the tree and find the nearest WP.
     * 
     * @param root
     * @param wp
     * @return
     */
    static public LatLng findNearestWp(KDTNode root, LatLng wp) {
        KDTResult result = new KDTResult();
        findNearestWpRecursive(root, wp, result);
        return result.nearestDest;
    }
    
    private static void findNearestWpRecursive(KDTNode node, LatLng wp,
            KDTResult result) {
        double lat = wp.latitude;
        double lon = wp.longitude;
        /* If a leaf node, calculate distance and return. */
        if (node.isLeaf) {
            LatLng dest = node.wp;
            double latDiff = dest.latitude - lat;
            double lonDiff = dest.longitude - lon;
            double squareDist = latDiff * latDiff + lonDiff * lonDiff;
            // Replace a previously found nearestDest only if the new one is
            // nearer.
            if (result.nearestDest == null
                    || result.squareDistance > squareDist) {
                result.nearestDest = dest;
                result.squareDistance = squareDist;
            }
            return;
        }
        boolean devidedByLat = node.depth % 2 == 0;
        boolean goLeft;
        /* Check whether left or right is more promising. */
        if (devidedByLat) {
            goLeft = lat < node.splitValue;
        } else {
            goLeft = lon < node.splitValue;
        }
        KDTNode child = goLeft ? node.left : node.right;
        findNearestWpRecursive(child, wp, result);
        /*
         * Check whether result needs to be checked also against the less
         * promising side.
         */
        if (result.squareDistance > node.minSquareDistance) {
            KDTNode otherChild = goLeft ? node.right : node.left;
            findNearestWpRecursive(otherChild, wp, result);
        }
    
    }
    
    private KDTNode createTreeRecursive(int depth, List<LatLng> recList) {
        KDTNode node = new KDTNode();
        node.depth = depth;
        if (recList.size() == 1) {
            // Leafnode found
            node.isLeaf = true;
            node.wp = recList.get(0);
            return node;
        }
        boolean divideByLat = node.depth % 2 == 0;
        sortRecListByDimension(recList, divideByLat);
        List<LatLng> leftList = getHalfOf(recList, true);
        List<LatLng> rightList = getHalfOf(recList, false);
        // Get split point and distance to last left and first right point.
        LatLng lastLeft = leftList.get(leftList.size() - 1);
        LatLng firstRight = rightList.get(0);
        double minDistanceToSplitValue;
        double splitValue;
        if (divideByLat) {
            minDistanceToSplitValue = (firstRight.latitude - lastLeft.latitude) / 2;
            splitValue = lastLeft.latitude + Math.abs(minDistanceToSplitValue);
        } else {
            minDistanceToSplitValue = (firstRight.longitude - lastLeft.longitude) / 2;
            splitValue = lastLeft.longitude + Math.abs(minDistanceToSplitValue);
        }
        node.splitValue = splitValue;
        node.minSquareDistance = minDistanceToSplitValue
                * minDistanceToSplitValue;
        /** Call next level */
        depth++;
        node.left = createTreeRecursive(depth, leftList);
        node.right = createTreeRecursive(depth, rightList);
        return node;
    }
    
    /**
     * Return a sublist representing the left or right half of a List. Size of
     * recList must be at least 2 !
     * 
     * IMPORTANT !!!!! Note: The original list must not be modified after
     * extracting this sublist, as the returned subList is still backed by the
     * original list.
     */
    List<LatLng> getHalfOf(List<LatLng> recList, boolean leftHalf) {
        int mid = recList.size() / 2;
        if (leftHalf) {
            return recList.subList(0, mid);
        } else {
            return recList.subList(mid, recList.size());
        }
    }
    
    private void sortRecListByDimension(List<LatLng> recList, boolean sortByLat) {
        Comparator<LatLng> comparator = sortByLat ? latComparator
                : lonComparator;
        Collections.sort(recList, comparator);
    }
    
    class LatLonComparator implements Comparator<LatLng> {
        private boolean byLat;
    
        public LatLonComparator(boolean sortByLat) {
            this.byLat = sortByLat;
        }
    
        @Override
        public int compare(LatLng lhs, LatLng rhs) {
            double diff;
            if (byLat) {
                diff = lhs.latitude - rhs.latitude;
            } else {
                diff = lhs.longitude - rhs.longitude;
            }
            if (diff > 0) {
                return 1;
            } else if (diff < 0) {
                return -1;
            } else {
                return 0;
            }
        }
    
    }
    }
    

    KDTNode:

    /** Node of the KDTree */
    public class KDTNode {
    
    KDTNode left;
    KDTNode right;
    boolean isLeaf;
    /** latitude or longitude of the nodes division line. */
    double splitValue;
    /** Distance between division line and first point. */
    double minSquareDistance;
    /**
     * Depth of the node in the tree. An even depth devides the tree in the
     * latitude-axis, an odd depth devides the tree in the longitude-axis.
     */
    int depth;
    /** The Waypoint in case the node is a leaf node. */
    LatLng wp;
    
    }
    

    KDTResult:

    /** Holds the result of a tree traversal. */
    public class KDTResult {
    LatLng nearestDest;
    // I use the square of the distance to avoid square-root operations.
    double squareDistance;
    }
    

    Please note, that I am using a simplified distance calculation, which works in my case, as I am only interested in very nearby waypoints. For points further apart, this may result in getting not exactly the nearest point. The absolute difference of two longitudes expressed as east-west distance in meters, depends on the latitude, where this difference is measured. This is not taken into account in my algorithm and I am not sure about the relevance of this effect in your case.

    0 讨论(0)
自定义标题
段落格式
字体
字号
代码语言
提交回复
热议问题