Mongo 地理位置查询:海量密集点转换成聚合信息
通俗来说:将地图上的海量密集点通过网格分割的方式实现聚合;
需求:用mongo实现设备地理位置聚合查询 :多边形,矩形查询;
背景:上万设备数据量
目的:分享Mongo地理位置查询,以及文末对在此之前的两种实现方式做分析比较,纠正一些开发中的错误认知;
1、自定义:数据库查询
//多边形 $polygon
@Query(value = "{'position': {$exists: true}, $and: [{'position': {$geoWithin: { $polygon : ?0 }}}, {'deletedAt': null}]}" , fields = "{ 'position': 1 }")List<Thing> findByProvinceBoundary(double[][] provinceBoundary);//矩形 $box@CountQuery("{$and: [{'position': {$exists: true}}, {'deletedAt': null},"+ "{'position': {$geoWithin: { $box: [?0, ?1] }}}]}")long countByProvinceBoundaryOrBoundingBox(double[] bottomLeft, double[] topRight);
注意:MongoDB 不支持在一个查询中同时使用 $polygon 和 $box;
2、如果需要求中心点,可以使用聚合查询,实现加权平均权重点
@Aggregation(pipeline = {"{ $match: { $and: [ { 'position': { $exists: true } }, { 'deletedAt': null }, { 'position': { $geoWithin: { $box: [?0, ?1] } } } ] } }","{ $group: { _id: null, longitude: { $avg: '$position.longitude' }, latitude: { $avg: '$position.latitude' } } }"})GeoPoint findWeightedCenter(double[] bottomLeft, double[] topRight);
3、完整设备位置聚合信息查询:最佳方案
@Overridepublic ThingGeo getAggregatedThingGeo(ThingGeoReqDTO reqDTO) {Area area = areaRepository.getAreaByCode();//行政编码//1.行政编码区域的中心点,查询没有位置的设备总数:JSONObject properties = area.getBound().getJSONArray("features").getJSONObject(0).getJSONObject("properties");JSONArray centerPosition= properties.getJSONArray("center"); //中心点位置double centerLon = centerPosition.getDouble(0);double centerLat = centerPosition.getDouble(1);GeoPoint centerPoint = new GeoPoint(centerLon, centerLat);long noGeoThingCount = thingRepository.countByNoGeoPosition();GridCellThing noGeoThings = new GridCellThing(centerPoint,noGeoThingCount);//2.网格查询有位置信息的设备总数以及权重点double[] topRight = reqDTO.getTopRight();double[] bottomLeft = reqDTO.getBottomLeft();// 计算X和Y的差值(视图长和宽)double deltaX = topRight[0] - bottomLeft[0];double deltaY = topRight[1] - bottomLeft[1];// 计算X和Y的平均值double avgX = deltaX / 4;double avgY = deltaY / 4;// 使用右上角作为起始点double x = topRight[0];double y = topRight[1];List<GridCellThing> gridCellThings = new ArrayList<>();// 循环生成4*4=16网格for (int a = 0; a < 4; a++) {for (int i = 0; i < 4; i++) {// 计算网格边界double minX = x - (i + 1) * avgX;double maxX = x - i * avgX;double minY = y - (a + 1) * avgY;double maxY = y - a * avgY;//小网格:两个对角经纬度double[] boxTopRight = new double[] {maxX, maxY};double[] boxBottomLeft = new double[] {minX, minY};long boxCount = thingRepository.countByBoundingBox(boxBottomLeft,boxTopRight);if (boxCount > 0) {GeoPoint center = thingRepository.findWeightedCenter(boxBottomLeft, boxTopRight);GeoPoint boxCenter = new GeoPoint(center.getLongitude(),center.getLatitude());GridCellThing gridCellThing = new GridCellThing();gridCellThing.setThingCount(boxCount);gridCellThing.setPosition(boxCenter);gridCellThings.add(gridCellThing);}}}ThingGeo thingGeo = new ThingGeo();thingGeo.setGridCellList(gridCellThings);thingGeo.setNoGeoThings(noGeoThings);return thingGeo;}
4、在此之前的 踩坑错误实现代码:在数据量多的时候,导致内存溢出;
因为拿到几万条设备信息导致内存溢出;
public static List<GridCellThing> getGridCellThings(ThingGeoReqDTO reqVO, List<Thing> things) {double[] topRight = reqVO.getTopRight();double[] bottomLeft = reqVO.getBottomLeft();// 计算X和Y的差值(视图长和宽)double deltaX = topRight[0] - bottomLeft[0];double deltaY = topRight[1] - bottomLeft[1];// 计算X和Y的平均值double avgX = deltaX / 4;double avgY = deltaY / 4;// 使用右上角作为起始点double x = topRight[0];double y = topRight[1];List<GridCellThing> gridCellThings = new ArrayList<>();// 循环生成4*4=16网格for (int a = 0; a < 4; a++) {for (int i = 0; i < 4; i++) {GridCellThing gridCellThing = new GridCellThing();// 计算网格边界double minX = x - (i + 1) * avgX;double maxX = x - i * avgX;double minY = y - (a + 1) * avgY;double maxY = y - a * avgY;double centerTotalX = 0;double centerTotalY = 0;int count = 0;// 遍历设备列表for (Thing thing : things) {double longitude = thing.getPosition().getLongitude();double latitude = thing.getPosition().getLatitude();// 检查设备是否在当前网格内,根据最大值最小值区分来做到去重边界相交的设备;if (longitude > minX && longitude <= maxX && latitude > minY && latitude <= maxY) {count++;//得到有效设备的经纬度总和centerTotalX += longitude;centerTotalY += latitude;}}// 如果有符合条件的设备,则记录到响应对象列表中if (count > 0) {//分别把经度总和、纬度总和 除以count(网格区域内设备总数)得到经纬度的加权平均值double centerLongitude = centerTotalX / count;double centerLatitude = centerTotalY / count;GeoPoint geoPoint = new GeoPoint();geoPoint.setLongitude(centerLongitude);geoPoint.setLatitude(centerLatitude);gridCellThing.setPosition(geoPoint);gridCellThing.setThingCount(count);gridCellThings.add(gridCellThing);}}}return gridCellThings;}
两种实现方式分析:
方法一:服务端实现 查询数据库后拿到上万设备信息后再在业务实现层进一步对数据进行循环过滤等操作,
方法二:在数据库查询以及在数据库实现其他操作;
在服务端实现设备地理位置聚合信息时,方法一和方法二的选择取决于多个因素,如性能、可维护性、复杂性和数据量。下面是对这两种方法的详细分析:
方法一:在业务实现层进行过滤和操作
优点:
- 灵活性:在业务层处理数据,您可以更灵活地应用复杂的业务逻辑。
- 可维护性:业务逻辑和数据库查询逻辑分开,更易于管理和调试。
- 缓存机制:可以更容易地实现数据缓存,以减少频繁的数据库查询。
缺点:
- 性能瓶颈:将大量数据加载到内存中进行处理,会占用大量的内存资源,可能导致性能瓶颈,尤其是在数据量很大的情况下。
- 网络开销:传输大量数据从数据库到应用服务器,会增加网络带宽的开销。
- 延迟:处理大量数据会增加响应时间,导致用户体验不佳。
方法二:在数据库中进行聚合和操作
优点:
- 高效:数据库系统通常对大规模数据的处理进行了高度优化,能够更高效地执行聚合和过滤操作。
- 减少数据传输:只传输必要的聚合结果而不是原始数据,减少网络带宽的使用。
- 性能优势:数据库层面的操作可以利用索引、缓存等优化机制,提升查询性能。
缺点:
- 复杂性:在数据库中实现复杂的业务逻辑可能会增加查询的复杂性,难以调试和维护。
- 数据库负载:将大量计算操作放在数据库中,可能增加数据库服务器的负载。
- 灵活性:可能需要编写复杂的数据库脚本,对于变更和扩展不如业务层处理灵活。
选择建议:
在大多数情况下,方法二(在数据库中进行聚合和操作)通常是首选,特别是在处理大数据量时,理由如下:
- 性能:数据库聚合操作通常比在业务层进行大规模数据处理更快。
- 减少数据传输:只传输必要的聚合结果,减少网络带宽的使用。
- 简化业务逻辑:让数据库处理繁重的数据操作,简化业务层的代码。
然而,也有一些场景可能更适合方法一:
结论:
最终选择应根据实际情况、系统架构和业务需求综合考虑。
- 复杂业务逻辑:如果聚合逻辑非常复杂,数据库难以实现或者维护,可以考虑在业务层处理。
- 数据库负载:如果数据库负载已经很高,可能需要将部分处理移到应用层。
- 优先选择方法二:对于处理大量数据和需要高效聚合操作的场景,优先选择在数据库中进行操作。
- 灵活调整:根据具体业务需求和系统架构,灵活调整部分处理逻辑在业务层和数据库层之间的分配。
相关文章:
Mongo 地理位置查询:海量密集点转换成聚合信息
通俗来说:将地图上的海量密集点通过网格分割的方式实现聚合; 需求:用mongo实现设备地理位置聚合查询 :多边形,矩形查询; 背景:上万设备数据量 目的:分享Mongo地理位置查询,以及文…...
bpmn+vue 中文文档
1.初始化项目 <script> import BpmnModeler from bpmn-js/lib/Modeler import { xmlStr } from /mock/xmlStr export default {mounted () {this.init()},methods: {init () {// 获取到属性ref为“canvas”的dom节点const canvas this.$refs.canvas// 建模const custom…...
React Router v5 和 v6 中,路由对象声明方式有什么区别?
一、在React Router 6.x开始,路由对象的声明需要引用RouteObject。 import { RouteObject } from react-router-dom;const routes: RouteObject[] [{path: /,element: <Home />},{path: /about,element: <About />},// ... ];二、一些老项目使用的是R…...
【全开源】知识库文档系统(ThinkPHP+FastAdmin)
📚知识库文档系统:解锁知识的无限可能 一款基于ThinkPHPFastAdmin开发的知识库文档系统,可用于企业工作流程的文档管理,结构化记录沉淀高价值信息,形成完整的知识体系,能够轻松提升知识的流转和传播效率&a…...
Python赋能自然语言处理,解锁通往AI的钥匙
NLTK(Natural Language Toolkit)是一个用于 Python 的自然语言处理库,提供了丰富的工具和资源,帮助处理、分析和理解人类语言数据.它广泛应用于学术研究、教育和商业应用中. 安装 #首先要安装 NLTK:pip install nltk安装完成后,还需要下载…...
Ktor库的高级用法:代理服务器与JSON处理
在现代网络编程中,Ktor是一个高性能且易于使用的框架,它提供了对异步编程、WebSockets、HTTP客户端和服务器等特性的原生支持。Ktor是使用Kotlin语言编写的,充分利用了Kotlin的协程特性来简化异步编程。本文将深入探讨Ktor库的高级用法&#…...
VS2017配置OpenCV4.5.1
VS2017配置OpenCV 一、下载OpenCV二、配置OpenCV的电脑环境变量三、配置visual Studio添加路径复制文件到C盘 四、如何使用注意运行时选择Debug x64 五、报错:VSOpencv出现:xxx处有未经处理的异常: Microsoft C 异常: cv::Exception,位于内存…...
phpstudy配置的站点不能访问了
无法打开站点 打开网站的时候出现如下 没有人为主动去更改配置项,今天就不能正常访问了 检查了一遍配置,发现并无大碍,那就重新配置一遍看看 配置phpstudy 1、新建网站 2、选择项目入口文件夹 3、配置伪静态 4. 确认保存 在我的电脑 C:\…...
Java Web学习笔记2——Web开发介绍
什么是Web? Web:全球广域网,也称为万维网(WWW World Wide Web),能够通过浏览器访问的网站。 1)淘宝、京东、唯品会等电商系统; 2)CRM、OA、ERP企业管理系统࿱…...
从零开始实现自己的串口调试助手(3) - 显示底部收发,优化串口打开/关闭
注意: 1. 我们要实现自发自收,要将tx,rx连起来 2.发送的 不能是中文符号,因为这可能导致,读取到的是英文符号 --> 导致接收到的size 和发送的size 大小不一致 3.注意同时定义两个槽函数的时候两个槽函数都会被调用,…...
更改Web网站设计——css和css框架
虽然使用HTML可以定义文章的结构,但是其中不包含设计相关的信息。此时CSS就派上用场,可以用它对HTML文章指定设计样式。由于可以决定Web网页的外观风格,因此,它有时也被称为格式表。 如果使用CSS设置背景色,文…...
持续监控和优化的简单介绍
DevOps 监控提供了有关生产环境状况的全面且最新的信息,以及有关其服务、基础设施和应用程序的详细信息。通过从日志和指标中收集数据,您可以在软件开发生命周期的每个步骤中监控合规性和性能。 监控不仅仅针对生产问题,它涵盖了规划、开发、…...
针对硅基氮化镓高电子迁移率晶体管(GaN-HEMT)的准物理等效电路模型,包含基板中射频漏电流的温度依赖性
来源:Quasi-Physical Equivalent Circuit Model of RF Leakage Current in Substrate Including Temperature Dependence for GaN-HEMT on Si(TMTT 23年) 摘要 该文章提出了一种针对硅基氮化镓高电子迁移率晶体管(GaN-HEMT&…...
基于websocket与node搭建简易聊天室
一、前言 上一篇文章介绍了websocket的详细用法与工具类的封装,本篇就基于websocket搭建一个简易实时的聊天室。 在本篇开始之前也可以去回顾一下websocket详细用法:WebSocket详解与封装工具类 二、基于node搭建后台websocket服务 首先确认本机电脑中…...
DevOps全面综述:从概念到实践
一、背景与概述 1.1 DevOps的起源与发展 DevOps(Development and Operations的缩写)是软件工程领域中的一种文化和实践方法,旨在促进开发团队与运维团队之间的协作,从而实现更高效、更可靠的软件交付。DevOps起源于敏捷软件开发方…...
[C++]vector的模拟实现
下面是简单的实现vector的功能,没有涉及使用内存池等复杂算法来提高效率。 一、vector的概述 (一)、抽象数据类型定义 容器:向量(vector)vector是表示大小可以变化的数组的序列容器。像数组一样…...
【云原生】Kubernetes----POD控制器
目录 引言 一、Pod控制器概述 二、Pod控制器的种类 (一)ReplicaSet (二)Deployment (三)StatefulSet (四)DaemonSet (五)Job 三、使用POD控制器 &a…...
Java环境配置(超详细)
Java环境配置(超详细) 引言1、安装 JDK1.1、下载安装JDK1.2、配置环境变量:JAVA_HOME1.3、将JAVA_HOME添加到Path中 2、安装 Maven2.1、下载安装Maven2.2、配置maven的环境变量: M2_HOME2.3、将Maven变量添加到Path中 引言 Java开发环境的配…...
【操作系统】(详细理解进程的状态)执行状态、就绪状态、阻塞状态、挂起状态
下面是进程的几种状态的概念: 执行状态:当一个进程已获得必要资源,并占有CPU进行执行。 就绪状体:进程已分配到除CPU外的所有必要资源,只要获取CPU允许就可立即执行。 阻塞状态:正在执行的进程,…...
C++ -- string常用接口的底层实现
一.string介绍 1. string是表示字符串的字符串类,对C语言的字符串指针进行了包装。 2. 该类的接口与常规容器的接口基本相同,有增删查改等,再添加了一些专门用来操作string的常规操作。 二.成员变量 创建string类的时候要在自己的命名空间…...
以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...
高考志愿填报管理系统---开发介绍
高考志愿填报管理系统是一款专为教育机构、学校和教师设计的学生信息管理和志愿填报辅助平台。系统基于Django框架开发,采用现代化的Web技术,为教育工作者提供高效、安全、便捷的学生管理解决方案。 ## 📋 系统概述 ### 🎯 系统定…...
文件上传漏洞防御全攻略
要全面防范文件上传漏洞,需构建多层防御体系,结合技术验证、存储隔离与权限控制: 🔒 一、基础防护层 前端校验(仅辅助) 通过JavaScript限制文件后缀名(白名单)和大小,提…...
