A vertical view is used to visualize the third dimension of a list of points. Figure 1, “An example of a vertical view” shows the vertical view of a flight, for instance.

vertical1
Figure 1. An example of a vertical view

A vertical view typically shows a main profile and its associated sub-profiles. The flight profile in Figure 1, “An example of a vertical view” is the main profile. As sub-profiles, Figure 1, “An example of a vertical view” shows the altitude range between which an aircraft has to fly and the terrain profile. The line section drawn between two successive points of the main profile is called a segment.

A sub-profile can contain different points per main profile segment. For each of these sub-profile points, it is possible to retrieve the minimum and maximum altitude. A sub-profile is divided into sub-profile steps for each segment of the main profile. The number of steps of the sub-profile is equal to the number of sub-profile points in a segment minus one. In Figure 1, “An example of a vertical view”, the altitude range sub-profile has one step for each segment. The terrain sub-profile step has multiple steps for each segment. A sub-profile step has a ratio, that is the percentage on the X-axis it represents, relative to the segment of the main profile.

Modeling the profile

To model the profile of a vertical view, LuciadLightspeed provides the interface ILcdVVModel . An ILcdVVModel holds the main profile points of the vertical view and the information about every sub-profile associated with the main profile.

You can add a javax.swing.event.ChangeListener to an ILcdVVModel to receive a notification when the internal state of the model has changed and recalculation is required. For more information on listening to changes, refer to Notifying objects of changes with listeners.

Implementations of ILcdVVModel provide methods to:

  • Get the number of points of the main profile and get the point at a specified index

  • Get the distance between two points on the main profile

  • Indicate if editing of the main profile is allowed and change the Z dimension of a point on the main-profile by dragging it

  • Get the number of sub-profiles

  • Get the number of points of a sub-profile in a specified segment of the main profile

  • Get the minimum or the maximum altitude for a specific sub-profile-point

  • Get the step length ratio of a given sub-profile step

  • Manage and notify change listeners

ILcdVVModel implementations

LuciadLightspeed offers an ILcdVVModel implementation, TLcdVVTerrainModel, that shows the profile of the polyline as the main profile, and the terrain profile as a sub-profile. The lightspeed.vertical sample shows how to use this ILcdVVModel.

Adding the profile to the view

To add a profile to the view, LuciadLightspeed provides the interface TLcdVVJPanel. A TLcdVVJPanel is a javax.swing.JPanel that allows to display one main profile and a number of sub-profiles that are associated with the main profile.

Associated with a TLcdVVJPanel are:

Configuring a TLcdVVJPanel

The TLcdVVJPanel provides some methods to set the ILcdVVModel and the renderers. If the ILcdVVModel is set, the TLcdVVJPanel is automatically registered as javax.swing.event.ChangeListener to display changes immediately. Methods are also provided to indicate if the X-axis should be labeled, if the main points should be labeled, and if the vertical cursor should be visible. The different renderers describe the way in which the labels and vertical cursor are painted if they are visible.

Additionally, some methods are provided to change the part of the model that has to be visible in the vertical view:

  • indicate a start point and an end point of the main profile to take into account

  • define a ratio (between 0 and 1) of the main profile to hide at the left and the right side

  • set the lowest and the highest altitude that should be painted when displaying values

Creating a TLcdVVJPanel with controllers

A TLcdVVWithControllersJPanel contains a TLcdVVJPanel and some controllers that allow users to change the values that define the visible part of the model in the vertical view. You can define in the constructor of the TLcdVVWithControllersJPanel which controllers to include.

Listening to cursor changes

The user can position a vertical cursor anywhere along the main profile. When the position of the cursor changes, all the registered ILcdVVCursorChangeEventListener objects are notified. The TLcdVVCursorChangeEvent contains the point of the main profile on the left of the cursor position, on the right of the cursor position, and the percentage of the segment on the left side of the cursor.

Rendering the profile

An ILcdVVRenderer is responsible for rendering, or painting, the main profile and the sub-profiles. You can render a profile in (a combination of) the following modes:

  • ILcdVVRenderer.NO_PAINTING: the profile is not painted

  • ILcdVVRenderer.TOP_LINE: draws a line that is constituted by invoking maxZ of the points of a sub-profile

  • ILcdVVRenderer.BOTTOM_LINE: draws a line that is constituted by invoking minZ of the points of a sub-profile

  • ILcdVVRenderer.LEFT_LINE: draws a line that connects the leftmost point of the topline and the leftmost point of the bottomline

  • ILcdVVRenderer.RIGHT_LINE: draws a line that connects the rightmost point of the topline and the rightmost point of the bottomline

  • ILcdVVRenderer.FILLED: draws a topline and bottomline and fills the space between them

  • ILcdVVRenderer.POLYGON: draws a topline, bottomline, leftline and rightline

For the main profile, only NO_PAINTING and TOP_LINE are valid options.

