How do I highlight a feature in my map?
This really depends on what you mean by a "feature" and what you mean by "hightlighting". Are the features vector features (say a WFS layer) or are they WMS so you have to fetch the feature separately? Are you familiar with WMS and SLD styling? Do you need the feature to behave when someone mouses over it or clicks on it? All of these questions are important in deciding which highlighting technology is appropriate for your case.
WFS features overlaid atop a WMS layer
Overview
If you have a WMS Layer (or something equally "rasterish") and want to overlay a Vector feature over top of it, this would be your method.
Your map contains a Vector layer, which contains no features. At some later point in time, you make a WFS GetFeature call to fetch features' geometry (the code below is intended for multiple features, just remember not to push it). The WFS results are passed to the Vector layer, and are drawn.
Pros
- Vector layer means client-side styles.
- Vector layer means the ability of the features to react to mouse events such as hovering and clicking.
- It is also very easy to accomplish.
Cons
- Requires that the same data layer be available via WFS (for fetching the vector info) and via WMS (or whatever you're using for the image) so that the overlaid vector feature matches the raster-drawn feature -- then again, this may not be a concern if you intend for the two layers to mismatch.
- Has a low limit on the number and complexity of features. More than a few hundred vertices, and you're likely to crash their browser. Remember that transferring vector data is inherently slow.
- Vector layers do not support labelling of the highlighted features. That would require some other mechanism for the labels.
Code
// define a blank Vector layer and add it to the map
var highlight_style = { fillColor:'#99CCFF', strokeColor:'#3399FF', fillOpacity:0.7 };
hilites = new OpenLayers.Layer.Vector("Highlighted",
{isBaseLayer:false, features:[], visibility:true, style:highlight_style}
);
map.addLayer(hilites);
// define the WFS server which will fill requests
var wfs_url = '/cgi-bin/mapserv?map=/maps/spraywatch2/wms/mapfile.map&SERVICE=WFS&VERSION=1.0.0';
/* highlightFeatures() makes the WFS request, then highlight_them() callback does the real work
* Args for WFS filter: typename, attribute, value
*/
function highlightFeatures(typename,attribute,value) {
var wfsurl = wfs_url + '&REQUEST=getfeature&typename=' + typename +
'&Filter=<Filter><PropertyIsEqualTo><PropertyName>'+attribute+'</PropertyName><Literal>'+value+'</Literal></PropertyIsEqualTo></Filter>';
OpenLayers.loadURL(wfsurl,'',null,highlight_them);
}
function highlight_them(response) {
// use the GML parser to turn the XML into a list of Feature objects
var features = new OpenLayers.Format.GML().read(response.responseText);
// have the Vector layer purge its feature list, replace them with the new ones
hilites.destroyFeatures();
hilites.addFeatures(features);
hilites.setVisibility(true);
};
}
And an example of usage would be:
highlightFeatures('parcels','parcel_id','123456')
Highlighting with WMS atop WMS, using Mapserver
Overview
If you have a WMS or Mapserver layer, and would like to simulate highlighting by drawing a second Mapserver layer, this could be your method.
The trick is that Mapserver can accept arbitrary params in a mode=map query, and these params can be interpolated into the mapfile, e.g. into a FILTER definition. We have a Mapserver layer containing FILTER "id IN (%ids%)", we set the JavaScript value of ids and regenerate the layer's URL, then call layer.redraw() to update the highlighting.
Pros
- Does not have an implicit limit on the number of vertices nor features, so is suitable for numerous complex polygons.
- Can often be faster than WFS, as there's less data being transferred.
- Does not require a WFS server.
- The Mapserver layer could easily incorporate labels as well as the highlighting.
Cons
- Only works if your WMS server is Mapserver.
- The resulting layer is WMS, which means no spatial geometry effects, e.g. zooming to the highlighted area.
Code
Define the Mapserver layer in your mapfile. Note the FILTER clause, causing this layer's features to only match the as-yet-undefined list of ids on the query string.
LAYER
NAME "highlights"
STATUS on
TYPE polygon
CONNECTIONTYPE postgis
CONNECTION "host=localhost dbname=xxx user=xxx password=xxx"
DATA "the_geom from parcels"
PROCESSING "CLOSE_CONNECTION=DEFER"
FILTER "id IN (0,%ids%)"
PROJECTION
"init=epsg:4326"
END
CLASS
STYLE
SYMBOL "polygon_solid"
COLOR 153 204 255
OUTLINECOLOR 51 153 255
END
END
END
Define the Mapserver layer. Note the additional 'id' flag being passed to Mapserver, which matches that filter in the Mapserver layer.
layers['highlight'] = new OpenLayers.Layer.MapServer("Highlighting",
"/cgi-bin/mapserv?map=/maps/mapfile.map",
{layers:'highlights', format:'image/png', 'ids':0 },
{isBaseLayer:false, visibility:true, opacity:0.5, transparent:true }
);
The following JavaScript defines a list of ID#s, and highlightSelected() will use that list to generate a new URL for the Mapserver layer. Given this, you can create a function which alters the contents of the selected_feature_ids arraym and then calls highlightSelected() to refresh the highlighting.
var selected_feature_ids = [123,456,789];
function highlightSelected() {
var highlight_layer = layers['highlight'];
var url = highlight_layer.getFullRequestString( {parcelids:selected_feature_ids.join(',')} );
highlight_layer.url = url;
highlight_layer.redraw();
}
