Overview

This article describes the LuciadLightspeed WMS Server API, which provides a framework to set up a Web Map Service. The WMS is implemented as a Java servlet with all required request handling facilities and support for different output formats: JPEG, PNG. Support for Styled Layer Descriptors is also available: clients of a LuciadLightspeed WMS can use remote or embedded SLDs in GetMap requests.

A regular WMS, which does not provide SLD support, contains a number of layers, optionally associated with a number of styles. Both the layers and styles are each identified by a unique name. The unique names are published in the result of a GetCapabilities request. Clients can use the unique names in their GetMap or GetFeatureInfo requests. The layers and styles are defined on server-side and are in general fixed for a certain period, depending on how often the capabilities of a server are updated.

A WMS that supports the use of SLD in client requests allows the user to define own styling rules for the layers available at the server. Furthermore, it optionally also allows the user to define custom layers: such user-defined layers define a dataset that is located on a remote server, which can be an OGC Web Coverage Service or Web Feature Service. This requires the WMS to contact the specified remote server, query the necessary data, and style them according to the specified SLD. More information about SLD and its application within a WMS can be found in the OGC Styled Layer Descriptor specification. This developer guide assumes that the reader is familiar with this specification.

Configuring a WMS introduces the main concepts of the WMS Server API and explains how to build a regular WMS server. The main task that remains for the user of the API is to implement the server-side data repository for the WMS. The user may choose to read geographic data from files, a database, or any other data source. Providing support for Styled Layer Descriptors continues this discussion and describes how a WMS server can be built that accepts SLD requests. The WMS Server API is located in the package com.luciad.wms.server.

Configuring a WMS

The LuciadLightspeed WMS is a Java HTTP servlet. A Java HTTP servlet must implement the abstract class javax.servlet.http.HttpServlet, which is located in the Java EE API. The class TLcdWMSServlet in the WMS Server API provides an implementation of this class. This servlet uses an instance of ALcdWMSCommandDispatcher to handle all incoming requests. The ALcdWMSCommandDispatcher instance is created during server startup, and is used throughout the lifetime of the servlet. The creation of an ALcdWMSCommandDispatcher instance uses a Factory pattern: the method createWMSCommandDispatcher(ServletConfig) of the factory class ALcdWMSCommandDispatcherFactory allows to create and return a specific implementation of ALcdWMSCommandDispatcher. This mechanism is illustrated in Figure 1, “Main classes of the LuciadLightspeed WMS Server API.”.

WMSServletDiagram
Figure 1. Main classes of the LuciadLightspeed WMS Server API.

It is up to the user to provide an implementation of ALcdWMSCommandDispatcherFactory that initializes a ALcdWMSCommandDispatcher. For convenience, a default implementation TLcdOGCWMSCommandDispatcherFactory and an abstract extension ALcdOGCWMSCommandDispatcherFactory of ALcdWMSCommandDispatcherFactory are provided that implement the factory method. This extension returns a TLcdOGCWMSCommandDispatcher as command dispatcher implementation. Next to the implementation of the factory method, ALcdOGCWMSCommandDispatcherFactory adds protected methods that are used to configure the created TLcdOGCWMSCommandDispatcher instance at server startup. This initialization procedure is illustrated in the sequence diagram in Figure 2, “Sequence diagram of the WMS initialization procedure.”. The following sections describe these configuration methods in more detail.

WMSInitializationSequence
Figure 2. Sequence diagram of the WMS initialization procedure.

Reading data

Each time the server receives a GetMap or GetFeatureInfo request, an internal view or map is built containing the layers described in the request parameters. This process is similar to a stand-alone LuciadLightspeed application:

  1. Data is decoded into models through a model decoder.

  2. Layers are created for each model through a layer factory.

  3. All layers are added to a view or map.

The internal view, its layers and models are all created through factory classes. These factory classes can be configured by the user of the API, through the ALcdOGCWMSCommandDispatcherFactory class.

Loading data sources: createModelDecoderFactories()