An ILcdVVRenderer provides some basic methods to paint the different parts of the main profile and the sub-profiles. The basic methods for painting the main profile are: paintPointIcon, drawProfileLine, paintPointLabel. The paintYAxisParallelLine method paints a vertical line from a given point on the main profile to the X-axis. The paintVerticalCursor method paints a vertical cursor. The basic methods for painting the sub-profiles are drawSubProfileLine and fillSubProfileStepPolygon. The sub-profile in Figure 1, “An example of a vertical view” is rendered in FILLED mode. Figure 2, “An example of rendering mode BOTTOM_LINE” shows the same example, but with the sub-profile rendered in BOTTOM_LINE mode.

verticalbottom
Figure 2. An example of rendering mode BOTTOM_LINE

The default implementation of ILcdVVRenderer is TLcdDefaultVVRenderer . Another implementation is TLcdDefaultVVRendererJ2D that you can only use with Java 2D.

Configuring a TLcdDefaultVVRenderer

TLcdDefaultVVRenderer has several properties, which you can change by means of the corresponding set methods. Program: Creation and configuration of an TLcdDefaultVVRenderer shows the creation of a TLcdDefaultVVRenderer and its configuration using several set methods:

  • setMainProfileRenderingMode to set the rendering mode for the main profile

  • setProfileColor to set the color for the main profile

  • setSubProfileRenderingMode to set the rendering mode for the sub-profiles

  • setSubProfileColor to set the color for the sub-profiles

Program: Creation and configuration of an TLcdDefaultVVRenderer (from samples/gxy/vertical/MainPanel)
TLcdDefaultVVRendererJ2D viewRenderer = new TLcdDefaultVVRendererJ2D();
verticalView.setVVRenderer(viewRenderer);

// only label the highlighted point of the main profile
verticalView.setPaintAllLabels(false);
viewRenderer.setPointFormat(new VVAltitudePointFormat(verticalView));

// The main profile (our flight plan) is rendered using a line connecting the points.
viewRenderer.setMainProfileRenderingMode(ILcdVVRenderer.TOP_LINE);
// The subprofile (the terrain) is rendered as filled polygons
// are drawn at each subProfileStepIndex.
viewRenderer.setSubProfileRenderingMode(ILcdVVRenderer.FILLED | ILcdVVRenderer.TOP_LINE);

// Color configuration.
viewRenderer.setPointIcon(MapColors.createHandleIcon());
viewRenderer.setProfilePaint(Color.BLACK);
viewRenderer.setSubProfilePaintArray(aColors);
viewRenderer.setMainProfileLabelPaint(Color.DARK_GRAY);

Decorating the X-axis

An ILcdVVXAxisRenderer is responsible for decorating the X-axis. This interface only contains two methods: getHeight to get the number of pixels that the renderer needs for decorating the X-axis and paintOnXAxis to decorate the X-axis.

ALcdVVXAxisRendererJ2D is an abstract implementation of ILcdVVXAxisRenderer. It paints icons on the X-axis and paints labels underneath the icons. The labels can be rotated over a rotation angle. This class can only be used with Java 2D.

Painting the grid

All the painting that needs to be done for the grid is delegated to an ILcdVVGridRenderer . This interface provides methods for painting the basic grid lines, for painting sub grid lines, for rendering ordinate labels for the grid, and for retrieving the font size of the labels.

TLcdDefaultVVGridRenderer is a default implementation of ILcdVVGridRenderer. This class provides some set methods to customize the default behavior.

Using an ILcdVVGridLineOrdinateProvider

An ILcdVVGridLineOrdinateProvider is responsible for the snapped range and the grid-line ordinates of the vertical view. The interface provides methods for retrieving the range after snapping and for retrieving the grid-line and sub grid-line ordinates.

A use case of the vertical view

This section describes a use case that shows how to implement a vertical view that displays the altitude of some flights. Each flight is a list of 3D points. A normal view only shows the first two coordinates(X,Y) of each of the points. The vertical view is used to show the vertical profile of the flight. In this case, one sub-profile is associated with the view that expresses the altitude-range between which the aircraft has to fly. For each segment of the flight, the minimum and maximum altitudes are shown. Thus, for each segment of the main profile, the sub-profile has only one step. Because there is just one sub-profile step for each segment, the ratio of each of the sub-profile steps is 1.

verticalsample
Figure 3. The vertical view of the use case

Figure 3, “The vertical view of the use case” shows three panels: the upper panel contains a 2D view with two flights. If the user selects one of the flights, the vertical view of the selected flight is shown in the bottom panel. The panel on the right enables a user to change the altitude unit on the vertical view.

A TLcdVVWithControllersJPanel is used to display the vertical view. This javax.swing.JPanel contains a TLcdVVJPanel. To configure a TLcdVVJPanel, you can set an ILcdVVModel, an ILcdVVRenderer, an ILcdVVGridRenderer and an ILcdVVXAxisRenderer.

