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 control measure symbols to 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 need to extend those standards to add your own symbology set.
How does it work?
There are two ways you could add a custom Control Measure symbol to your symbology. You could use the styling of an existing
symbol, but applied to a different geometry or you could create a completely custom symbol with its own styling and geometry.
Both of these can be achieved by creating a custom ALspStyler
which can submit default military TLspAPP6ASymbolStyle
or TLspMS2525bSymbolStyle
to custom geometries or submit a completely custom ALspStyle
.
Since your custom symbols would likely have a SIDC code which is not defined in a supported military standard, you would need
to define your own custom military domain object.
This is required because the standard TLcdEditableAPP6AObject
or TLcdEditableMS2525bObject
only allow creation of symbols with a SIDC defined in their respective symbology standards.
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 with
a military symbol styled according to the MIL-STD-2525 B specification, but with a custom geometry and another military object
with completely custom styling. We will represent these military objects with our own custom domain object called MyMilitaryObject
.
Create a custom domain object
We will create a simple MyMilitaryObject
class with three properties. A symbology code, to represent the SIDC, a title, which we will use as label for the object
and a ILcdShape
to represent the object’s geometry.
It is required to make you domain objects implement |
/**
* Simple custom domain object that contains a symbology code,
* a title and a {@link ILcdShape}
*/
private static class MyMilitaryObject implements ILcdBounded {
private final String fSymbologyCode;
private final String fTitle;
private final ILcdShape fShape;
public MyMilitaryObject(String aSymbologyCode, String aName, ILcdShape aShape) {
fSymbologyCode = aSymbologyCode;
fTitle = aName;
fShape = aShape;
}
public String getSymbologyCode() {
return fSymbologyCode;
}
public ILcdShape getShape() {
return fShape;
}
public String getTitle() {
return fTitle;
}
@Override
public ILcdBounds getBounds() {
return fShape.getBounds();
}
}
Create a custom ALspStyler
To implement the desired behavior it is necessary to create custom styler which extends from ALspStyler
. This styler will be called MySymbolStyler
.
For brevity we will make |
In our MySymbolStyler.style(Collection<?> aObject, ALspLabelStyleCollector, TLspContext)
method, we will loop over the aObjects
collection and style all instances of MyMilitaryObject
.
We check whether the military object’s symbology code is a standard MIL-STD-2525 B SIDC code or not, and perform either the
standard MIL-STD-2525 B styling or our custom styling in the methods styleStandardSymbol(MyMilitaryObject, ALspStyleCollector)
and styleCustomSymbol(MyMilitaryObject aSymbol, ALspStyleCollector aStyleCollector)
respectively.
@Override
public void style(Collection<?> aObjects,
ALspStyleCollector aStyleCollector,
TLspContext aContext) {
// iterate over the objects,
// determine if the symbol's symbology code is a standard SIDC or not
// style standard symbols according to the MIL-STD-2525 B specification
// and custom symbols according to our custom styling
for (Object object : aObjects) {
if (object instanceof MyMilitaryObject) {
MyMilitaryObject symbol = (MyMilitaryObject) object;
if (isStandardSymbol(symbol)) {
styleStandardSymbol(symbol, aStyleCollector);
} else {
styleCustomSymbol(symbol, aStyleCollector);
}
}
}
}
To check whether the symbology code is a valid MIL-STD-2525 B SIDC, we simply try to create a TLcdEditableMS2525bObject
with the military object’s symbology code.
If the constructor of TLcdEditableMS2525bObject
throws an IllegalArgumentException
it means the given symbology code is not valid for the given ELcdMS2525Standard
.
private boolean isStandardSymbol(MyMilitaryObject aSymbol) {
try {
// try to create a new TLcdEditableMS2525bObject with the symbols symbology code
new TLcdEditableMS2525bObject(aSymbol.getSymbologyCode(),
ELcdMS2525Standard.MIL_STD_2525d);
return true;
} catch (IllegalArgumentException ex) {
// The constructor of TLcdEditableMS2525bObject will throw an IllegalArgumentException
// if the symbol code is not part of the MIL-STD-25258 B specification
return false;
}
}
The styleStandardSymbol(MyMilitaryObject, ALspStyleCollector)
create and submits a TLspMS2525bSymbolStyle
, which will add the default MIL-STD-2525 B styling.
If the ALspStyleCollector
is a ALspLabelStyleCollector
, this will perform the default MIL-STD-2525 B styling.
When we pass a non-standard geometry to the ALspLabelStyleCollector.geometry method, this will cause the MIL-STD-2525 B styling to be applied to that (non-standard) geometry instead of the standard
one.
If you want the styling to be applied to a standard geometry, you need to pass an instance of TLcdEditableMS2525bObject
to both the ALspLabelStyleCollector.object and ALspLabelStyleCollector.geometry methods.
private void styleStandardSymbol(MyMilitaryObject aSymbol,
ALspStyleCollector aStyleCollector) {
// Create a TLcdEditableMS2525bObject based on the military symbol's symbology Code
TLcdEditableMS2525bObject standardSymbol
= new TLcdEditableMS2525bObject(aSymbol.getSymbologyCode(),
ELcdMS2525Standard.MIL_STD_2525d);
// set the unique designation modifier
standardSymbol.putTextModifier(ILcdMS2525bCoded.sUniqueDesignation, aSymbol.getTitle());
// create a default ILcdMS2525bStyle and adjust some style settings
TLcdDefaultMS2525bStyle style = TLcdDefaultMS2525bStyle.getNewInstance();
style.setLabelFont(Font.decode("Dialog-12"));
style.setLabelColor(Color.WHITE);
style.setLabelHaloEnabled(true);
style.setLabelHaloColor(Color.BLACK);
style.setAffiliationColorEnabled(true);
// then create default symbology ALspStyle for the given military symbol and ILcdMS2525bStyle
TLspMS2525bSymbolStyle symbologyStyle
= TLspMS2525bSymbolStyle.newBuilder()
.ms2525bCoded(standardSymbol)
.ms2525bStyle(style)
.build();
// finally submit the style
aStyleCollector.object(aSymbol)
.geometry(aSymbol.getShape())
.style(symbologyStyle)
.submit();
}
The styleSCustomSymbol(MyMilitaryObject, ALspStyleCollector)
method only does a check to see whether or not the ALspStyleCollector
is an instance of ALspLabelStyleCollector
.
If it is, the styling is delegated to styleCustomSymbolLabel(MyMilitaryObject, ALspLabelStyleCollector)
, which performs the label styling, it is not the styling is delegated to
styleCustomSymbolBody(MyMilitaryObject, ALspStyleCollector)
, which performs the symbol styling.
private void styleCustomSymbol(MyMilitaryObject aSymbol,
ALspStyleCollector aStyleCollector) {
if (aStyleCollector instanceof ALspLabelStyleCollector) {
// If the style collector is a ALspLabelStyleCollector,
// we need to perform the custom label styling
styleCustomSymbolLabel(aSymbol, (ALspLabelStyleCollector) aStyleCollector);
} else {
// otherwise we need to perform the symbol styling
styleCustomSymbolBody(aSymbol, aStyleCollector);
}
}
The styleCustomSymbolLabel(MyMilitaryObject, ALspLabelStyleCollector)
and styleCustomSymbolBody(MyMilitaryObject, ALspStyleCollector)
methods just perform regular Lighstpeed styling and no longer use any of the military symbology apis. styleCustomSymbolLabel(MyMilitaryObject, ALspLabelStyleCollector)
creates a basic FixedTextProviderStyle with the military symbol’s title and adds some text style to it.
styleCustomSymbolLabel(MyMilitaryObject, ALspLabelStyleCollector)
creates a complex stroke style which it applies to the military symbol’s geometry.
More information about Lightpeed labeling can be found in the vector labeling tutorial. More information about complex strokes can be found in the complex stroking guide.
Note that we style all custom symbols the same way in our |
private void styleCustomSymbolLabel(MyMilitaryObject aSymbol,
ALspLabelStyleCollector aStyleCollector) {
// create a label context style that displays the military symbol's title
ALspStyle labelContentStyle = FixedTextProviderStyle.newBuilder()
.text(aSymbol.getTitle())
.build();
// create a text style to adjust some font settings
ALspStyle textStyle = TLspTextStyle.newBuilder()
.font(Font.decode("Dialog-12"))
.textColor(Color.WHITE)
.haloColor(Color.BLACK)
.build();
// submit the styles
aStyleCollector.object(aSymbol)
.geometry(aSymbol.getShape())
.styles(labelContentStyle, textStyle)
// use TLspOnPathLabelingAlgorithm to display the label on the symbol body
.algorithm(new TLspOnPathLabelingAlgorithm())
.submit();
}
private void styleCustomSymbolBody(MyMilitaryObject aSymbol,
ALspStyleCollector aStyleCollector) {
// Create a complex stroke style
TLspComplexStrokedLineStyle stokeStyle = getDashDotArrowStyle(Color.GREEN);
// submit the style
aStyleCollector.object(aSymbol)
.geometry(aSymbol.getShape())
.style(stokeStyle)
.submit();
}
// creates a complex stroke style with a dash-dot-arrow pattern: -.>-.>-.>
private TLspComplexStrokedLineStyle getDashDotArrowStyle(Color aColor) {
ALspComplexStroke arrow = ALspComplexStroke.arrow()
.size(12)
.lineColor(aColor)
.lineWidth(2)
.build();
ALspComplexStroke dashdot = ALspComplexStroke.append(
ALspComplexStroke.line()
.length(10)
.lineColor(aColor)
.lineWidth(2)
.build(),
ALspComplexStroke.gap(3),
ALspComplexStroke.filledArc()
.length(5)
.minorRadius(2.5)
.fillColor(aColor)
.build(),
ALspComplexStroke.gap(3)
);
ALspComplexStroke plain = ALspComplexStroke.line()
.lengthRelative(1)
.lineColor(aColor)
.lineWidth(2)
.build();
return TLspComplexStrokedLineStyle.newBuilder()
.regular(dashdot)
.fallback(plain)
.decoration(0.25, arrow)
.decoration(0.5, arrow)
.decoration(0.75, arrow)
.decoration(1.0, arrow)
.build();
}
Configure the military layer
With our fancy MySymbolStyler
completed, we can start the configuration of our military layer.
We will configure our layer using the TLcdMS2525bLayerBuilder
.
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 MySymbolStyler
as bodyStyler
and labelStyler
.
protected static ILspLayer createCustomLayer() {
// Create a Military object with a standard MIL-STD 2525d SIDC code,
// but with an Ellipse geometry (as opposed to a polygon geometry, as is specified in the standard)
MyMilitaryObject standardSymbol
= new MyMilitaryObject("10062500001512030000",
"Custom Geometry",
createEllipse());
// Create a Military object with a custom SIDC code, which will have custom styling
MyMilitaryObject customSymbol
= new MyMilitaryObject("MY_CUSTOM_SYMBOL",
"Custom Symbol",
createPolyline());
// create a vector model and add the created symbols to it
TLcdVectorModel ms2525Model = new TLcdVectorModel(new TLcdGeodeticReference(),
new TLcdModelDescriptor());
ms2525Model.addElement(standardSymbol, ILcdModel.NO_EVENT);
ms2525Model.addElement(customSymbol, ILcdModel.NO_EVENT);
// create a new military layer based of that model,
// configure a custom body styler that will style standard SIDC's according to the specification
// and custom SIDC's according to our own custom styling
return TLspMS2525bLayerBuilder.newBuilder()
.model(ms2525Model)
.bodyStyler(TLspPaintState.REGULAR, new MySymbolStyler())
.labelStyler(TLspPaintState.REGULAR, new MySymbolStyler())
.build();
}
private static ILcdEllipse createEllipse() {
TLcdLonLatEllipse ellipse = new TLcdLonLatEllipse();
ellipse.move2D(5.5, 52);
ellipse.setA(80000);
ellipse.setB(50000);
return ellipse;
}
private static ILcdPolyline createPolyline() {
TLcdLonLatPolyline polyline = new TLcdLonLatPolyline();
polyline.insert2DPoint(0, 4, 50.5);
polyline.insert2DPoint(1, 5, 51);
polyline.insert2DPoint(2, 6, 50.5);
polyline.insert2DPoint(3, 7, 51);
return polyline;
}
Visualizing the military symbol
Finally we can visualize our decorated symbol by creating a Lightspeed view with our military layer.
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
// create the view
ILspAWTView view = TLspViewBuilder.newBuilder()
.buildAWTView();
// create the custom decoration layer and add it to the view
ILspLayer customLayer = createCustomLayer();
view.addLayer(customLayer);
// display the view in a frame
JFrame frame = new JFrame("Custom Control Measure Symbols");
frame.add(view.getHostComponent(), BorderLayout.CENTER);
frame.setSize(350, 350);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// fit on the layer
FitUtil.fitOnLayers(frame, view, false, customLayer);
});
}