[GIS] OpenLayers event cluster feature id does not appear on layer vector feature id list

javascriptlayersopenlayers-2popup

My application's objective is to:

  1. click on a cluster and produce a popup
  2. with a list of all placemarks clustered there
  3. where every entry is a hyperlink
  4. that closes current popup
  5. and opens a new popup with data from placemark

I can execute everything EXCEPT FINDING the EVENT.FEATURE (placemark) in the layer. I can read the layer (vector) feature list, but the original clustered feature (placemark) is not among the features that compose the layer.

Is the clustered feature "hidden" while its cluster is displayed? if so, how can I access it?

I am using OpenLayers with JavaScript, loosely based on the sundials example, yet with a cluster strategy.

1 is a standard cluster strategy.

var pointStyleTaxi = new OpenLayers.Style({
    externalGraphic: "img/taxi.png",
        pointRadius: 20,
    graphicYOffset: -30,
    fillOpacity: 0.6,
        'label': "${label}"
  }, {
    context: {
      label: function(feature) {
    // clustered features count or blank if feature is not a cluster
    return feature.cluster ? feature.cluster.length : "";  
      },
      radius: function(feature) {
     return Math.min(feature.attributes.count*5, 20) + 5;
      }
    }
});

var styleMapClusterTaxi = new OpenLayers.StyleMap({
  'default': pointStyleTaxi,
});

var clusterStrategyTaxi = new OpenLayers.Strategy.Cluster({ distance: 35, threshold: 2 });
var refresh = new OpenLayers.Strategy.Refresh({force: true, active: true});

var stops = new OpenLayers.Layer.Vector("Paradas", {
    styleMap: styleMapClusterTaxi,
    strategies: [new OpenLayers.Strategy.Fixed(), clusterStrategyTaxi, refresh],
    projection: map.displayProjection,
    protocol: new OpenLayers.Protocol.HTTP({
        url: "<?php echo $fileNameKML; ?>",
        format: new OpenLayers.Format.KML({
        extractStyles: true,
        extractAttributes: true,
        maxDepth: 2
        })
    })
});

map.addLayers([wms, stops]);

I achieve 2 by identifying that the selected feature is a cluster inside onFeatureSelect(event)

if (feature.cluster) {

and proceeding to extract the name of each clustered placemark.

for (var i = 0; i < feature.attributes.count; i++) {
    var feat = feature.cluster[i];
    var featId = feature.cluster[i].id;
    var featName = feat.attributes.name;

3 is obtained by creating a JavaScript hyperlink for each clustered placemark

    content += "<br/><a href=\"javascript:onFeatureSelectFeature(";
    content += '\'';
    content += featId;
    content += '\'';
    content += ");\">"+featName+"</a>";
}

Screen capture from final popup (has feature count and layer name '2 Paradas')

after clicking on link, 4 is obtained by calling

function popupClear() {
    //alert('number of popups '+map.popups.length);
    while( map.popups.length ) {
         map.removePopup(map.popups[0]);
    }
}

Which produces <a href="javascript:onFeatureSelectFeature('OpenLayers_Feature_Vector_58');">Jecabitiba</a>

Which is a JavaScript call to onFeatureSelectFeature('OpenLayers_Feature_Vector_58');

function onFeatureSelectFeature(featId) {
    var layerNow = map.getLayersByName('Paradas')[0];
    var layerFeatCount = layerNow.features.length;
    //alert ("featId: "+featId+"\r\nlayer name:"+layerNow.name+"\r\nlayerFeatCount:"+layerFeatCount);
    for(var i=0; i<layerFeatCount; i++){
          if(layerNow.features[i].id == featId){

I'm stuck on 5 because I can't find the clustered feature in the layer feature list.

Any suggestions?

Best Answer

found the answer: the clustered feature (placemark) is "inside" the cluster feature, not the layer.

SOLUTION 1 first way out (not most efficient): inside onFeatureSelectFeature(featureId) i loop thru cluster features as seen below:

function onFeatureSelectFeature(featId) {
    var layerNow = map.getLayersByName('Paradas')[0];
    var layerFeatCount = layerNow.features.length;
    for(var i=0; i<layerFeatCount; i++){
        var featureCluster = layerNow.features[i].cluster;
        if (featureCluster) {
            for (var k = 0; k < featureCluster.length; k++) {
                var flagFeatureFound = (featureCluster[k].id == featId);
                if (flagFeatureFound) {
                    onFeatureSelectPlacemarkClick(featureCluster[k]);
                    i=layerFeatCount;
                    k = featureCluster.length;
                }
            }
        } 
    }
}

SOLUTION 2 i also solved the problem changing the link javascript and sending along the clusterId to onFeatureSelectFeature(featureId, clusterId).

content += "<br/><a href=\"javascript:onFeatureSelectFeature(";
content += '\'';
content += featId;
content += '\'';
content += ', \'';
content += feature.id;
content += '\'';
content += ");\">"+feat.attributes.name+"</a>"; 

where feature is a cluster (as seen in code above).

my onFeatureSelectFeature function now receives 2 strings (featureId, clusterId) and i dont loop thru all features (which can be resource intensive), but use the layer.getFeatureById() method.

function onFeatureSelectFeature(featId, clusterId) {
    // cluster is a feature which "hides" the placemark features
    var layerNow = map.getLayersByName('Paradas')[0];
    var featureCluster= layerNow.getFeatureById(clusterId);
    var clusterCount = featureCluster.attributes.count;
    for (var k = 0; k < clusterCount; k++) {
        var flagFeatureFound = (featureCluster.cluster[k].id == featId);
        //alert ("featureCluster[k].id:"+featureCluster.cluster[k].id+"  flagFeatureFound:"+flagFeatureFound);
        if (flagFeatureFound) {
            onFeatureSelectPlacemarkClick(featureCluster.cluster[k]);
            k = featureCluster.length;
        }
    }   
}

this function (both versions) calls a variation of onFeatureSelect(event) which is

function onFeatureSelectPlacemarkClick(feature) {
    popupClear();
    var content = "unInitialized";
    if ( (feature.attributes.name > '') || (feature.attributes.description > '') ) {
        content = feature.attributes.name + "<br/ >"+feature.attributes.description;
        var lonlatCenter = feature.geometry.getBounds().getCenterLonLat();
        //alert ("feature.coordinate: "+feature.geometry.getBounds().getCenterLonLat());
        map.setCenter(lonlatCenter, 13);
    }
    popup = new OpenLayers.Popup.FramedCloud("chicken", 
                             feature.geometry.getBounds().getCenterLonLat(),
                             new OpenLayers.Size(100,100),
                             content,
                             null, true, onFeatureUnselect);
    feature.popup = popup;
    map.addPopup(popup);
    lastfeature = feature;
}

at some point in the future, i will probably overload the onFeatureSelect(event) to onFeatureSelect(event,feature) and do a basic consistency check to see which object is being passed.