The WMS server uses an ILcdModelDecoderFactory to create model decoders that load the data. Its method createModelDecoder(String) is invoked with a data source name as argument and returns an ILcdModelDecoder that can read the data from that source. The data source can be a single file, an URL, a database, and so on. The command dispatcher factory must provide an array of one or more valid model decoder factories, or the WMS will not be able to load any data. To optimize performance, TLcdOGCWMSCommandDispatcher maintains a cache of models so that they do not need to be decoded on every request.

The default implementation within the ALcdOGCWMSCommandDispatcherFactory creates a TLcdModelDecoderFactory that retrieves ILcdModelDecoder instances using the service look-up mechanism.

Caching data sources: createModelProvider(ILcdModelDecoderFactory[])

To decode models, the server does not use the model decoder factories and model decoders directly. Instead, the server uses an ILcdModelProvider which provides central access to all models. A model provider holds a reference to the model decoder factories and uses them to decode the actual data. The main advantage of this centralized approach is that it allows implementations to define additional functionality, that is global for all model decoders. A typical example is a caching mechanism, to improve the performance.

The default implementation returns a model provider that maintains a cache for all decoded models. To prevent memory problems, it makes use of java.lang.ref.SoftReference objects, which are cleared at the discretion of the garbage collector in response to memory demand. Soft references are most often used to implement memory-sensitive caches.

In general, it is not necessary to create a custom implementation of ILcdModelProvider. A situation where it can be useful is when a server needs to be dynamic, in the sense that its capabilities can be updated at runtime. If a certain dataset has been updated or removed, the cache of the model provider needs to be updated in some cases. A removed dataset typically does not pose any problems in that case, since the default implementation uses soft references for the cached models. An updated dataset can cause a problem however, since the cached model will not be up to date anymore. Configuration of the capabilities further explains the concept of dynamic capabilities.

Visualizing data sources: createWMSGXYLayerFactories()

Once a model decoder has been created with the appropriate model decoder factory, the server is ready to actually load data. To create layers for the decoded models, the WMS server uses an ILcdWMSGXYLayerFactory. The command dispatcher factory must provide an array of one or more valid layer factories, or the WMS will not be able to render any data.

The default implementation within the ALcdOGCWMSCommandDispatcherFactory creates a ILcdWMSGXYLayerFactory that uses the service look-up mechanism to pick implementations of ILcdWMSGXYLayerFactory, ILcdSLDGXYLayerFactory, and ILcdGXYLayerFactory to make a composite layer factory.

Creating views: createWMSGXYViewFactory()

Finally, the created layers are added to an internal view, which is positioned at the requested bounding box and has the requested dimensions. This view is a TLcdGXYViewBufferedImage, which is an implementation of the ILcdGXYView interface using an off-screen image. It does not depend on AWT or Swing components, and is well-suited for generating images on the server-side to be sent to clients. The construction and initialization of the view is done via the factory class TLcdWMSGXYViewFactory, returned by default in createWMSGXYViewFactory().

TLcdWMSGXYViewFactory creates and initializes regular TLcdGXYViewBufferedImage instances, but other implementations may be defined that return extensions of TLcdGXYViewBufferedImage or that set additional configuration properties on a view. For example, if each map returned by a GetMap request should contain a fixed logo, an implementation could be defined that adds this logo to each created view instance, by using the method putCornerIcon() available in TLcdGXYViewBufferedImage.

Sending data to the client

Encoding views: createGXYViewEncoders()

As soon as the WMS server has constructed the view with the requested layers in a GetMap request, it must be encoded to a pictorial format like JPEG or PNG before it can be sent to the client. To encode a view, the WMS server uses an ILcdGXYViewEncoder. To determine the output format, the interface contains a method getContentType() which returns a MIME[1] type. The command dispatcher factory must provide an array of one or more valid view encoders, or the WMS will not be able to encode a view. The default implementation in ALcdOGCWMSCommandDispatcherFactory returns an array containing a subset of the available view encoders in the package com.luciad.wms.server.viewencoder:

TLcdGXYViewJPEGEncoder

: Encoder for the JPEG format, MIME type image/jpeg.

TLcdGXYViewPNGEncoder

: Encoder for the PNG format, MIME type image/png.

The default implementation within the ALcdOGCWMSCommandDispatcherFactory creates a list of ILcdGXYViewEncoder implementations using the service look-up mechanism.

Encoding feature info results: createWMSGetFeatureInfoEncoders()

The GetFeatureInfo request is an optional operation designed to retrieve feature information for a specified location on a map. When the server receives such a request, it automatically searches the objects that are located at the given location in the layers of the map. The selected objects are then placed in a ILcdGXYLayerSubsetList, which defines a list of one or more ILcdGXYLayer subset(s). As soon as the ILcdGXYLayerSubsetList is created, it is submitted to an ILcdWMSGetFeatureInfoRequestEncoder. This interface is used to retrieve the feature information from the selected object(s) in the ILcdGXYLayerSubsetList and to encode it to a specific format. The format of a ILcdWMSGetFeatureInfoRequestEncoder is determined by a MIME type returned by the method getContentType(). Which information about the selected objects is actually returned, can be chosen freely by the ILcdWMSGetFeatureInfoRequestEncoder implementation, as this cannot be specified within a GetFeatureInfo request.

The creation and initialization of a ILcdGXYLayerSubsetList with the selected objects is done automatically by the command dispatcher. The initialization and registration of one or more ILcdWMSGetFeatureInfoRequestEncoder implementations is left to the user of the API, because the user needs to decide which output formats for a GetFeatureInfo request are available and which information must be returned in practice.

The default implementation of createWMSGetFeatureInfoEncoders() creates a list of ILcdWMSGetFeatureInfoRequestEncoder encoders using the service look-up mechanism. By default the API contains an implementation that can generate the feature info result as JSON.

To offer support for the GetFeatureInfo request, an implementation of createWMSGetFeatureInfoEncoders() must return one or more valid encoders. Furthermore, the capabilities of the server should reflect that support for the GetFeatureInfo request is offered: one or more available layers should be marked queryable and the capabilities should list the available output formats for the GetFeatureInfo request. How to configure the capabilities of a server is further described in createWMSCapabilitiesDecoder().

Configuration of the capabilities

The WMS capabilities

The capabilities of a server refer to the content that it offers. This includes a list of the available layers, the supported output formats, the supported reference systems, and some metadata about the server itself. The capabilities of a server can be queried by a client using a GetCapabilities request. To model these capabilities, the WMS server uses the abstract class ALcdWMSCapabilities. When a client sends a GetCapabilities request, the information stored in ALcdWMSCapabilities is encoded to the WMS capabilities format defined by the OGC WMS specification and sent to the client.

Figure 3, “Structure of ALcdWMSCapabilities” shows the structure of this class. Note that the available layers in the capabilities are of the type ALcdWMSLayer and not of the type ILcdGXYLayer. An ILcdGXYLayer is used for displaying the objects of an ILcdModel in an ILcdGXYView. An ALcdWMSLayer on the other hand is not used for visualization but for declaring that a layer is available at the server. As illustrated in Figure 3, “Structure of ALcdWMSCapabilities”, ALcdWMSLayer instances can be arranged in a hierarchical way. Its most important properties are:

  • A name that uniquely identifies the layer. It is used by a client to refer to a layer in a request. Through the method isNameVisible(), one can prevent that a client sees this name when querying the capabilities, making it impossible to use it in requests: this is done when an ALcdWMSLayer is only used to group a number of layers (for example when they offer related content) instead of representing an actual data layer.

  • A title, which is a short description of the layer. The title is typically used by clients as display name for the layer.

  • A property indicating whether the layer is queryable or not. This property indicates whether a client can invoke GetFeatureInfo requests on the layer.

  • A property indicating whether the layer is opaque or not. If this property is absent or false, then maps made from this layer will generally have significant no-data areas that a client may display as transparent. If this property is true, the layer represents an area-filling coverage. For example, a map that represents topography and bathymetry as regions of differing colors will have no transparent areas, while a map representing vector data such as lines and points will typically have several transparent areas. The opaque declaration should be taken as a hint to the client to place such a layer at the bottom of a stack of maps. Note that this attribute only describes the layer’s data content, not the picture format of the map response.

  • An optional list of dimensions, which can be used to represent multi-dimensional data. This is further explained in Providing support for multi-dimensional data.

  • A source name, indicating the data source to use for the layer. The source name is required when a layer can be requested by a client, for example when isNameVisible() returns true, and when it has no associated dimensions. If the layer has one or more dimensions, there is no exact mapping between the layer and a data source because this relation depends on the dimensional parameters provided by a client. More information regarding multi-dimensional data can be found in Providing support for multi-dimensional data. If the layer does not have any dimensions, the server uses the source name to decode an ILcdModel containing the data. This information is not part of the capabilities that are sent to the client.