FlightVVModel is an ILcdVVModel that contains all the information about flights and the altitude-ranges. TLcdDefaultVVRenderer is used as ILcdVVRenderer and some of its properties are configured using its set methods. Since no ILcdVVGridRenderer is set in the sample, the default implementation TLcdDefaultVVGridRenderer is used. This implementation can take the altitude unit of the vertical view into account to display labels.

Notice that changing the unit might have an influence on the upper and lower limits of the vertical view as it attempts to set round numbers as the limits (see Figure 4, “Setting units on the vertical view (meter, feet, none)”). The X-axis is not decorated, so no ILcdVVXAxisRenderer is set.

vertical unit example
Figure 4. Setting units on the vertical view (meter, feet, none)

Creating the flight model

FlightVVModel is an implementation of ILcdVVModel. It holds the main profile points of a flight and the minimum and maximum altitudes associated to the segments of the flight.

Program: An implementation of ILcdVVModel used for flights (from samples/gxy/vertical/FlightVVModel)
public class FlightVVModel extends ALcdVVModel {

  private final Flight fFlight;
  private final ILcdModel fModel;

  public FlightVVModel(Flight aFlight, ILcdModel aModel) {
    fFlight = aFlight;
    fModel = aModel;
    // informs the vertical view of any changes in the model
    fModel.addModelListener(aEvent -> fireChangeModel());
  }

  // points of the main profile
  @Override
  public ILcdPoint getPoint(int aIndex) throws IndexOutOfBoundsException {
    return fFlight.getPoint(aIndex);
  }

  // there is only one sub-profile: the air route associated to each segment
  @Override
  public int getSubProfileCount() {
    return 1;
  }

  // for each segment, the sub-profile has just 2 points: the begin point and
  // the end point of the segment
  @Override
  public int subProfilePointCount(int aSegmentIndex, int aSubProfileIndex)
      throws IndexOutOfBoundsException {
    return 2;
  }

  // the sub-profile contains one step for each segment so the ratio of the
  // step length is always 1
  @Override
  public float stepLenghtRatio(int aSubProfilePointIndex, int aSegmentIndex, int aSubProfileIndex)
      throws IndexOutOfBoundsException {
    return 1;
  }

  @Override
  public double minZ(int aSubProfilePointIndex, int aSegmentIndex, int aSubProfileIndex)
      throws IndexOutOfBoundsException {
    return fFlight.getRouteMinAltitude(aSegmentIndex);
  }

  @Override
  public double maxZ(int aSubProfilePointIndex, int aSegmentIndex, int aSubProfileIndex)
      throws IndexOutOfBoundsException {
    return fFlight.getRouteMaxAltitude(aSegmentIndex);
  }
// ...

Program: An implementation of ILcdVVModel used for flights shows a part of the implementation of FlightVVModel. The implementation of the methods of ILcdVVModel is straightforward.

Program: Construction and configuration of the TLcdVVWithControllersJPanel shows the creation and configuration of the TLcdVVWithControllersJPanel, the javax.swing.JPanel that contains the vertical view.

Program: Construction and configuration of the TLcdVVWithControllersJPanel (from samples/gxy/vertical/MainPanel)
TLcdVVJPanel verticalView = new TLcdVVJPanel();

// enable X-axis labels
verticalView.setXAxisLabeled(true);
verticalView.setVVXAxisRenderer(new VVXAxisDistanceRenderer());

// enable Y-axis grid lines in flight levels or meters/feet
verticalView.setVVGridLineOrdinateProvider(new VVGridLineOrdinateProvider());
TLcdDefaultVVRendererJ2D viewRenderer = new TLcdDefaultVVRendererJ2D();
verticalView.setVVRenderer(viewRenderer);

// only label the highlighted point of the main profile
verticalView.setPaintAllLabels(false);
viewRenderer.setPointFormat(new VVAltitudePointFormat(verticalView));

// The main profile (our flight plan) is rendered using a line connecting the points.
viewRenderer.setMainProfileRenderingMode(ILcdVVRenderer.TOP_LINE);
// The subprofile (the terrain) is rendered as filled polygons
// are drawn at each subProfileStepIndex.
viewRenderer.setSubProfileRenderingMode(ILcdVVRenderer.FILLED | ILcdVVRenderer.TOP_LINE);

// Color configuration.
viewRenderer.setPointIcon(MapColors.createHandleIcon());
viewRenderer.setProfilePaint(Color.BLACK);
viewRenderer.setSubProfilePaintArray(aColors);
viewRenderer.setMainProfileLabelPaint(Color.DARK_GRAY);
// Constructs a TLcdVVWithControllersJPanel that adds controllers to zoom in on the X and Y axis.
TLcdVVWithControllersJPanel vvWithControllers =
    new TLcdVVWithControllersJPanel(aVerticalView,
                                    LEFT_RIGHT_OFFSET | TOP_BOTTOM_OFFSET);