You are displaying the symbols defined in Military Symbology standards MIL-STD 2525 or APP-6 in your LuciadLightspeed application. Now, to fulfill specific requirements of your project, you need to add custom decorations the standard symbols.

Why do it?

LuciadLightspeed provides military symbols that comply with the MIL-STD 2525B/C and APP-6A/B/C standards, but you may want to add decorations to the standards symbols to provide additional information to the users.

How does it work?

A military symbol is either a TLcdEditableAPP6AObject or TLcdEditableMS2525bObject instance. The default military symbology styler will style these symbols are styled by either submitting aa TLspAPP6ASymbolStyler or a TLspMS2525bSymbolStyler respectively.

You create configure a custom ALspLabelStyler instead of the defautl military symbology styler on a military layer. This styler can submit both the default TLspAPP6ASymbolStyle or TLspMS2525bSymbolStyle, which will apply the default military standard label styling. Your custom styler can also submit additional ALspStyles to add your custom decorations.

We recommend using a custom ALspLabelStyler to add decorations to military symbols, because the ALspLabelStyleCollector allows to configure the placement of your decoration by specifying a location manually or by using a ILspLabelingAlgorithm. This makes it easy to set your decoration in a position that does not overlap with the military symbol. Customize a military icon symbol by using a custom body ALspStyler is explained in the custom symbol icons how to article.

How to do it?

To illustrate how to add custom decorations to military symbols, we are going to show how to configure a military layer styled according to the MIL-STD-2525 B specification where military symbols get an additional country flag decoration. We will do this by configuring the layer with a custom ALspLabelStyler. This styler will submit a default MIL-STD-2525 B label style and an additional country flag icon style based on the military symbol’s country code, contained in the symbol’s SIDC code.

LuciadLightspeed allows you to add more than just one icon decorations to a symbol if you need to.

Create a custom ALspLabelStyler

The first step of implementing this behavior is creating custom styler which extends from ALspLabelStyler. This styler will be affectionately called MyLabelStyler. In our MyLabelStyler.style(Collection<?> aObject, ALspLabelStyleCollector, TLspContext) method, we will loop over the aObjects collection and style all instances of ILcdMS2525bCoded.

We need to be able to cast the objects to the ILcdMS2525bCoded interface in order to access the military symbol’s SIDC code trough the ILcdMS2525bCoded.getMS2525Code() method. Further styling of the symbols will be delegated to the styleMilitarySymbol(ILcdMS2525bCoded, ALspLabelStyleCollector) method.

Program: MyLabelStyler’s style() method
    @Override
    public void style(Collection<?> aObjects,
                      ALspLabelStyleCollector aStyleCollector,
                      TLspContext aContext) {
      // iterate over the objects and style all ILcdMS2525bCoded instances
      for (Object object : aObjects) {
        if (object instanceof ILcdMS2525bCoded) {
          ILcdMS2525bCoded symbol = (ILcdMS2525bCoded) object;
          styleSymbol(symbol, aStyleCollector);
        }
      }
    }

The styleMilitarySymbol(ILcdMS2525bCoded, ALspLabelStyleCollector) method submits at most two styles.

It will first create and submit a TLspMS2525bSymbolStyle, which will add the default MIL-STD-2525 B label styling, with slightly adjusted font settings.

Additionally the military symbol’s country code will be extracted from the SIDC code. Using this country code we will create a TLcdImageIcon with an image of the flag of the country code’s country. A TLspIconStyle containing a the created TLcdImageIcon will be submitted at an offset, so it does not overlap with our military symbol. If the country has not been configured on the military symbol, no additional style will be submitted.

The creation of the icon style is delegated to the createCountryFlagStyle(String) method.

Program: MyLabelStyler’s styleMilitarySymbol() method
    private void styleSymbol(ILcdMS2525bCoded aSymbol,
                             ALspLabelStyleCollector aStyleCollector) {
      // 1) Create and submit default symbology label styles
      // first create a default ILcdMS2525bStyle
      TLcdDefaultMS2525bStyle style = TLcdDefaultMS2525bStyle.getNewInstance();
      // and adjust some label styling options
      style.setLabelFont(Font.decode("Dialog-12"));
      style.setLabelColor(Color.WHITE);
      style.setLabelHaloEnabled(true);
      style.setLabelHaloColor(Color.BLACK);

      // then create default symbology ALspStyle
      // for the given military symbol and ILcdMS2525bStyle
      TLspMS2525bSymbolStyle symbologyStyle = TLspMS2525bSymbolStyle.newBuilder()
                                                                    .ms2525bCoded(aSymbol)
                                                                    .ms2525bStyle(style)
                                                                    .build();

      // finally submit the style
      aStyleCollector.object(aSymbol)
                     .style(symbologyStyle)
                     .submit();

      // 2) Create and submit an icon style from the symbol's country code
      // first extract the country code from the symbol's SIDC
      String symbolCode = aSymbol.getMS2525Code();
      String countryCode = symbolCode.substring(12, 14);

      // then try to create an icon style from that country code
      Optional<TLspIconStyle> flagStyleOptional = createCountryFlagStyle(countryCode);

      // submit the icon flag style if a flag icon exists for the symbol's country code
      flagStyleOptional.ifPresent(flagStyle -> {
        aStyleCollector.styles(flagStyle)
                       .object(aSymbol)
                       .label("FLAG_LABEL")
                       .locations(20, TLspLabelLocationProvider.Location.NORTH_EAST)
                       .submit();
      });
    }

