This tutorial series show you how to build your first 2D LuciadLightspeed application with a GXY view:

Goal

In this tutorial, we add vector data from a SHP file.

Starting point

We take the code written in the Add raster data article as our starting point, and expand from there.

Adding vector data to the map

Now that we have set up the map, we can add some data to it. Adding vector data is a two-step process:

  1. First we create an ILcdModel that contains the data.

  2. Then we create an ILcdGXYLayer for the model, which we can add to the map. The ILcdModel contains the data only. The ILcdGXYLayer defines how that data is visualized on the map.

Create a model from the data

The LuciadLightspeed API supports a number of geospatial formats out-of-the-box. It offers ILcdModelDecoder implementations to create an ILcdModel for data of those formats.

In this tutorial, we use a TLcdCompositeModelDecoder to decode the data. As the class name suggests, this is a composite version of the ILcdModelDecoder interface.

This composite implementation is populated with model decoders for all supported formats.

Program: The creation of the composite model decoder
ILcdModelDecoder decoder =
    new TLcdCompositeModelDecoder(TLcdServiceLoader.getInstance(ILcdModelDecoder.class));

The mechanism that makes this work is the Java ServiceLoader. LuciadLightspeed registers ILcdModelDecoder implementations for each of the supported formats in the service registry. We request all those implementations using the TLcdServiceLoader utility class, and pass them all to the constructor of the TLcdCompositeModelDecoder.

The resulting model decoder is capable of decoding data of all supported formats.

Once we have the decoder, we pass the path to the data file to the decode method of the model decoder. The model decoder reads the data in the file and creates an ILcdModel for it.

Program: Creating an ILcdModel for vector data
private ILcdModel createSHPModel() throws IOException {
  // This composite decoder can decode all supported formats
  ILcdModelDecoder decoder =
      new TLcdCompositeModelDecoder(TLcdServiceLoader.getInstance(ILcdModelDecoder.class));

  // Decode city_125.shp to create an ILcdModel
  ILcdModel shpModel = decoder.decode("Data/Shp/Usa/city_125.shp");

  return shpModel;
}

This snippet only works from your IDE if you activate its annotation processing settings.

See the Installation documentation for more details on how to set up your IDE, and the Fixing service registry errors article for specific tips related to annotation processing.

Create a layer for the model

The layer defines how the data from the ILcdModel is visualized on the map. The LuciadLightspeed API uses the concept of an ILcdGXYLayerFactory to create layers for an ILcdModel.

Just like we did for the model decoder, we use a composite ILcdGXYLayerFactory implementation. We make use of the TLcdServiceLoader class once more to populate the composite instance with layer factories for all supported formats.

Program: Creating the composite layer factory
TLcdCompositeGXYLayerFactory layerFactory =
    new TLcdCompositeGXYLayerFactory(TLcdServiceLoader.getInstance(ILcdGXYLayerFactory.class));
TLcdCompositeGXYLayerFactory layerFactory =
    new TLcdCompositeGXYLayerFactory(TLcdServiceLoader.getInstance(ILcdGXYLayerFactory.class));

We can use the createGXYLayer method of the layer factory to create our layer.

Program Creating a layer using the composite layer factory
private ILcdGXYLayer createLayer(ILcdModel aModel) {
  TLcdCompositeGXYLayerFactory layerFactory =
      new TLcdCompositeGXYLayerFactory(TLcdServiceLoader.getInstance(ILcdGXYLayerFactory.class));

  ILcdGXYLayer layer = layerFactory.createGXYLayer(aModel);
  if (layer != null) {
    return layer;
  }
  throw new RuntimeException("Could not create a layer for " + aModel.getModelDescriptor().getDisplayName());
}

Add the layer to the map

Because the layer is responsible for visualizing the data in the model, we need to insert the layers into a map to make the data visible. You can use ILcdGXYView.addGXYLayer for that:

Program: Adding the created layer to the map
ILcdGXYLayer backgroundLayer = createLayer(createRasterModel());
view.addGXYLayer(backgroundLayer);

ILcdGXYLayer citiesLayer = createLayer(createSHPModel());
view.addGXYLayer(citiesLayer);

Moving the grid layer to the top

The TLcdMapJPanel constructor added a grid layer to the map. Now that the vector data has been added to the map, we want the grid layer to appear on top of it.

We move the grid layer to the top of the map:

Program: Move the grid layer to the top of the map
//Move the grid layer to the top of the view,
//so that it is rendered on top of the other layers
view.moveLayerAt(view.layerCount() - 1, view.getGridLayer());

The full code

Program: The full code
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.io.IOException;

import javax.swing.JComponent;
import javax.swing.JFrame;

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.ILcdGXYLayerFactory;
import com.luciad.view.gxy.ILcdGXYView;
import com.luciad.view.gxy.TLcdCompositeGXYLayerFactory;
import com.luciad.view.map.TLcdMapJPanel;
import com.luciad.view.swing.TLcdLayerTree;

public class FirstGXYApplicationTutorial {

  public JFrame createUI() {
    JFrame frame = new JFrame("First GXY application");

    TLcdMapJPanel view = createView();
    frame.add(view, BorderLayout.CENTER);

    try {
      ILcdGXYLayer backgroundLayer = createLayer(createRasterModel());
      view.addGXYLayer(backgroundLayer);

      ILcdGXYLayer citiesLayer = createLayer(createSHPModel());
      view.addGXYLayer(citiesLayer);
    } catch (IOException e) {
      throw new RuntimeException("Problem during data decoding", e);
    }

    //Move the grid layer to the top of the view,
    //so that it is rendered on top of the other layers
    view.moveLayerAt(view.layerCount() - 1, view.getGridLayer());

    JComponent layerControl = createLayerControl(view);
    frame.add(layerControl, BorderLayout.EAST);

    frame.setSize(800, 600);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    return frame;
  }

  private TLcdMapJPanel createView() {
    TLcdMapJPanel view = new TLcdMapJPanel();

    TLcdCompositeGXYLayerFactory layerFactory =
        new TLcdCompositeGXYLayerFactory(TLcdServiceLoader.getInstance(ILcdGXYLayerFactory.class));
    //Install the layer factory on the view
    //When adding models to the view, this factory is used to create layers for those models
    view.setGXYLayerFactory(layerFactory);

    return view;
  }

  private ILcdModel createSHPModel() throws IOException {
    // This composite decoder can decode all supported formats
    ILcdModelDecoder decoder =
        new TLcdCompositeModelDecoder(TLcdServiceLoader.getInstance(ILcdModelDecoder.class));

    // Decode city_125.shp to create an ILcdModel
    ILcdModel shpModel = decoder.decode("Data/Shp/Usa/city_125.shp");

    return shpModel;
  }

  private ILcdModel createRasterModel() throws IOException {
    // This composite decoder can decode all supported formats
    ILcdModelDecoder decoder =
        new TLcdCompositeModelDecoder(TLcdServiceLoader.getInstance(ILcdModelDecoder.class));

    // Decode a sample data set (imagery data)
    ILcdModel geopackageModel = decoder.decode("Data/GeoPackage/bluemarble.gpkg");

    return geopackageModel;
  }

  private ILcdGXYLayer createLayer(ILcdModel aModel) {
    TLcdCompositeGXYLayerFactory layerFactory =
        new TLcdCompositeGXYLayerFactory(TLcdServiceLoader.getInstance(ILcdGXYLayerFactory.class));

    ILcdGXYLayer layer = layerFactory.createGXYLayer(aModel);
    if (layer != null) {
      return layer;
    }
    throw new RuntimeException("Could not create a layer for " + aModel.getModelDescriptor().getDisplayName());
  }

  private JComponent createLayerControl(ILcdGXYView aView) {
    return new TLcdLayerTree(aView);
  }

  public static void main(String[] args) {
    //Swing components must be created on the Event Dispatch Thread
    EventQueue.invokeLater(() -> {
      JFrame frame = new FirstGXYApplicationTutorial().createUI();
      frame.setVisible(true);
    });
  }
}