开放层 3 - 修改集群层的功能

OpenLayer 3 - modify features of Cluster layer

本文关键字:功能 修改      更新时间:2023-09-26

以下问题已经在"OL3 Dev"上提出,但已移至StackOverflow以符合组策略。

我已经使用 OpenLayers 3 一段时间了,并实现了一个简单的测试应用程序,在那里我模拟了地图上几个对象的移动。我使用矢量图层和相应的矢量源。

假设我有大约 1000 个具有 ol.geom.Point 几何的特征,每 20-30 秒更新一次。

我可以通过修改几何坐标获得相当好的结果,结果很流畅并且工作正常。

现在,我尝试使用群集功能对已关闭的功能进行分组。不幸的是,在这种情况下,结果非常缓慢且不规则。我认为问题是由于每次更改单个特征的几何形状时都会触发 change() 事件,所以我想知道:

有没有办法防止集群立即考虑对要素的修改,并仅在特定时间间隔触发它?

在下面,您可以找到两个示例,第一个没有集群源,第二个示例带有集群源。

  1. 无群集:http://jsfiddle.net/sparezenny/dwLpmqvc/

    var mySource =  new ol.source.Vector({
        features : new Array()
    });
    var myLayer = new ol.layer.Vector({
        source: mySource,
        style: function(feature, resolution) {
                 var myStyle = [new ol.style.Style({
                    image: new ol.style.Circle({
                      radius: 10,
                      stroke: new ol.style.Stroke({
                        color: '#fff'
                      }),
                      fill: new ol.style.Fill({
                        color: '#3399CC'
                      })
                    })
                })];
               return myStyle;
      }
    });
    var map = new ol.Map({
            target: 'map',
            layers: [
              new ol.layer.Tile({
                source: new ol.source.MapQuest({layer: 'sat'})
              }),
              myLayer
            ],
            view: new ol.View({
              center: ol.proj.transform([37.41, 8.82], 'EPSG:4326', 'EPSG:3857'),
              zoom: 4
            })
    });
    var positions = new Array();
    function Mock(id, longitude, latitude){
            this.id=id;
            this.latitude=latitude;
            this.longitude=longitude;       
    };
    function updatePositions(mocks){
        var featuresToBeAdded = new Array();
        for (var i=0; i < mocks.length; i++){
            var mock = mocks[i];
            var id = mock.id;
            var previousPosition = positions[id];       
            var resultFeature;
            var position;
            // new
            if (previousPosition==undefined || previousPosition==null){
                position = ol.proj.transform([ mock.longitude, mock.latitude ],
                                                 'EPSG:4326', 'EPSG:3857');
                positions[id] = mock;
                resultFeature = new ol.Feature({
                    geometry: new ol.geom.Point(position)
                });
                featuresToBeAdded.push(resultFeature);
            }
            // update
            else{
                resultFeature  = positions[id].feature;
                positions[id] = mock;
                position = ol.proj.transform([ mock.longitude, mock.latitude ],
                                                 'EPSG:4326', 'EPSG:3857');
                resultFeature.getGeometry().setCoordinates(position);
            }
            positions[id].feature = resultFeature;
        }
        if (featuresToBeAdded.length>0){
            mySource.addFeatures(featuresToBeAdded);
        }
        //map.render();
    }
    var myMocks = new Array(1000);
    for (var i=0; i<1000; i++){
        myMocks[i] = new Mock(i, 
            37.41+(Math.random()>0.5?0.01:-0.01)*i, 
            8.82 +(Math.random()>0.5?0.01:-0.01)*i);
    }
    setInterval(
        function(){
            var j = Math.round(Math.random()*980);
            for (var i=0; i<20; i++){
                myMocks[j+i].latitude = myMocks[j+i].latitude + (Math.random()>0.5?0.01:-0.01);
                myMocks[j+i].longitude = myMocks[j+i].longitude + (Math.random()>0.5?0.01:-0.01);
            }
            console.debug("updatePositions..");
            updatePositions(myMocks);
    }, 5000);
    
  2. 集群:http://jsfiddle.net/sparezenny/gh7ox9nj/

    var mySource =  new ol.source.Vector({
        features : new Array()
    });
    var clusterSource = new ol.source.Cluster({
        distance: 10,
        source: mySource
    });
    var myLayer = new ol.layer.Vector({
        source: clusterSource,
        style: function(feature, resolution) {
                var clusteredFeatures = feature.get('features');
                var size = feature.get('features').length;
                 var myStyle = [new ol.style.Style({
                    image: new ol.style.Circle({
                      radius: 10,
                      stroke: new ol.style.Stroke({
                        color: '#fff'
                      }),
                      fill: new ol.style.Fill({
                        color: '#3399CC'
                      })
                    }),
                    text: new ol.style.Text({
                      text: size.toString(),
                      fill: new ol.style.Fill({
                        color: '#fff'
                      })
                    })
                })];
               return myStyle;
      }
    });
    var map = new ol.Map({
            target: 'map',
            layers: [
              new ol.layer.Tile({
                source: new ol.source.MapQuest({layer: 'sat'})
              }),
              myLayer
            ],
            view: new ol.View({
              center: ol.proj.transform([37.41, 8.82], 'EPSG:4326', 'EPSG:3857'),
              zoom: 4
            })
    });
    var positions = new Array();
    function Mock(id, longitude, latitude){
            this.id=id;
            this.latitude=latitude;
            this.longitude=longitude;       
    };
    function updatePositions(mocks){
        var featuresToBeAdded = new Array();
        for (var i=0; i < mocks.length; i++){
            var mock = mocks[i];
            var id = mock.id;
            var previousPosition = positions[id];       
            var resultFeature;
            var position;
            // new
            if (previousPosition==undefined || previousPosition==null){
                position = ol.proj.transform([ mock.longitude, mock.latitude ],
                                                 'EPSG:4326', 'EPSG:3857');
                positions[id] = mock;
                resultFeature = new ol.Feature({
                    geometry: new ol.geom.Point(position)
                });
                featuresToBeAdded.push(resultFeature);
            }
            // update
            else{
                resultFeature  = positions[id].feature;
                positions[id] = mock;
                position = ol.proj.transform([ mock.longitude, mock.latitude ],
                                                 'EPSG:4326', 'EPSG:3857');
                resultFeature.getGeometry().setCoordinates(position);
            }
            positions[id].feature = resultFeature;
        }
        if (featuresToBeAdded.length>0){
            mySource.addFeatures(featuresToBeAdded);
        }
        //map.render();
    }
    var myMocks = new Array(1000);
    for (var i=0; i<1000; i++){
        myMocks[i] = new Mock(i, 
            37.41+(Math.random()>0.5?0.01:-0.01)*i, 
            8.82 +(Math.random()>0.5?0.01:-0.01)*i);
    }
    setInterval(
        function(){
            var j = Math.round(Math.random()*980);
            for (var i=0; i<20; i++){
                myMocks[j+i].latitude = myMocks[j+i].latitude + (Math.random()>0.5?Math.random()*0.01:-Math.random()*0.01);
                myMocks[j+i].longitude = myMocks[j+i].longitude + (Math.random()>0.5?Math.random()*0.01:-Math.random()*0.01);
            }
            console.debug("updatePositions..");
            updatePositions(myMocks);
    }, 5000);
    

