The ability to deal with input events is an important part of your application. LuciadRIA provides built-in controls for the most common tasks, such as panning and zooming on a map. These controls are automatically enabled when you create a new Map.

By default, you get the following behavior on a Map, implemented by DefaultController:

Table 1. Default controller behavior
Mouse Touch device Result API class

Drag the left or middle mouse button

Drag a single finger

Pans the map

PanController

Drag the right mouse button around the center of a 2D map

Two-finger twist gesture

Rotates the 2D map

RotateController

Drag the right mouse button left and right on a 3D map

Two-finger twist gesture

Rotates the 3D map

RotateController

Drag the right mouse button up and down on a 3D map

Two-finger drag up and down

Pitches the 3D map

RotateController

Scroll

Two-finger pinch gesture

Zooms the map

ZoomController

Scroll with SHIFT key held down

None

Zooms the map slowly

ZoomController

Click on selectable object

Tap on selectable object

Selects the object

SelectController

Click on selectable object, with SHIFT key held down

/

Adds or removes the object from the current selection

SelectController

Right-click on selectable object

"Long press" on selectable object

Opens a context menu

ContextMenuController

Move mouse over hoverable object

/

Make the object hovered

HoverController

Double-click with left mouse button

Double tap

Zooms in at the clicked / tapped location

ZoomController

Double-click with right mouse button

/

Zooms out from the clicked location

ZoomController

You can override this default behavior by implementing a custom Controller. In the LuciadRIA API, the Controller class deals with input events from the user.

You can activate and de-activate controllers by setting the Map.controller and Map.defaultController properties. Map.defaultController is typically set to controllers that deal with navigation, hovering and selection, like the ones in Table 1, “Default controller behavior”. These remain active throughout the lifetime of the map.

By contrast, Map.controller is typically set to controllers that deal with shorter, more specialized interactions: creating or editing a Feature or performing a measurement, for example. If an input event isn’t handled by Map.controller, it gets passed to Map.defaultController. For example, if your EditController doesn’t handle a scroll event, the scroll event triggers the the default zoom behavior.

When you set these properties, you de-activate any controller that was configured before, and activate the newly configured controller. The controllers get notified of this state change through the Controller.onDeactivate() and Controller.onActivate() methods.

In this tutorial, we describe some Controller implementations in the LuciadRIA API. You can also create your own custom controllers to handle custom user interaction. See Implementing custom user interaction for more information.

Creating new vector features using the CreateController

The CreateController class makes it possible to start an interactive session of vector feature creation.

By default, users can place feature vertices through mouse clicks or free-hand drawing. You can configure the drawing mode and the minimum and maximum number of points to determine the actions you expect from the user.

For your convenience, LuciadRIA provides a concrete implementation, BasicCreateController. This controller implements Createcontroller.onCreateNewObject() by creating a new shape based on a shape type token passed to its constructor. It also wraps the shape in a Feature instance.

This is an illustration of the usage of the BasicCreateController class:

Program: Using BasicCreateController (from samples/createedit/main.js)
const createController = new BasicCreateController(shapeType, {}, {
  finishOnSingleClick: true
});

See the sample CreateController in samples/common/ui/CreateShapeToolbar.tsx and samples/symbology/cop/ObjectWithUUIDCreateController.ts for complete illustrations.

Editing existing vector features

Graphical editing

To begin an editing session, use an EditController. During a graphical editing session, users can change the shape position by dragging the shape.

They can also change the shape geometry by dragging one of its vertices. Vertices automatically snap to other objects when they get close to them. To turn off snapping while editing, users can hold down the Ctrl-key.

They can also insert new points by activating one of the control points located between the vertices of a shape.

User can also delete vertices from the shape. Depending on the device they’re using, they can delete a point by pressing and holding a control point, or by pressing the Ctrl-key and clicking the control point. In a desktop environment, users can also right-click a control point and select Delete from the pop-up menu.

graphicalEdit
Figure 1. Graphically editing a line shape

When a user is editing an object, a commit is performed only at the end of an editing session. This prevents overly frequent updates of the Store during editing.

WebGL maps support editing in 2D and 3D.

Only 2D shapes or 2D aspects of 3D shapes are editable. For example, you can edit only the base shape of an extruded shape.

Textual editing of vector features via the context menu

You can execute custom actions from the context menu. This is the menu that pops up when a user right-clicks an object. Those actions are typically used for editing or changing textual properties of a domain object.

You can add actions to any FeatureLayer by implementing the onCreateContextMenu method.

See the Create and edit sample for example implementations of FeatureLayer.onCreateContextMenu.

Deleting vector features

You can delete vector features by removing them from their container model.

Customizing the default map behavior

You can override the default behavior by setting a new Map.defaultController.

You can use DefaultController and the controllers in @luciad/ria/view/controller to mimic, or completely customize, the default map behavior:

Program: Customizing the default map behavior
// a PanController without inertia
class CustomPanController extends PanController {

  isInertia(gestureEvent: GestureEvent): boolean {
    return false;
  }

}

// a select controller that only allows selection by clicking on labels
class CustomSelectController extends SelectController {

  getPaintRepresentations(event: GestureEvent): PaintRepresentation[] {
    return [PaintRepresentation.LABEL];
  }

}

const navigateController = new NavigateController({
  panController: new CustomPanController(),
  rotateController: null // disable rotation
  // default zoom (zoomController is undefined)
});
map.defaultController = new DefaultController({
  selectController: new CustomSelectController(),
  contextMenuController: new ContextMenuController(),
  navigateController,
  // default hover (hoverController is undefined)
});

If you want to prevent the default behavior completely, you can set Map.defaultController to null on the map. This prevents all default behavior.

Program: Preventing all default map behavior
map.defaultController = null; // prevent _all_ default map behavior

Combining multiple controllers

You can combine multiple controllers with a CompositeController. This controller allows you to chain multiple controllers together, one after the other.

The CompositeController forwards onGestureEvent calls down the chain, until the first controller in the chain handles the event.

The order of the controllers in the chain is important. To avoid issues with onGestureEvent delegation, it’s recommended to chain only Controllers that handle distinct types of events. For example, avoid combining 2 controllers that both handle drag events.

If a CompositeController does not suffice, you can also implement your own Controller that delegates to specific Controller classes, using custom logic.