What is a controller?

A controller allows the user of the application to interact with the map: navigating around, selecting or editing objects, creating new objects, and so on.

It is the task of the controller to capture the input provided by the user, such as the java.awt.MouseEvent, and translate this into concrete actions, panning the map to a new location for example.

A controller in the LuciadLightspeed API is represented by the ILspController interface. The API comes with a number of pre-defined controllers, for example:

  • A controller to navigate on the map

  • A controller to edit data

  • A controller to create new data

  • A controller to measure distances

The following section describes how to use and customize the main implementations of ILspController through controller chaining.

Chaining controllers

The default controllers in the API typically perform only one task. For example, the TLspZoomController allows the user to zoom in and out on the map, but does not support panning the map. The TLspPanController allows the user to pan the map around, but does not support zooming in or out.

Most of the time, you want a controller that combines the behavior of several controllers. You can create such a combination by chaining the controllers: events come in at the top of the chain, and each controller decides whether or not it handles the event. If the controller does not handle the event, or only handles it partially, the event is passed to the next controller in the chain.

lls controllers chainable

Controllers decide whether or not to handle an event by checking their event filters. You can configure each controller in the LuciadLightspeed API with a filter for AWT Events. Events that do not pass the filter are delegated directly to the next controller in the chain. The controller implementation handles Events that pass the filter, but may still delegate them if the controller has no use for them, based on the current state for instance. You can use the class TLcdAWTEventFilterBuilder to create complex filters.

The combination of chaining and filtering possibilities makes it easier to create complex controllers. For example, Program: Creating a composite navigation controller., shows the creation of a number of navigation controllers, along with some event filters. Those navigation controllers are then chained together. The result is a single navigation controller that offers a wide range of navigation functions.

Program: Creating a composite navigation controller. (from samples/lightspeed/common/controller/ControllerFactory)
/*
 * The default navigation controller: fly-to, pan, zoom and rotate.
 */
public static ALspController createNavigationController() {
  // First we create the controllers we want to chain.
  ALspController zoomToController = createZoomToController();
  ALspController panController = createPanController();
  ALspController zoomController = createZoomController();
  ALspController rotateController = createRotateController();

  //Chain the controllers together, events will be offered to the first and trickle down.
  zoomToController.appendController(panController);
  zoomToController.appendController(zoomController);
  zoomToController.appendController(rotateController);

  //Set general properties on the top of the chain.
  zoomToController.setIcon(TLcdIconFactory.create(TLcdIconFactory.HAND_ICON));
  zoomToController.setShortDescription(
      "<html><p>Navigate:</p><p><b>Left mouse</b>: <ul><li>Drag: pan</li>" +
      "<li>Double click: fly to</li></ul></p><p><b>Mouse wheel</b>: zoom</p>" +
      "<p><b>Right mouse</b>: rotate</p></html>"
  );

  return zoomToController;
}

/*
 * Fly-to controller with left mouse button filter. This controller will only use double
 * click events, so in combination with the applied filter, only left mouse double
 * clicks or right mouse double clicks will trigger a fly-to.
 * Left mouse zooms in and right mouse zooms out.
 */
private static ALspController createZoomToController() {
  final NoObjectSelectedFilter noObjectSelectedFilter = new NoObjectSelectedFilter();
  TLspZoomToController zoomToController = new TLspZoomToController() {
    @Override
    public void startInteraction(ILspView aView) {
      super.startInteraction(aView);
      noObjectSelectedFilter.setView(aView);
    }

    @Override
    public void terminateInteraction(ILspView aView) {
      super.terminateInteraction(aView);
      noObjectSelectedFilter.setView(null);
    }
  };
  zoomToController.setAWTFilter(TLcdAWTEventFilterBuilder.newBuilder()
                                                         .customFilter(noObjectSelectedFilter)
                                                         .and()
                                                         .leftMouseButton()
                                                         .or()
                                                         .customFilter(noObjectSelectedFilter)
                                                         .and()
                                                         .rightMouseButton()
                                                         .build());
  return zoomToController;
}

/*
 * Panning is the backup left mouse button behaviour (if editing is not possible), as well
 * as the default action mapped to the middle mouse button.
 */
public static ALspController createPanController() {
  // Use a pan controller that consumes events during panning, e.g. mouse wheel events.
  TLspPanController panController = new GreedyPanController();
  panController.setEnableInertia(true);
  panController.setAWTFilter(
      TLcdAWTEventFilterBuilder
          .newBuilder()
          .leftMouseButton()
          .or()
          .middleMouseButton()
          .or()
          .mouseWheelFilter()
          .build());
  return panController;
}

/*
 * Zooming is the default action mapped to the mouse-wheel.
 */
public static ALspController createZoomController() {
  TLspZoomController zoomController = new TLspZoomController();
  zoomController.setAWTFilter(TLcdAWTEventFilterBuilder.newBuilder().
      mouseWheelFilter().build());
  return zoomController;
}

/*
 * Rotating is the default action mapped to the right mouse button.
 */
public static ALspController createRotateController() {
  TLspRotateController rotateController = new TLspRotateController();
  rotateController.setAWTFilter(TLcdAWTEventFilterBuilder.newBuilder().
      rightMouseButton().build());
  return rotateController;
}