Introduction

To create, visualize and edit military symbols that comply with military symbology standards, LuciadRIA offers the Defense Symbology component.

The Defense Symbology functionality comes in a @luciad/ria-milsym NPM package.

The LuciadRIA Defense Symbology component provides support for several military symbology standards, including MIL-STD 2525b, MIL-STD 2525c, MIL-STD 2525d, APP-6A, APP-6B, APP-6C and APP-6D. These standards define a symbol set that is used to plan and execute military operations in support of Command, Control, Communications, Computers, and Intelligence (C4I) functions.

milsym
Figure 1. The Military Symbols Sample demonstrates a few of the available military symbols

The MIL-STD 2525 standards were developed by the United States Department of Defense (DOD), while the APP-6 standards were developed by the North Atlantic Treaty Organization (NATO). As the APP-6 standards are partially derived from MIL-STD 2525a, they share many symbols.

The Defense Symbology component allows a LuciadRIA user to create MIL-STD 2525b, MIL-STD 2525c, MIL-STD 2525d, APP-6A, APP-6B, APP-6C and APP-6D symbols, and visualize them on a map.

Using the Symbology API

A Symbology is a set of named symbols and tactical graphics that are visualized on a map by means of icons and shapes, like polygons and polylines. In LuciadRIA, a Symbology corresponds to one of the symbology standards APP6 and MS2525. This page explains how to use the LuciadRIA Symbology API to:

  • Import a symbology for use. Before any symbol can be created, visualized or manipulated, you must import a Symbology into your application. Symbologies provide the metadata to work with individual symbols or groups of symbols. See Importing a symbology for more information.

  • Visualize a domain object with a military symbol, using a symbology.military.MilitarySymbologyPainter. See Visualizing a symbol for more information.

  • Create new symbols, as discussed in Creating a new Symbol.

  • Manipulate properties of a symbol, as discussed in Manipulating symbol properties.

Importing a symbology

To import a symbology, just import the symbology/military/<symbologyname> module. This returns a singleton instance of the symbology with <symbologyname>.

Program: Importing a military symbology
import {MilitarySymbologyPainter} from "@luciad/ria-milsym/symbology/military/MilitarySymbologyPainter.js";
import {MIL_STD_2525b} from "@luciad/ria-milsym/symbology/military/MIL_STD_2525b.js";

The available symbologies correspond to the following standards.

  • MIL_STD_2525b

  • MIL_STD_2525c

  • MIL_STD_2525d

  • APP_6A

  • APP_6B

  • APP_6C

  • APP_6D

All symbology sets are represented as a hierarchical tree structure consisting of symbology nodes.

Note that it’s also possible to load a symbology using the symbology/SymbologyProvider module. This module can act as an AMD plugin that takes the name of the required Symbology as a parameter. The result is a symbology instance that’s passed into the module factory function. An alternative is to use the SymbologyProvider.getSymbology() method. This method returns a promise for the symbology. Both methods are deprecated since LuciadRIA 2018.1, however, and should no longer be used.

Using a hierarchical symbology

The symbology instance is an instance of type symbology.HierarchicalSymbology. This is because all military symbology standards have a hierarchical tree structure, in which related symbols with similar properties are grouped under the same parent. Each symbol or group of symbols in the hierarchy are instances of type symbology.SymbologyNode.

The main purpose of the HierarchicalSymbology instance is that it provides access to all the symbols defined in the standard.

With it, you can:

  • Enumerate over all symbols, starting from the root of the symbology.

  • Use a symbol’s identifier,which is the code of the symbol, to retrieve a particular symbol.

Importing a symbology is a prerequisite for visualizing domain objects with military symbols. See Visualizing a symbol for more information.

Using a symbology node

Each individual symbol is an instance of symbology.SymbologyNode.

Such a SymbologyNode object has two main purposes:

  • Provide metadata, such as the symbol’s name and unique identifier

  • Create a shape with a geometry that corresponds to the symbol’s requirements. For example, a Point would be the corresponding shape for a tactical icon, a helipad for example, while a PolyLine would be the corresponding shape for a symbol indicating a barrier, a fence for example.

Creating a new Symbol contains more details about how this functionality can be used to create new features.

Visualizing a symbol

To visualize a military symbol, create a MilitarySymbologyPainter.

To visualize a symbol, the API does not place any requirements on the structure of the domain objects in the model. When constructing a MilitarySymbologyPainter, you only need to supply a set of functions that map the domain object to a meaningful value in the context of the symbol. Visualizing military symbols in LuciadRIA is thus solely the responsibility of the view components in an application.

To construct a symbol painter, you need to supply two mandatory parameters.

  • A symbology. This is a reference to a symbology standard. How to import one is explained in Importing a symbology.

  • A code function. This function must map a feature, the domain object, to the SIDC military symbol code. As a shorthand, you can also use a string which refers to the property of the feature that contains the code.

Other parameters are optional and revert to default behavior if they haven’t been defined. See the API documentation for a list of optional modifiers, and their possible values.

Program: Minimum code needed to create a MilitarySymbologyPainter.
  const symbologyPainter = new MilitarySymbologyPainter(MIL_STD_2525b, {
    //this is shorthand for a function that returns feature.properties.code
    codeFunction: "code"
  })

Customizing the styling of the symbol

In practice, you may need to modify the styling of the symbol according to some properties of the military symbology specification, your own preferences, or a combination of both.

For example, Program: Setting up a symbology painter to paint bodies and labels of military icons and tactical graphics. illustrates how to paint military icon symbols and tactical graphics and apply custom symbol styling, using the MilitarySymbologyPainter and its constructor parameters.

Program: Setting up a symbology painter to paint bodies and labels of military icons and tactical graphics. (from samples/symbology/symbology/main.tsx)
  //The military standard this painter should paint.
  return new MilitarySymbologyPainter(symbology, {
    codeFunction: function(feature: Feature): string {
      //We return the SIDC of the military symbol
      return feature.properties.code;
    },
    modifiers: function(feature: Feature): any {
      //We return the modifiers in the properties of our feature.
      //These include both text modifiers and graphical modifiers, as defined
      //in the MS2525 and APP6 specifications.
      return feature.properties.modifiers;
    },
    style: function(feature: Feature, paintState: PaintState): MilSymStyle {
      //We return our style. Note that it is also possible to have feature-specific
      //styling if you choose to use the feature parameter.
      return {
        selectionColor: "#FF9900",
        // The body will not be drawn if there is a feature property "body" that is set to false.
        // If so, a skeleton (tactical graphic without details and decorations) will be drawn.
        body: feature.properties.body ?? true,
        // The skeleton will be drawn if there is no body, or, as a selection style on top of the body
        // on WebGL maps (this allows immediate response upon selection).
        skeleton: !(feature.properties.body ?? true) || (paintState.selected),
        // A selection rectangle will be drawn for icons when selected.
        rectangle: paintState.selected,
        affiliationColor: {
          Friend: feature.properties.friend || FRIEND_COLOR,
          Hostile: HOSTILE_COLOR
        },
        lineWidth: feature.properties.lineWidth,
        iconSize: feature.properties.iconSize,
        cornerSmoothness: feature.properties.cornerSmoothness,
        haloColor: (paintState.hovered && !paintState.selected) ? "rgba(255,255,255,0.7)"
                                                                : feature.properties.haloColor,
        haloWidth: (paintState.hovered && !paintState.selected) ? 2 : feature.properties.haloWidth,
      };
    },
    width: function(feature: Feature): number {
      //We return the width we use for arrow-type tactical graphics.
      return feature.properties.width;
    }
  });

For simple properties, it’s possible to use strings as a parameter instead of functions. This is a shorthand notation for properties that exist within the feature properties. The example below uses short-hand strings instead of functions wherever possible to achieve the same result as Program: Setting up a symbology painter to paint bodies and labels of military icons and tactical graphics..

