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

天地图实现海量聚合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;
}

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

相关文章:

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

本文章详细的讲解了前后端代码来 实现uniapp天地图功能的实现 以及 后端海量数据的聚合查询 和网格算法实现思路。 并对当数据量增加和用户频繁请求接口时可能导致服务器负载过高做了前后端优化。 前端uniapp&#xff1a; 实现了天地图的行政区划边界/地图切换/比例尺/海量数…...

Bug | 项目中数据库查询问题

问题描述 理论上&#xff0c;点击查询后&#xff0c;表头应当显示中文。而不是上面的在数据库中的表头【如上图示】 正常点击查询后&#xff0c;如果没有输入值&#xff0c;应当是查询所有的信息。 原因分析&#xff1a; 这里是直接使用SELECT * 导致的。例如&#xff1a; S…...

C++入门基础知识129—【关于C 库函数 - time()】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于C 库函数 - time()的相关内容&#xff0…...

大文件秒传,分片上传,断点续传

大文件分片上传 一 功能描述 1.文件通过web端分片多线程上传到服务端&#xff0c;然后web端发起分片合并&#xff0c;完成大文件分片上传功能 2.上传过的大文件&#xff0c;实现秒传 3.上传过程中&#xff0c;服务异常退出&#xff0c;实现断点续传 二 流程图 三 代码运行…...

多生境扩增子探秘:深度溯源与多样性解析

分析微生物组数据的组成结构的一个主要挑战是确定其潜在来源。在微生物来源分析中&#xff0c;随机森林、SourceTracker和FEAST都有较广泛应用。今天&#xff0c;小编就带大家看一篇发表在《iMeta》的文章&#xff0c;使用溯源技术追踪微生物的来源与去向&#xff0c;揭示生物在…...

Selenium4自动化测试常用函数总结,各种场景操作实战

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 seleninum作为自动化测试的工具&#xff0c;自然是提供了很多自动化操作的函数&#xff0c;下面列举下比较常用的函数&#xff0c;更多可见官方文档&#xff1a;…...

图像生成新范式:智源推出全能视觉生成模型 OmniGen

大型语言模型&#xff08;LLM&#xff09;的出现统一了语言生成任务&#xff0c;并彻底改变了人机交互。然而&#xff0c;在图像生成领域&#xff0c;能够在单一框架内处理各种任务的统一模型在很大程度上仍未得到探索。近日&#xff0c;智源推出了新的扩散模型架构 OmniGen&am…...

实现RPC接口的demo记录

1.Thrift RPC 接口实现 Demo Service public class DemoServiceImpl implements DemoService.Iface {private static final Logger logger LoggerFactory.getLogger(DemoServiceImpl.class);Overridepublic String sayHello(Context context, String msg) throws TException …...

Python期末题目 | 期末练习题【概念题+代码】

一、前言 Python 是一门功能强大且易于学习的编程语言&#xff0c;在高校中被广泛用作教学语言。Python 的期末考试通常会包含基础知识和编程实践&#xff0c;以考察学生的理解与应用能力。本文整理了一套 Python 期末练习题&#xff0c;包括选择题、填空题、判断题和代码题。…...

OpenCV基本操作(python开发)——(6)视频基本处理

OpenCV——视频基本处理 一、读取摄像头 import numpy as np import cv2cap cv2.VideoCapture(0) # 实例化VideoCapture对象, 0表示第一个摄像头 while cap.isOpened():ret, frame cap.read() # 捕获帧cv2.imshow("frame", frame)c cv2.waitKey(1) # 等待1毫…...

详解Java之Spring MVC篇一

目录 Spring MVC 官方介绍 MVC RequestMapping 传递参数 无参数 单个参数 针对String类型 针对Integer类型 针对int类型 针对自定义类型 多个参数 参数重命名 参数强制一致 参数不强制一致 传递数组 ​编辑传递List ​编辑 传递JSON ​编辑 从路径中获取参…...

ubuntu20.04上使用 Verdaccio 搭建 npm 私有仓库

安装nvm 首先安装必要的工具&#xff1a; apt update apt install curl下载并执行nvm安装脚本&#xff1a; curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash添加环境变量&#xff08;如果安装脚本没有自动添加&#xff09;。编辑 ~/.bash…...

Python实现办公自动化的数据可视化与报表生成

在 Python 中&#xff0c;可以利用多个库来实现办公自动化中的数据可视化与报表生成。以下是具体的方法&#xff1a; 一、数据可视化 使用 matplotlib 库 matplotlib 是一个强大的 Python 绘图库&#xff0c;可以创建各种类型的静态、动态和交互式图表。示例代码&#xff1a;i…...

前端知识串联笔记(更新中...)

1.MVVM MVVM 是指 Model - View - ViewModel&#xff0c;Model 是数据与业务逻辑&#xff0c;View 是视图&#xff0c;ViewModel 用于连接 View 和 Model Model ---> View&#xff1a;将数据转化成所看到的页面&#xff0c;实现的方式&#xff1a;Data Bindings -- 数据绑定…...

PostgreSQL根据字符串的长度排序

PostgreSQL根据字符串的长度排序 在 PostgreSQL 中&#xff0c;你可以使用 LENGTH 函数来获取字符串的长度&#xff0c;并根据这个长度进行排序。LENGTH 函数会返回字符串的字符数。 以下是一个基本的 SQL 查询示例&#xff0c;它根据 some_column 字符串列的长度对表中的行进…...

计算机网络:网络层 —— IP数据报的发送和转发过程

文章目录 IP数据报的发送和转发过程主机发送IP数据报路由器转发IP数据报示例 IP数据报的发送和转发过程 IP 数据报的发送和转发过程包含以下两个过程&#xff1a; 主机发送IP数据报路由器转发IP数据报 直接交付&#xff1a;源主机与目的主机在同一网络中间接交付&#xff1a;…...

【算力基础】GPU算力计算和其他相关基础(TFLOPS/TOPS/FP32/INT8...)

文章目录 :one: 算力的常见指标:two: 算力计算:three: 常用链接 &#x1f680; 本文主要是聚焦于深度学习领域的 GPU的算力估计&#xff0c;其他类型的硬件设备如CPU可以类比参考。 1️⃣ 算力的常见指标 算力衡量主要与运算速度和精度这两个指标有关。 &#x1f314;速度指…...

UI自动化测试(app端)4.0

✨博客主页&#xff1a; https://blog.csdn.net/m0_63815035?typeblog &#x1f497;《博客内容》&#xff1a;.NET、Java.测试开发、Python、Android、Go、Node、Android前端小程序等相关领域知识 &#x1f4e2;博客专栏&#xff1a; https://blog.csdn.net/m0_63815035/cat…...

C#与C++交互开发系列(十):数组传递的几种形式

前言 在C#和C的交互开发中&#xff0c;数组传递是一个非常常见且实用的场景。数组可以作为方法的参数&#xff0c;也可以作为响应结果返回。在本篇博客中&#xff0c;我们将探讨几种常见的数组传递方式&#xff0c;展示如何在C#与C之间进行有效的数据交换。我们将主要介绍以下…...

【C++复习】第一弹-基础性语法

前言 学习了C语法这么久了&#xff0c;我其实觉得&#xff0c;我们学习一门语言应该更加注重使用性&#xff0c;对于语法的细节可以通过具体的项目去重新造轮子的时候再去抠细节&#xff0c;也就是说你得学会先走&#xff0c;在去想我们如何走的&#xff0c;身体的哪些肌肉在发…...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

省略号和可变参数模板

本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...