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()
                                                  .model(geometryModel)
                                                  .editable(true)
                                                  .editCreateGeometryProvider(CustomerGeometryModel::createEditGeometryProvider())
                                                  .build();

map->getLayerList()->add(geometryLayer);

C#

var geometryModel = CustomerGeometryModel.Create();
var geometryLayer = FeatureLayer.NewBuilder()
    .Model(geometryModel)
    .Editable(true)
    .EditCreateGeometryProvider(CustomerGeometryModel.CreateEditGeometryProvider())
    .Build();
Map.LayerList.Add(geometryLayer);

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) {
    return;
  }

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

  canvas.drawIcon()
      .anchor(geometry)
      .draped(context.getDetailLevel() == 0 ? true : false)
      .icon(isSelected ? constants.iconSelected : constants.iconRegular)
      .orientation(Azimuth(azimuth.value_or(0)))
      .submit();

  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) {
      return;
    }

    // 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) {
      return;
    }

    auto line = GeometryFactory::createLine(location->getReference(), location->getLocation(), lineEnd.value(), LineInterpolationType::Geodesic);
    auto style = isSelected ? constants.lineStyleSelected : constants.lineStyleRegular;
    canvas.drawGeometry().geometry(line).stroke(style).draped(false).submit();

    // 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();
      canvas.drawLabel()
          .anchor(geometry)
          .text(*callSign)
          .textStyle(isSelected ? _textStyleSelected : _textStyle)
          .pointPositions({getLabelPosition(Azimuth(azimuth.value() + 180.0), 20.0)})
          .pinStyle(pinStyle)
          .submit();
    }
  }
}

C#

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

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

        canvas.DrawIcon()
            .Anchor(geometry)
            .Draped(context.DetailLevel == 0 ? true : false)
            .Icon(isSelected ? IconSelected : IconRegular)
            .Orientation(azimuth != null ? azimuth.Value : 0)
            .Submit();

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

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

            var line = GeometryFactory.CreateLine(location.Reference, location.Location, lineEnd.Value, LineInterpolationType.Geodesic);
            canvas.DrawGeometry()
                .Geometry(line)
                .Stroke(isSelected ? LineStyleSelected : LineStyleRegular)
                .Draped(false)
                .Submit();

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

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.