1. 程式人生 > >LBS根據經緯檢視附近商家的實現

LBS根據經緯檢視附近商家的實現

           現在大多基於地圖的應用都有檢視附近商家的實現,之前在網上看到別的一些方法,大多是去資料庫中檢視每條資料庫記錄的lbs  經緯度,然後跟當前經緯度計算距離,在某個範圍內的則是周圍商家。

        資料庫記錄小還好,如果資料庫非常龐大,每條都要做對比,豈不是很耗效能?

        地球是圓形,每條緯度不等長。

         一 :

如果我們把每條資料庫記錄lbs資訊抽取到一個集合(陣列),然後根據當前位置以及距離(假設周圍3公里)算出在同一緯度,左右各三公里。然後依據左右三公里處的經度資訊

  利用二分查詢確定該經度在集合中的位置(或者一個合適的位置,因為集合中不一定存在該經度值)。這樣只對在該範圍內的商家lbs資訊進行距離計算即可。看一張圖:

                                 

       假設半徑為3公里。我們只需要計算在該半徑內的商店lbs資訊,計算量就會少很多。

二:實現。實體類Location.java

/**
 * 經緯度
 * @author luhuanju
 */
public class Location{
	private int id;//id  代表商家
	private double Latitude;//緯度
	private double Longitude;//經度
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public double getLatitude() {
		return Latitude;
	}
	public void setLatitude(double latitude) {
		Latitude = latitude;
	}
	public double getLongitude() {
		return Longitude;
	}
	public void setLongitude(double longitude) {
		Longitude = longitude;
	}
	public Location(double latitude, double longitude) {
		super();
		Latitude = latitude;
		Longitude = longitude;
	}
	public Location(int id, double latitude, double longitude) {
		super();
		this.id = id;
		Latitude = latitude;
		Longitude = longitude;
	}
}

       然後新建測試集合。按照經度大小排序(要用到二分分治)。

import java.util.Comparator;
/**
 * 排序
 * @author luhuanju
 */
public class Compare implements Comparator<Object> {
	@Override
	public int compare(Object o1, Object o2) {
		Location location1 = (Location) o1;
		Location location2 = (Location) o2;
		if (location1.getLongitude() > location2.getLongitude()) {
			return 1;
		} else if (location1.getLongitude() < location2.getLongitude()) {
			return -1;
		} else {
			return 0;
		}
	}
}

public static void main(String[] args) {
		List<Location> list=new ArrayList<Location>();
		list.add(new Location(1,31.0214710000,121.4432520000));
		list.add(new Location(1,31.0188710000,121.4446900000));
		list.add(new Location(1,31.0241940000,121.4341970000));
		list.add(new Location(1,31.0203560000,121.4336230000));
		list.add(new Location(1,31.0210990000,121.4314670000));
		list.add(new Location(1,31.0208013402,121.4322355312));
		list.add(new Location(1,31.0218913198,121.4279013257));
		list.add(new Location(1,31.0173210199,121.4294606657));
		list.add(new Location(1,31.0229044299,121.4411514014));
		Compare compare=new Compare();//實現了Comparator介面,排序
		Collections.sort(list, compare);//進行按照經度排序}

二:    接下來找到距離中心點左右各三公里的經緯點。

   /**
    * @ luhuanju
    * @param Latitude 當前緯度
    * @param Longitude  當前經度
    * @param distance  距離(米)
    * @return  返回4個位置點。即半徑為距離的上下左右位置點
    */
	static Location[] mLocations = new Location[4];
	private static final double EARTH_RADIUS = 6378137;
	public static Location[] getRectangle4Point(double Latitude,
			double Longitude, int distance) {
		double dlng = 2 * Math.asin(Math.sin(distance / (2 * EARTH_RADIUS))
				/ Math.cos(Latitude));
		dlng = Math.toDegrees(dlng);
		double dlat = distance / EARTH_RADIUS;
		dlat = Math.toDegrees(dlat);

		Location mLeft = new Location( Latitude,Longitude - dlng);
		Location mRight = new Location(Latitude,Longitude + dlng);
		Location mTop = new Location(Latitude + dlat,Longitude );
		Location mBottom = new Location(Latitude - dlat,Longitude);
		mLocations[0] = mLeft;
		mLocations[1] = mRight;
		mLocations[2] = mTop;
		mLocations[3] = mBottom;
		return mLocations;
	}

三:     

           得到左右3公里位置點後,對位置點的經度資訊在集合中進行二分查詢。以找到確定或者合適的位置:

        /**

* 得到左,右側臨界點下標

*/

intLeftIndex=LbsUtil.LeftIndex(list,locations[0].getLongitude());

int RightIndex=LbsUtil.RightIndex(list, locations[1].getLongitude());

             LeftIndex-RightIndex之間的元素是我們需要的進行距離比對的。

         對於中心的左側位置點而言,我們需要的是位置點右側方向的值。即集合元素下標往大:

	public static int LeftIndex(List<Location> list, double des) {
		int low = 0;
		int high = list.size() - 1;
		while (low <= high) {
			int middle = (low + high) / 2;
			if (des == list.get(middle).getLongitude()) {
				/**
				 * 若集合剛好存在該元素,則返回下標
				 */
				System.out.println("剛好");
				just=middle;
				return middle;
			} else if (des < list.get(middle).getLongitude()) {
				/**
				 *若集合不存在該元素 返回最右側近鄰下標
				 */
				left = middle;
				high = middle - 1;
			} else {
				low = middle + 1;
			}
		}
		if(just!=0){ 
	        	return just;
	        }else{
	        	return left;
	        }
	}

          對於中心的右側位置點而言,我們需要的是位置左右側方向的值。即集合元素下標往小:

public static int RightIndex(List<Location> list, double des) {
		int low = 0;
		int high = list.size() - 1;
		while (low <= high) {
			int middle = (low + high) / 2;
			if (des == list.get(middle).getLongitude()) {
				/**
				 * 若集合剛好存在該元素,則返回下標
				 */
				just = middle;
				return middle;
			} else if (des < list.get(middle).getLongitude()) {
				high = middle - 1;
			} else {
				/**
				 *若集合不存在該元素 返回最左側近鄰下標
				 */
				right = middle;
				low = middle + 1;
			}
		}
		 if(just!=0){
	        	return just;
	        }else{
	        	return right;
	        }
	}

四:     拿到左右三公里區間商家lbs位置資訊後,拿到每位商家位置點與中心點的位置資訊。

	/**
	 * 商家點與中心點的距離
	 * 單位米
	 * @param lng1
	 * @param lat1
	 * @param lng2
	 * @param lat2
	 * @return
	 */
	 public static double GetDistance(double lng1, double lat1, double lng2, double lat2){
		 double radLat1 = rad(lat1);
		 double radLat2 = rad(lat2);
		 double a = radLat1 - radLat2;
		 double b = rad(lng1) - rad(lng2);
		 double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) + 
				  Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2)));
		   s = s * EARTH_RADIUS;
		   s = Math.round(s * 10000) / 10000;
		   return s;
	 }

	 private static double rad(double d){
		 return d * Math.PI / 180.0;
	 }

  五:測試程式碼: