To visualize polygons and polylines with rounded corners, you can use the classes TLcdRoundedPolygon and TLcdRoundedPolyline.

You may be using other shapes in your model though, such as ILcdPolyline and ILcdPolygon. This article shows you how to round the corners of those shapes when you visualize them in a Lightspeed view.

How to do it?

Create a custom ALspStyleTargetProvider that provides rounded shapes as the visualization targets and the original shapes as the edit targets. With that, you can edit the original shapes as control points of the rounded shapes: you can move individual control points, move the whole set of control points, and insert or remove control points. You can easily convert the original shapes to rounded shapes using the rounding methods in TLcdShapeUtil.

A rounding style target provider
public static class RoundedLineStyleTargetProvider extends ALspStyleTargetProvider {

  @Override
  public void getStyleTargetsSFCT(Object aObject, TLspContext aContext, List<Object> aResultSFCT) {
    if (aObject instanceof ILcdPolygon) {
      aResultSFCT.add(TLcdShapeUtil.round(aContext.getModelReference(), (ILcdPolygon) aObject, 0.5));
    } else if (aObject instanceof ILcdPolyline) {
      aResultSFCT.add(TLcdShapeUtil.round(aContext.getModelReference(), (ILcdPolyline) aObject, 0.5));
    } else {
      aResultSFCT.add(aObject);
    }
  }

  @Override
  public void getEditTargetsSFCT(Object aObject, TLspContext aContext, List<Object> aResultSFCT) {
    // We return the original shapes as edit targets. We use the original shape for editing
    // to ensure we can edit our rounded shapes by moving the control points that are used
    // to define the rounded shapes.
    aResultSFCT.add(aObject);
  }
}

To apply the custom ALspStyleTargetProvider, create an ALspStyler that configures your ALspStyleTargetProvider as the geometry of the objects when it submits styles for them.

Simple styler for rounding corners
public static class LineStyler extends ALspStyler {

  private final boolean fSelected;

  public LineStyler(boolean aSelected) {
    fSelected = aSelected;
  }

  @Override
  public void style(Collection<?> aObjects, ALspStyleCollector aStyleCollector, TLspContext aContext) {
    TLspLineStyle.Builder<?> styleBuilder = TLspLineStyle.newBuilder()
                                                         .width(5);

    if (fSelected) {
      styleBuilder.color(Color.MAGENTA);
    } else {
      styleBuilder.color(Color.CYAN);
    }

    aStyleCollector.objects(aObjects)
                   // Set the style target provider
                   .geometry(new RoundedLineStyleTargetProvider())
                   .style(styleBuilder.build())
                   .submit();
  }
}

For more information about using a style target provider, see Deriving geometry from objects with a style target provider.

Full code

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.util.Collection;
import java.util.List;

import javax.swing.JFrame;

import com.luciad.model.ILcdModel;
import com.luciad.model.TLcdVectorModel;
import com.luciad.reference.TLcdGeodeticReference;
import com.luciad.shape.ILcdPolygon;
import com.luciad.shape.ILcdPolyline;
import com.luciad.shape.TLcdShapeUtil;
import com.luciad.shape.shape2D.TLcd2DEditablePointList;
import com.luciad.shape.shape2D.TLcdLonLatPolygon;
import com.luciad.shape.shape2D.TLcdLonLatPolyline;
import com.luciad.shape.shape2D.TLcdRoundedPolygon;
import com.luciad.shape.shape2D.TLcdRoundedPolyline;
import com.luciad.view.lightspeed.ILspAWTView;
import com.luciad.view.lightspeed.TLspContext;
import com.luciad.view.lightspeed.TLspViewBuilder;
import com.luciad.view.lightspeed.layer.ILspLayer;
import com.luciad.view.lightspeed.layer.TLspPaintState;
import com.luciad.view.lightspeed.layer.shape.TLspShapeLayerBuilder;
import com.luciad.view.lightspeed.style.TLspLineStyle;
import com.luciad.view.lightspeed.style.styler.ALspStyleCollector;
import com.luciad.view.lightspeed.style.styler.ALspStyleTargetProvider;
import com.luciad.view.lightspeed.style.styler.ALspStyler;

import samples.lightspeed.common.FitUtil;

public class RoundedLinesTutorial {

  public static class RoundedLineStyleTargetProvider extends ALspStyleTargetProvider {

    @Override
    public void getStyleTargetsSFCT(Object aObject, TLspContext aContext, List<Object> aResultSFCT) {
      if (aObject instanceof ILcdPolygon) {
        aResultSFCT.add(TLcdShapeUtil.round(aContext.getModelReference(), (ILcdPolygon) aObject, 0.5));
      } else if (aObject instanceof ILcdPolyline) {
        aResultSFCT.add(TLcdShapeUtil.round(aContext.getModelReference(), (ILcdPolyline) aObject, 0.5));
      } else {
        aResultSFCT.add(aObject);
      }
    }

    @Override
    public void getEditTargetsSFCT(Object aObject, TLspContext aContext, List<Object> aResultSFCT) {
      // We return the original shapes as edit targets. We use the original shape for editing
      // to ensure we can edit our rounded shapes by moving the control points that are used
      // to define the rounded shapes.
      aResultSFCT.add(aObject);
    }
  }

  public static class LineStyler extends ALspStyler {

    private final boolean fSelected;

    public LineStyler(boolean aSelected) {
      fSelected = aSelected;
    }

    @Override
    public void style(Collection<?> aObjects, ALspStyleCollector aStyleCollector, TLspContext aContext) {
      TLspLineStyle.Builder<?> styleBuilder = TLspLineStyle.newBuilder()
                                                           .width(5);

      if (fSelected) {
        styleBuilder.color(Color.MAGENTA);
      } else {
        styleBuilder.color(Color.CYAN);
      }

      aStyleCollector.objects(aObjects)
                     // Set the style target provider
                     .geometry(new RoundedLineStyleTargetProvider())
                     .style(styleBuilder.build())
                     .submit();
    }
  }

  private static ILcdModel createModel() {
    TLcdVectorModel model = new TLcdVectorModel(new TLcdGeodeticReference());

    //
    // Hereunder is an example of how to create a TLcdRoundedPolyline and TLcdRoundedPolygon
    // These shapes will not be transformed by our RoundedLineStyleTargetProvider
    // Note that TLcdRoundedPolyline and TLcdRoundedPolygon do not implement ILcdPolyline or ILcdPolygon
    //

    TLcd2DEditablePointList roundedLineControlPoints = new TLcd2DEditablePointList();
    roundedLineControlPoints.append2DPoint(0, 0);
    roundedLineControlPoints.append2DPoint(0, 1);
    roundedLineControlPoints.append2DPoint(1, 1);
    roundedLineControlPoints.append2DPoint(1, 2);
    roundedLineControlPoints.append2DPoint(2, 2);

    TLcdRoundedPolyline roundedLine = new TLcdRoundedPolyline(model.getModelReference(), roundedLineControlPoints, 0.5);
    model.addElement(roundedLine, ILcdModel.NO_EVENT);

    TLcd2DEditablePointList roundedPolygonControlPoints = new TLcd2DEditablePointList();
    roundedPolygonControlPoints.append2DPoint(-1, -1);
    roundedPolygonControlPoints.append2DPoint(-2, -1);
    roundedPolygonControlPoints.append2DPoint(-2, -2);
    roundedPolygonControlPoints.append2DPoint(-3, -2);
    roundedPolygonControlPoints.append2DPoint(-3, -3);

    TLcdRoundedPolygon roundedPolygon = new TLcdRoundedPolygon(model.getModelReference(), roundedPolygonControlPoints, 0.5);
    model.addElement(roundedPolygon, ILcdModel.NO_EVENT);

    //
    // Hereunder we add a 'regular' ILcdPolyline and ILcdPolygon to our model.
    // These shapes will be transformed into rounded shapes by our RoundedLineStyleTargetProvider
    //

    TLcd2DEditablePointList polyLineControlPoints = new TLcd2DEditablePointList();
    polyLineControlPoints.append2DPoint(-1, 0);
    polyLineControlPoints.append2DPoint(-1, 1);
    polyLineControlPoints.append2DPoint(-2, 1);
    polyLineControlPoints.append2DPoint(-2, 2);
    polyLineControlPoints.append2DPoint(-3, 2);

    ILcdPolyline polyLine = new TLcdLonLatPolyline(polyLineControlPoints);
    model.addElement(polyLine, ILcdModel.NO_EVENT);

    TLcd2DEditablePointList polygonControlPoints = new TLcd2DEditablePointList();
    polygonControlPoints.append2DPoint(0, -1);
    polygonControlPoints.append2DPoint(1, -1);
    polygonControlPoints.append2DPoint(1, -2);
    polygonControlPoints.append2DPoint(2, -2);
    polygonControlPoints.append2DPoint(2, -3);

    ILcdPolygon polygon = new TLcdLonLatPolygon(polygonControlPoints);
    model.addElement(polygon, ILcdModel.NO_EVENT);

    return model;
  }

  private static ILspLayer createLayer(ILcdModel aModel) {
    return TLspShapeLayerBuilder.newBuilder()
                                .model(aModel)
                                .bodyEditable(true)
                                .bodyStyler(TLspPaintState.REGULAR, new LineStyler(false))
                                .bodyStyler(TLspPaintState.SELECTED, new LineStyler(true))
                                .bodyStyler(TLspPaintState.EDITED, new LineStyler(true))
                                .build();
  }

  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      // create the view
      ILspAWTView view = TLspViewBuilder.newBuilder()
                                        .buildAWTView();

      // create the custom decoration layer and add it to the view
      ILcdModel model = createModel();
      ILspLayer lineLayer = createLayer(model);
      view.addLayer(lineLayer);

      // display the view in a frame
      JFrame frame = new JFrame("Rounded Lines");
      frame.add(view.getHostComponent(), BorderLayout.CENTER);
      frame.setSize(350, 350);
      frame.setVisible(true);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      // fit on the layer
      FitUtil.fitOnLayers(frame, view, false, lineLayer);
    });
  }
}