場景
Openlayers中使用Cluster實現縮放地圖時圖層聚合與取消聚合:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/122143275
上面實現縮放地圖時元素在指定距離內實現聚合效果。
實際場景中點位是變化的,不是靜態的點位,當多個點位在動態更新的過程中如果出現了重合則自動實現聚合效果
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
關注公眾號
霸道的程序猿
獲取編程相關電子書、教程推送與免費下載。
實現
1、首先模擬多個點位坐標的數據,實際場景可能是從后台或其他地方獲取點位信息
//定位數據源 var positionData = [ [{ x: '-11561139.941628069', y: '5538515.7834814', carNumber: '霸道的程序猿' }, { x: '-11552039.941628069', y: '5531515.7834814', carNumber: '公眾號' } ], [{ x: '-11560039.941628069', y: '5537515.7834814', carNumber: '霸道的程序猿' }, { x: '-11553039.941628069', y: '5536515.7834814', carNumber: '公眾號' } ], [{ x: '-11559039.941628069', y: '5536515.7834814', carNumber: '霸道的程序猿' }, { x: '-11554039.941628069', y: '5536515.7834814', carNumber: '公眾號' } ], [{ x: '-11558039.941628069', y: '5535515.7834814', carNumber: '霸道的程序猿' }, { x: '-11557039.941628069', y: '5534515.7834814', carNumber: '公眾號' } ], [{ x: '-11557039.941628069', y: '5534515.7834814', carNumber: '霸道的程序猿' }, { x: '-11556039.941628069', y: '5535515.7834814', carNumber: '公眾號' } ], [{ x: '-11556039.941628069', y: '5533515.7834814', carNumber: '霸道的程序猿' }, { x: '-11557039.941628069', y: '5536515.7834814', carNumber: '公眾號' } ], [{ x: '-11555039.941628069', y: '5533515.7834814', carNumber: '霸道的程序猿' }, { x: '-11558039.941628069', y: '5536515.7834814', carNumber: '公眾號' } ], [{ x: '-11554039.941628069', y: '5533515.7834814', carNumber: '霸道的程序猿' }, { x: '-11559039.941628069', y: '5536515.7834814', carNumber: '公眾號' } ], [{ x: '-11553039.941628069', y: '5533515.7834814', carNumber: '霸道的程序猿' }, { x: '-11560039.941628069', y: '5536515.7834814', carNumber: '公眾號' } ], [{ x: '-11552039.941628069', y: '5533515.7834814', carNumber: '霸道的程序猿' }, { x: '-11561039.941628069', y: '5536515.7834814', carNumber: '公眾號' } ] ];
這里模擬兩組坐標的點位數據,會在中間某個時刻有距離特別相近的點位。
2、創建聚合圖層要素
// 創建聚合圖層要素 var clusterSource = new ol.source.Vector();
3、新建聚合圖層數據源
// 聚合圖層數據源 var clusterSourceForLayer = new ol.source.Cluster({ source: clusterSource, distance: 50 })
這里的distance就是指的聚合的距離。
實際上就是將原來的普通的new ol.source.Vector圖層數據源更換為ol.source.Cluster
4、新建聚合圖層
var clusterLayer = new ol.layer.Vector({ source: clusterSourceForLayer, style: function (feature, resolution) { var size = feature.get('features').length; if (size == 1) { return new ol.style.Style({ image: new ol.style.Icon({ scale: 0.8, src: './icon/house.png', anchor: [0.48, 0.52] }), text: new ol.style.Text({ font: 'normal 12px 黑體', // // 對其方式 textAlign: 'center', // 基准線 textBaseline: 'middle', offsetY: -35, offsetX: 0, backgroundFill: new ol.style.Stroke({ color: 'rgba(0,0,255,0.7)', }), // 文本填充樣式 fill: new ol.style.Fill({ color: 'rgba(236,218,20,1)' }), padding: [5, 5, 5, 5], text: `霸道的程序猿`, }) }); } else { return new ol.style.Style({ image: new ol.style.Circle({ radius: 30, stroke: new ol.style.Stroke({ color: 'white' }), fill: new ol.style.Fill({ color: 'blue' }) }), text: new ol.style.Text({ text: size.toString(), fill: new ol.style.Fill({ color: 'white' }) }) }); } } });
實際上實現聚合與不聚合效果的關鍵代碼就是這部分的style改為函數並且根據條件動態返回。
如果包含多個元素,則返回聚合的style效果,否則就是返回普通的style效果。
5、map上添加圖層
var map = new ol.Map({ layers: [layer], target: 'map', view: view }); this.map.addLayer(this.clusterLayer);
其中layer是地圖圖層。
然后新建一個定時器模擬定時獲取點位數據
//定時器循環模擬后台獲取數據實現定位效果 var index = 0; setInterval(() => { //坐標數據到頭了 就重新開始 if (index > this.positionData.length - 2) { index = 0; } //根據索引獲取數據 var item = this.positionData[index]; //清除上次的數據源 if (this.positonSource) { this.positonSource.clear(); } if (item) { clusterSource.clear(); for (var i = 0; i < item.length; ++i) { var feature = new ol.Feature({ geometry: new ol.geom.Point([Number(item[i].x), Number(item[i].y)]) }) //數據源添加要素 clusterSource.addFeature(feature); } } //移到下個點 index++; }, 1000);
在定時器中將聚合圖層的數據源先清理,然后再將坐標要素添加進聚合圖層數據源中。
其他相關代碼就是離線加載地圖顯示的功能,可以參考
Openlayers中實現地圖上打點並顯示圖標和文字:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/118631046
6、完整示例代碼
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>縮放地圖實現聚合與取消聚合-動態</title> <link rel="stylesheet" href="lib/ol65/ol.css" type="text/css"> <style> html, body, #map { padding: 0; margin: 0; width: 100%; height: 100%; overflow: hidden; } </style> </head> <body> <div id="map"></div> <script type="text/javascript" src="lib/ol65/ol.js"></script> <script type="text/javascript"> //定位數據源 var positionData = [ [{ x: '-11561139.941628069', y: '5538515.7834814', carNumber: '霸道的程序猿' }, { x: '-11552039.941628069', y: '5531515.7834814', carNumber: '公眾號' } ], [{ x: '-11560039.941628069', y: '5537515.7834814', carNumber: '霸道的程序猿' }, { x: '-11553039.941628069', y: '5536515.7834814', carNumber: '公眾號' } ], [{ x: '-11559039.941628069', y: '5536515.7834814', carNumber: '霸道的程序猿' }, { x: '-11554039.941628069', y: '5536515.7834814', carNumber: '公眾號' } ], [{ x: '-11558039.941628069', y: '5535515.7834814', carNumber: '霸道的程序猿' }, { x: '-11557039.941628069', y: '5534515.7834814', carNumber: '公眾號' } ], [{ x: '-11557039.941628069', y: '5534515.7834814', carNumber: '霸道的程序猿' }, { x: '-11556039.941628069', y: '5535515.7834814', carNumber: '公眾號' } ], [{ x: '-11556039.941628069', y: '5533515.7834814', carNumber: '霸道的程序猿' }, { x: '-11557039.941628069', y: '5536515.7834814', carNumber: '公眾號' } ], [{ x: '-11555039.941628069', y: '5533515.7834814', carNumber: '霸道的程序猿' }, { x: '-11558039.941628069', y: '5536515.7834814', carNumber: '公眾號' } ], [{ x: '-11554039.941628069', y: '5533515.7834814', carNumber: '霸道的程序猿' }, { x: '-11559039.941628069', y: '5536515.7834814', carNumber: '公眾號' } ], [{ x: '-11553039.941628069', y: '5533515.7834814', carNumber: '霸道的程序猿' }, { x: '-11560039.941628069', y: '5536515.7834814', carNumber: '公眾號' } ], [{ x: '-11552039.941628069', y: '5533515.7834814', carNumber: '霸道的程序猿' }, { x: '-11561039.941628069', y: '5536515.7834814', carNumber: '公眾號' } ] ]; var source = new ol.source.XYZ({ tileUrlFunction: function (xyz, obj1, obj2) { if (!xyz) return ""; var z = xyz[0]; var x = Math.abs(xyz[1]); var y = Math.abs(xyz[2]); var xyz_convert = self.convert_(z, x, y); x = xyz_convert[0]; y = xyz_convert[1]; z = xyz_convert[2]; var shift = z / 2; var half = 2 << shift; var digits = 1; if (half > 10) digits = parseInt(Math.log(half) / Math.log(10)) + 1; var halfx = parseInt(x / half); var halfy = parseInt(y / half); x = parseInt(x); y = parseInt(y) - 1; var url = "./images/EPSG_900913" + "_" + self.padLeft_(2, z) + "/" + self.padLeft_(digits, halfx) + "_" + self.padLeft_(digits, halfy) + "/" + self.padLeft_(2 * digits, x) + "_" + self.padLeft_(2 * digits, y) + "." + 'png'; return url; } }); //projections投影坐標系轉換相關的操作 var projection = new ol.proj.Projection({ code: 'EPSG:900913', units: 'm', axisOrientation: 'neu' }); //Layers 圖層管理類,用來管理圖層信息。主要包括Tile,Image,Vector,VectorTile等圖層。 var layer = new ol.layer.Tile({ source: source }); // 創建聚合圖層要素 var clusterSource = new ol.source.Vector(); // 聚合圖層數據源 var clusterSourceForLayer = new ol.source.Cluster({ source: clusterSource, distance: 50 }) // 聚合圖層 var clusterLayer = new ol.layer.Vector({ source: clusterSourceForLayer, style: function (feature, resolution) { var size = feature.get('features').length; if (size == 1) { return new ol.style.Style({ image: new ol.style.Icon({ scale: 0.8, src: './icon/house.png', anchor: [0.48, 0.52] }), text: new ol.style.Text({ font: 'normal 12px 黑體', // // 對其方式 textAlign: 'center', // 基准線 textBaseline: 'middle', offsetY: -35, offsetX: 0, backgroundFill: new ol.style.Stroke({ color: 'rgba(0,0,255,0.7)', }), // 文本填充樣式 fill: new ol.style.Fill({ color: 'rgba(236,218,20,1)' }), padding: [5, 5, 5, 5], text: `霸道的程序猿`, }) }); } else { return new ol.style.Style({ image: new ol.style.Circle({ radius: 30, stroke: new ol.style.Stroke({ color: 'white' }), fill: new ol.style.Fill({ color: 'blue' }) }), text: new ol.style.Text({ text: size.toString(), fill: new ol.style.Fill({ color: 'white' }) }) }); } } }); //View 視圖管理器,主要用來管理地圖視圖,分辨率或旋轉,中心、投影、分辨率、縮放級別等。 var view = new ol.View({ //中心點 center: [-11549894, 5533433], //縮放等級 zoom: 11, //投影坐標系 projection: projection, //邊界 extent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34] }); //Map Openlayers的核心組件,包含圖層、交互事件、UI控制元素等。 var map = new ol.Map({ layers: [layer], target: 'map', view: view }); this.map.addLayer(this.clusterLayer); //定時器循環模擬后台獲取數據實現定位效果 var index = 0; setInterval(() => { //坐標數據到頭了 就重新開始 if (index > this.positionData.length - 2) { index = 0; } //根據索引獲取數據 var item = this.positionData[index]; //清除上次的數據源 if (this.positonSource) { this.positonSource.clear(); } if (item) { clusterSource.clear(); for (var i = 0; i < item.length; ++i) { var feature = new ol.Feature({ geometry: new ol.geom.Point([Number(item[i].x), Number(item[i].y)]) }) //數據源添加要素 clusterSource.addFeature(feature); } } //移到下個點 index++; }, 1000); //xy行列轉換 function convert_(zoomLevel, x, y) { var extent = Math.pow(2, zoomLevel); if (x < 0 || x > extent - 1) { console.log("The X coordinate is not sane: " + x); return; } if (y < 0 || y > extent - 1) { console.log("The Y coordinate is not sane: " + y); return; } // openlayers 6.0版本 var gridLoc = [x, extent - y, zoomLevel]; // openlayers 4.5版本 // var gridLoc = [x, extent - y + 1, zoomLevel]; return gridLoc; } //字符截取 function padLeft_(num, val) { return (new Array(num).join('0') + val).slice(-num); } </script> </body> </html>