I've created an OpenLayers.Style that colours my polygons, a style that sizes my points and all that jazz, now I want to explain to the user what those styles represent.
I can't see anything in OpenLayers that allows me to draw my own legend using those styles. Everything seems to point me towards the assumed map server that is sending me data, which I don't have.
At the moment it looks like I'll have to draw some sample points/areas and screen grab them to make my own legend. Is there a better way to do it based straight off the Style so I don't have to regenerate the images when the Style gets changed?
Update I've had a good answer that relies on GeoExt (and thus ExtJS), I'd still like to hear if anyone has a jQuery compatible answer. Especially if it is plain Javascript and OpenLayers.
I was able to solve my legend needs using the ol.Map-class as a container for the symbols. Maybe a bit of a hack, but seems to work for most (?) vector layers (I have no WMS's).
So what I'm doing is:
loop through the map layers and pick out the vector type with
if(lyr instanceof ol.layer.Vector)
check what type of style is used and store in an array
var style = lyr.getStyle();
var image = style.getImage();
if(image){
if(image instanceof ol.style.Icon){
//raster icon from url
var icon2 = new ol.style.Icon( ({
src: image.getSrc()
}))
var iconStyle2 = new ol.style.Style({
image: icon2
});
row = {};
row.style = iconStyle2;
row.title = lyr.get('title');
}
else{ //ol.style.RegularShape?
row = {};
row.style = style;
row.title = lyr.get('title');
}
}else{
row = {};
row.style = style;
row.title = lyr.get('title');
}
also store the geometry type
//geometry type
var feats = lyr.getSource().getFeatures();
if (feats && feats.length>0){
if(feats[0].getGeometry() instanceof ol.geom.Point || feats[0].getGeometry() instanceof ol.geom.MultiPoint){
row.geomType="point";
}else if (feats[0].getGeometry() instanceof ol.geom.LineString || feats[0].getGeometry() instanceof ol.geom.MultiLineString){
row.geomType="line";
}else{
row.geomType="polygon";
}
}
loop through the stored to-be legend rows and construct the HTML elements needed, typically a div for the "mini-map" and the layer name
for (i = 0; i < legendRows.length; i++) {
row = document.createElement("tr");
//symbol
cell = document.createElement("td");
cell.style="width:35px";
var div = document.createElement("div");
div.style="width:32px; height:32px;";
div.id = "mapLegendRowSymbolDiv" + i;
tble.appendChild(row);
row.appendChild(cell);
cell.appendChild(div);
//layer title
cell = document.createElement("td");
tble.appendChild(row);
row.appendChild(cell);
cell.innerHTML=legendRows[i].title;
}
//append table
$( "#legendText" ).empty();
$( "#legendText" ).append(tble);
once HTML elements have been added to the page, initiate the maps and insert the fake features to display the symbols
//loop legend rows and and insert the maps
var extent = [0, 0, 32, 32];
var projection = new ol.proj.Projection({
code: 'xkcd-image',
units: 'pixels',
extent: extent
});
for (i = 0; i < legendRows.length; i++) {
//target div
var targetDiv = document.getElementById("mapLegendRowSymbolDiv" + i);
//layer for icon
var sourceLegend = new ol.source.Vector({wrapX: false});
var vectorLegend = new ol.layer.Vector({
source: sourceLegend,
style: legendRows[i].style
});
//map
var mapLegend = new ol.Map({
controls: [],
layers: [
new ol.layer.Image({
source: new ol.source.ImageStatic({
projection: projection,
imageExtent: extent
})
}),
vectorLegend
],
target: targetDiv,
view: new ol.View({
projection: projection,
center: ol.extent.getCenter(extent),
zoom: 2,
maxZoom: 2
})
});
//icon feature depending on type
var geom;
if(legendRows[i].geomType=='point'){
geom = new ol.geom.Point([16,16]);
}else if(legendRows[i].geomType=='polygon'){
var polyCoords = [];
polyCoords.push([15.7, 15.7]);
polyCoords.push([16.3, 15.7]);
polyCoords.push([16.3, 16.3]);
polyCoords.push([15.7, 16.3]);
polyCoords.push([15.7, 15.7]);
geom = new ol.geom.Polygon([polyCoords]);
}else{
var lineCoords = [];
lineCoords.push([15.6, 15.6]);
lineCoords.push([16, 16]);
lineCoords.push([16, 15.8]);
lineCoords.push([16.4, 16.2]);
geom = new ol.geom.LineString(lineCoords);
}
var feature = new ol.Feature({
geometry: geom
});
vectorLegend.getSource().addFeature(feature);
}
With this, I was able create and update a separate Legend dialog (jQuery UI):
I haven't tested a lot yet, there may be some problems with this approach...