This article explains how you can quickly add support for each data format used in your application. As a result, your application will be able to load each data type as soon as a user opens a new file via a dialog box, or drags and drops a file into the application’s view, for example.

LuciadLightspeed comes with pre-configured code that allows you to add all available formats with just a few lines of code, as demonstrated in Program: Adding GUI actions to open data from a file or a URL.

Program: Adding GUI actions to open data from a file or a URL (from samples/gxy/decoder/MainPanel)
fOpenSupport = createOpenSupport();
  fOpenSupport.addStatusListener(getStatusBar());
  toolBar.addAction(new OpenAction(fOpenSupport));
  toolBar.addAction(new OpenURLAction(fOpenSupport));

To set up the service look-up mechanism for each data format used in your application:

  1. Get a hold of the relevant format-specific services such as model decoders and layer factories. For more information, see Selecting data format services.

  2. Integrate these data decoding services into a GUI for opening data. See Configuring an action to load model data for more information.

  3. Integrate other format-specific services. See Using other format services for more information.

  4. Define and integrate your own format-specific services, if you have any. See How to plug in your own data format for more information.

This article goes into more detail about each step of the setup of the service look-up mechanism. To read more about such a look-up mechanism as well as other solutions for data format support, see Selecting data format services.

Selecting data format services

Most LuciadLightspeed formats simply consist of a model decoder, an implementation of ILcdModelDecoder, and a layer factory, an implementation of ILcdGXYLayerFactory or ILspLayerFactory. Some formats come with a few extra services, as explained in Using other format services. To add support for LuciadLightspeed 's formats, you need to instantiate and add these to your application.

There are several ways to add instant data format support in an application:

Direct instantiation:

The most straightforward way to add format support. Create your own composite model decoder and layer factory and add the model decoders and layer factories you require in your application. Direct instantiation of a model decoder and layer factory is demonstrated in the LuciadLightspeed fundamentals samples.

A dependency injection framework:

This approach wires the required model decoders and layer factories using a dependency injection framework such as Guice or Spring. For more information about dependency injection, refer to the documentation of the respective frameworks.

A service look-up:

Using a look-up mechanism to query all supported model decoders and layer factories at run-time. In this approach, the support provided for each data format, such as its model decoder and layer factory, is regarded as a service that can be looked up, retrieved and activated. LuciadLightspeed comes with a look-up mechanism that supports both the built-in formats, as well as your own, custom-defined or custom-configured data formats.

While all three options have their advantages and disadvantages, this article primarily focuses on the service look-up option, because it is used throughout the LuciadLightspeed samples, and because it does not depend on third-party libraries.

Looking up data format services

The service look-up mechanism in LuciadLightspeed is compatible with Java’s java.util.service.ServiceLoader, but offers extra functionality. The most important extra functionality of the service registry in LuciadLightspeed is the ability to sort services based on their priority.

The LuciadLightspeed model decoders and layer factories are automatically registered with the service registry through the annotation @LcdService. For more information about these annotations, see How to plug in your own data format. In the sample code, the look-up mechanism is accessed through TLcdServiceLoader. The API consists of a single method getInstance that returns an Iterable for the given Java class.

Program: Retrieving all available ILcdModelDecoder objects shows how to retrieve the ILcdModelDecoder instances that are available in LuciadLightspeed and the samples.

Program: Retrieving all available ILcdModelDecoder objects (from samples/common/formatsupport/OpenSupport)
TLcdServiceLoader.getInstance(ILcdModelDecoder.class)

In the same way, you can retrieve all available ILcdGXYLayerFactory or ILspLayerFactory instances. Next to some format-specific layer factories, LuciadLightspeed also offers layer factories like GXYUnstyledLayerFactory/UnstyledLayerFactory and GXYSLDFileLayerFactory/SLDFileLayerFactory. These sample layer factories handle formats that do not contain or impose specific styling, for example. They are also registered as a service with the service registry.

Composites are available for many services. Program: Creating composites for services shows how they can be instantiated. These composites, when instantiated using the service loader, combine all model decoders, layer factories or other services of all available optional components. They can therefore provide sensible default behavior for almost all data formats. For example the optional Ecdis maritime charts are decoded and styled correctly by default if this option is available. Do keep in mind that the exact behavior may change in future versions.

