原始数据分布
给老外做的Demo,所以是英文界面。
原始数据分布情况如下:
geojson文本内容:
三种方法基本原理
三种聚合算法来做一个例子(500条记录)。
方法1:按Ol默认方法进行聚类,使用Openlayers默认聚类方法,将任何特征聚合为满足最小距离的种子数据
方法2:按所属区域属性进行聚类,根据元素的属性进行聚合,即具有相同Name_2属性的元素的聚合
方法3:按所属空间网格进行聚类,将所有元素所在的区域划分为多个网格,在网格的中心创建特征,并将网格中的特征聚合到该网格中
上代码
JavaScript
var myMap = null;
var vectorLayerForOrginalMap = null;
var clusterLayerByOLMethod = null;
var clusterLayerByBelongsRegion = null;
var clusterLayerByBelongsGrid = null;
var lastSelectedFeature = null;
const initMap = () => {const raster = new ol.layer.Tile({source: new ol.source.OSM(),});const map = new ol.Map({layers: [raster],target: "map",view: new ol.View({center: [-137702.88482159126, 7165549.988880951],zoom: 6,}),});myMap = map;showTip();
};
const removeAllVecLayers = () => {if (!myMap) return;vectorLayerForOrginalMap && myMap.removeLayer(vectorLayerForOrginalMap);clusterLayerByOLMethod && myMap && myMap.removeLayer(clusterLayerByOLMethod);clusterLayerByBelongsRegion && myMap.removeLayer(clusterLayerByBelongsRegion);clusterLayerByBelongsGrid && myMap.removeLayer(clusterLayerByBelongsGrid);
};
const loadData = () => {removeAllVecLayers();const vectorSource = createVectorSource();const vectorLayer = new ol.layer.Vector({source: vectorSource,});vectorLayerForOrginalMap = vectorLayer;myMap && myMap.addLayer(vectorLayer);
};
// 方法一★★★★★★
const loadDataClusterOl = () => {removeAllVecLayers();const clusterSource = new ol.source.Cluster({distance: 100,minDistance: 80,source: createVectorSource(),});const styleCache = {};clusterLayerByOLMethod = new ol.layer.Vector({source: clusterSource,style: function (feature) {const size = feature.get("features").length;let style = styleCache[size];if (!style) {style = createStyle(15 + size / 20.0, `${size.toString()}`);styleCache[size] = style;}return style;},});myMap && myMap.addLayer(clusterLayerByOLMethod);
};
// 方法二★★★★★★
const loadDataClusterRegion = () => {removeAllVecLayers();const vectorSource = createVectorSource();const styleCache = {};clusterLayerByBelongsRegion = new ol.layer.Vector({source: vectorSource,style: function (feature) {let size = feature.features && feature.features.length;!size && (size = 15);let style = styleCache[size];if (!style) {style = createStyle(15 + size / 2.0, `${size.toString()}`);styleCache[size] = style;}return style;},});myMap && myMap.addLayer(clusterLayerByBelongsRegion);vectorSource.on("featuresloadend", function () {loadDataClusterRegionLoaded(vectorSource);});
};
const loadDataClusterRegionLoaded = (vectorSource) => {const fsMap = new Map();const fs = vectorSource.getFeatures();for (let i = 0; i < fs.length; i++) {const region = fs[i].getProperties()["NAME_2"];if (fsMap.has(region)) {fsMap.get(region).push(fs[i]);fs[i].del = true;continue;}if (!fs[i].features && !fs[i].del) {fs[i].features = [fs[i]];fsMap.set(region, fs[i].features);continue;}}for (let i = fs.length - 1; i >= 0; i--) {if (fs[i].del) {vectorSource.removeFeature(fs[i]);}}
};
// 方法三★★★★★★
const loadDataClusterGrid = () => {removeAllVecLayers();const vectorSource = createVectorSource();const styleCache = {};clusterLayerByBelongsGrid = new ol.layer.Vector({source: vectorSource,style: function (feature) {let size = feature.features && feature.features.length;!size && (size = 15);let style = styleCache[size];if (!style) {style = createStyle(size, `${size.toString()}`);styleCache[size] = style;}return style;},});myMap && myMap.addLayer(clusterLayerByBelongsGrid);vectorSource.on("featuresloadend", function () {loadDataClusterGridLoaded(vectorSource);});
};
const loadDataClusterGridLoaded = (vectorSource) => {const fs = vectorSource.getFeatures();const ext = vectorSource.getExtent();const disX = 200000,disY = 200000;const minX = ext[0],minY = ext[1];const maxX = ext[2],maxY = ext[3];for (let i = minX; i <= maxX; i += disX) {for (let j = minY; j <= maxY; j += disY) {const centerX = i + disX / 2,centerY = j + disY / 2;var feature = new ol.Feature();feature.features = [];feature.setGeometry(new ol.geom.Point([centerX, centerY]));for (let k = 0; k < fs.length; k++) {if (fs[k].del) continue;const geometry = fs[k].getGeometry();const coordinates = geometry.getCoordinates();const x = coordinates[0],y = coordinates[1];if (x <= i || x > i + disX) continue;if (y <= j || y > j + disY) continue;fs[k].del = true;feature.features.push(fs[k]);}feature.features.length > 0 && vectorSource.addFeature(feature);}}for (let i = fs.length - 1; i >= 0; i--) {vectorSource.removeFeature(fs[i]);}
};
const createVectorSource = () => {return new ol.source.Vector({url: "./data/postcodes.json.geojson",format: new ol.format.GeoJSON(),});
};
const createStyle = (size, text) => {size < 10 && (size = 9);let fillColors = {0: "pink",1: "#0c0",2: "#cc0",3: "#f00",4: "#f0f",5: "#0ff",6: "#00f",};return new ol.style.Style({image: new ol.style.Circle({radius: size,stroke: new ol.style.Stroke({color: "#fff",}),fill: new ol.style.Fill({color: fillColors[`${Math.floor(size / 10)}`],}),}),text: new ol.style.Text({text: text,fill: new ol.style.Fill({color: "#fff",}),}),});
};
const showTip = () => {myMap.on("pointermove", function (event) {var feature = myMap.forEachFeatureAtPixel(event.pixel, function (feature) {return feature;});lastSelectedFeature && lastSelectedFeature.setStyle();if (feature) {lastSelectedFeature = feature;lastSelectedFeature.setStyle(new ol.style.Style());const tooltip = document.getElementById("info");// Get the feature informationconst fs = feature.features || feature.getProperties()["features"];const date = new Date();const options = {weekday: "long",year: "numeric",month: "long",day: "numeric",hour: "numeric",minute: "numeric",second: "numeric",};const stringDate = date.toLocaleDateString("en-US", options);if (!fs) {tooltip.innerHTML = `${stringDate} : <br>not clustered`;return;}const infos = [];for (let i = 0; i < fs.length; i++) {const f = fs[i];infos.push(JSON.stringify({id: f.getProperties()["id"],NAME_2: f.getProperties()["NAME_2"],}));}tooltip.innerHTML = `${stringDate}<br>Clustered Features : <br>${infos.join("<br>")}`;}});
};
HTML 页面
<!DOCTYPE html>
<html><head><title>Cluster UK Postcodes </title><link rel="stylesheet" href="style.css"><link rel="stylesheet" href="libs/ol.css"><script src="./libs/ol.js" type="text/javascript"></script><script src="do.js" type="text/javascript"></script>
</head><body><div class="mcontainer"><div class="leftPanel"><div><button onclick="loadData();">Load Data Directly</button><span>Load data directly</span></div><div><button onclick="loadDataClusterOl();">Method 1:Cluster By Ol Default Method</button><span>Use Openlayers default cluster method,Aggregating any feature as seed data that satisfies theminimum distance</span></div><div><button onclick="loadDataClusterRegion();">Method 2:Cluster By Belonging Region Attribute</button><span>Aggregation based on the attributes of elements, i.e. aggregation of elements with the same Name_2attribute</span></div><div><button onclick="loadDataClusterGrid();">Method 3:Cluster By Belonging Spatial Grid</button><span>Divide the area where all elements are located into several grids, create features at the centerof the grid, and aggregate the features in the grid to that grid</span></div><div id="info"></div></div><div class="rightPanel"><div id="map"></div></div></div><script type="text/javascript">initMap();</script>
</body></html>
CSS
html,
body {margin: 0;padding: 0;height: 100%;
}
.mcontainer {display: flex;height: 100%;
}
.leftPanel {width: 25%;height: 100%;display: flex;flex-direction: row;flex-flow: column;overflow-y: auto;box-shadow: -5px 0px 0px 0px black, 5px 0px 0px 0px black;
}
.rightPanel {width: 75%;height: 100%;
}
#map {width: 100%;height: 100%;
}
.leftPanel div {display: flex;flex-direction: row;flex-flow: column;overflow-y: auto;border-top: 1px solid #ccc;
}
button {display: block;width: 80%;align-self: center;margin-top:.5rem;
}
#info {border-top: 1px solid #ccc;
}
效果图