Adding a feature model

You can add and remove data on the Map using Map::getLayerList().

To add a feature model to the map, you:

  1. Create a FeatureLayer based on the model and styling information (IFeaturePainter)

  2. Add that layer to the Map’s LayerList

This code snippet demonstrates how a feature model is added to the map, configured with a custom painter:

Program (C++): Adding a feature layer
std::shared_ptr<IFeatureModel> geometryModel = CustomerGeometryModel::create();
std::shared_ptr<FeatureLayer> geometryLayer = FeatureLayer::newBuilder()



var geometryModel = CustomerGeometryModel.Create();
var geometryLayer = FeatureLayer.NewBuilder()

Styling features

Apart from a mandatory IFeatureModel parameter on its constructor, the FeatureLayer::Builder also accepts an optional IFeaturePainter, which you can use to set up custom styling of the model features.

Creating your own IFeaturePainter requires an implementation of two methods:

  • IFeaturePainter::configureMetadata: used to retrieve metadata about the painter. This metadata is used by the layer to determine when a feature needs to be re-processed by the painter. For example, a feature needs to be painted differently after a certain property of the feature has changed. For more information, see the documentation of FeaturePainterMetadata.

  • IFeaturePainter::paint: used to determine how to visualize a feature. Given a Feature and context, this method performs draw calls on the FeatureCanvas parameter.

This code snippet shows an implementation of the IFeaturePainter::paint method:

Program (C++): Using the feature painter
void TrackFeaturePainter::paint(const Feature& feature, const FeaturePainterContext& context, FeatureCanvas& canvas) const {
  auto geometry = feature.findGeometry();
  if (!geometry) {

  bool isSelected = context.isFeatureStateEnabled(FeatureState::Selected);
  auto azimuth = feature.getValue<double>(CustomerTrackModel::getAzimuthPropertyPath());

      .draped(context.getDetailLevel() == 0 ? true : false)
      .icon(isSelected ? constants.iconSelected : constants.iconRegular)

  if (context.getDetailLevel() == 1) {
    // Paint a line along the track to show the distance it can cover in one minute at the current speed
    auto location = std::dynamic_pointer_cast<Point>(geometry);
    auto speed = feature.getValue<double>(CustomerTrackModel::getGroundSpeedValuePropertyPath());
    if (!location || !azimuth || !speed) {

    // Calculate the expected location within one minute (assuming the speed and azimuth don't change)
    auto lineEnd = _geodesy->extrapolate2D(location->getLocation(), azimuth.value(), speed.value() * 60.0);
    if (!lineEnd) {

    auto line = GeometryFactory::createLine(location->getReference(), location->getLocation(), lineEnd.value(), LineInterpolationType::Geodesic);
    auto style = isSelected ? constants.lineStyleSelected : constants.lineStyleRegular;

    // Show the call sign of the track
    auto callSign = feature.getValue<std::string>(CustomerTrackModel::getCallSignPropertyPath());
    if (callSign) {
      std::shared_ptr<PinStyle> pinStyle = PinStyle::newBuilder().build();
          .textStyle(isSelected ? _textStyleSelected : _textStyle)
          .pointPositions({getLabelPosition(Azimuth(azimuth.value() + 180.0), 20.0)})


    public void Paint(Feature feature, FeaturePainterContext context, FeatureCanvas canvas)
        var geometry = feature.FindGeometry();
        if (geometry == null)

        var isSelected = context.IsFeatureStateEnabled(FeatureState.Selected);
        var azimuth = feature.GetValue<double?>(CustomerTrackModel.AzimuthPropertyPath);

            .Draped(context.DetailLevel == 0 ? true : false)
            .Icon(isSelected ? IconSelected : IconRegular)
            .Orientation(azimuth != null ? azimuth.Value : 0)

        if (context.DetailLevel == 1)
            var location = geometry as Point;
            var speed = feature.GetValue<double?>(CustomerTrackModel.GroundSpeedValuePropertyPath);
            if (location == null || azimuth == null || speed == null)

            var lineEnd = _geodesy.Extrapolate2D(location.Location, azimuth.Value, speed.Value * 60.0);
            if (lineEnd == null)

            var line = GeometryFactory.CreateLine(location.Reference, location.Location, lineEnd.Value, LineInterpolationType.Geodesic);
                .Stroke(isSelected ? LineStyleSelected : LineStyleRegular)

            // Show the call sign of the track
            var callSign = feature.GetValue<string>(CustomerTrackModel.CallSignPropertyPath);
            if (callSign != null)
                var pinStyle = PinStyle.NewBuilder().Build();
                    .TextStyle(isSelected ? _textStyleSelected : _textStyle)
                    .PointPositions(new List<RelativePosition> { GetLabelPosition(new Luciad.Cartesian.Azimuth(azimuth.Value + 180), 20) })

Visualizing geometries

You can visualize geometries with the FeatureCanvas::drawGeometry method. This method requires at least a Geometry and a stroke and/or fill style.

You create Feature styles through builder methods on one of the style classes. The different types of feature styles are:

  • Fill Style: describes the style of a surface, a fill color or specific textures for example.

  • Line Style: describes the style of a line, a line color or width for example.

Drawing icons and text

You can draw icons and text on the map with FeatureCanvas::drawIcon and FeatureCanvas::drawText respectively.

Adding labels to features

You can attach icon and text labels to features with the FeatureCanvas::drawLabel method. See related article on adding labels for more information.