单位像素距离也可以根据地图级别进行计算;
{“level”: 0, “resolution”: 156543.033928},
{“level”: 1, “resolution”: 78271.5169639999},
{“level”: 2, “resolution”: 39135.7584820001},
{“level”: 3, “resolution”: 19567.8792409999},
{“level”: 4, “resolution”: 9783.93962049996},
{“level”: 5, “resolution”: 4891.96981024998},
{“level”: 6, “resolution”: 2445.98490512499},
{“level”: 7, “resolution”: 1222.99245256249},
{“level”: 8, “resolution”: 611.49622628138},
{“level”: 9, “resolution”: 305.748113140558},
{“level”: 10, “resolution”: 152.874056570411},
{“level”: 11, “resolution”: 76.4370282850732},
{“level”: 12, “resolution”: 38.2185141425366},
{“level”: 13, “resolution”: 19.1092570712683},
{“level”: 14, “resolution”: 9.55462853563415},
{“level”: 15, “resolution”: 4.77731426794937},
{“level”: 16, “resolution”: 2.38865713397468},
{“level”: 17, “resolution”: 1.19432856685505},
{“level”: 18, “resolution”: 0.597164283559817}
<!DOCTYPE html> <html> <head> <meta charset='utf-8' /> <title>创建样式聚类</title> <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' /> <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v1.1.1/mapbox-gl.js'></script> <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v1.1.1/mapbox-gl.css' rel='stylesheet' /> <style> body { margin:0; padding:0; } #map { position:absolute; top:0; bottom:0; width:100%; } </style> </head> <body> <div id='map'></div> <script> mapboxgl.accessToken = 'pk.eyJ1IjoiemhhbmcyMDIxIiwiYSI6ImNrbGthdmFvbDM0cTMyb3M2eXFiODR5d3QifQ.ow97I1ikb9zlP3p9qmFuBQ'; var map = new mapboxgl.Map({ container: 'map', style: 'mapbox://styles/mapbox/dark-v10', center: [-103.59179687498357, 40.66995747013945], zoom: 3 }); map.on('load', function() { // Add a new source from our GeoJSON data and set the // 'cluster' option to true. GL-JS will add the point_count property to your source data. map.addSource("earthquakes", { type: "geojson", // Point to GeoJSON data. This example visualizes all M1.0+ earthquakes // from 12/22/15 to 1/21/16 as logged by USGS' Earthquake hazards program. data: "https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson", cluster: true, clusterMaxZoom: 14, // Max zoom to cluster points on clusterRadius: 50 // Radius of each cluster when clustering points (defaults to 50) }); map.addLayer({ id: "clusters", type: "circle", source: "earthquakes", filter: ["has", "point_count"], paint: { // Use step expressions (https://docs.mapbox.com/mapbox-gl-js/style-spec/#expressions-step) // with three steps to implement three types of circles: // * Blue, 20px circles when point count is less than 100 // * Yellow, 30px circles when point count is between 100 and 750 // * Pink, 40px circles when point count is greater than or equal to 750 "circle-color": [ "step", ["get", "point_count"], "#51bbd6", 100, "#f1f075", 750, "#f28cb1" ], "circle-radius": [ "step", ["get", "point_count"], 20, 100, 30, 750, 40 ] } }); map.addLayer({ id: "cluster-count", type: "symbol", source: "earthquakes", filter: ["has", "point_count"], layout: { "text-field": "{point_count_abbreviated}", "text-font": ["DIN Offc Pro Medium", "Arial Unicode MS Bold"], "text-size": 12 } }); map.addLayer({ id: "unclustered-point", type: "circle", source: "earthquakes", filter: ["!", ["has", "point_count"]], paint: { "circle-color": "#11b4da", "circle-radius": 4, "circle-stroke-width": 1, "circle-stroke-color": "#fff" } }); // inspect a cluster on click map.on('click', 'clusters', function (e) { var features = map.queryRenderedFeatures(e.point, { layers: ['clusters'] }); var clusterId = features[0].properties.cluster_id; map.getSource('earthquakes').getClusterExpansionZoom(clusterId, function (err, zoom) { if (err) return; map.easeTo({ center: features[0].geometry.coordinates, zoom: zoom }); }); }); map.on('mouseenter', 'clusters', function () { map.getCanvas().style.cursor = 'pointer'; }); map.on('mouseleave', 'clusters', function () { map.getCanvas().style.cursor = ''; }); }); </script> </body> </html>
/** * @param visibleBounds 屏幕的范围 前端传 * @param list 所有的点数据 * @param mClusters 聚合后的数据集合 * @param isLattice 是否是方格和距离的算法 , 是 方格和距离的算法 , 否 距离算法 * @return */ private static void calculateOrgClusters(LatLngBounds visibleBounds, List<Map<String, Object>> list, List<Cluster> mClusters, double mClusterDistance, Integer zoom, boolean isLattice) { mIsCanceled = false; mClusters.clear(); ClusterItem clusterItem = new RegionItem(); LatLng mLatLng; for (int i = 0; i < list.size(); i++) { Map<String, Object> data = list.get(i); mLatLng = new LatLng(Double.parseDouble(data.get("lat").toString()), Double.parseDouble(data.get("lng").toString())); clusterItem = new RegionItem(mLatLng, data); if (mIsCanceled) { return; } LatLng latlng = clusterItem.getPosition(); if (visibleBounds.contains(latlng)) { Cluster cluster = null; if (isLattice) { // 方格和距离的算法 cluster = getClusterFangGe(latlng, mClusters); } else { // 距离算法 cluster = getCluster(latlng, mClusters, mClusterDistance, zoom); } if (cluster != null) { cluster.addCount(); } else { cluster = new Cluster(latlng); mClusters.add(cluster); cluster.addClusterItem(clusterItem); cluster.addCount(); cluster.setMap(data); // 设置中心点的范围 if (isLattice) { LatLngBounds latLngBounds = AMapUtils.getAround(latlng, mClusterDistance); cluster.setLatLngBounds(latLngBounds); } } } } }
<!DOCTYPE html> <html> <head> <meta charset='utf-8'/> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no'/> <!--本地加载的js及css文件--> <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v1.1.1/mapbox-gl.js'></script> <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v1.1.1/mapbox-gl.css' rel='stylesheet'/> <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <style> #m_infobar { position: absolute; bottom: 100px; left: 20px; width: 200px; height: 200px; overflow: auto; z-index: 201; background: greenyellow; padding-left: 10px; } #menu { background: #fff; position: absolute; z-index: 1; top: 70px; right: 50px; border-radius: 3px; width: 120px; border: 1px solid rgba(0, 0, 0, 0.4); font-family: 'Open Sans', sans-serif; } #menu a { font-size: 13px; color: #404040; display: block; margin: 0; padding: 0; padding: 10px; text-decoration: none; border-bottom: 1px solid rgba(0, 0, 0, 0.25); text-align: center; } #menu a:last-child { border: none; } #menu a:hover { background-color: #f8f8f8; color: #404040; } #menu a.active { background-color: #3887be; color: #ffffff; } #menu a.active:hover { background: #3074a4; } </style> </head> <body style="height: 100%;"> <div class="panel panel-default"> <div class="panel-heading"> <div class="row"> <div class="col-md-4"><strong>Geojson后端聚合轨迹点</strong></div> <div class="col-md-8"><label id="i_click"></label></div> </div> </div> <div class="panel-body"> <nav id="menu"></nav> <div id="map" style="width: 100%; height: 800px;"> <div id='m_infobar'> <div id="tooltip-name"></div> <div id='tooltip'></div> </div> </div> </div> <div class="panel-footer" style="bottom: 0px;"> <div class="row"> <div class="col-xs-4"><label id="i_coordinate"></label></div> <div class="col-xs-8"><label id="i_show"></label></div> </div> </div> </div> <script> mapboxgl.accessToken = 'pk.eyJ1IjoiemhhbmcyMDIxIiwiYSI6ImNrbGthdmFvbDM0cTMyb3M2eXFiODR5d3QifQ.ow97I1ikb9zlP3p9qmFuBQ'; var map = new mapboxgl.Map({ resizeEnable: true, container: 'map', style: 'mapbox://styles/mapbox/dark-v10', center: [105, 34], zoom: 4 }); map.on('load', function () { // Add a new source from our GeoJSON data and set the // 'cluster' option to true. GL-JS will add the mag property to your source data. map.addSource("earthquakes", { type: "geojson", // Point to GeoJSON data. This example visualizes all M1.0+ earthquakes // from 12/22/15 to 1/21/16 as logged by USGS' Earthquake hazards program. // data: "https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson", // 只需修改数据来源~~ data: "/geo?bounds=" + map.getBounds().toString().replaceAll('LngLat', '').replace('Bounds', '').replace('),', ';').replaceAll('(', '').replaceAll(')', '') + "&zoom=" + map.getZoom(), // cluster: true, // clusterMaxZoom: 20, // Max zoom to cluster points on //clusterRadius: 50 // Radius of each cluster when clustering points (defaults to 50) }); var url = "/geo?bounds=" + map.getBounds().toString().replaceAll('LngLat', '').replace('Bounds', '').replace('),', ';').replaceAll('(', '').replaceAll(')', '') + "&zoom=" + map.getZoom(); var zoomOri = map.getZoom(); console.log('url------: ' + url); map.addLayer({ id: "clusters", type: "circle", source: "earthquakes", filter: ["has", "mag"], paint: { // Use step expressions (https://docs.mapbox.com/mapbox-gl-js/style-spec/#expressions-step) // with five steps to implement three types of circles: // * 11b4da,1px circles when point mag is 1 // * 99CCCC,1px circles when point mag is less than 20 // * Blue, 20px circles when point mag is less than 100 // * Yellow, 30px circles when point mag is between 100 and 750 // * Pink, 40px circles when point mag is greater than or equal to 750 "circle-color": [ "step", ["get", "mag"], "#11b4da", 1, "#99CCCC", 20, "#3366FF", 100, "#f1f075", 750, "#f28cb1" ], "circle-radius": [ "step", ["get", "mag"], 20, 100, 30, 750, 40 ] } }); map.addLayer({ id: "cluster-mag", type: "symbol", source: "earthquakes", filter: ["has", "mag"], layout: { "text-field": "{mag}", "text-font": ["DIN Offc Pro Medium", "Arial Unicode MS Bold"], "text-size": 12 } }); // inspect a cluster on click map.on('click', 'clusters', function (e) { var features = map.queryRenderedFeatures(e.point, {layers: ['clusters']}); var deviceId = features[0].properties.deviceId; var clusterId = features[0].properties.clusterId; console.log("deviceId: ", deviceId); console.log("clusterId: ", clusterId); console.log("lng,lat: ", "[" + features[0].geometry.coordinates[0] + "," + features[0].geometry.coordinates[1] + "]"); console.log("lng,lat: ", JSON.stringify(features[0].geometry.coordinates)); map.easeTo({ center: features[0].geometry.coordinates, zoom: map.getZoom() }); }); map.on("mousemove", function (e) { var features = map.queryRenderedFeatures(e.point, {layers: ["clusters"]}); if (features.length) { //show name and value in sidebar var item = features[0].properties; var arr = []; arr.push("设备id:" + item.deviceId); arr.push("在线:" + item.online); arr.push("故障:" + item.fault); arr.push("重叠点数:" + item.mag); viewMapOption(); document.getElementById('tooltip').innerHTML = arr.join('<br/>'); } else { //if not hovering over a feature set tooltip to empty document.getElementById('tooltip').innerHTML = ""; } }); map.on('mouseenter', 'clusters', function () { map.getCanvas().style.cursor = 'pointer'; }); map.on('mouseleave', 'clusters', function () { map.getCanvas().style.cursor = ''; }); // 获取点数据 function queryData(str) { zoom = map.getZoom(); zoom = zoom.toString().substr(0, zoom.toString().indexOf(".")); if (zoom) { } else { console.log("queryData0---: ", zoom); zoom = zoomOri + 1; console.log("queryData1---: ", zoom); } var url = "/geo?bounds=" + map.getBounds().toString().replaceAll('LngLat', '').replace('Bounds', '').replace('),', ';').replaceAll('(', '').replaceAll(')', '') + "&zoom=" + zoom; console.log('queryData: ', str, url); $.ajax({ url: url, // ajax请求要请求的地址 type: "get", // 请求的类型 get post success: function (data) { // 请求成功之后要执行的方法 // data 接收请求成功之后的返回值 map.getSource('earthquakes').setData(data); }, error: function (error) { // 请求失败之后要执行的内容 } }) } // 移动结束事件 map.on('moveend', queryData); // 缩放完成事件 map.on('zoomend', queryData); // 拖拽完成事件 map.on('dragend', queryData); // 鼠标滚动 map.on("wheel", queryData); viewMapOption(); map.on('move', viewMapOption); map.on('mousemove', viewCoordinate); }); function locationByXY() { var px = parseFloat($("#m_x").val()); var py = parseFloat($("#m_y").val()); map.setCenter([px, py]); } /** * 容器改变触发 */ function resizeMap() { //初始化宽度、高度 $("#map").height($(window).height() - 150); //当文档窗口发生改变时 触发 $(window).resize(function () { $("#map").height($(window).height() - 150); }); } function viewCoordinate(evt) { if (evt != null) $("#i_coordinate").text("当前坐标:" + evt.lngLat.lng.toFixed(7) + "," + evt.lngLat.lat.toFixed(7)); } /** * 显示地图状态信息 */ function viewMapOption() { var bounds = map.getBounds(); console.log("级别=" + map.getZoom() + ",中心(" + bounds.getCenter().lng.toFixed(7) + "," + bounds.getCenter().lat.toFixed(7) + ")"); console.log("边界[" + bounds.getSouthWest().lng.toFixed(7) + "," + bounds.getSouthWest().lat.toFixed(7) + ";" + bounds.getNorthEast().lng.toFixed(7) + "," + bounds.getNorthEast().lat.toFixed(7) + "]"); $("#i_click").text("级别=" + map.getZoom() + ",中心(" + bounds.getCenter().lng.toFixed(7) + "," + bounds.getCenter().lat.toFixed(7) + ")"); $("#i_show").text("边界[" + bounds.getSouthWest().lng.toFixed(7) + "," + bounds.getSouthWest().lat.toFixed(7) + ";" + bounds.getNorthEast().lng.toFixed(7) + "," + bounds.getNorthEast().lat.toFixed(7) + "]"); } </script> </body> <link href="https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.css" rel="stylesheet"> </html>