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

About this tutorial

This tutorial series show you how to build your first LuciadLightspeed application that loads data and allows you to navigate on a 2D map.

Prerequisites

This tutorial series require no prior LuciadLightspeed knowledge, but you do need some basic familiarity with Java Swing.

Goal

This tutorial series provide a step-by-step guide to building a basic LuciadLightspeed application with a 2D, non-hardware-accelerated map. The application built in these series allows users to view major USA cities on a raster image of the world. This tutorial series discusses the setup of the basic View, Model and Controller components of the application, before expanding this basic configuration with more functions.

More specifically, the application offers the following functionality:

  • A map displayed inside a JFrame

  • A raster image of the world used as background data

  • The major cities of the USA, read from a .SHP file

  • The ability to navigate through the view

  • Check boxes to toggle the visibility of the data on the map

lls basic gxy app end result
Figure 1. The window with the components of a basic LuciadLightspeed application with a GXY view

You can find the complete, runnable code at the end of the tutorial.

This tutorial uses the non-hardware-accelerated GXY view. A similar tutorial for the hardware-accelerated Lightspeed view is available here.

Read High-level overview of the different technologies if you are unsure which view is most suitable for you.

Overview of the main API concepts

A standard LuciadLightspeed application follows the M(odel)-V(iew)-C(ontroller) architectural pattern. The idea behind the MVC architecture is to separate the data (model), the representation of the data (view), and the user interaction (controller) from each other. That separation results in a simpler design of the application and a higher flexibility and re-usability of code.

The MVC parts of the LuciadLightspeed API are defined as follows:

  • A LuciadLightspeed model stores and describes geographical data regardless of how the data is visualized and interacted with. For example: a model contains the location of a number of hospitals and additional hospital information such as capacity.

    In the LuciadLightspeed API, the model is an ILcdModel instance.

  • A LuciadLightspeed view contains all information needed for the visual representation of the data in the LuciadLightspeed models. A view doesn’t contain data. For example, in the case of the hospitals, the view uses a red cross to represent the location of a hospital.

    In the LuciadLightspeed API, an ILcdGXYLayer contains the information about how to visualize a single ILcdModel. All those layers are added to a ILcdGXYView, which can be visualized in a Swing container.

  • A LuciadLightspeed controller interprets user interaction and performs the required action on LuciadLightspeed models and views regardless of the type of model and view. For example: in the case of the hospitals, a mouse right-click on a red cross results in an information pop-up with the hospital location and capacity..

    In the LuciadLightspeed API, these are ILcdGXYController instances.

Separating the different parts of the application allows you to re-use objects for different purposes, and to re-define objects without changing other objects. You can, for example, change a view without making changes to the models represented in the view. You can also re-define the user interaction with a view without changing the view itself. Object re-use shortens the time for writing an application. In addition, it promotes a consistent design and functionality for all your applications.

Setting up your IDE

To get started with LuciadLightspeed development, you need to set up your preferred Integrated Development Environment (IDE). You can find more information in the IDE-specific articles for IntelliJ, Eclipse and NetBeans.

Creating the application UI

Before we can turn to the LuciadLightspeed API to create our map, we need a JFrame to display the map in, and a main method to start the application:

Program: Creating the application UI
public class FirstGXYApplicationTutorial {

  public JFrame createUI() {
    JFrame frame = new JFrame("First GXY application");
    frame.setSize(800, 600);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    return frame;
  }
  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);
    });
  }
}

Creating the map

Now that we have a frame, we need to create the map and add it to the UI. In LuciadLightspeed terminology, a map is known as a view. It is defined by the interface ILcdGXYView.

Because we are going to add our view to a Swing hierarchy, we use a Swing-based implementation of the ILcdGXYView interface: the TLcdMapJPanel class.

Program: Creating a view
private TLcdMapJPanel createView() {
  TLcdMapJPanel view = new TLcdMapJPanel();
  return view;
}

Once the view is created, we can add it to our UI.

Program: Adding the view to the JFrame in the createUI method
TLcdMapJPanel view = createView();
frame.add(view, BorderLayout.CENTER);

Running the program at this point results in a view with a grid layer. This grid layer was automatically created when we constructed a new TLcdMapJPanel instance. Continue with the next tutorial in these series to add some data to the map.

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);
    });
  }
}