Openlayers中多邊形的聚合

持續三天不斷嘗試之后獲得了一些不為人知的辛酸 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;
    }
}

最后附上實現效果圖
小比例尺


大比例尺


小比例尺高亮


大比例尺高亮


高亮后聚合


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容