当前位置: 首页 > web >正文

MongoDB06 - MongoDB 地理空间

MongoDB06 - MongoDB 地理空间

文章目录

  • MongoDB06 - MongoDB 地理空间
    • 一:地理空间数据基础
      • 1:地理数据表示方式
        • 1.1:GeoJSON 格式
        • 1.2:传统坐标对
      • 2:地理空间索引
        • 2.1:2dsphere 索引
        • 2.2:2d索引
        • 2.3:混合索引
    • 二:地理空间查询和聚合
      • 1:完全包含于几何图形
      • 2:与指定几何图形相交
      • 3:找附近点并按距离排序
      • 4:地理空间的聚合操作
      • 5:地理空间计算函数
    • 三:实际应用示例
      • 1:附近地点搜索
      • 2:地理围栏检查
      • 3:多点距离计算
    • 四:Spring Boot Data整合
      • 1:依赖和配置
      • 2:数据模型定义
      • 3:Repository 层
      • 4:服务Service层
      • 5:控制Controller层

一:地理空间数据基础

1:地理数据表示方式

MongoDB 支持两种主要的地理空间数据表示方式: GeoJSON 格式 & 传统坐标对[经度,纬度]

  • 经度在前:GeoJSON 规定坐标顺序为 [经度, 纬度]
  • 有效范围:经度:-180 到 180 & 纬度:-90 到 90
1.1:GeoJSON 格式

GeoJSON支持如下的类型:

Point - 点

{ type: "Point", coordinates: [longitude, latitude] 
}

LineString - 线

{ type: "LineString", coordinates: [[lon1,lat1], [lon2,lat2], ...] 
}

Polygon - 多边形(闭合环)

{ type: "Polygon", coordinates: [[[lon1,lat1], [lon2,lat2], ..., [lon1,lat1]] // 外环// 可以有多个内环(洞)
]}

还有一些不太常用的:MultiPoint、MultiLineString、MultiPolygon、GeometryCollection

1.2:传统坐标对

简单数组格式:[longitude, latitude] -> 仅适用于 2d 索引,不支持复杂几何形状

{loc: [longitude, latitude]
}

2:地理空间索引

2.1:2dsphere 索引

地球球面几何计算,支持所有的 GeoJSON 类型,计算球面距离

支持 $nearSphere、$geoWithin、$geoIntersects 等操作

db.collection.createIndex({ <locationField>: "2dsphere" } // 创建索引
)
2.2:2d索引

平面几何计算,仅支持点数据(坐标对),计算平面距离(不考虑地球曲率)。·性能更高但精度较低

db.collection.createIndex({ <locationField>: "2d" }
)
2.3:混合索引

可以组合地理空间索引与其他字段:

db.places.createIndex({ location: "2dsphere", name: 1 }
)

二:地理空间查询和聚合

1:完全包含于几何图形

$geoWithin -> 查找完全包含在指定几何图形内的文档

  • 使用 GeoJSON:$geometry
  • 使用传统坐标:$box$polygon$center$centerSphere
