The LuciadFusion Platform allows you to customize the legend encoding for the WMS and WMTS services.

By default, the LuciadFusion Platform responds with an icon graphic if it receives a GetLegendGraphic request from a WMS or WMTS service. The icon depends on the type of model linked to the server layer: elevation, raster or vector. For more information about these default implementations, see TLcdDefaultWMSGetLegendGraphicEncoder for WMS and TLcdDefaultWMTSGetLegendGraphicEncoder for WMTS.

If you want to offer something other than an icon as a legend, you can customize the response of the LuciadFusion Platform by implementing your own legend encoder.

This only affects WMS and WMTS services.

Customizing the legend encoding

You can customize the encoding by plugging in your own legend encoder.

To plug in a custom legend decoder, you register it with the service loader using the @LcdService annotation. You don’t have to specify a priority in the LcdService. By default, your custom legend encoder already has a higher priority than the pre-configured legend encoder. For more information about the services mechanism, see Working with the services mechanism.

The sample code includes two examples of custom legend encoders for WMS and WMTS.

Using OGC Symbology Encoding to encode a legend

The first example illustrates the legend encoding of an SLD feature type style using the code of Program: Symbology Encoding based WMS SLD legend encoder and Program: Symbology Encoding based WMTS SLD legend encoder.

This figure shows you a possible result for a raster style, containing a TLcdSLDRasterSymbolizer element with a color map:

GetLegendGraphic raster
Figure 1. Example raster style legend generated by the Custom WM(T)S legend encoder

This figure shows you a possible result for a vector style, containing one or more TLcdSLDPointSymbolizer, TLcdSLDLineSymbolizer, and TLcdSLDPolygonSymbolizer elements:

GetLegendGraphic vector
Figure 2. Example vector style legend generated by the Custom WM(T)S legend encoder
Program: Custom WMS SLD legend encoder
package samples.wms.server;
import java.io.IOException;
import java.io.OutputStream;
import javax.imageio.ImageIO;
import com.luciad.util.service.LcdService;
import com.luciad.model.ILcdModel;
import com.luciad.ogc.sld.model.TLcdSLDFeatureTypeStyle;
import com.luciad.wms.server.ILcdWMSGetLegendGraphicRequestEncoder;
import com.luciad.wms.server.TLcdWMSGetLegendGraphicRequestContext;
import com.luciad.wms.server.TLcdWMSRequestContext;
/**
* This WMS legend encoder creates a legend based upon an SLD Feature Type Style.
*/
@LcdService(service = ILcdWMSGetLegendGraphicRequestEncoder.class)
public class SLDWMSGetLegendGraphicEncoder implements ILcdWMSGetLegendGraphicRequestEncoder {
private final SLDLegendProvider fLegendProvider = new SLDLegendProvider();
@Override
public String getContentType() {
return "image/png";
}
@Override
public boolean encode(TLcdWMSGetLegendGraphicRequestContext aWMSGetLegendGraphicRequestContext,
TLcdWMSRequestContext aWMSRequestContext, OutputStream aOutputStream) throws IOException {
if (aWMSGetLegendGraphicRequestContext.getSLDFeatureTypeStyles() == null ||
aWMSGetLegendGraphicRequestContext.getSLDFeatureTypeStyles().length != 1) {
return false;
}
TLcdSLDFeatureTypeStyle featureTypeStyle = aWMSGetLegendGraphicRequestContext.getSLDFeatureTypeStyles()[0];
if (fLegendProvider.canGetLegend(featureTypeStyle)) {
ILcdModel model = aWMSGetLegendGraphicRequestContext.getModelSupplier().get().length > 0 ?
aWMSGetLegendGraphicRequestContext.getModelSupplier().get()[0] : null;
String legendOptionsParameter = aWMSRequestContext != null ? aWMSRequestContext.getRequest().getParameterIgnoreCase(SLDLegendOptions.LEGEND_OPTIONS) : null;
return ImageIO.write(fLegendProvider.getLegend(aWMSGetLegendGraphicRequestContext.getSLDFeatureTypeStyles()[0],
aWMSGetLegendGraphicRequestContext.getWidth(),
aWMSGetLegendGraphicRequestContext.getHeight(),
new SLDLegendOptions(legendOptionsParameter), model), "PNG", aOutputStream);
} else {
return false;
}
}
}
Program: Custom WMTS SLD legend encoder
package samples.wmts.server;
import java.io.IOException;
import java.io.OutputStream;
import javax.imageio.ImageIO;
import samples.wms.server.SLDLegendOptions;
import samples.wms.server.SLDLegendProvider;
import com.luciad.util.service.LcdService;
import com.luciad.model.ILcdModel;
import com.luciad.ogc.sld.model.TLcdSLDFeatureTypeStyle;
import com.luciad.wmts.server.ILcdWMTSGetLegendGraphicRequestEncoder;
import com.luciad.wmts.server.TLcdWMTSGetLegendGraphicRequestContext;
/**
* This WMTS legend encoder creates a legend based upon an SLD Feature Type Style.
*/
@LcdService(service = ILcdWMTSGetLegendGraphicRequestEncoder.class)
public class SLDWMTSGetLegendGraphicEncoder implements ILcdWMTSGetLegendGraphicRequestEncoder {
private final SLDLegendProvider fLegendProvider = new SLDLegendProvider();
@Override
public String getContentType() {
return "image/png";
}
@Override
public boolean encode(TLcdWMTSGetLegendGraphicRequestContext aWMTSGetLegendGraphicRequestContext,
OutputStream aOutputStream) throws IOException {
if (aWMTSGetLegendGraphicRequestContext.getSLDFeatureTypeStyles() == null ||
aWMTSGetLegendGraphicRequestContext.getSLDFeatureTypeStyles().length != 1) {
return false;
}
TLcdSLDFeatureTypeStyle featureTypeStyle = aWMTSGetLegendGraphicRequestContext.getSLDFeatureTypeStyles()[0];
if (fLegendProvider.canGetLegend(featureTypeStyle)) {
ILcdModel model = aWMTSGetLegendGraphicRequestContext.getModelSupplier().get();
return ImageIO.write(fLegendProvider.getLegend(featureTypeStyle,
aWMTSGetLegendGraphicRequestContext.getWidth(),
aWMTSGetLegendGraphicRequestContext.getHeight(),
new SLDLegendOptions(), model), "PNG", aOutputStream);
} else {
return false;
}
}
}

For a closer look at this legend code, see the implementations samples.wms.server.SLDWMSGetLegendGraphicEncoder for WMS and samples.wmts.server.SLDWMTSGetLegendGraphicEncoder for WMTS.

Using a static image file to encode a legend

In this example, the encoder looks for a static legend image that is placed next to the original model source. With this encoder, you can author an appropriate legend image yourself from scratch, or re-use existing legend images.

Program: WMS legend encoder that picks up an image file
package samples.wms.server;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import com.luciad.model.ILcdModel;
import com.luciad.model.ILcdModelTreeNode;
import com.luciad.util.service.LcdService;
import com.luciad.wms.server.ILcdWMSGetLegendGraphicRequestEncoder;
import com.luciad.wms.server.TLcdWMSGetLegendGraphicRequestContext;
import com.luciad.wms.server.TLcdWMSRequestContext;
/**
* This legend encoder picks up a {@code .legend.png} image that's in the same directory as the original data source.
* For example, if the original data source is {@code C:\Data\SHP\world.shp}, the encoder looks for
* {@code C:\Data\SHP\world.legend.png}.
*/
@LcdService(service = ILcdWMSGetLegendGraphicRequestEncoder.class)
public class FileBasedWMSGetLegendGraphicRequestEncoder implements ILcdWMSGetLegendGraphicRequestEncoder {
@Override
public String getContentType() {
return "image/png";
}
@Override
public boolean encode(TLcdWMSGetLegendGraphicRequestContext aWMSGetLegendGraphicRequestContext, TLcdWMSRequestContext aWMSRequestContext, OutputStream aOutputStream) throws IOException {
ILcdModel[] models = aWMSGetLegendGraphicRequestContext.getModelSupplier().get();
for (ILcdModel model : models) {
if (encode(aOutputStream, model)) {
return true;
}
}
return false;
}
public static boolean encode(OutputStream aOutputStream, ILcdModel aModel) throws IOException {
String dataSource = aModel.getModelDescriptor().getSourceName();
String legendSource = getFileNameNoExtension(dataSource) + ".legend.png";
if (Files.exists(Paths.get(legendSource))) {
Files.copy(Paths.get(legendSource), aOutputStream);
return true;
} else if (aModel instanceof ILcdModelTreeNode) {
for (ILcdModel child : ((ILcdModelTreeNode) aModel).getModels()) {
if (encode(aOutputStream, child)) {
return true;
}
}
}
return false;
}
private static String getFileNameNoExtension(String aFileName) {
String fileName = null;
int pointIndex = aFileName.lastIndexOf('.');
if (pointIndex > 0 && pointIndex < aFileName.length() - 1) {
fileName = aFileName.substring(0, pointIndex);
}
return fileName;
}
}
Program: WMTS legend encoder that picks up an image file
package samples.wmts.server;
import java.io.IOException;
import java.io.OutputStream;
import com.luciad.model.ILcdModel;
import com.luciad.util.service.LcdService;
import com.luciad.wmts.server.ILcdWMTSGetLegendGraphicRequestEncoder;
import com.luciad.wmts.server.TLcdWMTSGetLegendGraphicRequestContext;
import samples.wms.server.FileBasedWMSGetLegendGraphicRequestEncoder;
/**
* This legend encoder picks up a {@code .legend.png} image that's in the same directory as the original data source.
* For example, if the original data source would be {@code C:\Data\SHP\world.shp}, the encoder would look for
* {@code C:\Data\SHP\world.legend.png}.
*/
@LcdService(service = ILcdWMTSGetLegendGraphicRequestEncoder.class)
public class FileBasedWMTSGetLegendGraphicRequestEncoder implements ILcdWMTSGetLegendGraphicRequestEncoder {
@Override
public String getContentType() {
return "image/png";
}
@Override
public boolean encode(TLcdWMTSGetLegendGraphicRequestContext aWMTSGetLegendGraphicRequestContext, OutputStream aOutputStream) throws IOException {
ILcdModel model = aWMTSGetLegendGraphicRequestContext.getModelSupplier().get();
return FileBasedWMSGetLegendGraphicRequestEncoder.encode(aOutputStream, model);
}
}

See the implementations samples.wms.server.FileBasedWMSGetLegendGraphicRequestEncoder for WMS and samples.wmts.server.FileBasedWMTSGetLegendGraphicRequestEncoder for WMTS.