All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmMain.earth.worldwind.shape.milstd2525.MilStd2525.kt Maven / Gradle / Ivy

Go to download

The WorldWind Kotlin SDK (WWK) includes the library, examples and tutorials for building multiplatform 3D virtual globe applications for Android, Web and Java.

The newest version!
package earth.worldwind.shape.milstd2525

import ArmyC2.C2SD.RendererPluginInterface.ISinglePointInfo
import ArmyC2.C2SD.Utilities.*
import earth.worldwind.shape.TextAttributes
import sec.web.renderer.SinglePoint2525Renderer
import java.awt.Font

/**
 * This utility class generates MIL-STD-2525 symbols and tactical graphics using the MIL-STD-2525 Symbol Rendering Library
 * @see https://github.com/missioncommand/mil-sym-java
 */
actual object MilStd2525 {
    private const val GRAPHICS_LINE_WIDTH = 3f
    private const val SYMBOL_OUTLINE_WIDTH = 1

    /**
     * Controls the symbol modifiers visibility threshold
     */
    actual var modifiersThreshold = 3.2e4
    /**
     * Controls the tactical graphics labels visibility threshold
     */
    actual var labelScaleThreshold = 4.0
    actual var graphicsLineWidth = GRAPHICS_LINE_WIDTH
    var graphicsOutlineWidth = SYMBOL_OUTLINE_WIDTH.toFloat()
    /**
     * The actual rendering engine for the MIL-STD-2525 graphics.
     */
    private val renderer = SinglePoint2525Renderer()
    private val rendererSettings = RendererSettings.getInstance()

    /**
    * Initializes the static MIL-STD-2525 symbol renderer.
    */
    init {
        // Establish the default rendering values.
        // See: https://github.com/missioncommand/mil-sym-android/blob/master/Renderer/src/main/java/armyc2/c2sd/renderer/utilities/RendererSettings.java
        rendererSettings.symbologyStandard = RendererSettings.Symbology_2525C

        // Depending on screen size and DPI you may want to change the font size.
        rendererSettings.setLabelFont("Arial", Font.BOLD, 8)
        rendererSettings.setMPLabelFont("Arial", Font.BOLD, 12)

        // Configure modifier text output
        rendererSettings.textBackgroundMethod = RendererSettings.TextBackgroundMethod_OUTLINE
        rendererSettings.textOutlineWidth = 4

        // Configure Single point symbol outline width
        rendererSettings.singlePointSymbolOutlineWidth = SYMBOL_OUTLINE_WIDTH
    }

    /**
     * Creates an MIL-STD-2525 symbol from the specified symbol code, modifiers and attributes.
     *
     * @param symbolCode The MIL-STD-2525 symbol code.
     * @param modifiers  The MIL-STD-2525 modifiers. If null, a default (empty) modifier list will be used.
     * @param attributes The MIL-STD-2525 attributes. If null, a default (empty) attribute list will be used.
     *
     * @return An ImageInfo object containing the symbol's bitmap and metadata; may be null
     */
    @JvmStatic
    fun renderImage(symbolCode: String, modifiers: Map?, attributes: Map?): ISinglePointInfo? {
        val params = mutableMapOf()
        if (modifiers != null) {
            params.putAll(modifiers)
        }
        if (attributes != null) {
            params.putAll(attributes)
        }
        return renderer.render(symbolCode, params)
    }

    /**
     * Get symbol text description and hierarchy reference.
     */
    @JvmStatic
    fun getSymbolDef(sidc: String): SymbolDef? = SymbolDefTable.getInstance()
        .getSymbolDef(SymbolUtilities.getBasicSymbolID(sidc), rendererSettings.symbologyStandard)

    /**
     * Get symbol visual attributes like draw category, min points, max points, etc.
     */
    @JvmStatic
    fun getUnitDef(sidc: String): UnitDef? = UnitDefTable.getInstance()
        .getUnitDef(SymbolUtilities.getBasicSymbolID(sidc), rendererSettings.symbologyStandard)

    @JvmStatic
    fun applyTextAttributes(textAttributes: TextAttributes) = textAttributes.apply {
        font = earth.worldwind.render.Font(rendererSettings.labelFont)
        textColor.set(rendererSettings.labelForegroundColor.rgb)
        outlineColor.set(rendererSettings.labelBackgroundColor.rgb)
        outlineWidth = rendererSettings.textOutlineWidth.toFloat()
    }

    @JvmStatic
    actual fun getSimplifiedSymbolID(sidc: String) =
        setAffiliation(SymbolUtilities.getBasicSymbolID(sidc), sidc.substring(1, 2))

    @JvmStatic
    actual fun isTacticalGraphic(sidc: String) = SymbolUtilities.isTacticalGraphic(sidc)

    @JvmStatic
    actual fun setAffiliation(sidc: String, affiliation: String?) =
        // Weather symbols has no affiliation
        if (sidc.length >= 2 && !SymbolUtilities.isWeather(sidc) && affiliation != null && affiliation.length == 1) {
            val result = sidc.substring(0, 1) + affiliation.uppercase() + sidc.substring(2)
            if (SymbolUtilities.hasValidAffiliation(result)) result else sidc
        } else sidc

    @JvmStatic
    actual fun setStatus(sidc: String, status: String?) =
        // Weather symbols has no status
        if (sidc.length >= 4 && !SymbolUtilities.isWeather(sidc) && status != null && status.length == 1) {
            val result = sidc.substring(0, 3) + status.uppercase() + sidc.substring(4)
            if (SymbolUtilities.hasValidStatus(result)) result else sidc
        } else sidc

    @JvmStatic
    actual fun setEchelon(sidc: String, echelon: String?): String {
        val isTG = SymbolUtilities.isTacticalGraphic(sidc)
        return if (sidc.length >= 12 && (isTG && SymbolUtilities.canSymbolHaveModifier(sidc, ModifiersTG.B_ECHELON)
                    || !isTG && SymbolUtilities.canUnitHaveModifier(sidc, ModifiersUnits.B_ECHELON))
            && echelon != null && echelon.length == 1 && SymbolUtilities.getEchelonText(echelon).isNotEmpty()
        ) sidc.substring(0, 11) + echelon.uppercase() + sidc.substring(12) else sidc
    }

    @JvmStatic
    actual fun setSymbolModifier(
        sidc: String, hq: Boolean, taskForce: Boolean, feintDummy: Boolean, installation: Boolean, mobility: String?
    ): String {
        var result = sidc
        if (result.length >= 11 && !SymbolUtilities.isTacticalGraphic(result)) {
            // Check if mobility is applicable
            if (mobility != null && mobility.length == 2 && result.length >= 12
                && SymbolUtilities.canUnitHaveModifier(result, ModifiersUnits.R_MOBILITY_INDICATOR)
            ) {
                // Try applying mobility
                val sidcWithMobility = result.substring(0, 10) + mobility.uppercase() + result.substring(12)
                // Check if mobility is valid
                if (SymbolUtilities.isMobility(sidcWithMobility)) result = sidcWithMobility
            } else {
                // Check if HQ, TaskForce, Feint or Dummy symbol modifiers are applicable
                val isHQ = hq && SymbolUtilities.canUnitHaveModifier(result, ModifiersUnits.S_HQ_STAFF_OR_OFFSET_INDICATOR)
                val isTaskForce = taskForce && SymbolUtilities.canUnitHaveModifier(result, ModifiersUnits.D_TASK_FORCE_INDICATOR)
                val isDummy = feintDummy && SymbolUtilities.canUnitHaveModifier(result, ModifiersUnits.AB_FEINT_DUMMY_INDICATOR)
                val isInstallation = installation && SymbolUtilities.canUnitHaveModifier(result, ModifiersUnits.AC_INSTALLATION)
                val modifier = when {
                    !isDummy && isHQ && !isTaskForce -> 'A' // HEADQUARTERS
                    !isDummy && isHQ && isTaskForce -> 'B' // TASK FORCE + HEADQUARTERS
                    isDummy && isHQ && !isTaskForce -> 'C' // FEINT/DUMMY + HEADQUARTERS
                    isDummy && isHQ && isTaskForce -> 'D' // FEINT/DUMMY + TASK FORCE + HEADQUARTERS
                    !isDummy && !isHQ && isTaskForce -> 'E' // TASK FORCE
                    isDummy && !isHQ && !isTaskForce -> 'F' // FEINT/DUMMY
                    isDummy && !isHQ && isTaskForce -> 'G' // FEINT/DUMMY + TASK FORCE
                    else -> if (isInstallation) 'H' else null
                }
                // Apply symbol modifier
                if (modifier != null) result = result.substring(0, 10) + modifier + result.substring(11)
                // Fix Feint/Dummy modifier for installation
                if (isDummy && result.length >= 12 && SymbolUtilities.hasInstallationModifier(result)) {
                    result = result.substring(0, 11) + "B" + result.substring(12)
                }
            }
        }
        return result
    }

    @JvmStatic
    actual fun setCountryCode(sidc: String, countryCode: String?) =
        if (sidc.length >= 14 && countryCode != null && countryCode.length == 2) {
            val result = sidc.substring(0, 12) + countryCode.uppercase() + sidc.substring(14)
            if (SymbolUtilities.hasValidCountryCode(result)) result else sidc
        } else sidc

    @JvmStatic
    actual fun getLineColor(sidc: String) = SymbolUtilities.getLineColorOfAffiliation(sidc)?.rgb
        ?: rendererSettings.friendlyGraphicLineColor.rgb

    @JvmStatic
    actual fun getFillColor(sidc: String) = SymbolUtilities.getFillColorOfAffiliation(sidc)?.rgb
        ?: rendererSettings.friendlyGraphicFillColor.rgb

    @JvmStatic
    actual fun getUnfilledAttributes(sidc: String) = if (SymbolUtilities.isTacticalGraphic(sidc)) {
        SymbolUtilities.getLineColorOfAffiliation(sidc)
    } else {
        SymbolUtilities.getFillColorOfAffiliation(sidc)
    }?.rgb?.let {
        mapOf(
            MilStdAttributes.FillColor to "00000000",
            MilStdAttributes.LineColor to Integer.toHexString(it),
            MilStdAttributes.IconColor to Integer.toHexString(it)
        )
    } ?: emptyMap()
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy