commonMain.earth.worldwind.shape.milstd2525.AbstractMilStd2525TacticalGraphic.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of worldwind-jvm Show documentation
Show all versions of worldwind-jvm Show documentation
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 earth.worldwind.geom.AltitudeMode
import earth.worldwind.geom.Angle
import earth.worldwind.geom.Location
import earth.worldwind.geom.Sector
import earth.worldwind.render.AbstractSurfaceRenderable
import earth.worldwind.render.RenderContext
import earth.worldwind.render.Renderable
import earth.worldwind.shape.*
import earth.worldwind.shape.milstd2525.MilStd2525.labelScaleThreshold
import kotlin.jvm.JvmStatic
import kotlin.math.PI
import kotlin.math.ln
import kotlin.math.roundToInt
abstract class AbstractMilStd2525TacticalGraphic(
protected val sidc: String, protected val boundingSector: Sector,
modifiers: Map?, attributes: Map?,
) : AbstractSurfaceRenderable(boundingSector), Highlightable {
override var isHighlighted = false
var modifiers = modifiers
set(value) {
field = value
reset()
}
var attributes = attributes
set(value) {
field = value
reset()
}
private var minScale = Double.MIN_VALUE
private var maxScale = Double.MAX_VALUE
private val lodBuffer = mutableMapOf>()
private val lodSector = mutableMapOf()
protected companion object {
const val MAX_WIDTH_DP = 1e-3
const val MIN_WIDTH_DP = 1e-5
const val HIGHLIGHT_FACTOR = 2f
private const val ZERO_LEVEL_PX = 256
@JvmStatic
fun defaultBoundingSector(locations: List) = Sector().apply { locations.forEach { l -> union(l) } }
private fun computeNearestLoD(equatorialRadius: Double, scale: Double) =
(ln(2 * PI * equatorialRadius / ZERO_LEVEL_PX / scale) / ln(2.0)).roundToInt()
private fun computeLoDScale(equatorialRadius: Double, lod: Int) =
2 * PI * equatorialRadius / ZERO_LEVEL_PX / (1 shl lod)
}
init {
recalculateScaleLimits()
}
fun setBoundingSector(sector: Sector) {
boundingSector.copy(sector)
recalculateScaleLimits()
}
override fun doRender(rc: RenderContext) {
// Get the current map scale based on observation range.
val currentScale = rc.pixelSize * rc.densityFactor
// Limit scale based on clipping sector diagonal size
val limitedScale = currentScale.coerceIn(minScale, maxScale)
// Get renderables for current LoD
val equatorialRadius = rc.globe.equatorialRadius
val lod = computeNearestLoD(equatorialRadius, limitedScale)
// Set sector based on selected lod
sector.copy(lodSector[lod] ?: boundingSector)
// Check if tactical graphics visible
val terrainSector = rc.terrain.sector
if (!terrainSector.isEmpty && terrainSector.intersects(sector) && getExtent(rc).intersectsFrustum(rc.frustum)) {
val shapes = lodBuffer[lod] ?: run {
sector.setEmpty() // Prepare bounding box to be extended by real graphics measures
makeRenderables(computeLoDScale(equatorialRadius, lod)).also {
lodBuffer[lod] = it
lodSector[lod] = Sector(sector) // Remember real bounding box based on LoD
}
}
// Draw available shapes
for (renderable in shapes) {
if (renderable is Highlightable) renderable.isHighlighted = isHighlighted
if (renderable !is Label || isHighlighted || currentScale <= labelScaleThreshold) renderable.render(rc)
}
}
}
protected abstract fun makeRenderables(scale: Double): List // Platform dependent implementation
protected fun reset() {
lodBuffer.clear()
lodSector.clear()
}
protected fun applyShapeAttributes(shape: AbstractShape) = shape.apply {
altitudeMode = AltitudeMode.CLAMP_TO_GROUND
isFollowTerrain = true
maximumIntermediatePoints = 0 // Do not draw intermediate vertices for tactical graphics
highlightAttributes = ShapeAttributes(attributes).apply {
outlineWidth *= HIGHLIGHT_FACTOR
}
pickDelegate = this@AbstractMilStd2525TacticalGraphic
}
protected fun applyLabelAttributes(label: Label, angle: Angle) = label.apply {
altitudeMode = AltitudeMode.CLAMP_TO_GROUND
rotation = angle
rotationMode = OrientationMode.RELATIVE_TO_GLOBE
pickDelegate = this@AbstractMilStd2525TacticalGraphic
}
private fun recalculateScaleLimits() {
val diagonalDistance = Location(boundingSector.minLatitude, boundingSector.minLongitude)
.greatCircleDistance(Location(boundingSector.maxLatitude, boundingSector.maxLongitude))
minScale = diagonalDistance / MAX_WIDTH_DP
maxScale = diagonalDistance / MIN_WIDTH_DP
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy