持續三天不斷嘗試之后獲得了一些不為人知的辛酸 orz
關于OpenLayers的Cluster
ol.source.Cluster
ol利用這個js對象實現對地圖上feature進行聚合展示的控制,所有的features需要被放在一個feature數組中作為ol.source.Vector中features屬性進行載入。
下面就是官方針對Cluster的API文檔:
new ol.source.Cluster(options)
options: Constructor options.
Name | Type | Description |
---|---|---|
attributions | ol.AttributionLike | Attributions. |
distance | number | Minimum distance in pixels between clusters. Default is 20. |
extent | ol.Extent | Extent. |
geometryFunction | function | Function that takes an ol.Feature as argument and returns an ol.geom.Point as cluster calculation point for the feature. When a feature should not be considered for clustering, the function should return null. The default, which works when the underyling source contains point features only, is function(feature) {return feature.getGeometry();} See ol.geom.Polygon#getInteriorPoint for a way to get a cluster calculation point for polygons. |
format | ol.format.Feature | Format. |
logo | string | Logo. |
projection | ol.ProjectionLike | Projection. |
source | ol.source.Vector | Source. Required. |
wrapX | boolean | WrapX. Default is true |
之所以把這個貼出來是為了讓大家了解一個事情,那就是如果在Openlayers的官方API文檔中找你想要的東西,恐怕應該是一件需要碰運氣的事情。并不是說他們的文檔寫的不夠全面和完善,但當你想要從這種API文檔中快速的找到一些能夠立即實現你想要的開發效果的東西,那恐怕你得把他當前API所涉及到的所有js對象的API都看一遍才行了(事實上我就是這么做的)。本文的目的只是讓有GIS js開發基礎的人能夠快速的用openlayers實現多邊形聚合效果,因為在網上搜了很久都沒有類似的貼,所以才決定記錄下來,希望有可能幫助到遇到類似情況的人吧。
實現多邊形聚合效果
在網上翻了很多關于ol實現聚合的貼,基本上都是照本宣科搞幾個ol.geom.Point隨便弄一弄,然而我遇到的需求是我可能會get兩百個左右的ol.geom.Polygon,按照官網給的例子,根本行不通啊喂,報的錯也讓我很崩潰,說我的操作有問題。馬德我一個塞爾達可以單挑人馬活下來的人說我操作有問題我摔。
憤怒的我立即打開F12開始不斷的試錯,試圖證明我操作的問題是ol本身的問題引起的,后來API中的一個option讓我產生了想法,就是上面的那個geometryFunction,這個function可以將feature獲取到并且返回一個Point作為feature本身的聚合坐標,只要不妨礙進行聚合的計算,即使返回極點他也不攔著你,不過一般的Polygon都有一個getInteriorPoint方法可以獲取多邊形的內點,所以在geometryFunction中可以做到返回多邊形內點坐標(那么問題就來了,內點是哪個點呢,其實我也不知道,原諒我調試的時候沒有多去試試)。
代碼中把geometryFunction屬性加上,寫一個新的方法去獲取多邊形內點并返回。
var geometryFunc = function (feature) {
return feature.getGeometry().getInteriorPoint();
}
var vectorLayer = new ol.layer.Vector({
source: new ol.source.Cluster({
distance: distance, //調用方法時候輸入的聚合距離
source: new ol.source.Vector({ features: features }), //獲取到的feature數組
geometryFunction: geometryFunc //獲取多邊形內點的方法
})
});
然后保存刷新頁面,查詢一下,最后發現聚合效果正常運轉,我的操作終于沒問題了。
但是放大以后,我勒個擦,這些小圓圈是什么鬼,我的多邊形呢?
于是陷入了第二波痛苦。
直到在官方demo里面看到一個這樣的example:
Earthquake Cluster
然后打開了通往新世界的大門。
在ol.layer.Vector中有一個style屬性,用來將獲取到的feature和resolution進行處理,如下:
var maxFeatureCount, vector;
// 計算當前resolution下feature之間的距離以形成聚合圖形的基礎信息
function calculateClusterInfo(resolution) {
maxFeatureCount = 0;
var features = vector.getSource().getFeatures();
var feature, radius;
for (var i = features.length - 1; i >= 0; --i) {
feature = features[i];
var originalFeatures = feature.get('features');
var extent = ol.extent.createEmpty();
var j, jj;
for (j = 0, jj = originalFeatures.length; j < jj; ++j) {
ol.extent.extend(extent, originalFeatures[j].getGeometry().getExtent());
}
maxFeatureCount = Math.max(maxFeatureCount, jj);
radius = 0.25 * (ol.extent.getWidth(extent) + ol.extent.getHeight(extent)) /resolution;
feature.set('radius', radius);
}
}
var currentResolution;
function styleFunction(feature, resolution) {
if (resolution != currentResolution) {
// 根據resolution處理圖形聚合狀態
calculateClusterInfo(resolution);
currentResolution = resolution;
}
var style;
var size = feature.get('features').length;
if (size > 1) {
// 聚合圖形樣式,自動獲取對應半徑,調整顏色深度
style = new ol.style.Style({
image: new ol.style.Circle({
radius: feature.get('radius'),
fill: new ol.style.Fill({
color: [255, 153, 0, Math.min(0.8, 0.4 + (size / maxFeatureCount))]
})
}),
text: new ol.style.Text({
text: size.toString(),
fill: textFill,
stroke: textStroke
})
});
} else {
var originalFeature = feature.get('features')[0];
// 單個圖形樣式處理方法
style = createPolygonStyle(originalFeature);
}
return style;
}
vector = new ol.layer.Vector({
source: new ol.source.Cluster({
distance: 40,
source: new ol.source.Vector({
//獲取到的feature數組
feature: features
})
}),
style: styleFunction
});
這個方法首先對返回的resolution進行了判斷,如果地圖進行了縮放,則重新計算圖面上點與點之間的距離以規劃聚合圖形的半徑,然后對其返回對應單獨的圖形樣式。
接下來就要單獨講講上面單個圖形樣式處理方法的部分,由于在我的需求中,對每個圖形還單獨增加了點擊效果,聚合圖形不考慮在內,但凡出現沒有在聚合范圍內的Polygon,都要求能夠點擊并且高亮展示,所以上文的createPolygonStyle方法還需進行進一步的處理;首先在全局變量中需要添加一個highLightFeatureId用于記錄哪個Polygon是需要高亮的,然后在styleFunction回調的時候對需要高亮的圖形樣式進行調整,代碼如下:
var createPolygonStyle = function (feature) {
if (_highlightfeatureId == feature.attributes.id) {
// 高亮樣式
var style = new ol.style.Style({
geometry: feature.getGeometry(),
fill: new ol.style.Fill({
color: 'rgba(204,102,102,0.8)'
}),
stroke: new ol.style.Stroke({
color: '#CC3333',
width: 3
}),
image: new ol.style.Circle({
stroke: new ol.style.Stroke({
color: 'rgb(204,102,102)',
width: 1
}),
radius: 6,
fill: new ol.style.Fill({
color: [255, 255, 255, 0.6]
})
}),
text: new ol.style.Text({
text: feature.text,
textAlign: "center",
textBaseline: 'middle',
font: "14px serif",
fill: new ol.style.Fill({ color: "#FFFFFF" }),
stroke: new ol.style.Stroke({ color: "#000000", width: 3 })
})
});
return style;
} else {
// 普通多邊形的樣式
var style = new ol.style.Style({
geometry: feature.getGeometry(),
fill: new ol.style.Fill({
color: 'rgba(0,204,204,0.2)'
}),
stroke: new ol.style.Stroke({
lineDash: [20, 20],
color: '#0099CC',
width: 1
}),
image: new ol.style.Circle({
stroke: new ol.style.Stroke({
color: 'rgb(0,204,204)',
width: 1
}),
radius: 6,
fill: new ol.style.Fill({
color: [255, 255, 255, 0.6]
})
}),
text: new ol.style.Text({
text: feature.text,
textAlign: "center",
textBaseline: 'middle',
font: "14px serif",
fill: new ol.style.Fill({ color: "#FFFFFF" }),
stroke: new ol.style.Stroke({ color: "#000000", width: 3 })
})
});
return style;
}
}
最后附上實現效果圖
小比例尺
大比例尺
小比例尺高亮
大比例尺高亮
高亮后聚合