随着信息量的急剧增长,LBS技术现今已与我们的生活密不可分,今天主要分享的是基于MongoDB下,根据经纬度获取附近商家的实现。MongoDB是一个基于分布式文件存储的高性能数据库。
对于距离的计算我们知道两点间直线最短,当两个元素的距离不是很远时,通过区域划分,可以直接使用勾股定理就能算得元素之间的距离。我们平时使用的「附近的人」的功能,元素距离都不是很大,勾股定理算距离足矣。不过需要注意的是,经纬度坐标的密度不一样 (地球是一个椭圆),勾股定律计算平方差时之后再求和时,需要按一定的系数比加权求和,如果不求精确的话,也可以不必加权,当然我们现实生活处于一个三维空间,除了经度和维度之外还有一个高程的概念也就是海拔。
当前主流的距离算法(geoHash算法)包括redis和MongoDB在计算距离是都是使用该算法的,geohash简单来说就是将二位的平面坐标转换成一维的整数,对于地理坐标数据我们不好构建索引,当将其转成一维整数时我们就能够很容易的构建索引提高查询效率,无论是范围的Btree还是精确的hash索引。
首先假设我们将需要索引的整个地图分成16×16的方格,如下图(左下角为坐标0,0 右上角为坐标16,16):
MongoDB在建立索引的时候,会根据相应字段的坐标计算一个可以用来做索引的hash值,这个值叫做geohash,下面我们以地图上坐标为[4,6]的点(图中红叉位置)为例。
我们第一步将整个地图分成等大小的四块,这样我们在实际中 将各个区域的位置定义成对应的编号,00、01、10、11。这样之前的4,6坐标就变成了整数00。
我们可以继续往下细分那么之前的坐标点4,6就变成了0011,当我们继续往下细分的时候得到的结果值就更加精确了,MongoDB中是切了26次52位的,将得到结果建立索引提高查询效率。
A、插入地理坐标
db.mapinfo.insert({"address" : "深圳市丰巢","loc" : { "type": "Point", "coordinates": [111.111111,122.222222]}})
B、地理坐标索引构建
db.mapinfo.ensureIndex( { loc : "2dsphere" } )
C、查询一定范围内的坐标 倒序 单位是米
db.mapinfo.find({ "loc" : { "$near" : { "$geometry" :
{ "type" : "Point", "coordinates" : [111.111111,122.222222] },
"$maxDistance" : 5000 } } }).limit(50);
Java存储,存储到mongo的地理坐标需要重新构建一个对象存放,查询方圆一公里内距离最近的点,升序排列
DBObject query=new BasicDBObject();
query.put("geomp",newBasicDBObject("$nearSphere",
newBasicDBObject("$geometry",newBasicDBObject("type","Point")
.append("coordinates",newdouble[] {lng,lat})).append("$maxDistance",meter)));
if(StringFormatter.strIsNotNull(businessMongoEntity.getName())) {
Patternpattern= Pattern.compile(
"^.*"+businessMongoEntity.getName() +".*$",Pattern.CASE_INSENSITIVE);
query.put("name",pattern);
}
Query query1=new BasicQuery(query);
query1.skip(fromcount);
query1.limit(page.getSize());
List bme=template.find(query1,
BusinessMongoEntity.class);
inttotal=template.getCollection("businessMongoEntity").find(query).count();
// List list =
// template.getCollection("businessMongoEntity").find(query).skip(10).limit(20).toArray();
page.setRecords(bme);
page.setTotal(total);
return page;
java更新代码
DBObjectdbObject=newBasicDBObject();
dbObject.put("registernum",businessMongoEntity.getRegisternum());
DBObjectdbObjectupdate=newBasicDBObject();
dbObjectupdate.put("imgurl",businessMongoEntity.getImgurl());
dbObjectupdate.put("openid",businessMongoEntity.getOpenid());
dbObjectupdate.put("geomp",businessMongoEntity.getGeomp());
dbObjectupdate.put("username",businessMongoEntity.getUsername());
dbObjectupdate.put("password",businessMongoEntity.getPassword());
//template.getCollection("businessMongoEntity").update(dbObject, dbObjectupdate);
Updateupdate= Update.update("geomp",businessMongoEntity.getGeomp())
.set("imgurl",businessMongoEntity.getImgurl())
.set("openid",businessMongoEntity.getOpenid())
.set("username",businessMongoEntity.getUsername()).set("password",businessMongoEntity.getPassword());
Queryquery=newBasicQuery(dbObject);
template.upsert(query,update, BusinessMongoEntity.class)
计算两点间的距离
/**
* 通过经纬度获取距离(单位:米)
* @param lat1 纬度
* @param lng1 经度
* @param lat2 纬度
* @param lng2 经度
* @return
*/
public static double getDistance(double lat1, double lng1, double lat2,
double lng2) {
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 * 10000d) / 10000d;
s = s*1000;
return s;
}
导入架包
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
构建坐标对象对象
public class GeomArrayBean {
private String type;
private Double[] coordinates;
public GeomArrayBean(){
type = "Point";
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Double[] getCoordinates() {
return coordinates;
}
public void setCoordinates(Double[] coordinates) {
this.coordinates = coordinates;
}
}
数据存储
BusinessMongoEntity businessMongoEntity=new BusinessMongoEntity();
businessMongoEntity.setId(IdWorker.getInstance().nextId());
//构建存储对象
GeomArrayBean ga = new GeomArrayBean();
ga.setType("Point");
Double a = 118.803799d;
Double b = 31.979234d;
Double[] aa = {a, b};
ga.setCoordinates(aa);
businessMongoEntity.setName("林青2");
businessMongoEntity.setGeomp(ga);
businessMongoEntity.setUsername("18050010830");
businessMongoEntity.setPassword("010830");
businessMongoEntity.setRegisternum("666");
businessMongoEntity.setImgurl("www.baidu.com");
businessMongoEntity.setOpenid("7890");
businessMongoEntity.setTel("联系电话");
businessMongoEntity.setContact("联系人");
businessMongoService.insertBusinessMongoEntity(businessMongoEntity);