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

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

package earth.worldwind.shape.milstd2525

import ArmyC2.C2SD.Rendering.MultiPointRenderer
import ArmyC2.C2SD.Utilities.*
import earth.worldwind.geom.Angle.Companion.degrees
import earth.worldwind.geom.Location
import earth.worldwind.geom.Offset
import earth.worldwind.geom.Position
import earth.worldwind.geom.Sector
import earth.worldwind.render.Color
import earth.worldwind.render.Font
import earth.worldwind.render.Renderable
import earth.worldwind.render.image.ImageSource
import earth.worldwind.shape.*
import earth.worldwind.util.Logger
import java.awt.BasicStroke
import java.awt.geom.Point2D
import kotlin.math.roundToInt

actual open class MilStd2525TacticalGraphic @JvmOverloads actual constructor(
    sidc: String, locations: List,
    boundingSector: Sector, modifiers: Map?, attributes: Map?
) : AbstractMilStd2525TacticalGraphic(sidc, boundingSector, modifiers, attributes) {
    protected lateinit var controlPoints: ArrayList
    protected lateinit var pointUL: Point2D.Double

    init {
        setAnchorLocations(locations)
    }

    fun setAnchorLocations(locations: List) {
        if (this::controlPoints.isInitialized) controlPoints.clear() else controlPoints = ArrayList()
        for (location in locations) controlPoints.add(Point2D.Double(location.longitude.inDegrees, location.latitude.inDegrees))
        var left = controlPoints[0].x
        var top = controlPoints[0].y
        var right = controlPoints[0].x
        var bottom = controlPoints[0].y
        var pt: Point2D
        for (i in 1 until controlPoints.size) {
            pt = controlPoints[i]
            if (pt.x < left) left = pt.x
            if (pt.x > right) right = pt.x
            if (pt.y > top) top = pt.y
            if (pt.y < bottom) bottom = pt.y
        }
        if (right - left > 180.0) {
            left = controlPoints[0].x
            for (i in 1 until controlPoints.size) {
                pt = controlPoints[i]
                if (pt.x > 0.0 && pt.x < left) left = pt.x
            }
        }
        if (this::pointUL.isInitialized) pointUL.setLocation(left, top) else pointUL = Point2D.Double(left, top)
        reset()
    }

    override fun makeRenderables(scale: Double): List {
        val ipc = PointConverter3D(pointUL.x, pointUL.y, scale * 96.0 * 39.3700787)

//        // Calculate clipping rectangle
//        val leftTop = ipc.GeoToPixels(Point2D.Double(boundingSector.minLongitude.degrees, boundingSector.maxLatitude.degrees))
//        val rightBottom = ipc.GeoToPixels(Point2D.Double(boundingSector.maxLongitude.degrees, boundingSector.minLatitude.degrees))
//        val width = abs(rightBottom.x - leftTop.x)
//        val height = abs(rightBottom.y - leftTop.y)
//        val rect = if (width > 0 && height > 0) Rectangle2D.Double(leftTop.x, leftTop.y, width, height) else null

        // Create MilStd Symbol and render it
        val mss = MilStdSymbol(sidc, null, controlPoints, null)
        modifiers?.forEach { (key, value) ->
            when (key) {
                ModifiersTG.AM_DISTANCE, ModifiersTG.AN_AZIMUTH, ModifiersTG.X_ALTITUDE_DEPTH -> {
                    val elements = value.split(",")
                    for (j in elements.indices) mss.setModifier(key, elements[j], j)
                }
                else -> mss.setModifier(key, value)
            }
        }
        attributes?.run {
            mss.altitudeMode = get(MilStdAttributes.AltitudeMode)
            //mss.altitudeUnit = DistanceUnit.parse(get(MilStdAttributes.AltitudeUnits))
            //mss.distanceUnit = DistanceUnit.parse(get(MilStdAttributes.DistanceUnits))
        }
        MultiPointRenderer.getInstance().renderWithPolylines(mss, ipc, null /*rect*/)

        // Create Renderables based on Poly-lines and Modifiers from Renderer
        val shapes = mutableListOf()
        val outlines = mutableListOf()
        for (i in mss.symbolShapes.indices) convertShapeToRenderables(mss.symbolShapes[i], mss, ipc, shapes, outlines)
        for (i in mss.modifierShapes.indices) convertShapeToRenderables(mss.modifierShapes[i], mss, ipc, shapes, outlines)
        return outlines + shapes
    }

    protected open fun convertShapeToRenderables(
        si: ShapeInfo, mss: MilStdSymbol, ipc: IPointConversion, shapes: MutableList, outlines: MutableList
    ) {
        when (si.shapeType) {
            ShapeInfo.SHAPE_TYPE_POLYLINE, ShapeInfo.SHAPE_TYPE_FILL -> {
                val shapeAttributes = ShapeAttributes().apply {
                    outlineWidth = MilStd2525.graphicsLineWidth
                    (si.lineColor ?: si.fillColor)?.let { outlineColor = Color(it.rgb) } ?: return
                    (si.fillColor ?: si.lineColor)?.let { interiorColor = Color(it.rgb) } ?: return
                    val stroke = si.stroke
                    if (stroke is BasicStroke) {
                        val dash = stroke.dashArray
                        if (dash != null && dash.isNotEmpty()) outlineImageSource = ImageSource.fromLineStipple(
                            // TODO How to correctly interpret dash array?
                            factor = dash[0].roundToInt(), pattern = 0xF0F0.toShort()
                        )
                    }
                }
                val hasOutline = MilStd2525.graphicsOutlineWidth != 0f
                val outlineAttributes = if (hasOutline) ShapeAttributes(shapeAttributes).apply {
                    outlineColor = Color(SymbolDraw.getIdealTextBackgroundColor(si.lineColor ?: si.fillColor).rgb).apply { alpha = 0.5f }
                    outlineWidth += MilStd2525.graphicsOutlineWidth * 2f
                } else shapeAttributes
                for (idx in si.polylines.indices) {
                    val polyline = si.polylines[idx]
                    val positions = mutableListOf()
                    for (p in polyline.indices) {
                        val geoPoint = ipc.PixelsToGeo(polyline[p])
                        val position = Position.fromDegrees(geoPoint.y, geoPoint.x, 0.0)
                        positions.add(position)
                        sector.union(position) // Extend bounding box by real graphics measures
                    }
                    for (i in 0..1) {
                        val shape = if (si.shapeType == ShapeInfo.SHAPE_TYPE_FILL) {
                            Polygon(positions, if (i == 0) shapeAttributes else outlineAttributes)
                        } else {
                            Path(positions, if (i == 0) shapeAttributes else outlineAttributes)
                        }
                        applyShapeAttributes(shape)
                        if (i == 0) shapes += shape else outlines += shape
                        if (!hasOutline) break
                    }
                }
            }

            ShapeInfo.SHAPE_TYPE_MODIFIER, ShapeInfo.SHAPE_TYPE_MODIFIER_FILL -> {
                val rs = RendererSettings.getInstance()
                val textAttributes = TextAttributes().apply {
                    textColor = Color(mss.lineColor.rgb)
                    textOffset = Offset.center()
                    font = Font(rs.mpLabelFont)
                    outlineWidth = MilStd2525.graphicsOutlineWidth
                }
                val point = ipc.PixelsToGeo(si.modifierStringPosition ?: si.glyphPosition ?: return)
                val position = Position.fromDegrees(point.y, point.x, 0.0)
                sector.union(position) // Extend bounding box by real graphics measures
                val label = Label(position, si.modifierString, textAttributes)
                applyLabelAttributes(label, si.modifierStringAngle.degrees)
                shapes += label
            }
            else -> Logger.logMessage(Logger.ERROR, "MilStd2525TacticalGraphic", "convertShapeToRenderables", "unknownShapeType")
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy