When you are drawing a polyline, or any other shape, it may be useful to add labels to some of the points that make up the shape. You can use those to display intermediate distances between points for example, like the TLspRulerController does, or you can display way points that make up a trajectory.

How to do it?

If you are using a Lightspeed view, you can easily add labels to points with a custom label styler. Such a styler can:

  • Submit a label for the entire polyline

  • Submit a label for each individual point

This is demonstrated by the following code:

Program:Add labels to points with a custom label styler
  private static class PolylineStyler extends ALspStyler {

    @Override
    public void style(Collection<?> aObjects, ALspStyleCollector aStyleCollector, TLspContext aContext) {
      if (!(aContext.getModelReference() instanceof ILcdGeoReference)) {
        throw new IllegalArgumentException("Model reference should implement ILcdGeoReference");
      }
      ILcdGeoReference geoReference = (ILcdGeoReference) aContext.getModelReference();

      // Display the polyline with a line style
      aStyleCollector.objects(aObjects).style(TLspLineStyle.newBuilder().width(2).build()).submit();

      for (Object object : aObjects) {
        if (object instanceof ILcdPointList) {
          // Display a circle on each individual point
          ILcdPointList pointList = (ILcdPointList) object;
          for (int i = 0; i < pointList.getPointCount(); i++) {
            ILcdPoint point = pointList.getPoint(i);
            TLcdLonLatCircle circle = new TLcdLonLatCircle(point, 5000.0, geoReference.getGeodeticDatum().getEllipsoid());
            aStyleCollector.object(object).geometry(circle).style(TLspLineStyle.newBuilder().width(2).color(Color.orange).build()).submit();
          }
        }
      }
    }
  }

The result is the following image:

label points of polyline
Figure 1. Points of polyline labeled

The image was generated using the code at the end of this article.

When developers implement functionality like this, they often run into this problem: only one of the labels is shown, or even none of the labels are visible. To learn why that happens, and how you can fix the problem, see My object has multiple labels, but only one appears.

Full code

package samples.lightspeed.labels.polyline;

import java.awt.Color;
import java.io.IOException;
import java.util.Collection;

import com.luciad.model.ILcdModel;
import com.luciad.model.TLcdModelDescriptor;
import com.luciad.model.TLcdVectorModel;
import com.luciad.reference.ILcdGeoReference;
import com.luciad.reference.TLcdGeodeticReference;
import com.luciad.shape.ILcdPoint;
import com.luciad.shape.ILcdPointList;
import com.luciad.shape.shape2D.TLcdLonLatCircle;
import com.luciad.shape.shape2D.TLcdXYPolyline;
import com.luciad.view.lightspeed.TLspContext;
import com.luciad.view.lightspeed.layer.ILspInteractivePaintableLayer;
import com.luciad.view.lightspeed.layer.TLspPaintState;
import com.luciad.view.lightspeed.layer.shape.TLspShapeLayerBuilder;
import com.luciad.view.lightspeed.painter.label.style.ALspLabelTextProviderStyle;
import com.luciad.view.lightspeed.style.TLspLineStyle;
import com.luciad.view.lightspeed.style.TLspTextStyle;
import com.luciad.view.lightspeed.style.styler.ALspLabelStyleCollector;
import com.luciad.view.lightspeed.style.styler.ALspLabelStyler;
import com.luciad.view.lightspeed.style.styler.ALspStyleCollector;
import com.luciad.view.lightspeed.style.styler.ALspStyler;

import samples.lightspeed.common.FitUtil;
import samples.lightspeed.common.LightspeedSample;
import samples.lightspeed.labels.util.FixedTextProviderStyle;

/**
 * Sample that paints a label for every point on a polyline.
 */
public class LabelPointsOfPolylineSample extends LightspeedSample {

  @Override
  protected void addData() throws IOException {
    TLcdXYPolyline polyline = new TLcdXYPolyline();
    polyline.insert2DPoint(0, 0, 0);
    polyline.insert2DPoint(1, 1, 1);
    polyline.insert2DPoint(1, 2, 0.5);

    TLcdVectorModel model = new TLcdVectorModel();
    model.setModelDescriptor(new TLcdModelDescriptor());
    model.setModelReference(new TLcdGeodeticReference());

    model.addElement(polyline, ILcdModel.NO_EVENT);

    ILspInteractivePaintableLayer layer = TLspShapeLayerBuilder.newBuilder()
                                                               .model(model)
                                                               .label("Polyline")
                                                               .bodyStyler(TLspPaintState.REGULAR, new PolylineStyler())
                                                               .labelStyler(TLspPaintState.REGULAR, new PolylineLabelStyler())
                                                               .build();

    getView().addLayer(layer);
    FitUtil.fitOnLayers(this, layer);
  }

  private static class PolylineStyler extends ALspStyler {

    @Override
    public void style(Collection<?> aObjects, ALspStyleCollector aStyleCollector, TLspContext aContext) {
      if (!(aContext.getModelReference() instanceof ILcdGeoReference)) {
        throw new IllegalArgumentException("Model reference should implement ILcdGeoReference");
      }
      ILcdGeoReference geoReference = (ILcdGeoReference) aContext.getModelReference();

      // Display the polyline with a line style
      aStyleCollector.objects(aObjects).style(TLspLineStyle.newBuilder().width(2).build()).submit();

      for (Object object : aObjects) {
        if (object instanceof ILcdPointList) {
          // Display a circle on each individual point
          ILcdPointList pointList = (ILcdPointList) object;
          for (int i = 0; i < pointList.getPointCount(); i++) {
            ILcdPoint point = pointList.getPoint(i);
            TLcdLonLatCircle circle = new TLcdLonLatCircle(point, 5000.0, geoReference.getGeodeticDatum().getEllipsoid());
            aStyleCollector.object(object).geometry(circle).style(TLspLineStyle.newBuilder().width(2).color(Color.orange).build()).submit();
          }
        }
      }
    }
  }

  private static class PolylineLabelStyler extends ALspLabelStyler {

    private final TLspTextStyle fTextStyle = TLspTextStyle.newBuilder().build();
    private final ALspLabelTextProviderStyle fMainTextProviderStyle = FixedTextProviderStyle.newBuilder().text("Line Label").build();
    private final ALspLabelTextProviderStyle fPointTextProviderStyle = new ALspLabelTextProviderStyle() {
      @Override
      public String[] getText(Object aDomainObject, Object aSubLabelID, TLspContext aContext) {
        return new String[]{aSubLabelID.toString()};
      }
    };

    @Override
    public void style(Collection<?> aObjects, ALspLabelStyleCollector aStyleCollector, TLspContext aContext) {
      // Draw a label on the shape (e.g. polyline) itself
      aStyleCollector.objects(aObjects).label("mainLabel").styles(fTextStyle, fMainTextProviderStyle).submit();

      for (Object object : aObjects) {
        if (object instanceof ILcdPointList) {
          // for point lists, draw a label for every point that makes up the point list
          ILcdPointList pointList = (ILcdPointList) object;
          for (int i = 0; i < pointList.getPointCount(); i++) {
            ILcdPoint point = pointList.getPoint(i);
            aStyleCollector.object(object).geometry(point).label("pointLabel_" + i).styles(fTextStyle, fPointTextProviderStyle).submit();
          }
        }
      }
    }
  }

  public static void main(String[] aArgs) {
    startSample(LabelPointsOfPolylineSample.class, "Display labels on points of a polyline");
  }

}