FeatureLayer and FeaturePainter

You visualize vector features in a view/feature/FeatureLayer. The most important aspect of a FeatureLayer is that it must define a view/feature/FeaturePainter.

The feature painter determines how the layer visualizes the features. You use the feature painter to:

The most important method on the FeaturePainter is the paintBody method:

paintBody(geoCanvas, //The GeoCanvas to render on
          feature,   //The Feature that should be rendered
          shape,     //The Shape to render
          layer,     //The layer containing the feature
          map,       //The map containing the layer
          paintState //The paint state, indicating whether the Feature is selected or not
);

With this method, you have full control over how you want to render the Feature, and what styling you want to use. For example, a straightforward implementation could be:

const featurePainter = new FeaturePainter();
//Use a custom paintBody implementation
featurePainter.paintBody = function(geoCanvas, feature, shape, layer, map, paintState) {
  geoCanvas.drawShape(shape, {
    stroke: {
      color: "rgb(0,0,255)",
      width: 5
    },
    fill: {color: "rgba(0,0,255,0.5)"}
  });
};

A feature painter visualizes and styles features by drawing them on a view/style/GeoCanvas instance. The GeoCanvas instance is the first parameter of the painter’s draw methods. It performs vector feature drawing operations in a geographical coordinate reference, which is identified through the reference of the shape.

In this specific example, we:

  • Use the shape of the feature as the geometry to render

  • Specify a stroke and line color as styling

  • Use the same styling for all objects, even when they’re selected, because we’re ignoring the paint state.

More advanced examples are available in the rest of this tutorial.

FeaturePainter has more specialized sub-classes in the API, designed for certain use cases.

For example, to achieve the behavior of the preceding example, you use a BasicFeaturePainter.

Styling options

You can style a shape in several ways:

  • You can render the shape as its shape type, and make use of the shape styling options offered by view/style/ShapeStyle:

    • stroke style: determine the style of the strokes that form the polylines and the outlines of polygons. You can choose between simple stroke style options or more complex stroked line styles. For a simple stroke style, use view/style/LineStyle. It has options like the line color, line width and dashing. You can use view/style/complexstroke/ComplexStrokedLineStyle to achieve more complex stroked styles. It allows you to stroke a line with complex patterns composed of different shapes and text, through a view/style/complexstroke/PatternFactory.

    • line markers: add markers at the beginning and the end of a line.

    • fill style determine how to fill the polygons. You can configure a fill style as a fill color or as a fill pattern. Fill styles don’t apply to polylines.

    • paint order: decide the visualization order of shapes in a layer.

    • line type: decide how to interpret lines between consecutive points in the shape. For shapes defined in a geodetic coordinate system, the line type translates to geodetic lines, if you chose the SHORTEST_DISTANCE value, or rhumb lines if you chose the CONSTANT_BEARING value.

  • You can render the shape as an icon. An icon is an image like a JPEG or PNG file.

  • You can display a text string at a shape’s focus point.

For detailed information on how to apply these styling options, see the API reference documentation. The relevant classes and objects for styling are view/style/GeoCanvas, view/style/ShapeStyle, view/style/IconStyle, view/style/FillStyle, view/style/LineStyle and view/style/complexstroke/ComplexStrokedLineStyle.

Examples

Distinct styling for selected objects

In the following example, we use distinct styling for selected and non-selected objects. We check the `paintState`before deciding which styling to use.

In the example, zones are polygons drawn with a fill and stroke style. If a user selects an object, we want to use a distinct stroke color, stroke width and dashing pattern. Selected objects also have a fill pattern, on top of the background color.

Program: a FeaturePainter that styles polygons differently based on their selection status (from samples/selection/painters/AirspacePainter.ts)
export class AirspacePainter extends FeaturePainter {

  private readonly _selectedPattern: HTMLImageElement;
  private readonly _nameTemplate: string;

  constructor() {
    super();
    this._selectedPattern = new HatchedImageBuilder()
        .patterns([HatchedImageBuilder.Pattern.SLASH, HatchedImageBuilder.Pattern.BACKGROUND])
        .lineColor("rgba( 255, 255, 255, 0.5)")
        .lineWidth(4)
        .patternSize(20, 20)
        .backgroundColor("rgba(0 ,0 , 255, 0.5)")
        .build();
    this._nameTemplate = '<div style="color: rgb(250,255,250); font-size: 14px">$name</div>';
  }

  paintBody(geoCanvas: GeoCanvas, feature: Feature, shape: Shape, layer: Layer, map: Map,
            paintState: PaintState): void {
    const style: ShapeStyle = {
      stroke: {
        color: "rgb(0,0,255)",
        width: 5
      },
      fill: {
        color: "rgba(0,0,255,0.5)"
      }
    };

    const stroke = style.stroke as LineStyle;
    if (paintState.selected) {
      stroke.color = "rgb(255,99,71)";
      stroke.dash = [8, 2];
      style.fill!.image = this._selectedPattern;
    } else if (paintState.hovered) {
      style.fill!.color = "rgba(100,100,255,0.5)";
      stroke.color = "rgb(100,100,255)";
    }
    geoCanvas.drawShape(shape, style);
  };

  paintLabel(labelCanvas: LabelCanvas, feature: Feature, shape: Shape, layer: Layer, map: Map,
             paintState: PaintState): void {
    const name = this._nameTemplate.replace("$name", feature.properties.Name);
    if (paintState.selected) {
      labelCanvas.drawLabelInPath(name, shape, {});
    } else {
      labelCanvas.drawLabelInPath(name, shape, {});
    }
  };
}

Advanced example: flight plan with waypoints

This example shows a more complex painter. It draws a polyline that represents a flight track, as a wide yellow line. On top of this line, and each point of the track, it draws a waypoint icon.

When a user selects the track is selected, the line becomes red.

trackwaypoints
const style = {
  strokeWidth: 6,
  stroke: {
    color: "rgba(255,255,0,1)"
  }
};
const selectedStyle = {
  strokeWidth: 6,
  stroke: {
    color: "rgba(255,255,0,1)"
  }
};

const painter = new FeaturePainter();
painter.paintBody = function(geoCanvas, feature, shape, layer, map, paintState) {
  if (shape instanceof Polyline) {
    let i;
    // draw a line, style depends on whether the line is selected or not
    geoCanvas.drawShape(shape, paintState.selected ? selectedStyle : style);
    // and draw an icon on each point in the polyline
    i = shape.pointCount;
    while (i--) {
      geoCanvas.drawIcon(shape.getPoint(i), {
        url: "../../resources/img/waypoint.png",
        width: "32px",
        height: "32px",
        offsetX: -16,
        offsetY: -16
      });
    }
  }
};

For the complete list of supported drawing operations on the GeoCanvas, see the API reference.