[[shorthand_symbology_painter, Program: Setting up a symbology painter using the shorthand notation for retrieval of simple properties]] .Program: Setting up a symbology painter using the shorthand notation for retrieval of simple properties

  const symbologyPainter = new MilitarySymbologyPainter(
      //The military standard this painter should paint.
      MIL_STD_2525b, {
        //this is shorthand for a function that returns feature.properties.code
        codeFunction: "code",
        //this is shorthand for a function that returns feature.properties.modifiers
        modifiers: "modifiers",
        style: function(feature) {
          return {
            selectionColor: "#FF9900",
            affiliationColors: {
              "Friend": "rgb(128, 224, 255)",
              "Hostile": "#FF8080"
            },
            lineWidth: 2,
            iconSize: 64
          };
        },
        //this is shorthand for a function that returns feature.properties.width
        width: "width"
      }
  );

Customizing the styling of a label

symbollabel
Figure 2. Symbol labels in the Military Symbols Sample

Since LuciadRIA allows you to use HTML and CSS to style labels, you can modify the labels of military symbols by overriding the CSS styling properties of the lcdSymbologyLabel CSS class.

An example is provided in the sample below.

Program: Custom CSS style for the labels of icon symbols.
/*Configuring the labels of the symbology*/
.luciad .lcdSymbologyLabel {
  font-family: Arial, monospace;
  font-weight: normal;
  color: white;
  font-size: 10px;
  text-shadow: 0 0 5px rgba(0, 0, 0, 1)
}

Movement direction arrows

LuciadRIA shows movement direction arrows for moving symbols. Ground units have an arrow starting from a vertical stem.

movement arrows
Figure 3. Movement direction arrows for ground units (right) and other units (left)

The following modifiers affect the arrows:

  • movementDirection: a number indicating the heading of the object. Must be an azimuth: degrees, clock-wise, 0 is north.

  • speedLabel: a string expressing the movement speed, for example 17 km/h or 11 kn. Affects the length of the arrow. Only relevant for APP-6C and MIL-STD-2525C.

The length of the arrow is determined by the existing style property iconSize in combination with the speedLabel modifier if available. The line-width of the arrow is determined by the existing style properties lineWidth. In 3D, ground units have no stem.

Supported symbols

LuciadRIA can visualize all symbols in the listed symbologies.

Creating a new Symbol

You can use a CreateController to create a new military icon or tactical graphic, just like you would ordinarily do to create shapes in LuciadRIA.

The SymbologyNode.createTemplates() method creates a list of Shape objects that match the nature of the military symbol. Alternatively, if you want to provide your own custom geometry for the tactical graphics, you can use the ShapeFactory.createShape() method.

Program: Setting up the method CreateController.onCreateNewObject(map,layer). illustrates the recommended way to create a new symbol. Create a new shape, and then create a new Feature that includes the shape and the desired properties.

Program: Setting up the method CreateController.onCreateNewObject(map,layer). (from samples/symbology/components/CreationPanel.tsx)
// SampleCreateController is a CreateController that is re-used throughout RIA samples.
// It adds two-step Cancellation on Escape keypresses and creates undoables for created shapes.
class SymbologyCreateController extends SampleCreateController {
  private readonly _symbology: HierarchicalSymbology;
  private readonly _code: string;

  constructor(symbology: HierarchicalSymbology, code: string) {
    super(ShapeType.POINT);
    this._symbology = symbology;
    this._code = code;
  }

  onCreateNewObject(map: RIAMap, layer: Layer) {
    return new Feature(
        this._symbology
            .getSymbologyNode(this._code)!
            .createTemplates(layer.model!.reference, 0, 0, 1.6)?.[0] ?? null,
        {
          code: this._code,
          symbology: this._symbology,
        }
    );
  }

