Prerequisites
In this tutorial, it is assumed that the reader is familiar with the LuciadLightspeed concepts introduced in the Create your first 2D map application tutorial:
-
GXY views:
ILcdGXYView
-
GXY layers:
ILcdGXYLayer
-
Model and model decoders:
ILcdModel
andILcdModelDecoder
Goal
This tutorial teaches you the basic concepts in the LuciadLightspeed API for the styling of raster data on a GXY view.
This tutorial creates a JFrame
containing a GXY view.
We will add 2 layers to this view:
-
One background layer with satellite imagery. This layer will use the default styling.
-
One layer with raster data from the city of Treviso in Italy. We will change the brightness and the contrast of this layer.
You can find the complete, runnable code at the end of the tutorial.
Initial setup
This tutorial starts from an application that shows a GXY view in a JFrame
:
public class BasicGXYRasterStylingTutorial {
final TLcdMapJPanel fView = new TLcdMapJPanel();
public JFrame createUI() {
JFrame frame = new JFrame("Raster painting tutorial");
frame.getContentPane().add(fView, BorderLayout.CENTER);
frame.getContentPane().add(new TLcdLayerTree(fView), BorderLayout.EAST);
frame.setSize(800, 600);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
return frame;
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame frame = new BasicGXYRasterStylingTutorial().createUI();
frame.setVisible(true);
});
}
}
Apply default styling to the background layer
To make the rest of the code a bit more compact, we start by introducing a utility method to decode the data:
private ILcdModel decodeModel(String aSourceName) {
try {
TLcdCompositeModelDecoder modelDecoder =
new TLcdCompositeModelDecoder(TLcdServiceLoader.getInstance(ILcdModelDecoder.class));
return modelDecoder.decode(aSourceName);
} catch (IOException e) {
//In a real application, we would need proper error handling
throw new RuntimeException(e);
}
}
For the background layer, we use a GeoPackage raster file with low-resolution satellite imagery from the whole world.
To create a layer for that data, we use the TLcdGXYLayer
class.
This class can deal with both vector data and raster data.
We do not specify any style settings for the background layer,so that the default styling is applied. In the case of an imagery layer, this means that its images are displayed as-is.
TLcdGXYLayer
private void addSatelliteLayer() {
ILcdGXYLayer layer = TLcdGXYLayer.create(decodeModel("Data/GeoPackage/bluemarble.gpkg"));
//Wrap the layer with an async layer wrapper to ensure
//that the view remains responsive while data is being painted
layer = ILcdGXYAsynchronousLayerWrapper.create(layer);
fView.addGXYLayer(layer);
}
To prevent a blocked view painting thread while the raster is being painted, we wrap our layer with an asynchronous layer wrapper. This ensures that the data is painted on a background thread, and that we can still interact with the view while the raster is painted. You can find out out more about asynchronous painting here.
Apply custom styling to the Treviso layer
For the layer with data of the city of Treviso, we are going to customize the styling. The styling is specified on the painter. You can use setter methods to change it.
In this case, we adjust the contract and brightness of the raster images.
//Setup the styling for the raster using the setters on the painter
TLcdGXYImagePainter painter = new TLcdGXYImagePainter();
painter.setBrightness(1.1f);
painter.setContrast(1.5f);
Most raster data is suitable to be shown at certain scales only. For example, it makes no sense to show very detailed raster data of a certain area when a map has been zoomed out, because the details will no longer be distinguishable on the map.
To avoid the loading of unnecessary raster data in such cases, the GXY map shows just the geographical bounds of the raster data set when it is zoomed out. As a result, the performance of the application increases, and the consumption of huge amounts of memory is prevented.
When the default styling of the TLcdGXYImagePainter
is applied, those bounds are styled with a red outline.
We are going to tweak this, and also define a hatch pattern to fill that outline:
//Define the styling for the raster bounds when zoomed out
painter.setOutlineColor(Color.RED);
painter.setOutlineAreaFillStyle(new TLcdGXYHatchedFillStyle(EnumSet.of(TLcdGXYHatchedFillStyle.Pattern.SLASH), Color.RED));
painter.setFillOutlineArea(true);
On the map, this looks like:
To allow the layer to use that painter, we need to install it on the layer using the setGXYPainterProvider
method:
private void addTrevisoLayer() {
//Setup the styling for the raster using the setters on the painter
TLcdGXYImagePainter painter = new TLcdGXYImagePainter();
painter.setBrightness(1.1f);
painter.setContrast(1.5f);
//Define the styling for the raster bounds when zoomed out
painter.setOutlineColor(Color.RED);
painter.setOutlineAreaFillStyle(new TLcdGXYHatchedFillStyle(EnumSet.of(TLcdGXYHatchedFillStyle.Pattern.SLASH), Color.RED));
painter.setFillOutlineArea(true);
TLcdGXYLayer layer = TLcdGXYLayer.create(decodeModel("Data/Rst/treviso.rst"));
layer.setGXYPainterProvider(painter);
//Wrap the layer with an async layer wrapper to ensure
//that the view remains responsive while data is being painted
fView.addGXYLayer(ILcdGXYAsynchronousLayerWrapper.create(layer));
}
Just like the background layer, we wrap the layer with an asynchronous layer wrapper before adding it to the view.
The full code
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.io.IOException;
import java.util.EnumSet;
import javax.swing.JFrame;
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.gxy.ILcdGXYLayer;
import com.luciad.view.gxy.TLcdGXYHatchedFillStyle;
import com.luciad.view.gxy.TLcdGXYLayer;
import com.luciad.view.gxy.asynchronous.ILcdGXYAsynchronousLayerWrapper;
import com.luciad.view.gxy.painter.TLcdGXYImagePainter;
import com.luciad.view.map.TLcdMapJPanel;
import com.luciad.view.swing.TLcdLayerTree;
public class BasicGXYRasterStylingTutorial {
final TLcdMapJPanel fView = new TLcdMapJPanel();
public JFrame createUI() {
JFrame frame = new JFrame("Raster painting tutorial");
addSatelliteLayer();
addTrevisoLayer();
frame.getContentPane().add(fView, BorderLayout.CENTER);
frame.getContentPane().add(new TLcdLayerTree(fView), BorderLayout.EAST);
frame.setSize(800, 600);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
return frame;
}
private ILcdModel decodeModel(String aSourceName) {
try {
TLcdCompositeModelDecoder modelDecoder =
new TLcdCompositeModelDecoder(TLcdServiceLoader.getInstance(ILcdModelDecoder.class));
return modelDecoder.decode(aSourceName);
} catch (IOException e) {
//In a real application, we would need proper error handling
throw new RuntimeException(e);
}
}
private void addSatelliteLayer() {
ILcdGXYLayer layer = TLcdGXYLayer.create(decodeModel("Data/GeoPackage/bluemarble.gpkg"));
//Wrap the layer with an async layer wrapper to ensure
//that the view remains responsive while data is being painted
layer = ILcdGXYAsynchronousLayerWrapper.create(layer);
fView.addGXYLayer(layer);
}
private void addTrevisoLayer() {
//Setup the styling for the raster using the setters on the painter
TLcdGXYImagePainter painter = new TLcdGXYImagePainter();
painter.setBrightness(1.1f);
painter.setContrast(1.5f);
//Define the styling for the raster bounds when zoomed out
painter.setOutlineColor(Color.RED);
painter.setOutlineAreaFillStyle(new TLcdGXYHatchedFillStyle(EnumSet.of(TLcdGXYHatchedFillStyle.Pattern.SLASH), Color.RED));
painter.setFillOutlineArea(true);
TLcdGXYLayer layer = TLcdGXYLayer.create(decodeModel("Data/Rst/treviso.rst"));
layer.setGXYPainterProvider(painter);
//Wrap the layer with an async layer wrapper to ensure
//that the view remains responsive while data is being painted
fView.addGXYLayer(ILcdGXYAsynchronousLayerWrapper.create(layer));
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame frame = new BasicGXYRasterStylingTutorial().createUI();
frame.setVisible(true);
});
}
}