当前位置:   article > 正文

基于MongoDB的空间数据存储与查询_mongodb 空间查询

mongodb 空间查询

一、概念说明

1.1 空间地理数据


MongoDB 中使用 GeoJSON对象 或 坐标对 描述空间地理数据。MongoDB使用 WGS84 参考系进行地理空间数据查询。
1、MongoDB支持空间数据的存储,数据类型需要限制为GeoJSON;
2、MongoDB可以为GeoJSON类型数据建立索引,提升空间查询的效率;

1.2 GeoJSON对象


GeoJSON 对象格式

  1. <field>: { type: <GeoJSON type> , coordinates: <coordinates> }

GeoJSON 对象有两个filed,分别是 type 和 coordinates.其中,

  • type 指明是哪种空间地理数据类型

  • coordinates: 是描述 Geo对象的坐标数组,经度在前(经度取值范围 -180到 180),纬度在后(纬度取值范围是-90到90

二、功能演示操作

2.1 准备环境与初始数据


2.1.1、使用SpringBoot 和 MongoTemplate操作
增加MongoDB连接配置

  1. spring:
  2. data:
  3. # MongoDB配置
  4. mongodb:
  5. uri: mongodb://usr:usrpassword@192.168.xx.xx:27017/
  6. database: filedata
  7. authentication-database: admin
  8. #自动创建索引
  9. auto-index-creation: true
  10. connections-num-min-size: 5
  11. connections-num-max-size: 10

2.1.2、创建GeoData对象存储空间数据

  1. @Data
  2. @ApiModel
  3. @Document(collection = "GEO-DATA")
  4. public class GeoData {
  5. @ApiModelProperty(name = "_id",value = "_id")
  6. private String _id;
  7. @ApiModelProperty(name = "recordId",value = "recordId")
  8. private String recordId;
  9. @ApiModelProperty(name = "name",value = "名称")
  10. private String name;
  11. /** 经度 */
  12. @ApiModelProperty(name = "lng",value = "经度")
  13. private Double lng;
  14. /** 维度 */
  15. @ApiModelProperty(name = "lat",value = "维度")
  16. private Double lat;
  17. /**
  18. * 位置信息
  19. */
  20. @ApiModelProperty(name = "location",value = "位置信息", hidden = true)
  21. private GeoJsonPoint location;
  22. @ApiModelProperty(name = "time",value = "录入时间")
  23. private Long time;
  24. }

2.1.3、增加集合GEO-DATA并创建对应的空间索引

db.getCollection("GEO-DATA").ensureIndex( { location :"2dsphere" } )

2.1.4、创建测试类MongoGeoTest

  1. @Slf4j
  2. @RunWith(SpringRunner.class)
  3. @SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
  4. public class MongoGeoTest {
  5. @Autowired
  6. private MongoTemplate mongoTemplate;
  7. }

2.1.5、增加批量插入数据的方法

  1. /**
  2. * 批量插入数据
  3. */
  4. public void batchInsertData() {
  5. //准备数据
  6. List<GeoData> geoDataList = new ArrayList<>();
  7. for (int i = 0; i < 10; i++) {
  8. GeoData geoData = new GeoData();
  9. geoData.setRecordId(UUID.fastUUID().toString(Boolean.TRUE));
  10. geoData.setName(RandomUtil.randomNumbers(12));
  11. geoData.setTime(new Date().getTime());
  12. //经度
  13. double lng = 116.3180D + RandomUtil.randomDouble(0.1d, 1.0d);
  14. geoData.setLng(lng);
  15. //维度
  16. double lat = 39.9857D + RandomUtil.randomDouble(0.1d, 1.0d);
  17. geoData.setLat(lat);
  18. geoData.setLocation(new GeoJsonPoint(lng, lat));
  19. geoDataList.add(geoData);
  20. }
  21. //保存数据
  22. Long start = System.currentTimeMillis();
  23. mongoTemplate.insert(geoDataList, "GEO-DATA");
  24. log.info("Mongo save documents to GEO-DATA 耗时:{} 毫秒", System.currentTimeMillis() - start);
  25. }

2.2 多边形区域内查询


2.2.1、创建查询参数类MultiPositionPageQueryParam

  1. @Data
  2. @ApiModel
  3. public class MultiPositionPageQueryParam {
  4. @ApiModelProperty(name = "positions",value = "位置集合")
  5. private List<BDSPosition> positions;
  6. @ApiModelProperty(name = "geoType", value = "类型: 1-多点(位置)查询;2-面(区域)查询")
  7. private Integer geoType;
  8. @NotNull
  9. @ApiModelProperty(name = "pageNum",value = "pageNum 起始数字为 0")
  10. private Long pageNum;
  11. @NotNull
  12. @ApiModelProperty(name = "pageSize",value = "pageSize")
  13. private Long pageSize;
  14. @ApiModelProperty(name = "needCount",value = "是否需要统计总记录数")
  15. private Boolean needCount = Boolean.FALSE;
  16. }

2.2.2、增加多边形区域查询方法

  1. /**
  2. * 多边形区域内
  3. *
  4. * @param queryParam
  5. */
  6. public void queryGeoDataByMultiPositionPageQueryParam(MultiPositionPageQueryParam queryParam) {
  7. Query query = new Query();
  8. Criteria criteria = new Criteria();
  9. List<Criteria> criteriaList = new LinkedList<>();
  10. //过滤字段
  11. query.fields().include("recordId", "_id", "name", "time", "lng", "lat", "location");
  12. //位置集合过滤
  13. if (ObjectUtil.isNotNull(queryParam.getPositions()) && queryParam.getPositions().size() > 0) {
  14. // 类型: 1-多点(位置)查询;2-面(区域)查询
  15. if (ObjectUtil.isNotNull(queryParam.getGeoType()) && queryParam.getGeoType() == 2 && queryParam.getPositions().size() > 2) {
  16. List<Point> pointList = new LinkedList<>();
  17. //经纬度获取
  18. for (BDSPosition position : queryParam.getPositions()) {
  19. Point point = new Point(position.getLng(), position.getLat());
  20. pointList.add(point);
  21. }
  22. pointList.add(pointList.get(0));
  23. GeoJsonPolygon geoJsonPolygon = new GeoJsonPolygon(pointList);
  24. Criteria areaCriteria = Criteria.where("location").within(geoJsonPolygon);
  25. query.addCriteria(areaCriteria);
  26. criteriaList.add(areaCriteria);
  27. } else {
  28. List<Criteria> orCriteriaList = new LinkedList<>();
  29. //经纬度判断
  30. for (BDSPosition position : queryParam.getPositions()) {
  31. orCriteriaList.add(Criteria.where("lng").is(position.getLng()).and("lat").is(position.getLat()));
  32. }
  33. Criteria orPositionCriteria = new Criteria().orOperator(orCriteriaList);
  34. query.addCriteria(orPositionCriteria);
  35. criteriaList.add(orPositionCriteria);
  36. }
  37. }
  38. //总记录数统计
  39. Long total = null;
  40. if (queryParam.getNeedCount()) {
  41. total = mongoTemplate.findDistinct(query, "recordId", "GEO-DATA", String.class).stream().count();
  42. }
  43. //排序
  44. List<Sort.Order> orders = new LinkedList<>();
  45. orders.add(Sort.Order.desc("time"));
  46. AggregationOptions aggregationOptions = AggregationOptions.builder().allowDiskUse(Boolean.TRUE).build();
  47. Aggregation aggregation = null;
  48. if (criteriaList.size() > 0) {
  49. criteria = criteria.andOperator(criteriaList);
  50. aggregation = Aggregation.newAggregation(
  51. Aggregation.project("recordId", "_id", "name", "time", "lng", "lat", "location"),
  52. //查询条件
  53. Aggregation.match(criteria),
  54. //分组条件
  55. Aggregation.group("recordId").max("time").as("time")
  56. .first("recordId").as("recordId")
  57. .last("time").as("time"),
  58. Aggregation.sort(Sort.by(orders)),
  59. //分页条件
  60. Aggregation.skip(queryParam.getPageNum()),
  61. Aggregation.limit(queryParam.getPageSize())
  62. ).withOptions(aggregationOptions);
  63. } else {
  64. aggregation = Aggregation.newAggregation(
  65. Aggregation.project("recordId", "_id", "name", "time", "lng", "lat", "location"),
  66. //分组条件
  67. Aggregation.group("recordId").max("time").as("time")
  68. .first("recordId").as("recordId")
  69. .first("time").as("time"),
  70. Aggregation.sort(Sort.by(orders)),
  71. //分页条件
  72. Aggregation.skip(queryParam.getPageNum()),
  73. Aggregation.limit(queryParam.getPageSize())
  74. ).withOptions(aggregationOptions);
  75. }
  76. List<GeoData> list = mongoTemplate.aggregate(aggregation, "GEO-DATA", GeoData.class).getMappedResults();
  77. log.info("Data: {}", list);
  78. }

2.3 圆形区域内查询

2.3.1、创建查询参数类CirclePageQueryParam

  1. @Data
  2. @ApiModel
  3. public class CirclePageQueryParam {
  4. @NotNull
  5. @ApiModelProperty(name = "lng", value = "经度")
  6. private Double lng;
  7. @NotNull
  8. @ApiModelProperty(name = "lat", value = "维度")
  9. private Double lat;
  10. @NotNull
  11. @ApiModelProperty(name = "radius", value = "半径")
  12. private Double radius;
  13. @NotNull
  14. @ApiModelProperty(name = "pageNum",value = "pageNum 起始数字为 0")
  15. private Long pageNum;
  16. @NotNull
  17. @ApiModelProperty(name = "pageSize",value = "pageSize")
  18. private Long pageSize;
  19. @ApiModelProperty(name = "needCount",value = "是否需要统计总记录数")
  20. private Boolean needCount = Boolean.FALSE;
  21. }

2.3.2、增加圆形区域查询方法

  1. /**
  2. * 圆形区域内查询
  3. * @param queryParam
  4. */
  5. public void queryGeoDataByCircle(CirclePageQueryParam queryParam) {
  6. Query query = new Query();
  7. Criteria criteria = new Criteria();
  8. List<Criteria> criteriaList = new LinkedList<>();
  9. //过滤字段
  10. query.fields().include("recordId", "_id", "name", "time", "lng", "lat", "location");
  11. //位置集合过滤
  12. if (ObjectUtil.isNotNull(queryParam.getLat()) && ObjectUtil.isNotNull(queryParam.getLng())
  13. && ObjectUtil.isNotNull(queryParam.getRadius())) {
  14. Point point = new Point(queryParam.getLng(), queryParam.getLat());
  15. Distance distance = new Distance(queryParam.getRadius(), Metrics.MILES);
  16. Circle circle = new Circle(point, distance);
  17. Criteria areaCriteria = Criteria.where("location").withinSphere(circle);
  18. query.addCriteria(areaCriteria);
  19. criteriaList.add(areaCriteria);
  20. }else{
  21. log.info("参数有误,必要参数为空。");
  22. return;
  23. }
  24. //总记录数统计
  25. Long total = null;
  26. if (queryParam.getNeedCount()) {
  27. total = mongoTemplate.findDistinct(query, "recordId", "GEO-DATA", String.class).stream().count();
  28. }
  29. //排序
  30. List<Sort.Order> orders = new LinkedList<>();
  31. orders.add(Sort.Order.desc("time"));
  32. AggregationOptions aggregationOptions = AggregationOptions.builder().allowDiskUse(Boolean.TRUE).build();
  33. Aggregation aggregation = null;
  34. if (criteriaList.size() > 0) {
  35. criteria = criteria.andOperator(criteriaList);
  36. aggregation = Aggregation.newAggregation(
  37. Aggregation.project("recordId", "_id", "name", "time", "lng", "lat", "location"),
  38. //查询条件
  39. Aggregation.match(criteria),
  40. //分组条件
  41. Aggregation.group("recordId").max("time").as("time")
  42. .first("recordId").as("recordId")
  43. .last("time").as("time"),
  44. Aggregation.sort(Sort.by(orders)),
  45. //分页条件
  46. Aggregation.skip(queryParam.getPageNum()),
  47. Aggregation.limit(queryParam.getPageSize())
  48. ).withOptions(aggregationOptions);
  49. } else {
  50. aggregation = Aggregation.newAggregation(
  51. Aggregation.project("recordId", "_id", "name", "time", "lng", "lat", "location"),
  52. //分组条件
  53. Aggregation.group("recordId").max("time").as("time")
  54. .first("recordId").as("recordId")
  55. .first("time").as("time"),
  56. Aggregation.sort(Sort.by(orders)),
  57. //分页条件
  58. Aggregation.skip(queryParam.getPageNum()),
  59. Aggregation.limit(queryParam.getPageSize())
  60. ).withOptions(aggregationOptions);
  61. }
  62. List<GeoData> list = mongoTemplate.aggregate(aggregation, "GEO-DATA", GeoData.class).getMappedResults();
  63. log.info("Data: {}", list);
  64. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/503977
推荐阅读
相关标签
  

闽ICP备14008679号