  onObjectCreated(map: RIAMap, layer: FeatureLayer, object: Feature) {
    addCreateFeatureUndoable(map, layer.model, object);
    return Promise.resolve(layer.workingSet.add(object)).then(() => {
      //select the created object (standard action is adding without selecting)
      map.selectObjects([
        {
          layer,
          objects: [object],
        },
      ]);
    });
  }
}

The Feature property should at least contain a reference to the military code used to create the shape. This is necessary because the MilitarySymbologyPainter needs to retrieve the code to visualize the object properly. More details about using the MilitarySymbologyPainter can be found in Visualizing a symbol.

For more information about creating and working with a SymbologyNode, refer to Using a symbology node.

Creating a preview image of a Symbol

You can use the MilitarySymbologyPainter.createSymbolImage(feature, size) to create a preview HTLMImageElement of a symbol. Figure 1, “The Military Symbols Sample demonstrates a few of the available military symbols” shows an example of such a symbol preview.

Program: getting a preview image CreationPanel.getInstructions(…​). illustrates the recommended way to create a symbol preview. Create a template shape, and then create a new Feature that includes the shape and the desired properties. With that Feature, ask the Painter to generate a preview.

Program: getting a preview image CreationPanel.getInstructions(…​). (from samples/symbology/components/CreationPanel.tsx)
  const layer: FeatureLayer = layerMap.get(symbology) as FeatureLayer;
  const painter: MilitarySymbologyClusteringPainter = (layer.painter as MilitarySymbologyClusteringPainter);
  const shape = symbologyNode.createTemplates(layer.model!.reference, 0, 0, 0.5)?.[0] ?? null;
  const properties = {
    code: symbologyNode.code,
    symbology: symbology,
    modifiers: {}
  }
  const feature = new Feature(shape, properties, 1);
  const size: number = minPointCount === 0 ? 100 : 150;
  return painter.createSymbolImage(feature, size)
      .then(function(previewImage: HTMLImageElement | null) {

Manipulating symbol properties

In the APP6 and MS2525 standards, the visualization of a symbol depends on the type of the symbol, and on a variety of symbol attributes. For example, a symbol can have an affiliation such as "Friend", "Neutral", or "Hostile", or it can show the status of an action. An action can have a "Planned" or "Anticipated" status, for example.

The military symbology standard defines which modifiers are allowed for each symbol. The section Using a hierarchical symbology explains how you can import a symbology standard, and retrieve particular symbols for it. These components do not allow you to edit any properties, however.

To modify the properties of a symbol, LuciadRIA offers the symbology.military.MilitarySymbol component. This object allows you to manipulate the attributes of a symbol, and read out the code of that symbol. Program: Changing the properties of a symbol and retrieving the associated code. shows how you can create a new MilitarySymbol, modify its properties and read out the correct symbol code.

Program: Changing the properties of a symbol and retrieving the associated code. (from samples/allsymbols/main.tsx)
      const symbol = new MilitarySymbol(symbology, child.code);
      for (const modifier in symbol) {
        if (symbol.hasOwnProperty(modifier)) {
          const possibleValues = symbol.possibleValues(modifier);
          if (possibleValues !== null) {
            //using a randomised value for a modifier with possible values
            const index = random.nextInt(possibleValues.length);
            (symbol as any)[modifier] = possibleValues[index]!;
          } else {
            //using a default value for a modifier that has no defined possible values
            if (MODIFIER_DEFAULTS.hasOwnProperty(modifier)) {
              (symbol as any)[modifier] = (MODIFIER_DEFAULTS as Record<string, string>)[modifier];
            }
          }
        }
      }

Symbol icons from the Symbology Service

The Symbology Service is a web service that serves MS2525/APP6 icons with specified styling information. See the LuciadFusion Defense Symbology component documentation for more details on setting up and configuring the symbology service.

LuciadFusion still makes this service available, but LuciadRIA no longer needs it, nor uses it to show Military Symbology icons. LuciadRIA now generates the icons on the client.