天地图实现海量聚合marker--uniapp后端详细实现

本文章详细的讲解了前后端代码来 实现uniapp天地图功能的实现 以及 后端海量数据的聚合查询 和网格算法实现思路。

并对当数据量增加和用户频繁请求接口时可能导致服务器负载过高做了前后端优化。

前端uniapp:

实现了天地图的行政区划边界/地图切换/比例尺/海量数据聚合marker/获取地图当前可视范围坐标/文本信息窗口 /使用节流、防抖的方式来减少过量请求 等功能

后端java:

实现了海量数据的聚合查询,并对查询语句和逻辑做了优化以及sql索引优化/并通过网格算法来解决数据精准度的难点。

效果如下:

      

                       

前端uniapp实现代码如下:
uniapp/static/skymap.html
<!DOCTYPE html>
<html lang="en">
<head><script src="http://api.tianditu.gov.cn/api?v=4.0&tk=你自己的key"></script><style>body {margin: 0;padding: 0;overflow: hidden;height: 100vh;font-family: "Microsoft YaHei";}#viewDiv {width: 100%;height: 100%;position: absolute;top: 0;left: 0;}</style>
</head>
<body><div id="viewDiv"></div><script>let markerClusterer; // 用于保存聚合器const map = new T.Map("viewDiv");const fetchInterval = 5000; // 节流时间间隔(5秒)let lastFetchTime = 0;function load() {addGeoBoundary(map);map.enableScrollWheelZoom();map.addControl(new T.Control.MapType());map.addControl(new T.Control.Scale());// 添加缩放和移动事件监听器map.addEventListener('zoomend', () => updateMarkers(map));map.addEventListener('moveend', () => updateMarkers(map));// 初始加载标记setTimeout(() => updateMarkers(map), 1000);}async function updateMarkers(map) {const currentTime = Date.now();if (currentTime - lastFetchTime < fetchInterval) {return; // 节流:如果距离上次请求不够,则返回}lastFetchTime = currentTime;const bounds = map.getBounds();const sw = bounds.getSouthWest();const ne = bounds.getNorthEast();const requestData = {bottomLeft: [sw.lng, sw.lat],topRight: [ne.lng, ne.lat]};try {const response = await fetch('http://localhost:10086/things/aggregated-geo', {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify(requestData)});if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);}const data = await response.json();console.log("响应数据:", data);createMarkers(map, data.data);} catch (error) {console.error("请求失败:", error);}}function createMarkers(map, data) {// 清除旧的聚合标记if (markerClusterer) {markerClusterer.clearMarkers();}const markers = [];const { gridCellList, noGeoThings } = data;gridCellList.forEach(item => {for (let i = 0; i < item.thingCount; i++) {const marker = new T.Marker(new T.LngLat(item.position.longitude, item.position.latitude), {title: `Thing Count: ${item.thingCount}`});markers.push(marker);}});if (noGeoThings && noGeoThings.thingCount > 0) {const point = new T.LngLat(noGeoThings.position.longitude, noGeoThings.position.latitude);const marker = new T.Marker(point);map.addOverLay(marker);const markerInfoWin = new T.InfoWindow("无位置设备: " + noGeoThings.thingCount);marker.addEventListener("click", () => marker.openInfoWindow(markerInfoWin));}// 使用聚合器聚合标记markerClusterer = new T.MarkerClusterer(map, { markers });}function addGeoBoundary(map) {fetch('https://geo.datav.aliyun.com/areas_v3/bound/geojson?code=520322').then(response => response.json()).then(data => {const coordinates = data.features[0].geometry.coordinates;const centroid = data.features[0].properties.centroid;map.centerAndZoom(new T.LngLat(centroid[0], centroid[1]), 8);coordinates.forEach(polygon => {polygon.forEach(boundary => {const boundaryPolygon = new T.Polygon(boundary.map(coord => new T.LngLat(coord[0], coord[1])), {color: "#7C7BF6",weight: 1,opacity: 0.7,fillColor: "#ABAAF3",fillOpacity: 0.1});boundaryPolygon.addEventListener("mouseover", () => {boundaryPolygon.setFillColor("#ABAAF3");boundaryPolygon.setFillOpacity(0.6);});boundaryPolygon.addEventListener("mouseout", () => {boundaryPolygon.setFillColor("#DCDBF0");boundaryPolygon.setFillOpacity(0.6);});map.addOverLay(boundaryPolygon);});});}).catch(error => console.error('Error fetching GeoJSON:', error));}load();</script>
</body>
</html>
pages/index.vue
<uni-section title="地区分布" class="item map-container" type="line"><iframe src="/static/skymap.html" class="map-frame"></iframe></uni-section>
后端java实现代码如下:
impl.java
@Overridepublic ThingGeo getAggregatedThingGeo(ThingGeoReqDTO reqDTO) {//TODO 租户过滤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 count = thingRepository.countByBoundingBox(boxBottomLeft, boxTopRight);if (count > 0) {GeoPoint center = thingRepository.findWeightedCenter(boxBottomLeft, boxTopRight);GeoPoint geoPoint = new GeoPoint(center.getLongitude(), center.getLatitude());GridCellThing gridCellThing = new GridCellThing();gridCellThing.setThingCount(count);gridCellThing.setPosition(geoPoint);gridCellThings.add(gridCellThing);}}}ThingGeo thingGeo = new ThingGeo();thingGeo.setGridCellList(gridCellThings);thingGeo.setNoGeoThings(noGeoThings);return thingGeo;}
ThingRepository.java
public interface ThingRepository extends MongoRepository<Thing, String> { @CountQuery("{$and: [{'position': {$exists: true}}, {'deletedAt': null},"+ "{'position': {$geoWithin: { $box: [?0, ?1] }}}]}")long countByBoundingBox(double[] bottomLeft, double[] topRight);@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);@CountQuery("{ $or: [ { 'position': { $exists: false } }, { 'position': null }, { 'position.longitude': 0, 'position.latitude': 0 } ], 'deletedAt': null }")long countByNoGeoPosition();
}
 Entity
EntityThingGeo.java@Data
@NoArgsConstructor
@AllArgsConstructor
public class ThingGeo {private List<GridCellThing> gridCellList; //各个网格单元内的设备总数private GridCellThing  noGeoThings; // 编码区域内没有地理位置的设备总数
}//**************************//GridCellThing .java@Data
@NoArgsConstructor
@AllArgsConstructor
public class GridCellThing {private GeoPoint position;private long thingCount;
}

如果对您的工作有所启发和帮助,点个搜藏加关注吧~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/58261.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构预备知识---Java集合框架、List接口、包装类、装箱拆箱和泛型

文章目录 Java集合框架List接口包装类 和 装箱、拆箱泛型 Java集合框架 Java 集合框架 Java Collection Framework &#xff0c;又被称为容器 container &#xff0c;是定义在 java.util 包下的一组接口 interfaces和其实现类 classes .其主要表现为将多个元素 element 置于一个…

Oracle自动处理表空间不足脚本

关注过我的朋友们应该知道我分享过一些常用的监控脚本&#xff0c;其中最常用的就是监控表空间使用率的脚本&#xff0c;具体可以参考如下链接​&#xff1b; oracle常用监控脚本&#xff08;纯干货&#xff0c;没有EMCC,ZABBIX也不怕&#xff09;_oracle 监控及日常处理脚本-…

基于springboot+vue实现的免费体育馆场地预约系统 (源码+L文+ppt)4-099

基于springbootvue实现的免费体育馆场地预约系统 &#xff08;源码L文ppt&#xff09;4-099 4.1 系统总体结构设计 本系统是基于B/S架构的网站系统&#xff0c;分为系统前台和系统后台&#xff0c;前台主要是提供给注册用户和未注册登录的游客使用的&#xff0c;包括首页、场馆…

文案语音图片视频管理分析系统-视频矩阵

文案语音图片视频管理分析系统-视频矩阵 1.产品介绍 产品介绍方案 产品名称&#xff1a; 智驭视频矩阵深度分析系统&#xff08;SmartVMatrix&#xff09; 主要功能&#xff1a; 深度学习驱动的视频内容分析多源视频整合与智能分类高效视频检索与编辑实时视频监控与异常预警…

openlayers 封装加载本地geojson数据 - vue3

Geojson数据是矢量数据&#xff0c;主要是点、线、面数据集合 Geojson数据获取&#xff1a;DataV.GeoAtlas地理小工具系列 实现代码如下&#xff1a; import {ref,toRaw} from vue; import { Vector as VectorLayer } from ol/layer.js; import { Vector as VectorSource } fr…

OpenCV图像处理方法:腐蚀操作

腐蚀操作 前提 图像数据为二值的&#xff08;黑/白&#xff09; 作用 去掉图片中字上的毛刺 显示图片 读取一个图像文件&#xff0c;并在一个窗口中显示它。用户可以查看这个图像&#xff0c;直到按下任意键&#xff0c;然后程序会关闭显示图像的窗口 # cv2是OpenCV库的P…

【运维心得】U盘启动安装Dell服务器踩坑指南

目录 第一坑&#xff1a;没有键盘选择 第二坑&#xff1a;没有修改mount路径 最近碰到一台Dell服务器R720需要重新安装centos操作系统&#xff0c;由于之前已经配置好了Raid&#xff0c;这里就节省了配置磁盘的步骤。 以前都是通过光盘安装的&#xff0c;考虑到R720是支持U盘…

RAGChecker:显著超越RAGAS,一个精细化评估和诊断 RAG 系统的创新框架

RAG应用已经是当下利用大模型能力的典型应用代表&#xff0c;也获得了极大的推广&#xff0c;各种提升RAG性能的技术层出不穷。然而,如何全面、准确地评估 RAG 系统一直是一个挑战。传统评估方法存在诸多局限性:无法有效评估长文本回复、难以区分检索和生成模块的错误来源、与人…

Jmeter自动化实战

