
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