Program: Creating composites for services
ILcdModelDecoder modelDecoder =
    new TLcdCompositeModelDecoder(TLcdServiceLoader.getInstance(ILcdModelDecoder.class));
ILcdGXYLayerFactory gxyLayerFactory =
    new TLcdCompositeGXYLayerFactory(TLcdServiceLoader.getInstance(ILcdGXYLayerFactory.class));
ILspLayerFactory layerFactory =
    new TLspCompositeLayerFactory(TLcdServiceLoader.getInstance(ILspLayerFactory.class));

If you need full control, or if customization is desired, you’re recommended to implement your own service. Many examples are available as sample code. If you annotate them with @LcdService and generate the services files using the annotation processor, your services take precedence when using the default, or higher, priority. See How to plug in your own data format for more information on the annotation processor.

Configuring an action to load model data

This section shows how to set up and configure an ILcdAction to load ILcdModel data from an external source. The OpenSupport class offers the most important functionality for this. It wires a composite model decoder with a customizable GUI support class.

The openSource method of the support class finds the most appropriate model decoder and decodes the given source into an ILcdModel.

The GXYOpenSupport and LspOpenSupport classes further build upon this, creating a layer for the decoded model and adding it to a view. This is shown in Program: Adding a layer for a decoded model.

When adding layers to a GXY view for client side use, you will likely want to wrap the layers with an asynchronous layer. That is not taken care of by the layer factories discovered using TLcdServiceLoader as it is undesired for server-side views. GXYOpenSupport decorates layers with asynchronous behavior using AsynchronousLayerFactory.

Program: Adding a layer for a decoded model (from samples/common/formatsupport/GXYOpenSupport)
fLayerFactory = new TLcdCompositeGXYLayerFactory(aLayerFactories);
  ILcdGXYLayer layer = new AsynchronousLayerFactory(fLayerFactory).createGXYLayer(aEvent.getModel());
  if (layer == null) {
    noLayerFactory(getParentComponent(), aEvent.getModel());
  } else {
    GXYLayerUtil.addGXYLayer(fView, layer);
    if (layer.isVisible()) {
      GXYLayerUtil.fitGXYLayer(fView, layer);
    }
  }

Three clients of this support class link the data loading functionality with the functionality for data selection by the application user:

OpenAction

presents the user with a file chooser dialog

OpenURLAction

presents the user with a dialog box that asks for a URL

OpenTransferHandler

allows dragging files or URLs onto the view

This is illustrated in the decoder sample.

Using other format services

The format functionality in LuciadLightspeed is not restricted to model decoders and layer factories: other format services can be registered with the service registry as well. One example is the ILcdModelMeasureProviderFactory, which is used in samples.gxy.common.MouseLocationComponent for reading out measurements under the current mouse location. See the samples for an illustration of the readout functionality.

How to plug in your own data format

To add your own model decoder or layer factory to the service look-up mechanism, simply annotate it with @LcdService. If your class implements multiple interfaces, make sure that you also specify the interface you want to include as a service, for example, ILcdModelDecoder.class. Optionally, you can specify a priority for the service. The default priority ensures that your own service takes precedence over the LuciadLightspeed services. See the API reference documentation of LcdService for a list of available priorities.

Program: Annotating a layer factory as a service shows how to annotate a layer factory with low priority.

Program: Annotating a layer factory as a service (from samples/gxy/decoder/custom1/Custom1LayerFactory)
@LcdService(service = ILcdGXYLayerFactory.class, priority = LcdService.LOW_PRIORITY)
public class Custom1LayerFactory implements ILcdGXYLayerFactory {

During compilation, the TLcdServiceAnnotationProcessor makes sure that the annotation is translated into a service entry in the META-INF directory. The ServiceRegistry look-up mechanism queries this directory at run-time to to provide the requested format services. This is illustrated in Figure 1, “Service look-up mechanism”.

service loader
Figure 1. Service look-up mechanism

Note that annotation processing should be enabled when compiling and that the jar files containing the annotation processor (lcd_annotations*) should be in the compiler classpath. All major IDE implementations support annotation processing. Also note that the NetBeans IDE contains a known bug causing annotation processing not to work well in combination with the 'Compile on Save' option. A workaround is to disable this option.