地理信息索引分为两类:2D平面索引,2DSphere球面索引。在2D索引里面基本上能够保存的信息都是坐标,而且坐标保存的就是经纬度坐标。
具体的应用场景在:微信的摇一摇,大众点评等查询附近的住宿地。滴滴、膜拜、OFO等基于位置进行查询的场景都可以使用MongoDB的位置索引。下面是使用的实例,在dubbo层进行一层业务封装
客户端检测用户的地理位置,当变化大于500米时或每隔5分钟,向服务端发送地理位置。
package com.tanhua.dubbo.server.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.index.CompoundIndex;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "user_location")
//指定我们UserLocation这个实例中使用的是2dsphere二维球面的索引
@CompoundIndex(name = "location_index", def = "{'location': '2dsphere'}")
public class UserLocation implements java.io.Serializable{
private static final long serialVersionUID = 4508868382007529970L;
@Id
private ObjectId id;
@Indexed
private Long userId; //用户id
private GeoJsonPoint location; //x:经度 y:纬度
private String address; //位置描述
private Long created; //创建时间
private Long updated; //更新时间
private Long lastUpdated; //上次更新时间
}
package com.tanhua.dubbo.server.api;
public interface UserLocationApi {
/**
* 更新用户地理位置
*
* @return
*/
String updateUserLocation(Long userId, Double longitude, Double latitude, String address);
}
package com.tanhua.dubbo.server.api;
import com.alibaba.dubbo.config.annotation.Service;
import com.tanhua.dubbo.server.pojo.UserLocation;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
@Service(version = "1.0.0")
public class UserLocationApiImpl implements UserLocationApi {
@Autowired
private MongoTemplate mongoTemplate;
@Override
public String updateUserLocation(Long userId, Double longitude, Double latitude, String address) {
UserLocation userLocation = new UserLocation();
userLocation.setAddress(address);
userLocation.setLocation(new GeoJsonPoint(longitude, latitude));
userLocation.setUserId(userId);
Query query = Query.query(Criteria.where("userId").is(userLocation.getUserId()));
UserLocation ul = this.mongoTemplate.findOne(query, UserLocation.class);
if (ul == null) {
//新增----mongodb中没有该用户数据
userLocation.setId(ObjectId.get());
userLocation.setCreated(System.currentTimeMillis());
userLocation.setUpdated(userLocation.getCreated());
userLocation.setLastUpdated(userLocation.getCreated());
this.mongoTemplate.save(userLocation);
return userLocation.getId().toHexString();
} else {
//更新
Update update = Update
.update("location", userLocation.getLocation())
.set("updated", System.currentTimeMillis())
.set("lastUpdated", ul.getUpdated());
this.mongoTemplate.updateFirst(query, update, UserLocation.class);
}
return ul.getId().toHexString();
}
}
前端使用的是百度的地图,向后端传送经、纬度以及地址,后端调用dubbo进行地理位置更新。
package com.tanhua.server.controller;
import com.tanhua.server.service.BaiduService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("baidu")
public class BaiduController {
@Autowired
private BaiduService baiduService;
/**
* 更新位置
*
* @param param
* @return
*/
@PostMapping("location")
public ResponseEntity<Void> updateLocation(@RequestBody Map<String, Object> param) {
try {
Double longitude = Double.valueOf(param.get("longitude").toString());
Double latitude = Double.valueOf(param.get("latitude").toString());
String address = param.get("addrStr").toString();
Boolean bool = this.baiduService.updateLocation(longitude, latitude, address);
if (bool) {
return ResponseEntity.ok(null);
}
} catch (Exception e) {
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
package com.tanhua.server.service;
import com.alibaba.dubbo.config.annotation.Reference;
import com.tanhua.dubbo.server.api.UserLocationApi;
import com.tanhua.server.pojo.User;
import com.tanhua.server.utils.UserThreadLocal;
import org.springframework.stereotype.Service;
@Service
public class BaiduService {
@Reference(version = "1.0.0")
private UserLocationApi userLocationApi;
public Boolean updateLocation(Double longitude, Double latitude, String address) {
try {
User user = UserThreadLocal.get();
this.userLocationApi.updateUserLocation(user.getId(), longitude, latitude, address);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
//根据userId查找用户位置
@Override
public UserLocationVo queryByUserId(Long userId) {
Query query = Query.query(Criteria.where("userId").is(userId));
UserLocation one = mongoTemplate.findOne(query, UserLocation.class);
if (null != one) {
return UserLocationVo.format(one);
}
return null;
}
@Override
public List<UserLocationVo> queryUserFromUserLocation(Double x, Double y, Integer range) {
//找到中心点
GeoJsonPoint geoJsonPoint = new GeoJsonPoint(x, y);
//半径---Metric为接口,里面传入他的具体实现类
Distance distance = new Distance(range / 1000, Metrics.KILOMETERS);
//画圆
Circle circle = new Circle(geoJsonPoint, distance);
Query query = Query.query(Criteria.where("location").withinSphere(circle));
List<UserLocation> userLocations = mongoTemplate.find(query, UserLocation.class);
if (userLocations != null) {
return UserLocationVo.formatToList(userLocations);
}
return null;
}