The implementation of createCountryFlagStyle(String) method is very straightforward. We need to add the resource folder resources/countryFlags, which contains all flag images, to our classpath which contains all flag images. These images have the same name as their corresponding country code (as defined in the MIL-STD-2525 B specification).

We use png images such as this one to represent the flag icons:

tw
Figure 1. Decoration icon: tw.png

In createCountryFlagStyle(String) we use the ImageIO class to read these image files, which we wrap in a TLcdImageIcon. We then create a TLspIconStyle with this TLcdImageIcon and some scaling applied to it.

If there is no image for the given country code, or any other IOException occurs, we simply return an empty Optional.

Program: MyLabelStyler’s createCountryFlagStyle(String) method
    private Optional<TLspIconStyle> createCountryFlagStyle(String aCountryCode) {
      String sourceName = "images/countryflags/" + aCountryCode.toLowerCase() + ".png";

      try {
        // try to read the flag icon image file
        // this is an expensive operation, you should consider caching the results
        // in a real application
        // we omitted this step in this tutorial for simplicity
        BufferedImage image = ImageIO.read(
            fInputStreamFactory.createInputStream(sourceName)
        );

        // create a TLcdImageIcon based of the image
        ILcdIcon imageIcon = new TLcdImageIcon(image);

        // create an icon style based off the image icon
        TLspIconStyle iconStyle = TLspIconStyle.newBuilder()
                                               .icon(imageIcon)
                                               .elevationMode(ILspWorldElevationStyle
                                                                  .ElevationMode
                                                                  .ABOVE_TERRAIN)
                                               .scale(0.5)
                                               .build();

        return Optional.of(iconStyle);
      } catch (IOException ignored) {
        // no flag icon exists for given country code, just ignore and an empty Optional
        // no flag icon will be displayed for this symbol
        return Optional.empty();
      }
    }

Configure the military layer

With our fancy MyLabelStyler completed, we can start the configuration of our military layer. We will configure our layer using the TLspMS2525bLayerBuilder. In this example we will create a simple model containing a single TLcdEditableMS2525bObject. We will build a layer using this model and instance of our MyLabelStyler as labelStyler.

Program: Configuration of the military layer
  protected static ILspLayer createCustomDecorationLayer() {
    // Create a MIL-STD-2525B military symbol
    // This creates a friendly infantry unit symbol, as you can derive from the SIDC code
    TLcdEditableMS2525bObject symbol
        = new TLcdEditableMS2525bObject("SFGPUCI--------", ELcdMS2525Standard.MIL_STD_2525b);
    // set the symbol's country
    symbol.setCountry("Republic of China Taiwan");
    // set some other text modifiers
    symbol.putTextModifier(ILcdMS2525bCoded.sUniqueDesignation, "1st Infantry Battalion");
    symbol.putTextModifier(ILcdMS2525bCoded.sAdditionalInformation, "Custom Decorations");

    // create a vector model and add the created symbol to it
    TLcdVectorModel ms2525Model = new TLcdVectorModel(new TLcdGeodeticReference(),
                                                      new TLcdModelDescriptor());
    ms2525Model.addElement(symbol, ILcdModel.NO_EVENT);

    // create a new military layer based of that model,
    // configure a label styler that will add country flag icon decorations as labels
    return TLspMS2525bLayerBuilder.newBuilder()
                                  .model(ms2525Model)
                                  .labelStyler(TLspPaintState.REGULAR,
                                               new SymbolDecorationsSnippets.MyLabelStyler())
                                  .build();
  }

Visualizing the military symbol

Finally we can visualize our decorated symbol by creating a Lightspeed view with our military layer.

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

      // add the custom decoration layer
      view.addLayer(createCustomDecorationLayer());

      // display the view in a frame
      JFrame frame = new JFrame("Custom Symbol Decorations");
      frame.add(view.getHostComponent(), BorderLayout.CENTER);
      frame.setSize(350, 350);
      frame.setVisible(true);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    });
  }
custom decorations
Figure 2. Decorated symbol visualized