|
|
|
|
@@ -1,12 +1,17 @@
|
|
|
|
|
package org.maplibre.android.testapp.activity.particle
|
|
|
|
|
|
|
|
|
|
import android.os.Bundle
|
|
|
|
|
import android.graphics.Color
|
|
|
|
|
import android.os.Bundle
|
|
|
|
|
import android.text.InputType
|
|
|
|
|
import android.util.Log
|
|
|
|
|
import android.widget.Button
|
|
|
|
|
import android.widget.EditText
|
|
|
|
|
import android.widget.PopupMenu
|
|
|
|
|
import android.widget.TextView
|
|
|
|
|
import androidx.appcompat.app.AlertDialog
|
|
|
|
|
import androidx.appcompat.app.AppCompatActivity
|
|
|
|
|
import org.json.JSONArray
|
|
|
|
|
import org.json.JSONObject
|
|
|
|
|
import org.maplibre.android.camera.CameraPosition
|
|
|
|
|
import org.maplibre.android.camera.CameraUpdateFactory
|
|
|
|
|
import org.maplibre.android.geometry.LatLng
|
|
|
|
|
@@ -39,6 +44,13 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
private var productionFeedMode = false
|
|
|
|
|
private var hostSubmissionMode = false
|
|
|
|
|
private var realWeatherMode = false
|
|
|
|
|
private var pureParticleMode = false
|
|
|
|
|
private var purePbfMode = false
|
|
|
|
|
private var navSeaPbfMode = false
|
|
|
|
|
private var navSeaPbfPosition = NavSeaPbfPosition.AFTER_CLIP
|
|
|
|
|
private var navSeaPbfRenderEnabled = true
|
|
|
|
|
private var navSeaPbfLayerVisibility = emptyList<NavSeaPbfLayerVisibility>()
|
|
|
|
|
private var retainedCacheEnabled = true
|
|
|
|
|
private var realWeatherTuningMenu: PopupMenu? = null
|
|
|
|
|
private var realWeatherMenuAnchor: android.view.View? = null
|
|
|
|
|
private var openRealWeatherTuningAfterStyle = false
|
|
|
|
|
@@ -57,7 +69,8 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
maskMode = savedInstanceState?.getInt(STATE_MASK_MODE, MASK_GLOBAL) ?: MASK_GLOBAL
|
|
|
|
|
fixtureMode = savedInstanceState?.getInt(STATE_FIXTURE_MODE, FIXTURE_UNIFORM_EAST) ?: FIXTURE_UNIFORM_EAST
|
|
|
|
|
timeMix = savedInstanceState?.getFloat(STATE_TIME_MIX, 0.0f) ?: 0.0f
|
|
|
|
|
particleCount = savedInstanceState?.getInt(STATE_PARTICLE_COUNT, PARTICLE_COUNT_256) ?: PARTICLE_COUNT_256
|
|
|
|
|
particleCount = (savedInstanceState?.getInt(STATE_PARTICLE_COUNT, PARTICLE_COUNT_256) ?: PARTICLE_COUNT_256)
|
|
|
|
|
.coerceIn(MIN_PARTICLE_COUNT, MAX_PARTICLE_COUNT)
|
|
|
|
|
lineWidth = savedInstanceState?.getFloat(STATE_LINE_WIDTH, DEFAULT_LINE_WIDTH) ?: DEFAULT_LINE_WIDTH
|
|
|
|
|
particleLength = savedInstanceState?.getFloat(STATE_PARTICLE_LENGTH, DEFAULT_PARTICLE_LENGTH) ?: DEFAULT_PARTICLE_LENGTH
|
|
|
|
|
particleOpacity = savedInstanceState?.getFloat(STATE_PARTICLE_OPACITY, DEFAULT_PARTICLE_OPACITY) ?: DEFAULT_PARTICLE_OPACITY
|
|
|
|
|
@@ -67,6 +80,14 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
timeWindowMode = savedInstanceState?.getBoolean(STATE_TIME_WINDOW, false) ?: false
|
|
|
|
|
hostSubmissionMode = savedInstanceState?.getBoolean(STATE_HOST_SUBMISSION, false) ?: false
|
|
|
|
|
realWeatherMode = savedInstanceState?.getBoolean(STATE_REAL_WEATHER, false) ?: false
|
|
|
|
|
pureParticleMode = savedInstanceState?.getBoolean(STATE_PURE_PARTICLE, false) ?: false
|
|
|
|
|
purePbfMode = savedInstanceState?.getBoolean(STATE_PURE_PBF, false) ?: false
|
|
|
|
|
navSeaPbfMode = savedInstanceState?.getBoolean(STATE_NAVSEA_PBF, false) ?: false
|
|
|
|
|
navSeaPbfPosition = savedInstanceState?.getString(STATE_NAVSEA_PBF_POSITION)
|
|
|
|
|
?.let { value -> NavSeaPbfPosition.values().firstOrNull { it.name == value } }
|
|
|
|
|
?: NavSeaPbfPosition.AFTER_CLIP
|
|
|
|
|
navSeaPbfRenderEnabled = savedInstanceState?.getBoolean(STATE_NAVSEA_PBF_RENDER_ENABLED, true) ?: true
|
|
|
|
|
retainedCacheEnabled = savedInstanceState?.getBoolean(STATE_RETAINED_CACHE_ENABLED, true) ?: true
|
|
|
|
|
super.onCreate(savedInstanceState)
|
|
|
|
|
Log.i(
|
|
|
|
|
TAG,
|
|
|
|
|
@@ -78,13 +99,16 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
mapView = findViewById(R.id.mapView)
|
|
|
|
|
status = findViewById(R.id.validationStatus)
|
|
|
|
|
mapView.addOnDidFailLoadingMapListener { errorMessage ->
|
|
|
|
|
if (realWeatherMode) {
|
|
|
|
|
if (navSeaPbfMode || purePbfMode) {
|
|
|
|
|
Log.e(TAG, "NAVSEA_PBF_STYLE_ERROR reason=$errorMessage")
|
|
|
|
|
} else if (realWeatherMode) {
|
|
|
|
|
Log.w(TAG, "ParticleField: event=BASEMAP_TILE_ERROR provider=GSI reason=$errorMessage failSoft=true")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
mapView.onCreate(savedInstanceState)
|
|
|
|
|
mapView.getMapAsync { map ->
|
|
|
|
|
maplibreMap = map
|
|
|
|
|
maplibreMap.setRetainedUnderlayEnabled(retainedCacheEnabled)
|
|
|
|
|
if (savedInstanceState == null) {
|
|
|
|
|
map.cameraPosition = CameraPosition.Builder()
|
|
|
|
|
.target(LatLng(0.0, 0.0))
|
|
|
|
|
@@ -92,6 +116,9 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
.build()
|
|
|
|
|
}
|
|
|
|
|
updateRealWeatherButtonState()
|
|
|
|
|
updateNavSeaPbfButtonState()
|
|
|
|
|
updatePbfModeButtonState()
|
|
|
|
|
updateRetainedCacheButtonState()
|
|
|
|
|
loadValidationStyle(active = active, enabled = enabled, reason = "INITIAL")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -160,10 +187,7 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
findViewById<Button>(R.id.validationParticle256).setOnClickListener { setParticleCount(PARTICLE_COUNT_256) }
|
|
|
|
|
findViewById<Button>(R.id.validationParticle1024).setOnClickListener { setParticleCount(PARTICLE_COUNT_1024) }
|
|
|
|
|
findViewById<Button>(R.id.validationParticle4096).setOnClickListener { setParticleCount(PARTICLE_COUNT_4096) }
|
|
|
|
|
findViewById<Button>(R.id.validationParticle16384).setOnClickListener { setParticleCount(PARTICLE_COUNT_16384) }
|
|
|
|
|
findViewById<Button>(R.id.validationParticle65536).setOnClickListener { setParticleCount(PARTICLE_COUNT_65536) }
|
|
|
|
|
findViewById<Button>(R.id.validationParticle262144).setOnClickListener { setParticleCount(PARTICLE_COUNT_262144) }
|
|
|
|
|
findViewById<Button>(R.id.validationParticle1048576).setOnClickListener { setParticleCount(PARTICLE_COUNT_1048576) }
|
|
|
|
|
findViewById<Button>(R.id.validationParticle10000).setOnClickListener { setParticleCount(MAX_PARTICLE_COUNT) }
|
|
|
|
|
findViewById<Button>(R.id.validationAdvanceWindow).setOnClickListener {
|
|
|
|
|
if (this::maplibreMap.isInitialized) advanceRuntimeFieldWindow()
|
|
|
|
|
}
|
|
|
|
|
@@ -221,15 +245,27 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
}
|
|
|
|
|
findViewById<Button>(R.id.validationMenuTime).setOnClickListener { dismissRealWeatherTuning(); showTimeMenu(it) }
|
|
|
|
|
findViewById<Button>(R.id.validationMenuVisual).setOnClickListener { dismissRealWeatherTuning(); showMenu(it, "VIS", intArrayOf(R.id.validationVisualDefault, R.id.validationVisualColor, R.id.validationVisualSizeDown, R.id.validationVisualSizeUp, R.id.validationVisualLengthDown, R.id.validationVisualLengthUp, R.id.validationVisualOpacityDown, R.id.validationVisualOpacityUp, R.id.validationVisualTrailDown, R.id.validationVisualTrailUp, R.id.validationVisualFadeDown, R.id.validationVisualFadeUp)) }
|
|
|
|
|
findViewById<Button>(R.id.validationMenuCount).setOnClickListener { dismissRealWeatherTuning(); showMenu(it, "COUNT", intArrayOf(R.id.validationParticle256, R.id.validationParticle1024, R.id.validationParticle4096, R.id.validationParticle16384, R.id.validationParticle65536, R.id.validationParticle262144, R.id.validationParticle1048576)) }
|
|
|
|
|
findViewById<Button>(R.id.validationMenuCount).setOnClickListener { dismissRealWeatherTuning(); showCountMenu(it) }
|
|
|
|
|
findViewById<Button>(R.id.validationMenuMap).setOnClickListener { dismissRealWeatherTuning(); showMenu(it, "MAP", intArrayOf(R.id.validationGlobal, R.id.validationOceanA, R.id.validationOceanB, R.id.validationDateline, R.id.validationWorldWrap)) }
|
|
|
|
|
findViewById<Button>(R.id.validationMenuRealWeather).setOnClickListener { activateRealWeather(it) }
|
|
|
|
|
findViewById<Button>(R.id.validationMenuNavSeaPbf).setOnClickListener { showNavSeaPbfMenu(it) }
|
|
|
|
|
findViewById<Button>(R.id.validationMenuPbfMode).setOnClickListener { showPbfModeMenu(it) }
|
|
|
|
|
findViewById<Button>(R.id.validationMenuRetainedCache).setOnClickListener { showRetainedCacheMenu(it) }
|
|
|
|
|
findViewById<Button>(R.id.validationMenuMore).setOnClickListener { dismissRealWeatherTuning(); showMenu(it, "MORE", intArrayOf(R.id.validationActive, R.id.validationStatic, R.id.validationDisable, R.id.validationEnable, R.id.validationUniformEast, R.id.validationUniformNorth, R.id.validationVortex, R.id.validationTimeWindow, R.id.validationHostFieldSubmit, R.id.validationProductionInvalid, R.id.validationHostFieldDuplicate, R.id.validationHostFieldInvalid, R.id.validationAdvanceWindow, R.id.validationDuplicateWindow)) }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private data class ParticleVisuals(val count: Int, val width: Float, val length: Float, val opacity: Float, val trailOpacity: Float, val trailFade: Float)
|
|
|
|
|
|
|
|
|
|
private enum class NavSeaPbfPosition(val label: String) {
|
|
|
|
|
BOTTOM("BOTTOM"),
|
|
|
|
|
AFTER_SEA("AFTER_SEA"),
|
|
|
|
|
AFTER_CLIP("AFTER_CLIP"),
|
|
|
|
|
BEFORE_SYMBOL_CLUSTER("BEFORE_SYMBOL_CLUSTER"),
|
|
|
|
|
TOP("TOP")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun activateRealWeather(anchor: android.view.View?) {
|
|
|
|
|
if (isPbfMatrixMode()) leaveRealWeatherMode()
|
|
|
|
|
realWeatherMenuAnchor = anchor ?: findViewById(R.id.validationMenuRealWeather)
|
|
|
|
|
if (realWeatherMode) {
|
|
|
|
|
if (realWeatherTuningMenu == null) showRealWeatherTuning(realWeatherMenuAnchor!!) else dismissRealWeatherTuning()
|
|
|
|
|
@@ -246,11 +282,16 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
productionFeedMode = false
|
|
|
|
|
hostSubmissionMode = true
|
|
|
|
|
realWeatherMode = true
|
|
|
|
|
pureParticleMode = false
|
|
|
|
|
purePbfMode = false
|
|
|
|
|
navSeaPbfMode = false
|
|
|
|
|
fixtureMode = FIXTURE_UNIFORM_EAST
|
|
|
|
|
timeMix = 0.0f
|
|
|
|
|
timelineState = "NOT READY"
|
|
|
|
|
openRealWeatherTuningAfterStyle = true
|
|
|
|
|
updateRealWeatherButtonState()
|
|
|
|
|
updateNavSeaPbfButtonState()
|
|
|
|
|
updatePbfModeButtonState()
|
|
|
|
|
if (this::maplibreMap.isInitialized) loadValidationStyle(active, enabled, "REAL_WEATHER")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -258,7 +299,7 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
dismissRealWeatherTuning()
|
|
|
|
|
weatherRuntime?.dispose()
|
|
|
|
|
weatherRuntime = null
|
|
|
|
|
if (realWeatherMode) {
|
|
|
|
|
if (realWeatherMode || isPbfMatrixMode()) {
|
|
|
|
|
preRealWeatherVisuals?.let { visuals ->
|
|
|
|
|
particleCount = visuals.count; lineWidth = visuals.width; particleLength = visuals.length
|
|
|
|
|
particleOpacity = visuals.opacity; trailOpacity = visuals.trailOpacity; trailFade = visuals.trailFade
|
|
|
|
|
@@ -266,8 +307,14 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
}
|
|
|
|
|
preRealWeatherVisuals = null
|
|
|
|
|
realWeatherMode = false
|
|
|
|
|
pureParticleMode = false
|
|
|
|
|
purePbfMode = false
|
|
|
|
|
navSeaPbfMode = false
|
|
|
|
|
navSeaPbfLayerVisibility = emptyList()
|
|
|
|
|
openRealWeatherTuningAfterStyle = false
|
|
|
|
|
updateRealWeatherButtonState()
|
|
|
|
|
updateNavSeaPbfButtonState()
|
|
|
|
|
updatePbfModeButtonState()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun updateRealWeatherButtonState() {
|
|
|
|
|
@@ -277,10 +324,67 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
button.setTextColor(if (realWeatherMode) Color.YELLOW else Color.WHITE)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun updateNavSeaPbfButtonState() {
|
|
|
|
|
val button = findViewById<Button>(R.id.validationMenuNavSeaPbf)
|
|
|
|
|
button.isSelected = navSeaPbfMode
|
|
|
|
|
button.isActivated = navSeaPbfMode
|
|
|
|
|
button.setTextColor(if (navSeaPbfMode) Color.YELLOW else Color.WHITE)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun updatePbfModeButtonState() {
|
|
|
|
|
val button = findViewById<Button>(R.id.validationMenuPbfMode)
|
|
|
|
|
button.isSelected = isPbfMatrixMode()
|
|
|
|
|
button.isActivated = isPbfMatrixMode()
|
|
|
|
|
button.setTextColor(if (isPbfMatrixMode()) Color.YELLOW else Color.WHITE)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun updateRetainedCacheButtonState() {
|
|
|
|
|
val button = findViewById<Button>(R.id.validationMenuRetainedCache)
|
|
|
|
|
button.text = if (retainedCacheEnabled) "CACHE ON" else "CACHE OFF"
|
|
|
|
|
button.setTextColor(if (retainedCacheEnabled) Color.GREEN else Color.YELLOW)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun showRetainedCacheMenu(anchor: android.view.View) {
|
|
|
|
|
PopupMenu(this, anchor).apply {
|
|
|
|
|
menu.add(0, 0, 0, "CACHE ON")
|
|
|
|
|
menu.add(0, 1, 1, "CACHE OFF")
|
|
|
|
|
menu.add(0, 2, 2, "READ SNAPSHOT")
|
|
|
|
|
setOnMenuItemClickListener { item ->
|
|
|
|
|
when (item.itemId) {
|
|
|
|
|
0 -> setRetainedCacheEnabled(true)
|
|
|
|
|
1 -> setRetainedCacheEnabled(false)
|
|
|
|
|
2 -> logRetainedCacheSnapshot()
|
|
|
|
|
}
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
show()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun setRetainedCacheEnabled(enabled: Boolean) {
|
|
|
|
|
retainedCacheEnabled = enabled
|
|
|
|
|
maplibreMap.setRetainedUnderlayEnabled(enabled)
|
|
|
|
|
updateRetainedCacheButtonState()
|
|
|
|
|
status.text = statusText()
|
|
|
|
|
Log.i(TAG, "RETAINED_CACHE_CONTROL enabled=$enabled")
|
|
|
|
|
logRetainedCacheSnapshot()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun logRetainedCacheSnapshot() {
|
|
|
|
|
val snapshot = maplibreMap.retainedUnderlayDiagnostics
|
|
|
|
|
retainedCacheEnabled = snapshot.enabled
|
|
|
|
|
updateRetainedCacheButtonState()
|
|
|
|
|
Log.i(
|
|
|
|
|
TAG,
|
|
|
|
|
"RETAINED_CACHE_SNAPSHOT enabled=${snapshot.enabled} build=${snapshot.build} reuse=${snapshot.reuse} " +
|
|
|
|
|
"fallback=${snapshot.fallback} invalidation=${snapshot.invalidation} lastReason=${snapshot.lastReason}"
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun dismissRealWeatherTuning() { realWeatherTuningMenu?.dismiss(); realWeatherTuningMenu = null }
|
|
|
|
|
|
|
|
|
|
private fun showRealWeatherTuning(anchor: android.view.View) {
|
|
|
|
|
if (!realWeatherMode) return
|
|
|
|
|
if (!usesRealWeatherAssets()) return
|
|
|
|
|
PopupMenu(this, anchor).also { popup ->
|
|
|
|
|
realWeatherTuningMenu = popup
|
|
|
|
|
popup.menu.add(0, 100, 0, "PARTICLE TUNING").isEnabled = false
|
|
|
|
|
@@ -338,6 +442,199 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
setVisualFloat("raster-particle-trail-fade", REAL_WEATHER_TRAIL_FADE) { trailFade = it }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun showNavSeaPbfMenu(anchor: android.view.View) {
|
|
|
|
|
dismissRealWeatherTuning()
|
|
|
|
|
val positions = NavSeaPbfPosition.values()
|
|
|
|
|
PopupMenu(this, anchor).apply {
|
|
|
|
|
positions.forEachIndexed { index, position ->
|
|
|
|
|
menu.add(0, index, index, position.label).isChecked = navSeaPbfMode && navSeaPbfPosition == position
|
|
|
|
|
}
|
|
|
|
|
menu.add(0, 101, 101, "PBF ON").apply {
|
|
|
|
|
isEnabled = hasProductionPbfStyle() && styleReady
|
|
|
|
|
isChecked = hasProductionPbfStyle() && navSeaPbfRenderEnabled
|
|
|
|
|
}
|
|
|
|
|
menu.add(0, 102, 102, "PBF OFF").apply {
|
|
|
|
|
isEnabled = hasProductionPbfStyle() && styleReady
|
|
|
|
|
isChecked = hasProductionPbfStyle() && !navSeaPbfRenderEnabled
|
|
|
|
|
}
|
|
|
|
|
menu.add(0, 100, 100, "PARTICLE TUNING")
|
|
|
|
|
setOnMenuItemClickListener { item ->
|
|
|
|
|
if (item.itemId in positions.indices) {
|
|
|
|
|
activateNavSeaPbf(positions[item.itemId], anchor)
|
|
|
|
|
} else if (item.itemId == 101) {
|
|
|
|
|
setNavSeaPbfRenderEnabled(true)
|
|
|
|
|
} else if (item.itemId == 102) {
|
|
|
|
|
setNavSeaPbfRenderEnabled(false)
|
|
|
|
|
} else if (item.itemId == 100) {
|
|
|
|
|
realWeatherMenuAnchor = anchor
|
|
|
|
|
showRealWeatherTuning(anchor)
|
|
|
|
|
}
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
show()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun activateNavSeaPbf(position: NavSeaPbfPosition, anchor: android.view.View) {
|
|
|
|
|
enterPbfMatrix()
|
|
|
|
|
realWeatherMenuAnchor = anchor
|
|
|
|
|
timeWindowMode = true
|
|
|
|
|
productionFeedMode = false
|
|
|
|
|
hostSubmissionMode = true
|
|
|
|
|
realWeatherMode = false
|
|
|
|
|
pureParticleMode = false
|
|
|
|
|
purePbfMode = false
|
|
|
|
|
navSeaPbfMode = true
|
|
|
|
|
navSeaPbfPosition = position
|
|
|
|
|
fixtureMode = FIXTURE_UNIFORM_EAST
|
|
|
|
|
timeMix = 0.0f
|
|
|
|
|
timelineState = "NOT READY"
|
|
|
|
|
openRealWeatherTuningAfterStyle = false
|
|
|
|
|
updateRealWeatherButtonState()
|
|
|
|
|
updateNavSeaPbfButtonState()
|
|
|
|
|
updatePbfModeButtonState()
|
|
|
|
|
if (this::maplibreMap.isInitialized) loadValidationStyle(active, enabled, "NAVSEA_PBF_${position.name}")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun showPbfModeMenu(anchor: android.view.View) {
|
|
|
|
|
PopupMenu(this, anchor).apply {
|
|
|
|
|
menu.add(0, 0, 0, "PURE PARTICLE").isChecked = pureParticleMode
|
|
|
|
|
menu.add(0, 1, 1, "PURE PBF").isChecked = purePbfMode
|
|
|
|
|
menu.add(0, 2, 2, "NAVSEA PBF").isChecked = navSeaPbfMode
|
|
|
|
|
setOnMenuItemClickListener { item ->
|
|
|
|
|
when (item.itemId) {
|
|
|
|
|
0 -> activatePureParticle(anchor)
|
|
|
|
|
1 -> activatePurePbf(anchor)
|
|
|
|
|
2 -> activateNavSeaPbf(navSeaPbfPosition, anchor)
|
|
|
|
|
}
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
show()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun showCountMenu(anchor: android.view.View) {
|
|
|
|
|
PopupMenu(this, anchor).apply {
|
|
|
|
|
menu.add(0, 0, 0, "256")
|
|
|
|
|
menu.add(0, 1, 1, "1K")
|
|
|
|
|
menu.add(0, 2, 2, "4K")
|
|
|
|
|
menu.add(0, 3, 3, "10K")
|
|
|
|
|
menu.add(0, 4, 4, "CUSTOM COUNT")
|
|
|
|
|
setOnMenuItemClickListener { item ->
|
|
|
|
|
when (item.itemId) {
|
|
|
|
|
0 -> setParticleCount(PARTICLE_COUNT_256)
|
|
|
|
|
1 -> setParticleCount(PARTICLE_COUNT_1024)
|
|
|
|
|
2 -> setParticleCount(PARTICLE_COUNT_4096)
|
|
|
|
|
3 -> setParticleCount(MAX_PARTICLE_COUNT)
|
|
|
|
|
4 -> showCustomParticleCountDialog()
|
|
|
|
|
}
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
show()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun showCustomParticleCountDialog() {
|
|
|
|
|
val input = EditText(this).apply {
|
|
|
|
|
inputType = InputType.TYPE_CLASS_NUMBER
|
|
|
|
|
setText(particleCount.toString())
|
|
|
|
|
selectAll()
|
|
|
|
|
hint = "$MIN_PARTICLE_COUNT..$MAX_PARTICLE_COUNT"
|
|
|
|
|
contentDescription = "CUSTOM PARTICLE COUNT"
|
|
|
|
|
}
|
|
|
|
|
val dialog = AlertDialog.Builder(this)
|
|
|
|
|
.setTitle("CUSTOM PARTICLE COUNT")
|
|
|
|
|
.setView(input)
|
|
|
|
|
.setNegativeButton("CANCEL", null)
|
|
|
|
|
.setPositiveButton("APPLY", null)
|
|
|
|
|
.create()
|
|
|
|
|
dialog.setOnShowListener {
|
|
|
|
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
|
|
|
|
val requested = input.text.toString().toIntOrNull()
|
|
|
|
|
if (requested == null || requested !in MIN_PARTICLE_COUNT..MAX_PARTICLE_COUNT) {
|
|
|
|
|
input.error = "Enter an integer from $MIN_PARTICLE_COUNT to $MAX_PARTICLE_COUNT"
|
|
|
|
|
} else if (setParticleCount(requested)) {
|
|
|
|
|
dialog.dismiss()
|
|
|
|
|
} else {
|
|
|
|
|
input.error = "Particle layer is not ready"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dialog.show()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun enterPbfMatrix() {
|
|
|
|
|
if (realWeatherMode) leaveRealWeatherMode()
|
|
|
|
|
if (!isPbfMatrixMode()) {
|
|
|
|
|
preRealWeatherVisuals = ParticleVisuals(particleCount, lineWidth, particleLength, particleOpacity, trailOpacity, trailFade)
|
|
|
|
|
particleCount = REAL_WEATHER_PARTICLE_COUNT
|
|
|
|
|
lineWidth = REAL_WEATHER_LINE_WIDTH
|
|
|
|
|
particleLength = REAL_WEATHER_STREAK_LENGTH
|
|
|
|
|
particleOpacity = REAL_WEATHER_PARTICLE_OPACITY
|
|
|
|
|
trailOpacity = REAL_WEATHER_TRAIL_OPACITY
|
|
|
|
|
trailFade = REAL_WEATHER_TRAIL_FADE
|
|
|
|
|
realWeatherTargetFps = WeatherRuntime.TargetFps.FPS_30
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun activatePureParticle(anchor: android.view.View) {
|
|
|
|
|
enterPbfMatrix()
|
|
|
|
|
realWeatherMenuAnchor = anchor
|
|
|
|
|
timeWindowMode = true
|
|
|
|
|
productionFeedMode = false
|
|
|
|
|
hostSubmissionMode = true
|
|
|
|
|
realWeatherMode = false
|
|
|
|
|
pureParticleMode = true
|
|
|
|
|
purePbfMode = false
|
|
|
|
|
navSeaPbfMode = false
|
|
|
|
|
fixtureMode = FIXTURE_UNIFORM_EAST
|
|
|
|
|
timeMix = 0.0f
|
|
|
|
|
timelineState = "NOT READY"
|
|
|
|
|
updateRealWeatherButtonState()
|
|
|
|
|
updateNavSeaPbfButtonState()
|
|
|
|
|
updatePbfModeButtonState()
|
|
|
|
|
if (this::maplibreMap.isInitialized) loadValidationStyle(active, enabled, "PURE_PARTICLE")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun activatePurePbf(anchor: android.view.View) {
|
|
|
|
|
enterPbfMatrix()
|
|
|
|
|
realWeatherMenuAnchor = anchor
|
|
|
|
|
timeWindowMode = false
|
|
|
|
|
productionFeedMode = false
|
|
|
|
|
hostSubmissionMode = false
|
|
|
|
|
realWeatherMode = false
|
|
|
|
|
pureParticleMode = false
|
|
|
|
|
purePbfMode = true
|
|
|
|
|
navSeaPbfMode = false
|
|
|
|
|
timelineState = "NOT READY"
|
|
|
|
|
updateRealWeatherButtonState()
|
|
|
|
|
updateNavSeaPbfButtonState()
|
|
|
|
|
updatePbfModeButtonState()
|
|
|
|
|
if (this::maplibreMap.isInitialized) loadValidationStyle(active, enabled, "PURE_PBF")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun isPbfMatrixMode(): Boolean = pureParticleMode || purePbfMode || navSeaPbfMode
|
|
|
|
|
private fun hasProductionPbfStyle(): Boolean = purePbfMode || navSeaPbfMode
|
|
|
|
|
private fun usesRealWeatherAssets(): Boolean = realWeatherMode || pureParticleMode || navSeaPbfMode
|
|
|
|
|
|
|
|
|
|
private fun setNavSeaPbfRenderEnabled(enabled: Boolean) {
|
|
|
|
|
if (!hasProductionPbfStyle() || !styleReady) return
|
|
|
|
|
if (navSeaPbfRenderEnabled == enabled) return
|
|
|
|
|
navSeaPbfRenderEnabled = enabled
|
|
|
|
|
applyNavSeaPbfRenderState()
|
|
|
|
|
status.text = statusText()
|
|
|
|
|
Log.i(TAG, "NAVSEA_PBF_RENDER enabled=$enabled affectedLayers=${navSeaPbfLayerVisibility.size}")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun applyNavSeaPbfRenderState() {
|
|
|
|
|
navSeaPbfLayerVisibility.forEach { layer ->
|
|
|
|
|
maplibreMap.setStyleLayerVisibility(
|
|
|
|
|
layer.id,
|
|
|
|
|
navSeaPbfRenderEnabled && layer.originallyVisible
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun showMenu(anchor: android.view.View, title: String, actions: IntArray) {
|
|
|
|
|
PopupMenu(this, anchor).apply {
|
|
|
|
|
actions.forEachIndexed { index, id -> menu.add(0, index, index, actionLabel(id)) }
|
|
|
|
|
@@ -383,7 +680,7 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
}
|
|
|
|
|
val commandAccepted = when {
|
|
|
|
|
accepted == WeatherRuntime.CommandResult.ACCEPTED -> true
|
|
|
|
|
realWeatherMode -> false
|
|
|
|
|
usesRealWeatherAssets() -> false
|
|
|
|
|
else -> maplibreMap.controlRasterArraySourceTimeline(PARTICLE_SOURCE_ID, command, value)
|
|
|
|
|
}
|
|
|
|
|
if (!commandAccepted) {
|
|
|
|
|
@@ -403,10 +700,7 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
R.id.validationProductionInvalid -> "INVALID PROD"
|
|
|
|
|
R.id.validationHostFieldDuplicate -> "HOST DUPLICATE"
|
|
|
|
|
R.id.validationHostFieldInvalid -> "HOST INVALID"
|
|
|
|
|
R.id.validationParticle1048576 -> "1M"
|
|
|
|
|
R.id.validationParticle262144 -> "256K"
|
|
|
|
|
R.id.validationParticle65536 -> "64K"
|
|
|
|
|
R.id.validationParticle16384 -> "16K"
|
|
|
|
|
R.id.validationParticle10000 -> "10K"
|
|
|
|
|
R.id.validationParticle4096 -> "4K"
|
|
|
|
|
R.id.validationParticle1024 -> "1K"
|
|
|
|
|
R.id.validationParticle256 -> "256"
|
|
|
|
|
@@ -440,6 +734,18 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun loadValidationStyle(active: Boolean, enabled: Boolean, reason: String = "STYLE_CHANGE") {
|
|
|
|
|
if (pureParticleMode) {
|
|
|
|
|
loadPureParticleStyle(active, enabled, reason)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (purePbfMode) {
|
|
|
|
|
loadPurePbfStyle(reason)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (navSeaPbfMode) {
|
|
|
|
|
loadNavSeaPbfStyle(active, enabled, reason)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
weatherRuntime?.dispose()
|
|
|
|
|
weatherRuntime = null
|
|
|
|
|
this.active = active
|
|
|
|
|
@@ -463,6 +769,7 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
}
|
|
|
|
|
status.text = statusText()
|
|
|
|
|
updateRealWeatherButtonState()
|
|
|
|
|
updateNavSeaPbfButtonState()
|
|
|
|
|
if (openRealWeatherTuningAfterStyle && realWeatherMode) {
|
|
|
|
|
openRealWeatherTuningAfterStyle = false
|
|
|
|
|
realWeatherMenuAnchor?.let { showRealWeatherTuning(it) }
|
|
|
|
|
@@ -470,6 +777,276 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private data class NavSeaPbfStyle(
|
|
|
|
|
val json: String,
|
|
|
|
|
val productionLayerCount: Int,
|
|
|
|
|
val seaAreaFill: Boolean,
|
|
|
|
|
val clipOutline: Boolean,
|
|
|
|
|
val belowCount: Int,
|
|
|
|
|
val aboveCount: Int,
|
|
|
|
|
val anchorLayer: String,
|
|
|
|
|
val pbfLayerVisibility: List<NavSeaPbfLayerVisibility>
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
private data class NavSeaPbfLayerVisibility(val id: String, val originallyVisible: Boolean)
|
|
|
|
|
|
|
|
|
|
private data class NavSeaProductionStyle(
|
|
|
|
|
val root: JSONObject,
|
|
|
|
|
val layers: JSONArray,
|
|
|
|
|
val layerIds: List<String>,
|
|
|
|
|
val seaIndex: Int,
|
|
|
|
|
val clipIndex: Int,
|
|
|
|
|
val specialPatternIndex: Int,
|
|
|
|
|
val hazardPointsIndex: Int,
|
|
|
|
|
val pbfLayerVisibility: List<NavSeaPbfLayerVisibility>
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
private fun loadPureParticleStyle(active: Boolean, enabled: Boolean, reason: String) {
|
|
|
|
|
weatherRuntime?.dispose()
|
|
|
|
|
weatherRuntime = null
|
|
|
|
|
this.active = active
|
|
|
|
|
this.enabled = enabled
|
|
|
|
|
styleReady = false
|
|
|
|
|
navSeaPbfLayerVisibility = emptyList()
|
|
|
|
|
val epoch = ++styleLoadEpoch
|
|
|
|
|
Log.i(TAG, "ParticleGPU: event=STYLE_LOAD epoch=$epoch reason=$reason")
|
|
|
|
|
maplibreMap.setStyle(Style.Builder().fromJson(pureParticleStyle(active, enabled))) {
|
|
|
|
|
if (pureParticleMode && epoch == styleLoadEpoch) {
|
|
|
|
|
styleReady = true
|
|
|
|
|
weatherRuntime = WeatherRuntime(
|
|
|
|
|
maplibreMap,
|
|
|
|
|
WeatherRuntime.Config(PARTICLE_SOURCE_ID, WEATHER_COLOR_LAYER_ID, PARTICLE_LAYER_ID)
|
|
|
|
|
)
|
|
|
|
|
weatherRuntime?.setTargetFps(realWeatherTargetFps)
|
|
|
|
|
setNavSeaPbfCamera(logNavSeaReady = false)
|
|
|
|
|
if (hostSubmissionMode) submitRealWeatherDataset()
|
|
|
|
|
status.text = statusText()
|
|
|
|
|
Log.i(
|
|
|
|
|
TAG,
|
|
|
|
|
"PURE_PARTICLE_READY camera=$NAVSEA_CAMERA_LATITUDE,$NAVSEA_CAMERA_LONGITUDE " +
|
|
|
|
|
"particleCount=$particleCount targetFps=${realWeatherTargetFps.name.removePrefix("FPS_")}"
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun loadPurePbfStyle(reason: String) {
|
|
|
|
|
weatherRuntime?.dispose()
|
|
|
|
|
weatherRuntime = null
|
|
|
|
|
styleReady = false
|
|
|
|
|
navSeaPbfLayerVisibility = emptyList()
|
|
|
|
|
val epoch = ++styleLoadEpoch
|
|
|
|
|
Log.i(TAG, "ParticleGPU: event=STYLE_LOAD epoch=$epoch reason=$reason")
|
|
|
|
|
maplibreMap.setStyle(Style.Builder().fromUri(NAVSEA_PBF_STYLE_URL)) { productionStyle ->
|
|
|
|
|
if (purePbfMode && epoch == styleLoadEpoch) {
|
|
|
|
|
val production = try {
|
|
|
|
|
inspectNavSeaProductionStyle(productionStyle.json)
|
|
|
|
|
} catch (error: Exception) {
|
|
|
|
|
Log.e(TAG, "NAVSEA_PBF_STYLE_ERROR reason=PURE_PBF_CONTRACT", error)
|
|
|
|
|
null
|
|
|
|
|
}
|
|
|
|
|
if (production != null) {
|
|
|
|
|
styleReady = true
|
|
|
|
|
navSeaPbfLayerVisibility = production.pbfLayerVisibility
|
|
|
|
|
applyNavSeaPbfRenderState()
|
|
|
|
|
setNavSeaPbfCamera(logNavSeaReady = false)
|
|
|
|
|
status.text = statusText()
|
|
|
|
|
Log.i(
|
|
|
|
|
TAG,
|
|
|
|
|
"PURE_PBF_READY layerCount=${production.layers.length()} seaAreaFill=${production.seaIndex >= 0} " +
|
|
|
|
|
"clipOutline=${production.clipIndex >= 0} camera=$NAVSEA_CAMERA_LATITUDE,$NAVSEA_CAMERA_LONGITUDE"
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun loadNavSeaPbfStyle(active: Boolean, enabled: Boolean, reason: String) {
|
|
|
|
|
weatherRuntime?.dispose()
|
|
|
|
|
weatherRuntime = null
|
|
|
|
|
this.active = active
|
|
|
|
|
this.enabled = enabled
|
|
|
|
|
styleReady = false
|
|
|
|
|
navSeaPbfLayerVisibility = emptyList()
|
|
|
|
|
val epoch = ++styleLoadEpoch
|
|
|
|
|
status.text = statusText()
|
|
|
|
|
Log.i(TAG, "ParticleGPU: event=STYLE_LOAD epoch=$epoch reason=$reason")
|
|
|
|
|
Log.i(TAG, "NAVSEA_PBF_STYLE_LOAD url=$NAVSEA_PBF_STYLE_URL position=${navSeaPbfPosition.name}")
|
|
|
|
|
maplibreMap.setStyle(Style.Builder().fromUri(NAVSEA_PBF_STYLE_URL)) { productionStyle ->
|
|
|
|
|
if (navSeaPbfMode && epoch == styleLoadEpoch) {
|
|
|
|
|
val augmented = try {
|
|
|
|
|
buildNavSeaPbfStyle(productionStyle.json, active, enabled, navSeaPbfPosition)
|
|
|
|
|
} catch (error: Exception) {
|
|
|
|
|
Log.e(TAG, "NAVSEA_PBF_STYLE_ERROR reason=AUGMENT_FAILED position=${navSeaPbfPosition.name}", error)
|
|
|
|
|
null
|
|
|
|
|
}
|
|
|
|
|
if (augmented != null) {
|
|
|
|
|
maplibreMap.setStyle(Style.Builder().fromJson(augmented.json)) {
|
|
|
|
|
if (navSeaPbfMode && epoch == styleLoadEpoch) {
|
|
|
|
|
styleReady = true
|
|
|
|
|
navSeaPbfLayerVisibility = augmented.pbfLayerVisibility
|
|
|
|
|
applyNavSeaPbfRenderState()
|
|
|
|
|
weatherRuntime = WeatherRuntime(
|
|
|
|
|
maplibreMap,
|
|
|
|
|
WeatherRuntime.Config(PARTICLE_SOURCE_ID, WEATHER_COLOR_LAYER_ID, PARTICLE_LAYER_ID)
|
|
|
|
|
)
|
|
|
|
|
weatherRuntime?.setTargetFps(realWeatherTargetFps)
|
|
|
|
|
updateRealWeatherButtonState()
|
|
|
|
|
updateNavSeaPbfButtonState()
|
|
|
|
|
Log.i(
|
|
|
|
|
TAG,
|
|
|
|
|
"NAVSEA_PBF_STYLE_READY layerCount=${augmented.productionLayerCount} " +
|
|
|
|
|
"seaAreaFill=${augmented.seaAreaFill} clipOutline=${augmented.clipOutline}"
|
|
|
|
|
)
|
|
|
|
|
Log.i(
|
|
|
|
|
TAG,
|
|
|
|
|
"NAVSEA_PBF_POSITION position=${navSeaPbfPosition.name} belowCount=${augmented.belowCount} " +
|
|
|
|
|
"aboveCount=${augmented.aboveCount} anchorLayer=${augmented.anchorLayer}"
|
|
|
|
|
)
|
|
|
|
|
setNavSeaPbfCamera()
|
|
|
|
|
if (hostSubmissionMode) submitRealWeatherDataset()
|
|
|
|
|
status.text = statusText()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun buildNavSeaPbfStyle(
|
|
|
|
|
productionJson: String,
|
|
|
|
|
active: Boolean,
|
|
|
|
|
enabled: Boolean,
|
|
|
|
|
position: NavSeaPbfPosition
|
|
|
|
|
): NavSeaPbfStyle {
|
|
|
|
|
val production = inspectNavSeaProductionStyle(productionJson)
|
|
|
|
|
val root = production.root
|
|
|
|
|
val sources = root.getJSONObject("sources")
|
|
|
|
|
check(!sources.has(PARTICLE_SOURCE_ID)) { "production style already contains $PARTICLE_SOURCE_ID" }
|
|
|
|
|
sources.put(PARTICLE_SOURCE_ID, JSONObject().put("type", "raster-array").put("host-field-submission", true))
|
|
|
|
|
|
|
|
|
|
val productionLayers = production.layers
|
|
|
|
|
val layerIds = production.layerIds
|
|
|
|
|
val seaIndex = production.seaIndex
|
|
|
|
|
val clipIndex = production.clipIndex
|
|
|
|
|
val specialPatternIndex = production.specialPatternIndex
|
|
|
|
|
val hazardPointsIndex = production.hazardPointsIndex
|
|
|
|
|
|
|
|
|
|
val insertionIndex = when (position) {
|
|
|
|
|
NavSeaPbfPosition.BOTTOM -> 0
|
|
|
|
|
NavSeaPbfPosition.AFTER_SEA -> seaIndex + 1
|
|
|
|
|
NavSeaPbfPosition.AFTER_CLIP -> clipIndex + 1
|
|
|
|
|
NavSeaPbfPosition.BEFORE_SYMBOL_CLUSTER -> hazardPointsIndex
|
|
|
|
|
NavSeaPbfPosition.TOP -> productionLayers.length()
|
|
|
|
|
}
|
|
|
|
|
val anchorLayer = when (position) {
|
|
|
|
|
NavSeaPbfPosition.BOTTOM -> layerIds.first()
|
|
|
|
|
NavSeaPbfPosition.AFTER_SEA -> NAVSEA_SEA_AREA_LAYER_ID
|
|
|
|
|
NavSeaPbfPosition.AFTER_CLIP -> NAVSEA_CLIP_OUTLINE_LAYER_ID
|
|
|
|
|
NavSeaPbfPosition.BEFORE_SYMBOL_CLUSTER -> NAVSEA_SPECIAL_PATTERN_LAYER_ID
|
|
|
|
|
NavSeaPbfPosition.TOP -> layerIds.last()
|
|
|
|
|
}
|
|
|
|
|
val augmentedLayers = JSONArray()
|
|
|
|
|
for (index in 0..productionLayers.length()) {
|
|
|
|
|
if (enabled && index == insertionIndex) augmentedLayers.put(navSeaParticleLayer(active))
|
|
|
|
|
if (index < productionLayers.length()) augmentedLayers.put(productionLayers.get(index))
|
|
|
|
|
}
|
|
|
|
|
root.put("layers", augmentedLayers)
|
|
|
|
|
return NavSeaPbfStyle(
|
|
|
|
|
json = root.toString(),
|
|
|
|
|
productionLayerCount = productionLayers.length(),
|
|
|
|
|
seaAreaFill = seaIndex >= 0,
|
|
|
|
|
clipOutline = clipIndex >= 0,
|
|
|
|
|
belowCount = insertionIndex,
|
|
|
|
|
aboveCount = productionLayers.length() - insertionIndex,
|
|
|
|
|
anchorLayer = anchorLayer,
|
|
|
|
|
pbfLayerVisibility = production.pbfLayerVisibility
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun inspectNavSeaProductionStyle(productionJson: String): NavSeaProductionStyle {
|
|
|
|
|
val root = JSONObject(productionJson)
|
|
|
|
|
val layers = root.getJSONArray("layers")
|
|
|
|
|
val layerIds = (0 until layers.length()).map { layers.getJSONObject(it).getString("id") }
|
|
|
|
|
val pbfLayerVisibility = (0 until layers.length()).mapNotNull { index ->
|
|
|
|
|
val layer = layers.getJSONObject(index)
|
|
|
|
|
if (layer.optString("source") != NAVSEA_PBF_SOURCE_ID) null else NavSeaPbfLayerVisibility(
|
|
|
|
|
id = layer.getString("id"),
|
|
|
|
|
originallyVisible = layer.optJSONObject("layout")?.optString("visibility", "visible") != "none"
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
val result = NavSeaProductionStyle(
|
|
|
|
|
root = root,
|
|
|
|
|
layers = layers,
|
|
|
|
|
layerIds = layerIds,
|
|
|
|
|
seaIndex = layerIds.indexOf(NAVSEA_SEA_AREA_LAYER_ID),
|
|
|
|
|
clipIndex = layerIds.indexOf(NAVSEA_CLIP_OUTLINE_LAYER_ID),
|
|
|
|
|
specialPatternIndex = layerIds.indexOf(NAVSEA_SPECIAL_PATTERN_LAYER_ID),
|
|
|
|
|
hazardPointsIndex = layerIds.indexOf(NAVSEA_HAZARD_POINTS_LAYER_ID),
|
|
|
|
|
pbfLayerVisibility = pbfLayerVisibility
|
|
|
|
|
)
|
|
|
|
|
check(
|
|
|
|
|
layers.length() == NAVSEA_PRODUCTION_LAYER_COUNT && result.seaIndex == 3 && result.clipIndex == 31 &&
|
|
|
|
|
result.specialPatternIndex == 37 && result.hazardPointsIndex == 38 && pbfLayerVisibility.isNotEmpty()
|
|
|
|
|
) {
|
|
|
|
|
"production style contract changed: count=${layers.length()} sea=${result.seaIndex} clip=${result.clipIndex} " +
|
|
|
|
|
"specialPattern=${result.specialPatternIndex} hazardPoints=${result.hazardPointsIndex} " +
|
|
|
|
|
"pbfLayers=${pbfLayerVisibility.size}"
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun pureParticleStyle(active: Boolean, enabled: Boolean): String {
|
|
|
|
|
val particleLayer = if (enabled) navSeaParticleLayer(active) else null
|
|
|
|
|
return JSONObject()
|
|
|
|
|
.put("version", 8)
|
|
|
|
|
.put("name", "Pure Particle Validation")
|
|
|
|
|
.put(
|
|
|
|
|
"sources",
|
|
|
|
|
JSONObject().put(
|
|
|
|
|
PARTICLE_SOURCE_ID,
|
|
|
|
|
JSONObject().put("type", "raster-array").put("host-field-submission", true)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
.put("layers", JSONArray().apply { if (particleLayer != null) put(particleLayer) })
|
|
|
|
|
.toString()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun navSeaParticleLayer(active: Boolean): JSONObject = JSONObject()
|
|
|
|
|
.put("id", PARTICLE_LAYER_ID)
|
|
|
|
|
.put("type", "raster-particle")
|
|
|
|
|
.put("source", PARTICLE_SOURCE_ID)
|
|
|
|
|
.put(
|
|
|
|
|
"paint",
|
|
|
|
|
JSONObject()
|
|
|
|
|
.put("raster-particle-active", active)
|
|
|
|
|
.put("raster-particle-mask-mode", maskMode)
|
|
|
|
|
.put("raster-particle-time-mix", timeMix)
|
|
|
|
|
.put("raster-particle-particle-count", particleCount)
|
|
|
|
|
.put("raster-particle-line-width", lineWidth)
|
|
|
|
|
.put("raster-particle-particle-length", particleLength)
|
|
|
|
|
.put("raster-particle-particle-opacity", particleOpacity)
|
|
|
|
|
.put("raster-particle-trail-opacity", trailOpacity)
|
|
|
|
|
.put("raster-particle-trail-fade", trailFade)
|
|
|
|
|
.put("raster-particle-speed-color-enabled", speedColorEnabled)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
private fun setNavSeaPbfCamera(logNavSeaReady: Boolean = true) {
|
|
|
|
|
maplibreMap.cameraPosition = CameraPosition.Builder()
|
|
|
|
|
.target(LatLng(NAVSEA_CAMERA_LATITUDE, NAVSEA_CAMERA_LONGITUDE))
|
|
|
|
|
.zoom(NAVSEA_CAMERA_ZOOM)
|
|
|
|
|
.bearing(NAVSEA_CAMERA_BEARING)
|
|
|
|
|
.tilt(NAVSEA_CAMERA_PITCH)
|
|
|
|
|
.build()
|
|
|
|
|
if (logNavSeaReady) {
|
|
|
|
|
Log.i(
|
|
|
|
|
TAG,
|
|
|
|
|
"NAVSEA_PBF_CAMERA_READY center=$NAVSEA_CAMERA_LATITUDE,$NAVSEA_CAMERA_LONGITUDE " +
|
|
|
|
|
"zoom=$NAVSEA_CAMERA_ZOOM bearing=$NAVSEA_CAMERA_BEARING pitch=$NAVSEA_CAMERA_PITCH"
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun setValidationProperties(active: Boolean, maskMode: Int, fixtureMode: Int = this.fixtureMode) {
|
|
|
|
|
this.active = active
|
|
|
|
|
this.maskMode = maskMode
|
|
|
|
|
@@ -509,23 +1086,13 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
Log.i(TAG, "ParticleGPU: event=MIX_PROPERTY_ONLY value=$timeMix styleEpoch=$styleLoadEpoch")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun setParticleCount(requestedParticleCount: Int) {
|
|
|
|
|
if (!styleReady) {
|
|
|
|
|
Log.i(TAG, "ParticleGPU: event=PARTICLE_COUNT_REJECTED reason=STYLE_LOADING requestedParticleCount=$requestedParticleCount styleEpoch=$styleLoadEpoch")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (!enabled) {
|
|
|
|
|
Log.i(TAG, "ParticleGPU: event=PARTICLE_COUNT_REJECTED reason=DISABLED requestedParticleCount=$requestedParticleCount styleEpoch=$styleLoadEpoch")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
Log.i(TAG, "ParticleGPU: event=PARTICLE_COUNT_REQUEST requestedParticleCount=$requestedParticleCount styleEpoch=$styleLoadEpoch")
|
|
|
|
|
if (!setParticleFloat("raster-particle-particle-count", requestedParticleCount.toFloat())) {
|
|
|
|
|
Log.i(TAG, "ParticleGPU: event=PARTICLE_COUNT_REJECTED reason=CORE_PROPERTY_REJECTED requestedParticleCount=$requestedParticleCount styleEpoch=$styleLoadEpoch")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
private fun setParticleCount(requestedParticleCount: Int): Boolean {
|
|
|
|
|
if (requestedParticleCount !in MIN_PARTICLE_COUNT..MAX_PARTICLE_COUNT || !styleReady || !enabled) return false
|
|
|
|
|
if (!setParticleFloat("raster-particle-particle-count", requestedParticleCount.toFloat())) return false
|
|
|
|
|
particleCount = requestedParticleCount
|
|
|
|
|
status.text = statusText()
|
|
|
|
|
Log.i(TAG, "ParticleGPU: event=PARTICLE_COUNT_APPLIED requestedParticleCount=$requestedParticleCount effectiveParticleCount=$particleCount styleEpoch=$styleLoadEpoch")
|
|
|
|
|
Log.i(TAG, "PARTICLE_COUNT_CHANGED count=$particleCount")
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun setVisualFloat(property: String, requested: Float, apply: (Float) -> Unit) {
|
|
|
|
|
@@ -572,7 +1139,7 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return runtimeResult == WeatherRuntime.CommandResult.ACCEPTED ||
|
|
|
|
|
(!realWeatherMode && runtimeResult == null && maplibreMap.setStyleLayerFloatProperty(PARTICLE_LAYER_ID, property, value))
|
|
|
|
|
(!usesRealWeatherAssets() && runtimeResult == null && maplibreMap.setStyleLayerFloatProperty(PARTICLE_LAYER_ID, property, value))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun resetVisualDefaults() {
|
|
|
|
|
@@ -647,11 +1214,15 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
timelineState = if (results.size == 4 && results.all { it.accepted() }) "READY" else "NOT READY"
|
|
|
|
|
fitRealWeatherBounds()
|
|
|
|
|
if (!navSeaPbfMode && !pureParticleMode) fitRealWeatherBounds()
|
|
|
|
|
status.text = statusText()
|
|
|
|
|
Log.i(TAG, "ParticleField: event=REAL_WEATHER_INITIAL_WINDOW_READY results=${results.joinToString(",") { it.name }} timeline=$timelineState")
|
|
|
|
|
Log.i(TAG, "WeatherColorLayer: event=WEATHER_COLOR_FIELD_READY source=$PARTICLE_SOURCE_ID scale=0..30mps")
|
|
|
|
|
Log.i(TAG, "ParticleField: event=REAL_WEATHER_COMPOSITE_READY source=$PARTICLE_SOURCE_ID basemap=GSI failSoft=true color=weather-color particle=raster-particle")
|
|
|
|
|
if (navSeaPbfMode) {
|
|
|
|
|
Log.i(TAG, "NAVSEA_PBF_PARTICLE_READY source=$PARTICLE_SOURCE_ID weatherColor=false")
|
|
|
|
|
} else if (!pureParticleMode) {
|
|
|
|
|
Log.i(TAG, "WeatherColorLayer: event=WEATHER_COLOR_FIELD_READY source=$PARTICLE_SOURCE_ID scale=0..30mps")
|
|
|
|
|
Log.i(TAG, "ParticleField: event=REAL_WEATHER_COMPOSITE_READY source=$PARTICLE_SOURCE_ID basemap=GSI failSoft=true color=weather-color particle=raster-particle")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun fitRealWeatherBounds() {
|
|
|
|
|
@@ -721,9 +1292,28 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
} else {
|
|
|
|
|
"EAST"
|
|
|
|
|
}
|
|
|
|
|
val playback = if (timeWindowMode) " · ${if (realWeatherMode) "REAL WEATHER" else if (hostSubmissionMode) "HOST" else if (productionFeedMode) "PRODUCTION" else "FIXTURE"} · TL $timelineState · $timelineSpeed" else ""
|
|
|
|
|
val summary = "$mask · $runtime · $fields$playback · MIX ${(timeMix * 100).toInt()}%"
|
|
|
|
|
return if (realWeatherMode) "$summary\n${realWeatherHudValues()}" else "$summary · $particleCount · W ${"%.1f".format(lineWidth)} L ${"%.3f".format(particleLength)} O ${(particleOpacity * 100).toInt()} T ${(trailOpacity * 100).toInt()} F ${(trailFade * 100).toInt()} C ${if (speedColorEnabled) "ON" else "OFF"}"
|
|
|
|
|
val playbackMode = when {
|
|
|
|
|
pureParticleMode -> "PURE PARTICLE"
|
|
|
|
|
purePbfMode -> "PURE PBF"
|
|
|
|
|
navSeaPbfMode -> "NAVSEA PBF"
|
|
|
|
|
realWeatherMode -> "REAL WEATHER"
|
|
|
|
|
hostSubmissionMode -> "HOST"
|
|
|
|
|
productionFeedMode -> "PRODUCTION"
|
|
|
|
|
else -> "FIXTURE"
|
|
|
|
|
}
|
|
|
|
|
val playback = if (timeWindowMode) " · $playbackMode · TL $timelineState · $timelineSpeed" else ""
|
|
|
|
|
val scene = when {
|
|
|
|
|
navSeaPbfMode -> "NAVSEA PBF · ${navSeaPbfPosition.label} · PBF ${if (navSeaPbfRenderEnabled) "ON" else "OFF"} · CACHE ${if (retainedCacheEnabled) "ON" else "OFF"} · "
|
|
|
|
|
pureParticleMode -> "PURE PARTICLE · CACHE ${if (retainedCacheEnabled) "ON" else "OFF"} · "
|
|
|
|
|
purePbfMode -> "PURE PBF · PBF ${if (navSeaPbfRenderEnabled) "ON" else "OFF"} · CACHE ${if (retainedCacheEnabled) "ON" else "OFF"} · "
|
|
|
|
|
else -> ""
|
|
|
|
|
}
|
|
|
|
|
val summary = "$scene$mask · $runtime · $fields$playback · MIX ${(timeMix * 100).toInt()}%"
|
|
|
|
|
return when {
|
|
|
|
|
purePbfMode -> summary
|
|
|
|
|
usesRealWeatherAssets() -> "$summary\n${realWeatherHudValues()}"
|
|
|
|
|
else -> "$summary · $particleCount · W ${"%.1f".format(lineWidth)} L ${"%.3f".format(particleLength)} O ${(particleOpacity * 100).toInt()} T ${(trailOpacity * 100).toInt()} F ${(trailFade * 100).toInt()} C ${if (speedColorEnabled) "ON" else "OFF"}"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun realWeatherHudValues(): String = "FPS ${realWeatherTargetFps.name.removePrefix("FPS_")} · P $particleCount · W ${"%.2f".format(lineWidth)} · L ${"%.4f".format(particleLength)} · O ${"%.2f".format(particleOpacity)} · T ${"%.2f".format(trailOpacity)} · F ${"%.2f".format(trailFade)}"
|
|
|
|
|
@@ -829,6 +1419,12 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
outState.putBoolean(STATE_TIME_WINDOW, timeWindowMode)
|
|
|
|
|
outState.putBoolean(STATE_HOST_SUBMISSION, hostSubmissionMode)
|
|
|
|
|
outState.putBoolean(STATE_REAL_WEATHER, realWeatherMode)
|
|
|
|
|
outState.putBoolean(STATE_PURE_PARTICLE, pureParticleMode)
|
|
|
|
|
outState.putBoolean(STATE_PURE_PBF, purePbfMode)
|
|
|
|
|
outState.putBoolean(STATE_NAVSEA_PBF, navSeaPbfMode)
|
|
|
|
|
outState.putString(STATE_NAVSEA_PBF_POSITION, navSeaPbfPosition.name)
|
|
|
|
|
outState.putBoolean(STATE_NAVSEA_PBF_RENDER_ENABLED, navSeaPbfRenderEnabled)
|
|
|
|
|
outState.putBoolean(STATE_RETAINED_CACHE_ENABLED, retainedCacheEnabled)
|
|
|
|
|
super.onSaveInstanceState(outState)
|
|
|
|
|
mapView.onSaveInstanceState(outState)
|
|
|
|
|
}
|
|
|
|
|
@@ -856,6 +1452,12 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
private const val STATE_TIME_WINDOW = "particle_validation.time_window"
|
|
|
|
|
private const val STATE_HOST_SUBMISSION = "particle_validation.host_submission"
|
|
|
|
|
private const val STATE_REAL_WEATHER = "particle_validation.real_weather"
|
|
|
|
|
private const val STATE_PURE_PARTICLE = "particle_validation.pure_particle"
|
|
|
|
|
private const val STATE_PURE_PBF = "particle_validation.pure_pbf"
|
|
|
|
|
private const val STATE_NAVSEA_PBF = "particle_validation.navsea_pbf"
|
|
|
|
|
private const val STATE_NAVSEA_PBF_POSITION = "particle_validation.navsea_pbf_position"
|
|
|
|
|
private const val STATE_NAVSEA_PBF_RENDER_ENABLED = "particle_validation.navsea_pbf_render_enabled"
|
|
|
|
|
private const val STATE_RETAINED_CACHE_ENABLED = "particle_validation.retained_cache_enabled"
|
|
|
|
|
private const val PARTICLE_LAYER_ID = "particle-raster-core"
|
|
|
|
|
private const val WEATHER_COLOR_LAYER_ID = "weather-color-real"
|
|
|
|
|
private const val PARTICLE_SOURCE_ID = "particle-fixture"
|
|
|
|
|
@@ -871,10 +1473,8 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
private const val PARTICLE_COUNT_2048 = 2048
|
|
|
|
|
private const val PARTICLE_COUNT_4096 = 4096
|
|
|
|
|
private const val PARTICLE_COUNT_8192 = 8192
|
|
|
|
|
private const val PARTICLE_COUNT_16384 = 16384
|
|
|
|
|
private const val PARTICLE_COUNT_65536 = 65536
|
|
|
|
|
private const val PARTICLE_COUNT_262144 = 262144
|
|
|
|
|
private const val PARTICLE_COUNT_1048576 = 1048576
|
|
|
|
|
private const val MIN_PARTICLE_COUNT = 1
|
|
|
|
|
private const val MAX_PARTICLE_COUNT = 10000
|
|
|
|
|
private const val DEFAULT_LINE_WIDTH = 2.0f
|
|
|
|
|
private const val DEFAULT_PARTICLE_LENGTH = 0.018f
|
|
|
|
|
private const val DEFAULT_PARTICLE_OPACITY = 1.0f
|
|
|
|
|
@@ -886,6 +1486,18 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
|
|
|
|
private const val REAL_WEATHER_PARTICLE_OPACITY = 0.75f
|
|
|
|
|
private const val REAL_WEATHER_TRAIL_OPACITY = 0.55f
|
|
|
|
|
private const val REAL_WEATHER_TRAIL_FADE = 0.88f
|
|
|
|
|
private val REAL_WEATHER_PARTICLE_COUNTS = intArrayOf(PARTICLE_COUNT_256, PARTICLE_COUNT_512, PARTICLE_COUNT_1024, PARTICLE_COUNT_2048, PARTICLE_COUNT_4096, PARTICLE_COUNT_8192, PARTICLE_COUNT_16384)
|
|
|
|
|
private const val NAVSEA_PBF_STYLE_URL = "https://pbf.jpcn.ccwu.cc/style/style.navsea-delivery-full-semantic-extentfix.json"
|
|
|
|
|
private const val NAVSEA_PBF_SOURCE_ID = "navsea_delivery"
|
|
|
|
|
private const val NAVSEA_PRODUCTION_LAYER_COUNT = 63
|
|
|
|
|
private const val NAVSEA_SEA_AREA_LAYER_ID = "sea-area-fill"
|
|
|
|
|
private const val NAVSEA_CLIP_OUTLINE_LAYER_ID = "clip-outline"
|
|
|
|
|
private const val NAVSEA_SPECIAL_PATTERN_LAYER_ID = "special-pattern-line-754"
|
|
|
|
|
private const val NAVSEA_HAZARD_POINTS_LAYER_ID = "hazard-points"
|
|
|
|
|
private const val NAVSEA_CAMERA_LATITUDE = 33.45
|
|
|
|
|
private const val NAVSEA_CAMERA_LONGITUDE = 129.97
|
|
|
|
|
private const val NAVSEA_CAMERA_ZOOM = 10.5
|
|
|
|
|
private const val NAVSEA_CAMERA_BEARING = 0.0
|
|
|
|
|
private const val NAVSEA_CAMERA_PITCH = 0.0
|
|
|
|
|
private val REAL_WEATHER_PARTICLE_COUNTS = intArrayOf(PARTICLE_COUNT_256, PARTICLE_COUNT_512, PARTICLE_COUNT_1024, PARTICLE_COUNT_2048, PARTICLE_COUNT_4096, PARTICLE_COUNT_8192, MAX_PARTICLE_COUNT)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|