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;
}
五:測試程式碼: