A comprehensive Java library for rendering military symbols according to MIL-STD-2525 standards. This library has been used in US Army Mission Command software for years and supports 2525D, 2525E, and potentially future versions.
- About
- Understanding MIL-STD-2525
- Symbol ID Structure
- Quick Start
- Creating Single-Point Symbols (Icons)
- Creating Multi-Point Symbols (Tactical Graphics)
- Using Modifiers
- Symbol Examples
- Build Instructions
- Documentation
- Ports
mil-sym-java is a well-worn set of Java libraries that have been used in US Army Mission Command software for years. In November 2013, Mission Command was given approval to release and maintain these libraries as public open source. Eventually work on the 2525C SEC Renderer ended and the project was retired.
This is a continuation of that effort and this library aims to support 2525D, 2525E and potentially more future versions.
Renderer: This is the component that can be used in Java applications to generate the entire MIL-STD-2525 symbol for both icon-based symbols and geometric symbols such as tactical graphics. The Renderer relies on the jar files generated by Core.
- jsvg 1.6.1 using MIT License
- svgSalamander 1.1.3 using LGPL or BSD (used for pre-processing files that are used in the renderer. Not used for rendering)
- Geodesy 1.1.3 using Apache 2.0 License
MIL-STD-2525 is a United States Department of Defense (DoD) interface standard that defines a comprehensive system for visualizing military symbology. It provides a standardized way to represent military units, equipment, installations, activities, and tactical graphics on maps and displays across all military services and NATO forces.
-
Universal Recognition: Ensures that military symbols are universally recognizable across all branches of service, allied nations, and military systems.
-
Information Encoding: Symbols encode critical information including:
- Unit/equipment type (aircraft, ground unit, ship, etc.)
- Affiliation (friendly, hostile, neutral, unknown)
- Status (present, planned, destroyed, etc.)
- Command level/echelon (team, platoon, company, battalion, etc.)
- Mobility (wheeled, tracked, towed, etc.)
-
Visual Hierarchy: Uses colors, shapes, and modifiers to quickly convey complex military information:
- Colors: Blue/Cyan for friendly, Red for hostile, Yellow for unknown, Green for neutral
- Shapes: Different frame shapes for different affiliations and contexts
- Modifiers: Text and graphic modifiers for additional details (e.g., unit names, quantities, directions)
This library supports:
- 2525D Change 1: Major update from 2525C, introducing significant structural changes to symbol codes
- 2525E Change 1: Latest version with additional symbols and refinements
- APP6 (Allied Procedural Publication 6): NATO equivalent with some variations
MIL-STD-2525 defines two primary categories of symbols:
These represent objects at a specific point on the map:
- Units: Infantry, armor, artillery, aviation units, etc.
- Equipment: Vehicles, weapons systems, sensors
- Installations: Bases, depots, headquarters
- Activities: Events, incidents, points of interest
These represent areas, lines, or complex shapes:
- Control Measures: Boundaries, phase lines, objectives
- Fire Support: Target areas, no-fire areas, fire support coordination measures
- Mobility/Survivability: Obstacles, minefields, routes
- Combat Service Support: Supply routes, medical evacuation routes
A MIL-STD-2525D/E Symbol Identification Code (SIDC) is a string that uniquely identifies a military symbol. The minimum length is 20 digits, but it can extend to 30 digits to include additional modifiers. Understanding this structure is crucial for creating symbols.
Position: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
Format: [V][V][C][A][SS ][S][H][EE ][EEEEEE ][MMMMMM ][optional 10 digits ]
Breakdown:
- Positions 1-2: Version (e.g., "10" = 2525D, "15" = 2525E Change 1)
- Position 3: Context (0=Reality, 1=Exercise, 2=Simulation)
- Position 4: Standard Identity/Affiliation (0=Pending, 1=Unknown, 2=Assumed Friend, 3=Friend, 4=Neutral, 5=Suspect, 6=Hostile)
- Positions 5-6: Symbol Set (01=Air, 10=Land Unit, 15=Land Equipment, 20=Installation, 25=Control Measure, 30=Sea Surface, etc.)
- Position 7: Status (0=Present, 1=Planned, 2=Fully Capable, 3=Damaged, 4=Destroyed, 5=Full to Capacity)
- Position 8: HQ/Task Force/Dummy (0=None, 1=Feint/Dummy, 2=Headquarters, 4=Task Force, 6=TF+HQ)
- Positions 9-10: Echelon/Mobility (11=Team/Crew, 12=Squad, 15=Company, 16=Battalion, 21=Division, etc.)
- Positions 11-16: Entity Code (specific unit/equipment type within the symbol set)
- Positions 17-20: Modifier 1 (0000 for none)
- Positions 21-30: Additional modifiers (optional, typically zeros for basic symbols)
Common Symbol Set values:
01- Air05- Space10- Land Unit11- Land Civilian Unit/Organization15- Land Equipment20- Land Installation25- Control Measure (Tactical Graphics)27- Dismounted Individuals30- Sea Surface35- Sea Subsurface40- Activities45- Atmospheric60- Cyberspace
<dependency>
<groupId>io.github.missioncommand</groupId>
<artifactId>mil-sym-java</artifactId>
<version>2.5.1</version>
</dependency>import armyc2.c5isr.renderer.MilStdIconRenderer;
import armyc2.c5isr.renderer.utilities.*;
import java.util.HashMap;
import java.util.Map;
// Get the renderer instance
MilStdIconRenderer renderer = MilStdIconRenderer.getInstance();
// Create a friendly infantry unit symbol
String symbolCode = "10031000001211000000000000000000";
Map<String, String> modifiers = new HashMap<>();
Map<String, String> attributes = new HashMap<>();
// Render the icon
ImageInfo imageInfo = renderer.RenderIcon(symbolCode, modifiers, attributes);
// Get the image
java.awt.image.BufferedImage image = imageInfo.getImage();import armyc2.c5isr.renderer.MilStdIconRenderer;
import armyc2.c5isr.renderer.utilities.*;
import java.util.HashMap;
import java.util.Map;
public class BasicInfantryExample {
public static void main(String[] args) {
// Get renderer instance
MilStdIconRenderer renderer = MilStdIconRenderer.getInstance();
// Build a 2525D friendly infantry unit at company level
// Symbol ID breakdown:
// 10 - Version 2525D
// 0 - Context: Reality
// 3 - Affiliation: Friend
// 10 - Symbol Set: Land Unit
// 0 - Status: Present
// 0 - HQ/TF: None
// 15 - Echelon: Company
// 120000 - Entity: Infantry
String symbolCode = "10031000001512000000000000000000";
Map<String, String> modifiers = new HashMap<>();
Map<String, String> attributes = new HashMap<>();
// Add unit designation
modifiers.put(Modifiers.T_UNIQUE_DESIGNATION_1, "A/1-325");
// Add higher formation
modifiers.put(Modifiers.M_HIGHER_FORMATION, "1-325 IN");
// Render the icon
ImageInfo imageInfo = renderer.RenderIcon(symbolCode, modifiers, attributes);
if (imageInfo != null && imageInfo.getImage() != null) {
// Save to file
try {
javax.imageio.ImageIO.write(
imageInfo.getImage(),
"PNG",
new java.io.File("infantry_company.png")
);
System.out.println("Symbol created successfully!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}public class HostileTankExample {
public static void main(String[] args) {
MilStdIconRenderer renderer = MilStdIconRenderer.getInstance();
// Build a 2525E hostile tank unit at battalion level
// 15 - Version 2525E Change 1
// 0 - Context: Reality
// 6 - Affiliation: Hostile
// 10 - Symbol Set: Land Unit
// 0 - Status: Present
// 0 - HQ/TF: None
// 16 - Echelon: Battalion
// 010000 - Entity: Armor/Armored, Tracked
String symbolCode = "15061000001601000000000000000000";
Map<String, String> modifiers = new HashMap<>();
modifiers.put(Modifiers.T_UNIQUE_DESIGNATION_1, "3rd Tank Bn");
modifiers.put(Modifiers.C_QUANTITY, "30");
Map<String, String> attributes = new HashMap<>();
ImageInfo imageInfo = renderer.RenderIcon(symbolCode, modifiers, attributes);
// Use the image...
}
}public class AircraftExample {
public static void main(String[] args) {
MilStdIconRenderer renderer = MilStdIconRenderer.getInstance();
// Friendly fighter aircraft
// 10 - Version 2525D
// 0 - Context: Reality
// 3 - Affiliation: Friend
// 01 - Symbol Set: Air
// 0 - Status: Present
// 0 - HQ/TF: None
// 11 - Echelon: Team/Crew
// 120000 - Entity: Military Fixed Wing, Fighter
String symbolCode = "10030100001112000000000000000000";
Map<String, String> modifiers = new HashMap<>();
modifiers.put(Modifiers.T_UNIQUE_DESIGNATION_1, "F-16C");
modifiers.put(Modifiers.C_QUANTITY, "4");
modifiers.put(Modifiers.V_EQUIPMENT_TYPE, "Block 52");
Map<String, String> attributes = new HashMap<>();
ImageInfo imageInfo = renderer.RenderIcon(symbolCode, modifiers, attributes);
// Use the image...
}
}public class EquipmentExample {
public static void main(String[] args) {
MilStdIconRenderer renderer = MilStdIconRenderer.getInstance();
// Friendly wheeled vehicle (truck)
// 10 - Version 2525D
// 0 - Context: Reality
// 3 - Affiliation: Friend
// 15 - Symbol Set: Land Equipment
// 0 - Status: Present
// 0 - HQ/TF: None
// 31 - Mobility: Wheeled Limited Cross Country
// 120000 - Entity: Truck
String symbolCode = "10031500003112000000000000000000";
Map<String, String> modifiers = new HashMap<>();
// Add unique designation
modifiers.put(Modifiers.T_UNIQUE_DESIGNATION_1, "HMMWV");
// Add quantity
modifiers.put(Modifiers.C_QUANTITY, "12 Vehicles");
// Add additional information
modifiers.put(Modifiers.H_ADDITIONAL_INFO_1, "Motor Pool A");
// Add staff comments
modifiers.put(Modifiers.G_STAFF_COMMENTS, "Ready");
// Add speed
modifiers.put(Modifiers.Z_SPEED, "55 KPH");
Map<String, String> attributes = new HashMap<>();
attributes.put(MilStdAttributes.PixelSize, "100");
ImageInfo imageInfo = renderer.RenderIcon(symbolCode, modifiers, attributes);
// Use the image...
}
}public class InstallationExample {
public static void main(String[] args) {
MilStdIconRenderer renderer = MilStdIconRenderer.getInstance();
// Friendly hospital
// 10 - Version 2525D
// 0 - Context: Reality
// 3 - Affiliation: Friend
// 20 - Symbol Set: Land Installation
// 0 - Status: Present
// 0 - HQ/TF: None
// 00 - Echelon: Not applicable
// 410000 - Entity: Medical Treatment Facility
String symbolCode = "10032000000041000000000000000000";
Map<String, String> modifiers = new HashMap<>();
modifiers.put(Modifiers.T_UNIQUE_DESIGNATION_1, "Field Hospital");
modifiers.put(Modifiers.C_QUANTITY, "200 Beds");
modifiers.put(Modifiers.H_ADDITIONAL_INFO_1, "Level III");
Map<String, String> attributes = new HashMap<>();
ImageInfo imageInfo = renderer.RenderIcon(symbolCode, modifiers, attributes);
// Use the image...
}
}Multi-point symbols represent tactical graphics such as boundaries, routes, and areas. These require multiple coordinate points.
import armyc2.c5isr.web.render.WebRenderer;
import armyc2.c5isr.renderer.utilities.*;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class BoundaryExample {
public static void main(String[] args) {
// Boundary line
// 10 - Version 2525D
// 0 - Context: Reality
// 3 - Affiliation: Friend
// 25 - Symbol Set: Control Measure
// 0 - Status: Present
// 0 - HQ/TF: None
// 00 - Echelon: Not applicable
// 020100 - Entity: Boundary
String symbolCode = "10032500000002010000000000000000";
// Define control points (longitude, latitude)
String controlPoints = "50.0,20.0 50.0,21.0 51.0,21.0";
Map<String, String> modifiers = new HashMap<>();
modifiers.put(Modifiers.T_UNIQUE_DESIGNATION_1, "PHASE LINE GREEN");
Map<String, String> attributes = new HashMap<>();
// Render as GeoJSON
String geoJson = WebRenderer.RenderSymbol(
"boundary_001", // id
"Unit Boundary", // name
"Division Boundary", // description
symbolCode, // symbol code
controlPoints, // control points
"clampToGround", // altitude mode
50000.0, // scale (1:50K)
"49.5,19.5,51.5,21.5", // bbox
modifiers, // modifiers
attributes, // attributes
WebRenderer.OUTPUT_FORMAT_GEOJSON // output format
);
System.out.println(geoJson);
}
}public class AssemblyAreaExample {
public static void main(String[] args) {
// Assembly Area
// 10 - Version 2525D
// 0 - Context: Reality
// 3 - Affiliation: Friend
// 25 - Symbol Set: Control Measure
// 0 - Status: Present
// 0 - HQ/TF: None
// 00 - Echelon: Not applicable
// 050600 - Entity: Assembly Area
String symbolCode = "10032500000005060000000000000000";
// Define area points (closed polygon)
String controlPoints = "50.0,20.0 50.5,20.0 50.5,20.5 50.0,20.5 50.0,20.0";
Map<String, String> modifiers = new HashMap<>();
modifiers.put(Modifiers.T_UNIQUE_DESIGNATION_1, "AA WOLF");
modifiers.put(Modifiers.H_ADDITIONAL_INFO_1, "2-325 IN");
modifiers.put(Modifiers.W_DTG_1, "241200ZNOV24"); // Date-Time Group
Map<String, String> attributes = new HashMap<>();
// Render as KML
String kml = WebRenderer.RenderSymbol(
"aa_wolf",
"Assembly Area Wolf",
"2-325 IN Assembly Area",
symbolCode,
controlPoints,
"clampToGround",
50000.0,
"49.5,19.5,51.0,21.0",
modifiers,
attributes,
WebRenderer.OUTPUT_FORMAT_KML
);
System.out.println(kml);
}
}public class RouteExample {
public static void main(String[] args) {
// Main Supply Route
// 10 - Version 2525D
// 0 - Context: Reality
// 3 - Affiliation: Friend
// 25 - Symbol Set: Control Measure
// 0 - Status: Present
// 0 - HQ/TF: None
// 00 - Echelon: Not applicable
// 030200 - Entity: Main Supply Route
String symbolCode = "10032500000003020000000000000000";
// Define route waypoints
String controlPoints = "50.0,20.0 50.2,20.3 50.5,20.5 50.8,20.7 51.0,21.0";
Map<String, String> modifiers = new HashMap<>();
modifiers.put(Modifiers.T_UNIQUE_DESIGNATION_1, "MSR TAMPA");
modifiers.put(Modifiers.H_ADDITIONAL_INFO_1, "Primary");
Map<String, String> attributes = new HashMap<>();
// Render as SVG
String svg = WebRenderer.RenderSymbol(
"msr_tampa",
"Main Supply Route Tampa",
"Primary MSR for brigade",
symbolCode,
controlPoints,
"clampToGround",
50000.0,
"49.5,19.5,51.5,21.5",
modifiers,
attributes,
WebRenderer.OUTPUT_FORMAT_GEOSVG
);
System.out.println(svg);
}
}Modifiers add additional information to symbols. Here are the most commonly used modifiers:
Map<String, String> modifiers = new HashMap<>();
// Unique Designation (main identifier)
modifiers.put(Modifiers.T_UNIQUE_DESIGNATION_1, "A/1-325");
// Higher Formation
modifiers.put(Modifiers.M_HIGHER_FORMATION, "1st BCT");
// Additional Information
modifiers.put(Modifiers.H_ADDITIONAL_INFO_1, "Mission: Attack");
modifiers.put(Modifiers.H1_ADDITIONAL_INFO_2, "Additional notes");
// Staff Comments
modifiers.put(Modifiers.G_STAFF_COMMENTS, "Combat Ready");
// Quantity
modifiers.put(Modifiers.C_QUANTITY, "12 Tanks");
// Combat Effectiveness
modifiers.put(Modifiers.K_COMBAT_EFFECTIVENESS, "75%");
// Signature Equipment
modifiers.put(Modifiers.V_EQUIPMENT_TYPE, "M1A2");
// Date-Time Group
modifiers.put(Modifiers.W_DTG_1, "241200ZNOV24");
modifiers.put(Modifiers.W1_DTG_2, "251200ZNOV24");
// Altitude/Depth
modifiers.put(Modifiers.X_ALTITUDE_DEPTH, "1000 AGL");
// Location
modifiers.put(Modifiers.Y_LOCATION, "Grid: NV123456");
// Speed
modifiers.put(Modifiers.Z_SPEED, "45 KPH");
// Direction of Movement
modifiers.put(Modifiers.Q_DIRECTION_OF_MOVEMENT, "090"); // degrees
// Evaluation Rating
modifiers.put(Modifiers.J_EVALUATION_RATING, "A3");
// Reinforced or Reduced
modifiers.put(Modifiers.F_REINFORCED_REDUCED, "+"); // + for reinforced, - for reduced
// Special C2 Headquarters
modifiers.put(Modifiers.AA_SPECIAL_C2_HQ, "C2");// Distance (for graphics that need distance, comma-separated for multiple)
modifiers.put(Modifiers.AM_DISTANCE, "1000,2000,3000");
// Azimuth (for graphics that need azimuth/direction)
modifiers.put(Modifiers.AN_AZIMUTH, "45,90,135");
// Width (for linear graphics)
modifiers.put(Modifiers.AM_DISTANCE, "200"); // metersAttributes control rendering behavior:
Map<String, String> attributes = new HashMap<>();
// Set icon pixel size (default is typically 35)
attributes.put(MilStdAttributes.PixelSize, "100");
// Set line width for multi-point graphics
attributes.put(MilStdAttributes.LineWidth, "3");
// Set line color (hex format)
attributes.put(MilStdAttributes.LineColor, "FFFF0000"); // ARGB format
// Set fill color
attributes.put(MilStdAttributes.FillColor, "80FF0000"); // Semi-transparent red
// Keep unit ratio (for scaling)
attributes.put(MilStdAttributes.KeepUnitRatio, "true");
// Center point (for positioning)
attributes.put(MilStdAttributes.CenterPoint, "100,100");| Symbol Type | Symbol Code Example | Description |
|---|---|---|
| Friendly Infantry | 10031000001512000000000000000000 |
Company-level infantry unit |
| Hostile Armor | 10061000001601000000000000000000 |
Battalion-level tank unit |
| Unknown Aircraft | 10010100001112000000000000000000 |
Fighter aircraft |
| Friendly Ship | 10033000000000000000000000000000 |
Surface vessel |
| Hostile Submarine | 10063500000000000000000000000000 |
Submarine |
| Friendly Helicopter | 10030100001511000000000000000000 |
Attack helicopter |
| Field Artillery | 10031000001510000000000000000000 |
Company-level artillery |
| Air Defense | 10031000001510500000000000000000 |
Air defense unit |
| Engineers | 10031000001513000000000000000000 |
Engineer unit |
| Supply Point | 10032000000012000000000000000000 |
Supply installation |
| Command Post | 10032000000000100000000000000000 |
Command facility |
| Objective | 10032500000006010000000000000000 |
Attack objective |
| Phase Line | 10032500000002010000000000000000 |
Control line |
| Route | 10032500000003020000000000000000 |
Movement route |
The SymbolID class provides helper methods for building symbol IDs:
import armyc2.c5isr.renderer.utilities.SymbolID;
// Start with a base symbol ID
String baseID = "10031000001512000000000000000000";
// Change the affiliation to hostile (position 4)
String hostileID = SymbolID.setAffiliation(baseID,
SymbolID.StandardIdentity_Affiliation_Hostile_Faker);
// Change the echelon/amplifier descriptor to battalion (positions 9-10)
String battalionID = SymbolID.setAmplifierDescriptor(baseID, 16);
// Change the status to planned (position 7)
String plannedID = SymbolID.setStatus(baseID, 1);
// Change the version
String newVersionID = SymbolID.setVersion(baseID, SymbolID.Version_2525Ech1);
// Get parts of a symbol ID
int version = SymbolID.getVersion(baseID);
int symbolSet = SymbolID.getSymbolSet(baseID);
String entityCode = SymbolID.getEntityCode(baseID);Build the project using Gradle:
./gradlew buildRun tests:
./gradlew testGenerate JavaDoc:
./gradlew javadoc- JavaDocs
- Wiki
- 2525C Renderer Overview (retired)
This library has been ported to multiple platforms:
- Java (this project)
- Android
- TypeScript
For the complete specification, refer to the official DoD documentation:
- MIL-STD-2525D Change 1 (20 November 2014)
- MIL-STD-2525E Change 1 (Latest version)
These documents provide:
- Complete symbol set definitions
- Entity code tables
- Modifier specifications
- Color specifications
- Symbol construction rules
The standard defines specific colors for affiliations:
- Friendly: Blue (RGB: 0, 255, 255 or Cyan)
- Hostile: Red (RGB: 255, 0, 0)
- Neutral: Green (RGB: 0, 255, 0)
- Unknown: Yellow (RGB: 255, 255, 0)
Frame shapes vary based on:
- Dimension: Air, land, sea, space, etc.
- Affiliation: Friend, hostile, neutral, unknown
- Context: Reality, exercise, simulation
Common shapes include:
- Circle: Unknown entities
- Rectangle: Friendly entities
- Diamond: Hostile entities
- Square: Neutral entities
- Cloverleaf: Friendly installations
GNU General Public License v3.0
Contributions are welcome! Please follow the existing code style and include tests for new functionality.
For issues, questions, or contributions, please visit the GitHub repository.