基于 Elasticsearch 實現地圖點聚合

在地圖類應用中,當需要展示大量地理興趣點時,直接將所有點渲染在地圖上會導致視覺混亂,影響用戶體驗。為此,我基于 Elasticsearch 提供的 geotile_gridgeo_bounding_box 查詢能力,實現了一套高效的 POI 聚合展示方案。

🧩 問題背景:POI 數量巨大,直接渲染效率低

在地圖類應用中,我們常常需要展示大量的地理興趣點(Point of Interest, POI),例如商圈、門店、用戶位置等。然而,當 POI 數量達到數萬甚至數十萬級別時,若將所有點一次性加載并渲染在地圖上,不僅會導致頁面卡頓、交互延遲,還會因標記重疊嚴重而降低用戶體驗。更嚴重的問題包括:

  • 前端性能瓶頸:大量 DOM 節點或圖形元素導致瀏覽器渲染壓力劇增;
  • 視覺混亂:點與點之間相互遮擋,信息難以辨識;
  • 無差別展示:無法根據地圖縮放層級動態調整展示粒度;
    在這里插入圖片描述
    因此,我們需要一種既能減少前端渲染壓力,又能保留關鍵信息的地圖聚合方案。

🛠? 技術方案:基于 Elasticsearch 的 geotile_grid 聚合機制

本方案基于 Elasticsearch 的 geotile_gridgeo_bounding_box 查詢能力,結合球面幾何算法,實現了poi高效聚合與展示。整個流程如下:

  1. 獲取地圖視口范圍:通過左上角和右下角坐標限定查詢范圍;
  2. 映射地圖縮放層級到 precision,根據當前地圖 zoom level 動態計算合適的 geotile_grid 聚合精度;
  3. 提取聚合點并計算代表點,對每個 tile 內最多 100 個點進行球面幾何中位數計算,得出一個最具代表性的點用于展示。

流程圖:
在這里插入圖片描述
效果展示:

Screen-2025-07-03-163545

📌 地圖聚合的核心:geotile_grid

在本方案中,我們使用了 Elasticsearch 的 geotile_grid 聚合方式來實現地圖興趣點的分區聚合。
geotile_grid 本質上是一種基于 Geohash 編碼的空間劃分機制。它將整個地圖視圖劃分為多個大小一致的矩形區域(稱為 tile),每個 tile 包含落在其范圍內的所有 POI。

  • tile 的粒度由 precision 控制:precision 越高,tile 越小,聚合越精細;
  • 結合 zoom level 動態映射 precision:根據當前地圖縮放層級,動態設置合適的精度值,使聚合結果與地圖展示粒度保持一致;
  • 支持子聚合操作:我們可以在每個 tile 中嵌套 top_hits 聚合,獲取最多 100 個原始點用于后續中心點計算;

通過這種方式,我們可以:

  • 高效篩選出當前地圖視口內的所有 tile;
  • 快速獲取每個 tile 內的 POI 數據;
  • 為每個 tile 計算出最具代表性的“中心點”,用于前端展示;
  • 這不僅顯著提升了后端查詢效率,也為前端提供了結構清晰、層次分明的地圖聚合展示能力。

🧑?💻 示例代碼片段

核心代碼:基于 geotile_grid 實現地圖點聚合