db.places.find({location: {$geoWithin: {// 返回在如下多边形中的文档$geometry: {type: "Polygon",coordinates: [[ [0,0], [3,6], [6,1], [0,0] ]}}}
})

2:与指定几何图形相交

$geoIntersects -> 查找与指定几何图形相交的文档

db.places.find({location: {$geoIntersects: {// 返回和这条线相交的文档$geometry: {type: "LineString",coordinates: [[0,0], [5,5]]}}}
})

3:找附近点并按距离排序

$near -> 找附近点并按距离排序

db.places.find({location: {$near: {// 找到举例给定中心点附近的点,最小距离100m,最大距离500m$geometry: {type: "Point",coordinates: [-73.9667, 40.78]},$maxDistance: 500,  // 米(2dsphere)$minDistance: 100}}
})

4:地理空间的聚合操作

使用$geoNear是基于距离的管道聚合,对于$geoNear有如下的说明:

  • 必须是管道的第一阶段
  • 自动按距离排序
  • 可返回计算的距离值
db.places.aggregate([{$geoNear: {// near:参考点// distanceField:存储距离的字段// maxDistance/minDistance:距离范围// spherical:是否使用球面计算// query:附加查询条件// $geoWithin 可在聚合中使用// 结合 $project 计算自定义地理空间数据near: { type: "Point", coordinates: [-73.9667, 40.78] },distanceField: "distance",maxDistance: 2000,spherical: true,query: { category: "Park" }}}
])

5:地理空间计算函数

$geoDistance:计算两点间距离

{$project: {distance: {$geoDistance: {// 起点是文档中的location字段start: "$location",// 终点是GeoJson的坐标点end: { type: "Point", coordinates: [-73.98, 40.77] },distanceMultiplier: 0.001 // 转换为公里}}}
}

三:实际应用示例

1:附近地点搜索

db.places.find({location: {$nearSphere: {// 在给定点1km之内的文档$geometry: {type: "Point",coordinates: [currentLng, currentLat]},$maxDistance: 1000 // 1公里内}},category: "restaurant"
}).limit(20)

2:地理围栏检查

// 检查点是否在配送区域内
db.deliveryZones.find({area: {$geoIntersects: {$geometry: {type: "Point",coordinates: [orderLng, orderLat]}}}
})

3:多点距离计算

db.stores.aggregate([{$geoNear: {// 指定参考点near: { type: "Point", coordinates: [userLng, userLat] },// 指定输出字段名,用于存储计算的距离值distanceField: "distance",// 使用球面spherical: true}},// 结果过滤,只要5km内的{ $match: { distance: { $lte: 5000 } } }, // 5公里内// 按照距离正序排序{ $sort: { distance: 1 } }
])

四:Spring Boot Data整合

1:依赖和配置

<dependencies><!-- Spring Boot Starter Data MongoDB --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependency><!-- 其他必要依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Lombok (可选) --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
</dependencies>
# application.properties
spring:data:mongodb:host: localhostport: 27017database: geo_db

2:数据模型定义

创建地理空间实体

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.mapping.Document;
import lombok.Data;@Data
@Document(collection = "places")
public class Place {@Idprivate String id;private String name;private String category;private GeoJsonPoint location;  // 使用GeoJsonPoint存储地理坐标// 构造方法、getter/setter等// Lombok的@Data注解会自动生成这些
}

地理空间索引配置

import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;@Configuration
public class MongoConfig {// 添加监听器@Beanpublic BeforeConvertListener beforeConvertListener() {return new BeforeConvertListener();}// 定义监听器,继承地理事件监听器public static class BeforeConvertListener extends AbstractMongoEventListener<Place> {@Overridepublic void onBeforeConvert(BeforeConvertEvent<Place> event) {// 说明要进行索引操作了IndexOperations indexOps = event.getCollection().getIndexOperations();// 定义索引// 确保location字段有2dsphere索引IndexDefinition indexDef = new GeoSpatialIndexDefinition("location").typed(GeoSpatialIndexType.GEO_2DSPHERE);// 添加索引indexOps.ensureIndex(indexDef);}}
}

3:Repository 层

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.repository.Query;
import java.util.List;public interface PlaceRepository extends MongoRepository<Place, String> {// 查找附近的点(按距离排序)List<Place> findByLocationNear(Point point, Distance distance);// 查找指定类别附近的点List<Place> findByCategoryAndLocationNear(String category, Point point, Distance distance);// 使用GeoJSON多边形查询@Query("{ 'location' : { $geoWithin : { $geometry : { type : 'Polygon', coordinates : ?0 } } }")List<Place> findWithinPolygon(List<List<Double[]>> polygonCoordinates);// 查找与指定线相交的地点@Query("{ 'location' : { $geoIntersects : { $geometry : { type : 'LineString', coordinates : ?0 } } }")List<Place> findIntersectingLine(List<Double[]> lineCoordinates);// 如果是聚合查询@Aggregation(pipeline = {"{ $geoNear: { " +"  near: { type: 'Point', coordinates: [ ?0, ?1 ] }, " +"  distanceField: 'distance', " +"  maxDistance: ?2, " +"  spherical: true " +"} }","{ $match: { category: ?3 } }","{ $sort: { distance: 1 } }","{ $limit: ?4 }"})List<Place> findNearbyPlacesWithAggregation(double longitude, double latitude, double maxDistanceInMeters,String category,int limit);
}

4:服务Service层

import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.stereotype.Service;
import java.util.List;@Service
public class GeoService {private final PlaceRepository placeRepository;public GeoService(PlaceRepository placeRepository) {this.placeRepository = placeRepository;}/*** 查找附近的地点* @param longitude 经度* @param latitude 纬度* @param distance 距离(公里)* @return 附近的地点列表*/public List<Place> findNearbyPlaces(double longitude, double latitude, double distance) {Point point = new Point(longitude, latitude);Distance radius = new Distance(distance, Metrics.KILOMETERS);return placeRepository.findByLocationNear(point, radius);}/*** 查找特定类别附近的地点*/public List<Place> findNearbyPlacesByCategory(double longitude, double latitude, double distance, String category) {Point point = new Point(longitude, latitude);Distance radius = new Distance(distance, Metrics.KILOMETERS);return placeRepository.findByCategoryAndLocationNear(category, point, radius);}/*** 多边形区域查询*/public List<Place> findWithinPolygon(List<List<Double[]>> polygonCoordinates) {return placeRepository.findWithinPolygon(polygonCoordinates);}/*** 与线相交的地点查询*/public List<Place> findIntersectingLine(List<Double[]> lineCoordinates) {return placeRepository.findIntersectingLine(lineCoordinates);}// 使用 GeoJson 对象public List<Place> findWithinGeoJsonPolygon(GeoJsonPolygon polygon) {return mongoTemplate.find(Query.query(Criteria.where("location").within(polygon)), Place.class);
}

5:控制Controller层

import org.springframework.web.bind.annotation.*;
import org.springframework.data.geo.Point;
import java.util.List;@RestController
@RequestMapping("/api/places")
public class PlaceController {private final GeoService geoService;public PlaceController(GeoService geoService) {this.geoService = geoService;}@GetMapping("/nearby")public List<Place> getNearbyPlaces(@RequestParam double longitude,@RequestParam double latitude,@RequestParam(defaultValue = "5") double distance) {return geoService.findNearbyPlaces(longitude, latitude, distance);}@GetMapping("/nearby/{category}")public List<Place> getNearbyPlacesByCategory(@PathVariable String category,@RequestParam double longitude,@RequestParam double latitude,@RequestParam(defaultValue = "5") double distance) {return geoService.findNearbyPlacesByCategory(longitude, latitude, distance, category);}@PostMapping("/within-polygon")public List<Place> getPlacesWithinPolygon(@RequestBody List<List<Double[]>> polygonCoordinates) {return geoService.findWithinPolygon(polygonCoordinates);}@PostMapping("/intersecting-line")public List<Place> getPlacesIntersectingLine(@RequestBody List<Double[]> lineCoordinates) {return geoService.findIntersectingLine(lineCoordinates);}
}
http://www.xdnf.cn/news/14815.html

相关文章:

  • PyQt5—QPushButton 功能 API 学习笔记
  • Zynq7020 Linux更新启动分区文件导致文件大小为0的处理方式
  • 力扣第84题-柱状图中最大的矩形
  • Webpack中的Loader详解
  • 用户行为序列建模(篇六)-【阿里】DSIN
  • 实战篇----利用 LangChain 和 BERT 用于命名实体识别-----完整代码
  • flask使用-链接mongoDB
  • Python爬虫-爬取汽车之家全部汽车品牌及车型数据
  • ListExtension 扩展方法增加 转DataTable()方法
  • Lua现学现卖
  • DOP数据开放平台(真实线上项目)
  • 电商返利APP架构设计:如何基于Spring Cloud构建高并发佣金结算系统
  • OpenLayers 下载地图切片
  • 解决cursor无法下载插件等网络问题
  • vue-29(创建 Nuxt.js 项目)
  • 从用户到权限:解密 AWS IAM Identity Center 的授权之道
  • 给定一个没有重复元素的数组,写出生成这个数组的MaxTree的函数
  • TDengine 如何使用 MQTT 采集数据?
  • lambda、function基础/响应式编程基础
  • [论文阅读] 软件工程 | 微前端在电商领域的实践:一项案例研究的深度解析
  • NLP中的同义词替换及我踩的坑
  • 创客匠人视角:创始人 IP 打造为何成为知识变现的核心竞争力
  • 【算法深练】单调栈:有序入栈,及时删除垃圾数据
  • 鸿蒙5:组件监听和部分状态管理V2
  • 为何需要防爆平板?它究竟有何能耐?
  • 【龙泽科技】新能源汽车故障诊断仿真教学软件【吉利几何G6】
  • 学习使用dotnet-dump工具分析.net内存转储文件(2)
  • vue-28(服务器端渲染(SSR)简介及其优势)
  • 舵机在不同类型机器人中的应用
  • Python 数据分析与可视化 Day 10 - 数据合并与连接