一、前言 由于系统业务流程很复杂,在不同的阶段需要不同的数据,且数据无法重复使用,每次造新的数据特别繁琐,故想着能不能使用jmeter一键造数据 二、创建录制模板 可参考:jmeter录制接口 首先创建一个录制模板 因为会有各种请求头,cookies,签名,认证信息等原因,导致手动复制…

JDK的下载

目录 JDK官网 Windows Ubantu 1.安装JDK 2.确定JDK版本 卸载OpenJDK Centos 1.下载JDK 2.安装JDK 3.验证JDK JDK官网 官网网址&#xff1a;Java Downloads | Oracle Windows 双击运⾏exe⽂件, 选择安装⽬录, 直⾄安装完成 Ubantu 1.安装JDK 更新软件包 sudo apt u…

【YOLO 系列】基于YOLO的工业自动化轴承缺陷检测系统【python源码+Pyqt5界面+数据集+训练代码】

前言 轴承作为机械设备中的关键部件&#xff0c;其性能直接影响到设备的稳定性和寿命。轴承缺陷的早期检测对于预防设备故障、减少维护成本和提高生产效率至关重要。然而&#xff0c;传统的轴承缺陷检测方法往往依赖于人工检查&#xff0c;这不仅效率低下&#xff0c;而且容易…

告别局域网限制:宝塔FTP结合内网穿透工具实现远程高效文件传输

文章目录 前言1. Linux安装Cpolar2. 创建FTP公网地址3. 宝塔FTP服务设置4. FTP服务远程连接小结 5. 固定FTP公网地址6. 固定FTP地址连接 前言 本文主要介绍宝塔FTP文件传输服务如何搭配内网穿透工具&#xff0c;实现随时随地远程连接局域网环境搭建的宝塔FTP文件服务并进行文件…

2024 前端面试题!!! html css js相关

常见的块元素、行内元素以及行内块元素&#xff0c;三者有何不同&#xff1f;​​​​​​​ HTML、XML、XHTML它们之间有什么区别&#xff1f;​​​​​​​ DOCTYPE(⽂档类型) 的作⽤ Doctype是HTML5的文档声明&#xff0c;通过它可以告诉浏览器&#xff0c;使用哪一个HTM…

业务逻辑与代码分离:规则引擎如何实现高效管理?

在这个快速变化、高度信息化的时代&#xff0c;软件系统和业务流程的复杂性日益增加。为了应对这种复杂性&#xff0c;越来越多的企业开始采用规则引擎来应对这种复杂性。我们这次结合JVS规则引擎来解析为什么越来越多人使用规则引擎。 规则引擎定义 规则引擎是一种用于管理和…

关键词排名技巧实用指南提升网站流量的有效策略

内容概要 在数字营销的世界中&#xff0c;关键词排名的影响不可小觑。关键词是用户在搜索引擎中输入的词语&#xff0c;通过精确选择和优化这些关键词&#xff0c;网站能够更轻松地被目标用户发现。提升关键词排名的第一步是了解基本概念&#xff0c;包括关键词的分类、重要性…

数据结构与算法——树与二叉树

树与二叉树 1.树的定义与相关概念 树的示例&#xff1a; 树的集合形式定义 Tree(K,R) 元素集合&#xff1a;K{ki|0<i<n,n>0,ki∈ElemType}&#xff08;n为树中结点数&#xff0c;n0则树为空&#xff0c;n>0则为非空树&#xff09; 对于一棵非空树&#xff0c…

51单片机应用开发---定时器(定时1S,LED以1S间隔闪烁)

实现目标 1、掌握定时器的配置流程&#xff1b; 2、掌握定时器初值的计算方法&#xff1b; 3、具体实现&#xff1a;&#xff08;1&#xff09;1mS中断1次&#xff0c;计数1000次中断&#xff0c;实现定时1S功能&#xff1b;&#xff08;2&#xff09;LED1每隔1S状态取反。 …

TCP/IP Attack Lab

网络拓扑&#xff1a; Task 1: SYN Flooding Attack 收到攻击之前&#xff0c;在Victim主机查看网络连接的状态: 在攻击之前使用User1主机(10.9.0.6)访问Victim(10.9.0.5)主机的 Telnet服务: Task 1.1: Launching the Attack Using Python 在Atacker上建立文件attack-1.py…

VictoriaMetrics 中文教程(10)集群版介绍

VictoriaMetrics 中文教程系列文章&#xff1a; VictoriaMetrics 中文教程&#xff08;01&#xff09;简介VictoriaMetrics 中文教程&#xff08;02&#xff09;安装VictoriaMetrics 中文教程&#xff08;03&#xff09;如何配置 Prometheus 使其把数据远程写入 VictoriaMetri…

深度学习(五):语音处理领域的创新引擎(5/10)

一、深度学习在语音处理中的崛起 在语音处理领域&#xff0c;传统方法如谱减法、维纳滤波等在处理复杂语音信号时存在诸多局限性。这些方法通常假设噪声是平稳的&#xff0c;但实际噪声往往是非平稳的&#xff0c;导致噪声估计不准确。同时&#xff0c;为了去除噪声&#xff0…