如您所见,我有 1000 个特征,我尝试每 5 秒更新其中 20 个特征的位置。在第一种情况下,与地图的交互是平滑的,而在第二种情况下,它经常停止或减慢。

关于如何避免这种情况的任何线索或建议?

提前致谢

现在这是一个旧链接,但是由于我遇到了完全相同的问题,我想我会发布我的解决方法。

这个想法是削弱集群源中的违规事件处理程序,并且仅在每帧呈现时触发相同的代码。

请注意,显示的代码:

  1. 真的是一个相当黑客,因为它在搞砸私人功能。
  2. 由于上述原因,它可能需要针对不同的非调试版本进行修改。
  3. 使用比 OP 更新的 OL3 版本(另请参阅第 2 点!

JSFiddle here

if (ol.source.Cluster.prototype.onSourceChange_)
{
    // Make a new pointer to the old sourceChange function.
    ol.source.Cluster.prototype.newSourceChange =
        ol.source.Cluster.prototype.onSourceChange_;
    // Nuke the old function reference.
    ol.source.Cluster.prototype.onSourceChange_ = function() {};
}
if (ol.source.Cluster.prototype.Ra)
{
    // As above, but for non-debug code.
    ol.source.Cluster.prototype.newSourceChange = 
        ol.source.Cluster.prototype.Ra;
    ol.source.Cluster.prototype.Ra = function() {};
}
// Later on..
map.on('postrender', clusterSource.newSourceChange, clusterSource);

如您所见,即使更新时间为 1000 毫秒,这也可以轻松处理 100 个功能。