// 1. 構建篩選條件(僅保留地理范圍查詢)
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
queryBuilder.must(QueryBuilders.geoBoundingBoxQuery("latLng").setCorners(new GeoPoint(topLeftLat, topLeftLon), new GeoPoint(bottomRightLat, bottomRightLon)));// 2. 構建 geotile_grid 聚合
GeoGridAggregationBuilder geoTileGridAgg = AggregationBuilders.geotileGrid("grid_agg").field("latLng").precision(15); // 可根據縮放級別動態設置 precision// 3. 添加子聚合 top_hits(獲取每個 tile 內的 latLng 坐標)
TopHitsAggregationBuilder topHitsAgg = AggregationBuilders.topHits("top_hits_agg").fetchSource("latLng", null).size(100);
geoTileGridAgg.subAggregation(topHitsAgg);// 4. 構建最終查詢
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(queryBuilder).withAggregations(geoTileGridAgg).withMaxResults(0) // 不需要返回實際文檔.build();// 5. 執行查詢并解析聚合結果
SearchHits<ShopESEntity> searchHits = elasticsearchRestTemplate.search(searchQuery, ShopESEntity.class);// 6. 解析聚合結果并生成展示點
List<DisplayPointVO> displayPoints = new ArrayList<>();
if (searchHits.hasAggregations()) {List<? extends MultiBucketsAggregation.Bucket> buckets = getBuckets(searchHits.getAggregations(), "grid_agg");for (MultiBucketsAggregation.Bucket bucket : buckets) {List<MapUtils.Poi> poiList = getPoiList(bucket); // 獲取該 tile 內最多 100 個點MapUtils.Poi meanPoi = MapUtils.computeSphericalMean(poiList); // 計算球面幾何中位點if (meanPoi != null) {DisplayPointVO vo = new DisplayPointVO();vo.setCount(bucket.getDocCount());vo.setLat(meanPoi.getLat());vo.setLng(meanPoi.getLng());displayPoints.add(vo);}}
}

最終DSL如下:

{"size": 0,"aggs": {"poi_agg": {"geotile_grid": {"field": "latLng","precision": 29},"aggs": {"points": {"top_hits": {"size": 100,"_source": {"includes": ["latLng"]}}}}}},"query": {"geo_bounding_box": {"latLng": {"top_left": {"lat": 30.293813,"lon": 120.10432},"bottom_right": {"lat": 30.167403,"lon": 120.217002}}}}
}

球面幾何中位數算法(簡化版)

public static class MapUtils {public static class Poi {private final double lat;private final double lon;public Poi(double lat, double lon) {this.lat = lat;this.lon = lon;}public double getLat() { return lat; }public double getLng() { return lon; }}public static Poi computeSphericalMean(List<Poi> poiList) {if (poiList.isEmpty()) return null;double sumX = 0, sumY = 0, sumZ = 0;for (Poi p : poiList) {double latRad = Math.toRadians(p.lat);double lonRad = Math.toRadians(p.lon);sumX += Math.cos(latRad) * Math.cos(lonRad);sumY += Math.cos(latRad) * Math.sin(lonRad);sumZ += Math.sin(latRad);}int n = poiList.size();double avgX = sumX / n;double avgY = sumY / n;double avgZ = sumZ / n;double hyp = Math.sqrt(avgX * avgX + avgY * avgY);double latAvg = Math.toDegrees(Math.atan2(avgZ, hyp));double lngAvg = Math.toDegrees(Math.atan2(avgY, avgX));return new Poi(latAvg, lngAvg);}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/87472.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/87472.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/87472.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Prometheus 】通過 Pushgateway 上報指標數據

Prometheus 是目前最流行的開源監控系統之一&#xff0c;其拉取&#xff08;pull&#xff09;模型非常適合服務發現和靜態目標的監控。然而&#xff0c;在某些場景下&#xff0c;例如短生命周期任務、批處理作業或無法暴露 HTTP 接口的服務&#xff0c;傳統的拉取方式并不適用。…

服務器 - - QPS與TPS介紹

1、QPS&#xff08;Queries Per Second 每秒查詢數&#xff09; 定義&#xff1a;常用于表示每秒的請求次數&#xff0c;衡量接口請求、數據庫查詢等動作的吞吐量&#xff08;單位時間內處理的數據量&#xff09; 計算&#xff1a;總請求數/請求時間&#xff0c;如&#xff1…

Cot2:思維鏈提示激發大型語言模型的推理能力

摘要 我們探討了生成思維鏈——一系列中間推理步驟——如何顯著提升大型語言模型執行復雜推理的能力。特別地&#xff0c;我們展示了在足夠大的語言模型中&#xff0c;這種推理能力如何通過一種簡單的方法——思維鏈提示&#xff08;chain-of-thought prompting&#xff09;自…

go交易數據后端

地址 https://gitee.com/EEPPEE_admin/go-stock-line-trading-datahttps://github.com/jerryshell/midas 需求 為了替代rust后端爬蟲端: 爬取東方財富數據到index-data目錄server端: 項目主要內容 todo 替代https://github.com/jerryshell/midas的前端量化概念性理解擴展: 存儲…

靈巧手概覽

第一章 靈巧手的技術演進與核心價值 1.1 技術演進的五個階段 仿生學啟蒙階段&#xff08;1960-1980&#xff09; 1968年斯坦福大學首臺3自由度機械夾爪標志機器人操作技術開端&#xff0c;1973年MIT提出"仿生手"概念&#xff0c;但受限于材料和控制技術&#xff0c;…

在設計提示詞(Prompt)時,關于信息位置的安排z怎么 結合模型特性和任務目標

在設計提示詞(Prompt)時,關于信息位置的安排z怎么 結合模型特性和任務目標 在設計提示詞(Prompt)時,關于信息位置的安排確實需要結合模型特性和任務目標。從自注意力機制的原理及應用場景來看,關鍵信息的位置選擇需遵循以下啟示,并結合具體場景靈活調整: 一、核心啟示…

七、性能優化

目錄 1. 如何檢測Flutter應用的性能問題&#xff1f;2. 什么是重繪邊界&#xff08;Repaint Boundary&#xff09;&#xff1f;3. 如何避免不必要的重建&#xff1f;4. const 構造函數在優化中起什么作用&#xff1f;5. 如何優化長列表的性能&#xff1f;6. 如何減少應用啟動時…

Webpack優化詳解

Webpack 5提供了一系列工具和功能,可以在本地開發和線上構建過程中進行優化,以提高開發效率和構建性能。 1. 本地開發優化 1.1. 開啟模塊熱替換(HMR) 模塊熱替換可以在不刷新整個頁面的情況下更新模塊,提高開發效率。 const webpack = require(webpack);module.export…

latency 對功耗的影響

文章目錄 1、Connection Interval(連接間隔) vs. Latency(從機延遲)2、為什么不能完全依賴 Connection Interval?3、什么時候可以不用 Latency?4、如何正確配置?5、結論調節連接間隔(Connection Interval)確實可以直接影響通信頻率和功耗,但 Latency(從機延遲)仍然…

10分鐘搭建 PHP 開發環境教程

下載、安裝 Xserver 下載 php 過程中如果提示需要安裝 vc 運行環境&#xff0c;按照引導下載安裝即可 安裝 nginx 安裝 Mysql 支持多個版本同時安裝 下載 php 過程中如果提示需要安裝 vc 運行環境&#xff0c;按照引導下載安裝即可mysql 默認用戶名為 root&#xff0c;默認密…

設計模式(六)

備忘錄模式&#xff08;Memento Pattern&#xff09;詳解 一、核心概念 備忘錄模式允許在不破壞封裝性的前提下&#xff0c;捕獲并保存對象的內部狀態&#xff0c;以便后續恢復。該模式通過三個角色實現&#xff1a; 原發器&#xff08;Originator&#xff09;&#xff1a;需…

迪杰斯特拉算法之解決單源最短路徑問題

迪杰斯特拉算法 迪杰斯特拉(Dijkstra)算法是典型**最短路徑算法**&#xff0c;用于計算一個結點到其它結點的最短路徑。它的主要特點是以起始點為中心向外擴展(利用廣度優先搜索思想)&#xff0c;直到擴展到終點。迪杰斯特拉(Dijkstra)算法最佳應用-最短路徑 戰爭時期&#xf…

風平浪靜、無事發生

2025年7月4日&#xff0c;16~25℃&#xff0c;陰雨緊急不緊急重要1.備考D1.物理備課不重要遇見&#xff1a;風平浪靜、無事發生&#xff01;感受或反思&#xff1a;體檢的結果收到了&#xff0c;醫生建議多吃綠蔬多喝水&#xff01;多運動&#xff0c;少和喝飲料........

QtitanRibbon打造現代辦公軟件新體驗:提升效率的專業界面解決方案

在現代辦公環境中&#xff0c;無論是日常公文處理、文檔編輯、任務協同還是數據分析&#xff0c;桌面辦公軟件仍扮演著不可替代的角色。然而&#xff0c;許多傳統系統依舊使用菜單繁雜、圖標混亂、交互老舊的界面&#xff0c;用戶操作效率低、上手慢、滿意度差。 QtitanRibbon…

MSPM0G3507學習筆記(一) 重置版:適配逐飛庫的ti板環境配置

由于使用逐飛庫&#xff0c;很多東西其實都不用配置了&#xff0c;也不需要自己移植空工程了&#xff0c;于是寫一個重置版的環境配置教程。 1.下載芯片支持包 MSPM0G3507芯片支持CCS、IAR、KEIL等IDE&#xff0c;選擇KEIL作為開發工具&#xff0c;首先安裝芯片支持包。 前往…

如何查看自己電腦的顯卡信息?

右鍵單擊底部導航欄選擇“任務管理器” 點開之后 選擇左側的性能一欄 查看你的顯卡的信息

使用Go語言實現智能EXE文件重命名工具

文章目錄 使用Go語言實現智能EXE文件重命名工具 &#x1f6e0;?引言工具功能概述核心技術實現Windows版本信息API調用大模型API集成交互式命令行界面 完整工作流程實際應用示例附錄完整代碼 使用Go語言實現智能EXE文件重命名工具 &#x1f6e0;? 引言 在日常開發和軟件管理…

3.1.1.9 安全基線檢查項目九:檢查是否設置限制su命令用戶組

限制su配置 關于限制su命令檢查項&#xff0c;對于大多數的Linux&#xff08;Redhat系列、Debian系列&#xff09;&#xff0c;進行本項檢查很簡單。只需要檢查/etc/pam.d/su中是否配置了&#xff1a; auth required pam_wheel.so use_uid [group用戶組名] 有些資料講說需要有…

【加解密與C】對稱加密(四) RC4

RC4算法概述RC4&#xff08;Rivest Cipher 4&#xff09;是由Ron Rivest在1987年設計的流密碼算法&#xff0c;廣泛應用于SSL/TLS、WEP等協議中。其核心是通過密鑰調度算法&#xff08;KSA&#xff09;和偽隨機生成算法&#xff08;PRGA&#xff09;生成密鑰流&#xff0c;與明…

醫科+AI!和鯨支持南京醫科大學醫學數據挖掘課程實踐教學落地

近兩年&#xff0c;生物統計學更多地進入了公眾視野。作為統計學、醫學與計算機科學交叉的前沿學科&#xff0c;伴隨測序技術革新與人工智能算法突破&#xff0c;其發展前景也被十分看好。 市場需求的背后是人才需求的爆發與人才培養的挑戰。目前&#xff0c;生物統計學專業在國…