Goal

Lightspeed raster and vector layers support a number of graphical effects that are applied to the layer as a whole, at the pixel level, in contrast to ILspStyler or ALspStyle that perform styling per object. For example, you can change the opacity of the entire layer, or you can map pixels of a certain color to another color.

These effects are represented by the class TLspLayerStyle.

In this tutorial, we use the TLspLayerStyle to toggle between a regular mode and a night mode for a layer:

  • In regular mode, the data is visualized with normal colors.

  • In night mode, we dim the display and use less saturated colors.

LayerStyleTutorial daymode
Figure 1. Data displayed in regular mode
LayerStyleTutorial nightmode
Figure 2. Data displayed in night mode

Initial setup

We start from a JFrame containing a Lightspeed view with a single raster background layer:

public class LayerStylingTutorial {

  final ILspAWTView fView = TLspViewBuilder.newBuilder().buildAWTView();

  public JFrame createUI() {
    JFrame frame = new JFrame("Layer Style tutorial");

    ILspLayer rasterlayer = createRasterLayer();
    fView.addLayer(rasterlayer);
    frame.add(fView.getHostComponent(), BorderLayout.CENTER);

    frame.setSize(800, 600);
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

    return frame;
  }

  private ILspLayer createRasterLayer() {
    String source = "Data/Earth/SanFrancisco/tilerepository.cfg";
    ILcdModel model = decodeData(source);
    return TLspRasterLayerBuilder.newBuilder()
                                 .model(model)
                                 .build();
  }

  private ILcdModel decodeData(String aSource) {
    try {
      TLcdCompositeModelDecoder modelDecoder =
          new TLcdCompositeModelDecoder(TLcdServiceLoader.getInstance(ILcdModelDecoder.class));
      return modelDecoder.decode(aSource);
    } catch (IOException aE) {
      //In a real application, we need proper error handling
      throw new RuntimeException(aE);
    }
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      JFrame frame = new LayerStylingTutorial().createUI();
      frame.setVisible(true);
    });
  }
}

Adding support for night mode

We want to add a checkbox to the UI that allows users to activate night mode.

When the user activates night mode, we will:

  • Reduce the contrast and brightness

  • De-saturate the colors

We replace the TLspLayerStyle instance of the layer with a custom one to set this up. You create TLspLayerStyle instances with a builder class:

  • You can adjust the contrast and the brightness directly on the builder

  • To desaturate the colors, you need to create a color matrix. This matrix serves to transform the RGBA color channel of each pixel.

Once you have the TLspLayerStyle, you can apply it to the layer using the ILspLayer.setLayerStyle method:

private JCheckBox createNightModeButton(ILspLayer layer) {
  JCheckBox checkBox = new JCheckBox("Activate night mode");
  checkBox.setSelected(false);

  TLspLayerStyle originalLayerStyle = layer.getLayerStyle();

  //Create a layer style representing night mode
  //For night mode, we
  //- Reduce the contrast and brightness
  //- Desaturate the colors
  TLspLayerStyle nightModeStyle = TLspLayerStyle.newBuilder()
                                                .contrast(0.9f)
                                                .brightness(0.65f)
                                                .colorMatrix(createDesaturateMatrix(0.85f))
                                                .build();

  //Apply the correct layer style, depending on whether the checkbox is selected or not
  checkBox.addItemListener(e -> {
    if (e.getStateChange() == ItemEvent.SELECTED) {
      layer.setLayerStyle(nightModeStyle);
    } else if (e.getStateChange() == ItemEvent.DESELECTED) {
      layer.setLayerStyle(originalLayerStyle);
    }
  });
  return checkBox;
}

private float[] createDesaturateMatrix(float desaturationFactory) {
  return new float[]{0.21f * desaturationFactory + (1 - desaturationFactory), 0.72f * desaturationFactory, 0.07f * desaturationFactory, 0.0f, 0.0f,
                     0.21f * desaturationFactory, 0.72f * desaturationFactory + (1 - desaturationFactory), 0.07f * desaturationFactory, 0.0f, 0.0f,
                     0.21f * desaturationFactory, 0.72f * desaturationFactory, 0.07f * desaturationFactory + (1 - desaturationFactory), 0.0f, 0.0f,
                     0.00f, 0.00f, 0.00f, 1.0f, 0.0f};
}

We now create a tool bar containing the check box, and add it to the UI.

JToolBar toolBar = new JToolBar();
toolBar.add(createNightModeButton(rasterlayer));
frame.add(toolBar, BorderLayout.NORTH);

Full code

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ItemEvent;
import java.io.IOException;

import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JToolBar;
import javax.swing.WindowConstants;

import com.luciad.model.ILcdModel;
import com.luciad.model.ILcdModelDecoder;
import com.luciad.model.TLcdCompositeModelDecoder;
import com.luciad.util.service.TLcdServiceLoader;
import com.luciad.view.lightspeed.ILspAWTView;
import com.luciad.view.lightspeed.TLspViewBuilder;
import com.luciad.view.lightspeed.layer.ILspLayer;
import com.luciad.view.lightspeed.layer.raster.TLspRasterLayerBuilder;
import com.luciad.view.lightspeed.layer.style.TLspLayerStyle;

public class LayerStylingTutorial {

  final ILspAWTView fView = TLspViewBuilder.newBuilder().buildAWTView();

  public JFrame createUI() {
    JFrame frame = new JFrame("Layer Style tutorial");

    ILspLayer rasterlayer = createRasterLayer();
    fView.addLayer(rasterlayer);

    JToolBar toolBar = new JToolBar();
    toolBar.add(createNightModeButton(rasterlayer));
    frame.add(toolBar, BorderLayout.NORTH);

    frame.add(fView.getHostComponent(), BorderLayout.CENTER);

    frame.setSize(800, 600);
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

    return frame;
  }

  private ILspLayer createRasterLayer() {
    String source = "Data/Earth/SanFrancisco/tilerepository.cfg";
    ILcdModel model = decodeData(source);
    return TLspRasterLayerBuilder.newBuilder()
                                 .model(model)
                                 .build();
  }

  private ILcdModel decodeData(String aSource) {
    try {
      TLcdCompositeModelDecoder modelDecoder =
          new TLcdCompositeModelDecoder(TLcdServiceLoader.getInstance(ILcdModelDecoder.class));
      return modelDecoder.decode(aSource);
    } catch (IOException aE) {
      //In a real application, we need proper error handling
      throw new RuntimeException(aE);
    }
  }

  private JCheckBox createNightModeButton(ILspLayer layer) {
    JCheckBox checkBox = new JCheckBox("Activate night mode");
    checkBox.setSelected(false);

    TLspLayerStyle originalLayerStyle = layer.getLayerStyle();

    //Create a layer style representing night mode
    //For night mode, we
    //- Reduce the contrast and brightness
    //- Desaturate the colors
    TLspLayerStyle nightModeStyle = TLspLayerStyle.newBuilder()
                                                  .contrast(0.9f)
                                                  .brightness(0.65f)
                                                  .colorMatrix(createDesaturateMatrix(0.85f))
                                                  .build();

    //Apply the correct layer style, depending on whether the checkbox is selected or not
    checkBox.addItemListener(e -> {
      if (e.getStateChange() == ItemEvent.SELECTED) {
        layer.setLayerStyle(nightModeStyle);
      } else if (e.getStateChange() == ItemEvent.DESELECTED) {
        layer.setLayerStyle(originalLayerStyle);
      }
    });
    return checkBox;
  }

  private float[] createDesaturateMatrix(float desaturationFactory) {
    return new float[]{0.21f * desaturationFactory + (1 - desaturationFactory), 0.72f * desaturationFactory, 0.07f * desaturationFactory, 0.0f, 0.0f,
                       0.21f * desaturationFactory, 0.72f * desaturationFactory + (1 - desaturationFactory), 0.07f * desaturationFactory, 0.0f, 0.0f,
                       0.21f * desaturationFactory, 0.72f * desaturationFactory, 0.07f * desaturationFactory + (1 - desaturationFactory), 0.0f, 0.0f,
                       0.00f, 0.00f, 0.00f, 1.0f, 0.0f};
  }

  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      JFrame frame = new LayerStylingTutorial().createUI();
      frame.setVisible(true);
    });
  }
}