The full list of properties can be found in the LuciadLightspeed reference documentation.

WMSServerCapabilitiesDiagram
Figure 3. Structure of ALcdWMSCapabilities

createWMSCapabilitiesProvider()

The implementation of the interface ILcdWMSCapabilitiesProvider is the main entry point to determine which data is served by the WMS server.

The interface is built such that for operations other than GetCapabilities only a sub-set of the information is requested to handle the request. This allows for implementations that load as little information in memory as possible.

The capabilities provider generates the capabilities only when a client request is performed. It avoids loading (data) in memory during server start up.

The use of the capabilities provider interface makes it easy to create a dynamic WMS server in which the capabilities are updated during the server’s runtime.

Take the following guidelines into account when you are implementing a capabilities provider that is dynamic:

  • The server keeps working while you are updating the capabilities, so precautions must be taken to prevent any inconsistencies. The capabilities returned by getCapabilities(ILcdRequest aRequest) should never return an instance that is changed directly. Instead, an implementation should create a new capabilities object representing the most recent capabilities. Or make a clone of the capabilities, apply the changes and set the new capabilities object internally within the provider.

  • When datasets can be updated, it could be useful to implement a custom ILcdModelProvider as described in Reading data. The default implementation uses a caching mechanism for all decoded models, which can cause problems in the case of updated data. Removed datasets should not cause any problems however, because soft references are used in the model cache.

createWMSCapabilitiesDecoder()

The WMS server API comes with a capabilities decoder, an implementation of the interface ILcdWMSCapabilitiesDecoder, that decodes the ALcdWMSCapabilities from a configuration file.

The default capabilities provider created within TLcdOGCWMSCommandDispatcherFactory uses the capabilities decoder to read from configuration what data needs to be served to client applications.

During server startup, a capabilities decoder is created using the method createWMSCapabilitiesDecoder() in ALcdOGCWMSCommandDispatcherFactory, as illustrated in the sequence diagram in Figure 2, “Sequence diagram of the WMS initialization procedure.”. Subsequently, the method decodeWMSCapabilities(String) on the capabilities decoder is invoked to retrieve the capabilities.

The single parameter in the method decodeWMSCapabilities(String) contains the source name of this capabilities file. It is stored as a servlet parameter in the deployment descriptor of the WMS servlet, which is described in Installing an OGC web server.

Additional configuration settings: configureSettings()

This method is used to apply additional configuration settings. By default, the JAI tile cache size is configured. These settings are further explained in Installing an OGC web server. One can override this method to apply additional settings.

Customizing the reference parsers and published CRSes: createReferenceParsersSFCT()

The command dispatcher factory class contains a default set of references parsers that are used to convert coordinate reference systems (CRS) from a string representation to an ILcdXYWorldReference needed within the WMS server.

During the creation of the method it also populates a list of coordinate reference systems for publishing within the WMS capabilities as being supported. This list of CRSes is populated by default with the most common used references.

It is possible to configure the list either programmatically or by configuration. When configuring it programmaticly you have two options. One is to override the createReferenceParsersSFCT method of the ALcdOGCWMSCommandDispatcherFactory. You can add references to the list or replace the contents of the list entirely with the ones you want. A second option is to do this entirely within the ILcdWMSCapabilitiesProvider where you have control over the list of CRSes that are put within the ALcdWMSCapabilities object. Next to this the list can be controlled through configuration via the web deployment descriptor. Controlling the list through configuration is explained in Installing an OGC web server.

