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.”.
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.
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:
-
Data is decoded into models through a model decoder.
-
Layers are created for each model through a layer factory.
-
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 anALcdWMSLayer
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()
returnstrue
, 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 anILcdModel
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.
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 benull
, if the client has not included a parameter for a dimension) and resolved through the methodgetDimensionExtent(ALcdWMSLayer, ALcdWMSDimension, TLcdWMSDimensionExtent, TLcdWMSRequestContext)
inALcdMultiDimensionalModelProvider
. 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 methodgetModel(ALcdWMSLayer, ALcdWMSDimension[], TLcdWMSDimensionExtent[], TLcdWMSRequestContext)
which maps a layer and the corresponding resolved dimensional parameters on anILcdModel
. Implementations can rely on the regulargetModel(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.