轉載請註明出處:http://blog.csdn.net/yianemail/article/details/47130525
現在大多基於地圖的應用都有查看附近商家的實現,之前在網上看到別的一些方法,大多是去數據庫中查看每條數據庫記錄的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;
}
五:測試代碼: