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

Goal

In this tutorial, we add a UI widget for layer management.

Starting point

We take the code written in the Navigate the map article as our starting point, and expand from there.

Adding a UI widget with available layers

To keep the user of our application informed about the layers that are available on the view, we add a widget showing the available layers: a TLcdLayerTree. The widget also includes check boxes to toggle the visibility of the individual layers.

Program: Creating the TLcdLayerTree widget
private JComponent createLayerControl(ILspView aView) {
  return new TLcdLayerTree(aView);
}

Once the widget is created, we can add it to our JFrame:

Program: Creating the TLcdLayerTree widget
JComponent layerControl = createLayerControl(view);
frame.add(layerControl, BorderLayout.EAST);
lls basic app layer control
Figure 1. Adding the layer control

The full code

Program: The full Swing code
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.util.Collection;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ButtonGroup;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JRadioButton;
import javax.swing.JToolBar;

import com.luciad.geodesy.TLcdGeodeticDatum;
import com.luciad.model.ILcdModel;
import com.luciad.model.ILcdModelDecoder;
import com.luciad.model.TLcdCompositeModelDecoder;
import com.luciad.projection.TLcdEquidistantCylindrical;
import com.luciad.reference.TLcdGridReference;
import com.luciad.util.service.TLcdServiceLoader;
import com.luciad.view.lightspeed.ILspAWTView;
import com.luciad.view.lightspeed.ILspView;
import com.luciad.view.lightspeed.TLspViewBuilder;
import com.luciad.view.lightspeed.layer.ILspLayer;
import com.luciad.view.lightspeed.layer.ILspLayerFactory;
import com.luciad.view.lightspeed.layer.TLspCompositeLayerFactory;
import com.luciad.view.lightspeed.painter.grid.TLspLonLatGridLayerBuilder;
import com.luciad.view.lightspeed.util.TLspViewTransformationUtil;
import com.luciad.view.swing.TLcdLayerTree;

public class FirstApplicationTutorial {


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

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

    addData(view);

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

    view.addLayer(createGridLayer());

    JToolBar toolBar = new JToolBar();

    JRadioButton b2d = new JRadioButton(createSwitchTo2DAction(view));
    b2d.setSelected(true);//start with a 2D view
    JRadioButton b3d = new JRadioButton(createSwitchTo3DAction(view));

    //Place the buttons in a ButtonGroup.
    //This ensures that only one of them can be selected at the same time
    ButtonGroup group = new ButtonGroup();
    group.add(b2d);
    group.add(b3d);

    toolBar.add(b2d);
    toolBar.add(b3d);

    frame.add(toolBar, BorderLayout.NORTH);

    frame.setSize(2000, 1500);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    return frame;
  }

  ILspAWTView createView() {
    return TLspViewBuilder.newBuilder().buildAWTView();
  }

  static void addData(ILspView view) {
    try {
      ILcdModel shpModel = createSHPModel();
      view.addLayer(createLayer(shpModel));

      ILcdModel rasterModel = createRasterModel();
      view.addLayer(createLayer(rasterModel));
    } catch (IOException e) {
      throw new RuntimeException("Problem during data decoding", e);
    }
  }

  private static 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 static 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 static ILspLayer createLayer(ILcdModel aModel) {
    TLspCompositeLayerFactory layerFactory =
        new TLspCompositeLayerFactory(TLcdServiceLoader.getInstance(ILspLayerFactory.class));

    if (layerFactory.canCreateLayers(aModel)) {
      Collection<ILspLayer> layers = layerFactory.createLayers(aModel);
      //We only expect a single layer for our data
      return layers.iterator().next();
    }
    throw new RuntimeException("Could not create a layer for " + aModel.getModelDescriptor().getDisplayName());
  }

  static ILspLayer createGridLayer() {
    return TLspLonLatGridLayerBuilder.newBuilder().build();
  }

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

  static Action createSwitchTo2DAction(ILspView aView) {
    AbstractAction action = new AbstractAction("2D") {
      @Override
      public void actionPerformed(ActionEvent e) {
        TLspViewTransformationUtil.setup2DView(
            aView,
            new TLcdGridReference(new TLcdGeodeticDatum(),
                                  new TLcdEquidistantCylindrical()),
            true
        );
      }
    };
    action.putValue(Action.SHORT_DESCRIPTION, "Switch the view to 2D");
    return action;
  }

  private Action createSwitchTo3DAction(ILspView aView) {
    AbstractAction action = new AbstractAction("3D") {
      @Override
      public void actionPerformed(ActionEvent e) {
        TLspViewTransformationUtil.setup3DView(aView, true);
      }
    };
    action.putValue(Action.SHORT_DESCRIPTION, "Switch the view to 3D");
    return action;
  }

  public static void main(String[] args) {
    //Swing components must be created on the Event Dispatch Thread
    EventQueue.invokeLater(() -> {
      JFrame frame = new FirstApplicationTutorial().createUI();
      frame.setVisible(true);
    });
  }
}
Program: The JavaFX code
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.ToolBar;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

import com.luciad.geodesy.TLcdGeodeticDatum;
import com.luciad.projection.TLcdEquidistantCylindrical;
import com.luciad.reference.TLcdGridReference;
import com.luciad.view.lightspeed.ILspView;
import com.luciad.view.lightspeed.TLspFXView;
import com.luciad.view.lightspeed.TLspViewBuilder;
import com.luciad.view.lightspeed.util.TLspViewTransformationUtil;

import samples.lightspeed.javafx.common.layercontrols.FXLayerControl;

public class FirstApplicationFXTutorial extends Application {


  @Override
  public void start(Stage primaryStage) {
    primaryStage.setTitle("First Lightspeed application");
    BorderPane borderPane = new BorderPane();

    TLspFXView view = createView();
    borderPane.setCenter(view.getHostNode());


    FirstApplicationTutorial.addData(view);

    Node layerControl = createLayerControl(view);
    borderPane.setRight(layerControl);

    view.addLayer(FirstApplicationTutorial.createGridLayer());

    ToolBar toolBar = new ToolBar();
    borderPane.setTop(toolBar);
    RadioButton b2d = new RadioButton("2D");
    RadioButton b3d = new RadioButton("3D");

    toolBar.getItems().add(b2d);
    toolBar.getItems().add(b3d);

    //Place the buttons in a group.
    //This ensures that only one of them can be selected at the same time
    ToggleGroup group = new ToggleGroup();
    b2d.setToggleGroup(group);
    b3d.setToggleGroup(group);

    group.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
      if (newValue == b2d) {
        TLspViewTransformationUtil.setup2DView(
            view,
            new TLcdGridReference(new TLcdGeodeticDatum(), new TLcdEquidistantCylindrical()), true);
      } else {
        TLspViewTransformationUtil.setup3DView(view, true);
      }
    });

    b2d.setSelected(true);//start with a 2D view


    Scene scene = new Scene(borderPane, 800, 600);
    primaryStage.setScene(scene);
    primaryStage.show();
  }

  private TLspFXView createView() {
    return TLspViewBuilder.newBuilder().buildFXView();
  }

  private Node createLayerControl(ILspView aView) {
    return new FXLayerControl(aView);
  }
}