2

I am new to using OpenLayers with JQuery to display vector data from KML files. I'm creating the layer like this:

newlayer = new OpenLayers.Layer.Vector( layerid, {
    projection: MAPVAR.displayProjection,
    strategies: [new OpenLayers.Strategy.Fixed()],
    protocol: new OpenLayers.Protocol.HTTP({
        url: layerurl,
        format: new OpenLayers.Format.KML({
            extractStyles: true, 
            extractAttributes: true,
            maxDepth: 2
        })
      }) 
    });

I want to avoid using these vector features with the OpenLayers.Control.SelectFeature control. Instead i would like to have each svg feature respond to a click event and pop open their own individual windows with the attributes.

I'm having trouble figuring out how to add the onclick function to ALL the vector features loaded.

For example, I can attach a function to the KML layer's "loadend" event and add the onclick function doing the following:

$('g[id$=vroot] > *').css("cursor", "pointer")
$('g[id$=vroot] > *').click(function(evt){ $w = $.WM_open(); $w.find('.windowcontent').append("SUCKER"); });

But this will only apply the handler to those vector elements in the view extent and not the ones that are waiting to be rendered in the HTML. I'm not sure how to access the unrendered ones -- they are in the DOM.

I've come up with a poor workaround to this problem, but I know there has to be a better way.

Is there some way I can dynamically create the "onclick" handler for each svg feature when they are getting parsed?

Would that require me to subclass OpenLayers.Layers.Vector or OpenLayers.Formats.KML?

Does anyone have an example or recommendation on other ways?


I still have not solved adding the onclick handler to the features. But i was able to override OpenLayers.Format.KML parseData() to add a pointer cursor to the feature styles before the loadend runs on the KML layer (additions demarcated with *** EDITS ***).

var FormatErmaKml = OpenLayers.Class(OpenLayers.Format.KML, { 
parseData: function (data, b) { 
    typeof data == "string" && (data = OpenLayers.Format.XML.prototype.read.apply(this, [data]));
    for (var c = ["Link", "NetworkLink", "Style", "StyleMap", "Placemark"], d = 0, e = c.length; d < e; ++d) {
        var f = c[d];
        var g = this.getElementsByTagNameNS(data, "*", f);  
        if (g.length != 0) switch (f.toLowerCase()) {
        case "link":
        case "networklink":
            this.parseLinks(g, b);  
            break;
        case "style":
            this.extractStyles && this.parseStyles(g, b);  
            break;
        case "stylemap":
            this.extractStyles && this.parseStyleMaps(g, b);  
            break;
        case "placemark":
            this.parseFeatures(g, b)
        }    
  
    }    
    **** EDITS BEGIN ****
    for(var count = 0; count < this.features.length; ++count){
            f = this.features[count];
            f.style.cursor = "pointer";
    }
    **** END EDITS ****
    return this.features
},});
5
  • I still have not solved adding the onclick handler to the features. But i was able to override OpenLayers.Format.KML parseData() to add a pointer cursor to the feature styles.
    – maztaz
    Jan 19, 2012 at 17:00
  • for a pointer cursor: When you define your layer you can define a stylemap and put cursor: "pointer".
    – CaptDragon
    Jan 19, 2012 at 17:39
  • I have tried to do what you are asking with no result. Maybe if you try an SVG framework like rafael or jQuery SVG. The problem i have with SelectFeature is that you can't just bind a click to a feature.. it automatically triggers select, redraw, etc. I'm assuming that is why you don't want to use SelectFeature. So if you do figure it out, please post the answer.
    – CaptDragon
    Jan 19, 2012 at 17:50
  • @capdragon -- yes, that's another reason why i would like to not use SelectFeature control
    – maztaz
    Jan 19, 2012 at 18:00
  • @maztaz just something to consider, if you haven't before, but IE 8 and lower is VML only, so Canvas-specific functionality won't work. But...of course depends on what browsers you're targeting.
    – Vadim
    Jan 19, 2012 at 18:14

3 Answers 3

4

I believe i found the solution.

SOLUTION DEMONSTRATION

It will:

  1. change the cursor for your feature to cursor:wait using jquery.
  2. It will change the svg fill color of the feature using jquery.
  3. And best of all it's efficient and will NOT trigger anything or disrupt anything else as far as my tests have shown.

Use a regular click Handler (EXAMPLE1, EXAMPLE2) and use the e.target to get the object on the map. If it returns a vector feature great, if not, ignore. It can work for all your layers or you can check the returned feature to see if it belongs to a layer you want it to trigger on.

I used Example #1 and replaced the latlon alert for this:

    if (e.target._featureId) {
        var feature = vectorLayer.getFeatureById(e.target._featureId);
        $(e.target).css('fill', '#000000');
        $(e.target).css('cursor', 'wait');
        $("div#info").append("<span>You just clicked on " + feature.id + "<span><br />");
    }
0
1

This is not an answer to your original question but show you how to define a cursor on all the features on a layer:

newlayer = new OpenLayers.Layer.Vector(layerid, {
    projection: MAPVAR.displayProjection,
    styleMap: new OpenLayers.StyleMap({
        "default": new OpenLayers.Style({
            strokeColor: '#000000',
            strokeOpacity: .3,
            strokeWidth: 2,
            fillColor: '#000000',
            fillOpacity: 0,
            cursor: "pointer"
        })
    }),
    strategies: [new OpenLayers.Strategy.Fixed()],
    protocol: new OpenLayers.Protocol.HTTP({
        url: layerurl,
        format: new OpenLayers.Format.KML({
            extractStyles: true,
            extractAttributes: true,
            maxDepth: 2
        })
    })
});

Here's an EXAMPLE

3
  • that didn't seem to work...debugging it now but "default" is overwritten with new style after loadend...hmmm
    – maztaz
    Jan 19, 2012 at 19:00
  • Sure it does... check my simple example i uploaded. It has the "waiting" cursor on the features. Make sure you are not overwriting the style on loadend.
    – CaptDragon
    Jan 19, 2012 at 19:14
  • thx....i'll have to debug mine more to figure out what's wrong
    – maztaz
    Jan 19, 2012 at 20:28
0

-- edited 1/19 9:45am per @capdragon's comment

Why add on-click to each feature? That is far too expensive. Why not add it to the layer?

Then the 2nd question is, how to add same handler to multiple layers.

  1. Create one click handler
  2. Keep an in-memory array of layers that you pass to this handler
  3. Update click handler whenever a new layer is added or removed
  4. Create all your handler functions, and inside the on-click you can find out easily what feature was clicked on

1)

var hoverAndClickControl = new OpenLayers.Control.SelectFeature(
                mylayers // this is the array of layers you're keeping in memory
                ,{
                    callbacks:
                    {
                          click: onFeatureSelect
                        , clickout: onFeatureUnselect
                        , over:  onFeatureHover
                        , out: onFeatureUnhover
                    }
                }
            );

3)

function addLayerToHandler(p_layer)
{
   // add layer to array
   // e.g. mylayers.push(p_layer);

   // update click handler
   // handler.setLayers(mylayers);  // since OL 2.9

}

4)

function onFeatureSelect(p_feature)
{
  // hey, feature you want is the parameter.  do whatever you need to do 

  // create a pop-up balloon, or send data to server, alert user, write out data, etc
}
8
  • 1
    OpenLayers.Control.SelectFeature DOES support multiple layers out of the box. You just add layers with myselfeatureControl.setLayers([layer1,layer2]); (+1) for trying to convince to use 1 handler instead of one for each feature.
    – CaptDragon
    Jan 19, 2012 at 17:32
  • @capdragon I stand corrected on the lack of setLayers() function. Just checked the trunk source, looks very similar to all the other rigamarole I was doing to deactivate handler, remove layers, re-add the layers and re-activate. One of my products is still on 2.8 and there's no such function in 2.8. Looks like it was introduce in 2.9.1.
    – Vadim
    Jan 19, 2012 at 17:41
  • @Vadim -- all that advice looks great...but I really want to avoid using OpenLayers.Control.SelectFeature control as I said in the original post even if other options are expensive. Is there another way?
    – maztaz
    Jan 19, 2012 at 17:45
  • @Vadim Ahh, yes you're right.
    – CaptDragon
    Jan 19, 2012 at 17:46
  • @maztaz I'm not sure why you don't want 1 handler? In the handler you can easily create an individual pop-up for each feature. It's not like one popup kills the other. You can certainly manage state (when one opens, kill all others) or you can let user manage it by clicking the "x" in popup. What we also do is keep track, if feature has popup and user clicks on feature again, we kill the popup. So it's a toggle on/off. You can do a lot in just the single handler and you're going to have a memory problem by attaching to each feature.
    – Vadim
    Jan 19, 2012 at 17:48

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.