Providing support for multi-dimensional data

The OGC WMS specification introduces additional concepts that can ease the integration of multi-dimensional datasets. Multi-dimensional datasets are spatial datasets that are similar in nature but differ in time, elevation or other dimensions. Common examples are daily composite images of the Earth and weather charts for various heights. Instead of defining a new layer in the capabilities for each data source in a multi-dimensional dataset, the OGC WMS specification allows to define one layer with one or more dimensions. These dimensions each have a dimensional axis with a unit of measure and a set of allowable values. This approach results in a more compact and orderly representation of the dataset in the capabilities of the server. A client can retrieve the available dimensions for a layer through the capabilities, and use this information to specify one or more dimension values in a GetMap or GetFeatureInfo request. The server uses these values to determine the correct data source.

The LuciadLightspeed WMS Server API provides support for multi-dimensional data. To include support in an implementation, there are two tasks that need to be carried out:

  • The required dimensions need to be defined in the capabilities for the correct layers.

  • The server must know how to map a certain request containing dimensional parameters to a concrete data source.

These steps are explained in the following paragraphs.

Defining dimensions in the capabilities

A dimension is represented by the class ALcdWMSDimension. It holds the dimension-related parameters, such as the name, the unit, and so on. The available values or intervals for an ALcdWMSDimension are represented by TLcdWMSDimensionExtent. One or more ALcdWMSDimension objects can be added to an ALcdWMSLayer to define a multi-dimensional layer. When a client sends a GetCapabilities request, the dimension information will be encoded automatically in the returned capabilities document. How to configure the capabilities of a server is described in createWMSCapabilitiesDecoder().

Decoding multi-dimensional datasets

As explained in Reading data, the WMS server uses an ILcdModelProvider as a central access point for all data sources. Its single method getModel(String) retrieves an ILcdModel for a given source name. The source name itself is retrieved from the ALcdWMSLayer requested by a client. In case of a multi-dimensional layer however, there is no exact mapping between the ALcdWMSLayer and a source name. In this situation, determining the source name depends both on the ALcdWMSLayer and dimensional parameters provided by the client. To model this step, an extension of ILcdModelProvider is introduced, namely ALcdMultiDimensionalModelProvider. This class serves two purposes:

  • Resolving the dimensional parameters supplied by the client. For each requested layer, the server checks if there are any dimensions defined for the layer at the server. If positive, it analyzes the client’s request to find any applicable dimensional parameters. For each dimension of the layer, a corresponding TLcdWMSDimensionExtent is created that represents the client’s parameter (may be null, if the client has not included a parameter for a dimension) and resolved through the method getDimensionExtent(ALcdWMSLayer, ALcdWMSDimension, TLcdWMSDimensionExtent, TLcdWMSRequestContext) in ALcdMultiDimensionalModelProvider. The purpose of this method is to determine the correct dimensional parameter, based on the parameter provided by the client. For example, if a supplied value does not match any available value, an implementation could perform interpolation to find a nearest value that is correct. The exact implementation requirements of this method can be found in the LuciadLightspeed reference documentation. By default, an implementation is provided that offers support for dimensions that use numeric values or intervals. For dimensions that use other units, such as specific date formats or non-numeric values and intervals, a proper implementation must be provided.

  • Mapping the resolved dimensional parameters to a concrete data source. After resolving the supplied dimensional parameters, the data source is decoded. For this purpose, ALcdMultiDimensionalModelProvider defines an abstract method getModel(ALcdWMSLayer, ALcdWMSDimension[], TLcdWMSDimensionExtent[], TLcdWMSRequestContext) which maps a layer and the corresponding resolved dimensional parameters on an ILcdModel. Implementations can rely on the regular getModel(String) method, by determining a concrete data source name.

To register an ALcdMultiDimensionalModelProvider implementation, the method createModelProvider(ILcdModelDecoderFactory[]) in ALcdOGCWMSCommandDispatcherFactory must be implemented, as described in Reading data.


1. More information about MIME Media Types can be found at http://www.iana.org/assignments/media-types/.