6

I have an OpenLayers 2.12 map with a vector layer with a cluster strategy. All the static behavior is how I want it. Now I'd like to add the possibility to update the features' position. I do this with refresh code outside of the OL API, so I can easily add extra events, options lists and so related to the incoming data.

The problem is that the clustering strategy doesn't update the position for features inside a cluster. If they are not clustered, they are moved.

At first, the features are added to an array and then the array is stored and used to add all the features to the layer:

var myUnit = {
...
this.layer = new OpenLayers.Layer.Vector(
        "Voertuigen",
        {
            strategies: [
                new OpenLayers.Strategy.Cluster({
                    active: true,
                    autoActivate: true,
                    distance: this.clusterDistance,
                    threshold: this.clusterThreshold})
            ],
            styleMap: new OpenLayers.StyleMap({
                "default": style,
                "hover": hoverstyle
            }),
            isBaseLayer: false,
            rendererOptions: {yOrdering: true}
        }
    );
...
myUnit.layer.addFeatures(myUnit.features);

and for the update of the position I find the feature in myUnit.features array and move it

feature.move(pos_lonlat);

This works for all features not in a cluster. I tried many other things, including removing all the features from the layer and re-adding them. Strangely this also doesn't move the cluster.

How can i trigger the strategy to recluster everything?

3 Answers 3

2

In my case, I found this solution to updating features. It's working very well:

1) In javascript I create a refresh method in every 5 seconds:

window.setInterval(loadUnits, 5000);

2) Create the layer :

function createUnitsLayer() {
    var def_style = new OpenLayers.Style({
        pointRadius : "${size}",
        fillOpacity: 0.5,
    });
    var sel_style = new OpenLayers.Style({
        fillOpacity: 1
    });
    var styleMap = new OpenLayers.StyleMap({"default" : def_style, "select": sel_style});
    unitsLayer = new OpenLayers.Layer.Vector("Units",  {styleMap: styleMap} );
    map.addLayer(unitsLayer);
}

Note the unitsLayer variable is global and declared in beginning of script.

3) Now, we fill the unitsLayer with the features. These come from a AJAX request. Call this after create the map and vector, so we have the initial positions, and every 5 seconds they will be refreshed.

function loadUnits() {
    var mapbounds = map.getExtent();
    mapbounds.transform(toProjection, fromProjection );
    bbox = mapbounds.toArray();
    var bleft = bbox[0];
    var bbottom = bbox[1];
    var bright = bbox[2];
    var btop = bbox[3];
    showLoader();
    var sourceURL = "getUnits?bleft=" + bleft + "&bbottom=" + bbottom + "&bright=" + bright + "&btop=" + btop;
    $.ajax({
            url: sourceURL,
            dataType: "json"
    }).done(function(data) {
        data = eval( data );
        var geojson_format = new OpenLayers.Format.GeoJSON({
            'internalProjection': map.baseLayer.projection,
            'externalProjection': new OpenLayers.Projection("EPSG:4326")
        });
        unitsLayer.removeAllFeatures();
        unitsLayer.addFeatures(geojson_format.read(data));
    });     
}

This is a simple jQuery ajax request ( you will need the jQuery libs ) Here I set a bounding box from the map's viewport and send this to my ajax responder. If is not your case, just ignore my projection transform. Then, I remove all features from unitsLayer and add the new that came from ajax (expected a json format).

This is the expected json :

{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"name":"TRON-02","serial":"TRON002","bearing":0,"color":"green","velocity":0.0,"size":15,"image":"img/unit_map3.png","pin_image":"img/pins/me/army.png","strength":0.0,"range":0.0},"geometry":{"type":"Point","coordinates":[-50.06542968749966,-23.749149728383717]}},{"type":"Feature","properties":{"name":"Magno","serial":"MAGNO","bearing":0,"color":"green","velocity":0.0,"size":15,"image":"img/unit_map3.png","pin_image":"img/pins/me/army.png","strength":0.0,"range":0.0},"geometry":{"type":"Point","coordinates":[-51.13659667968737,-25.228796984436144]}},{"type":"Feature","properties":{"name":"Flynn-01","serial":"FLYNN001","bearing":141,"color":"green","velocity":0.0,"size":15,"image":"img/unit_map3.png","pin_image":"img/pins/me/army.png","strength":0.0,"range":0.0},"geometry":{"type":"Point","coordinates":[-49.45019531249952,-26.769155570541734]}},{"type":"Feature","properties":{"name":"Stu-01","serial":"STU001","bearing":0,"color":"green","velocity":0.0,"size":15,"image":"img/unit_map3.png","pin_image":"img/pins/me/army.png","strength":0.0,"range":0.0},"geometry":{"type":"Point","coordinates":[-50.9443359374996,-26.29735617905113]}},{"type":"Feature","properties":{"name":"TRON-01","serial":"TRON001","bearing":124,"color":"green","velocity":0.0,"size":15,"image":"img/unit_map3.png","pin_image":"img/pins/me/army.png","strength":0.0,"range":0.0},"geometry":{"type":"Point","coordinates":[-49.603720399842345,-24.66298243986192]}},{"type":"Feature","properties":{"name":"Flynn-06","serial":"FLYNN006","bearing":0,"color":"green","velocity":0.0,"size":15,"image":"img/unit_map3.png","pin_image":"img/pins/me/army.png","strength":0.0,"range":0.0},"geometry":{"type":"Point","coordinates":[-48.016479492187294,-23.103987753888838]}}]}

To see well formated array you can copy and paste the data here . My ajax provider is a Java struts2 action using the Google Json library to transform a simple java bean in json.

This is the result :

enter image description here

2
  • Well, you've made a nice boundingbox strategy, but my problems was with the clustering of the features. Since I'll be adding many features, I do need that behavior
    – Jeroen
    Commented Dec 27, 2012 at 8:39
  • Hum. Sory I cant help you with this. I have few points and not need cluster. I think my JQuery AJAX will not fit your needs. It's more flexible to use but not support cluser. A way to solve this is doing the job in the SQL query, but native clustering is much more easy to use.
    – Magno C
    Commented Jan 3, 2013 at 10:24
1

you should check out this answer - OpenLayers Cluster Recalculate here.

It adds a method recluster which, when called, will cause the strategy to recalculate its clustering. Since we are altering the Cluster base class, any derived class should appropriately inherit the recluster method.

i hope it helps you...

4
  • I did, the only difference with the original is the extra recluster function and the check in the cluster loop. If I set it to true: "if(resolution != this.resolution || !this.clustersExist() || true) {" (to force the cluster loop) it still has the same problem
    – Jeroen
    Commented Oct 31, 2012 at 13:18
  • I might have found a problem with the feature.move call. In that function it also redraws that feature, which might be a problem. I'm looking into that now...
    – Jeroen
    Commented Oct 31, 2012 at 13:28
  • @Jeroen: I'm having the same problem, but I've found out that vector feature which is clustered has null layer, and vector.move checks especially, if the layer is not null. Did you manage to find out solution how to move the points in clusters?
    – mrówa
    Commented Mar 18, 2013 at 16:22
  • I've made somekind of a workaround, I've added the answer below
    – Jeroen
    Commented Mar 19, 2013 at 14:05
0

I didn't got a good sollution, but luckaly I did add some comments what I was thinking a few months back... I copied a little bit of code and removed the part that caused trouble.

/**
 * Method: myUnit.moveFeature
 * Moves a feature to the given position. Does not redraw the layer!
 *
 * Parameters:
 * feature      {object} OpenLayers.Feature.Vector
 * lontlat      {object} OpenLayers.LonLat
 */
moveFeature: function(feature, lonlat) {
    /**
     * feature.move(lonlat) doesn't work. In the move-function 
     * there is a call to this.layer.drawfeature(feature)
     * somehow this interferes with the clustering
     * I've copied the code for updating the position from that function
     * and left out the drawFeature call.
     * Together with the custom recluster function in the OpenLayers Cluster
     * Strategy this seems to work.
     */

    var pixel;
    pixel = this.layer.getViewPortPxFromLonLat(lonlat);

    var lastPixel = this.layer.getViewPortPxFromLonLat(feature.geometry.getBounds().getCenterLonLat());
    var res = tbUnit.layer.map.getResolution();
    feature.geometry.move(res * (pixel.x - lastPixel.x), res * (lastPixel.y - pixel.y));
},

This works with the recluster posted by @Aragon

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.