|
|
|
|
@@ -12,6 +12,7 @@ import com.facebook.react.uimanager.IllegalViewOperationException
|
|
|
|
|
import com.facebook.react.uimanager.UIManagerHelper
|
|
|
|
|
import com.facebook.react.uimanager.common.UIManagerType
|
|
|
|
|
import org.maplibre.android.maps.MapLibreMap
|
|
|
|
|
import org.maplibre.android.maps.Style
|
|
|
|
|
import org.maplibre.android.maps.WeatherRuntime
|
|
|
|
|
import org.maplibre.reactnative.components.mapview.MLRNMapView
|
|
|
|
|
|
|
|
|
|
@@ -24,8 +25,21 @@ class WeatherRuntimeBridgeModule(private val context: ReactApplicationContext) :
|
|
|
|
|
@ReactMethod
|
|
|
|
|
override fun attach(reactTag: Double, promise: Promise) = withMapView(reactTag, promise) { view, map ->
|
|
|
|
|
val style = map.style ?: return@withMapView reject(promise, "STYLE_NOT_READY", "The MapLibre style is not loaded")
|
|
|
|
|
if (style.getSource(WeatherIds.SOURCE) == null || style.getLayer(WeatherIds.COLOR_LAYER) == null || style.getLayer(WeatherIds.PARTICLE_LAYER) == null) {
|
|
|
|
|
return@withMapView reject(promise, "WEATHER_OBJECTS_NOT_READY", "The Test Host weather source/layers are absent from the current style")
|
|
|
|
|
val sourceExists = style.getSource(WeatherIds.SOURCE) != null
|
|
|
|
|
val weatherColorLayerExists = style.getLayer(WeatherIds.COLOR_LAYER) != null
|
|
|
|
|
val particleLayerExists = style.getLayer(WeatherIds.PARTICLE_LAYER) != null
|
|
|
|
|
if (!sourceExists || !weatherColorLayerExists || !particleLayerExists) {
|
|
|
|
|
return@withMapView reject(
|
|
|
|
|
promise,
|
|
|
|
|
"WEATHER_OBJECTS_NOT_READY",
|
|
|
|
|
weatherObjectsNotReadyDiagnostic(
|
|
|
|
|
reactTag.toInt(),
|
|
|
|
|
style,
|
|
|
|
|
sourceExists,
|
|
|
|
|
weatherColorLayerExists,
|
|
|
|
|
particleLayerExists,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
val runtime = map.replaceWeatherRuntime(WeatherRuntime.Config(WeatherIds.SOURCE, WeatherIds.COLOR_LAYER, WeatherIds.PARTICLE_LAYER))
|
|
|
|
|
// MLRN drops its MapView by detaching it. This listener captures only that concrete map,
|
|
|
|
|
@@ -126,7 +140,60 @@ class WeatherRuntimeBridgeModule(private val context: ReactApplicationContext) :
|
|
|
|
|
private fun snapshot(runtime: WeatherRuntime) = Arguments.createMap().apply {
|
|
|
|
|
putString("state", runtime.state.name); putString("playbackState", runtime.playbackState.name); putString("targetFps", runtime.targetFps.name); putBoolean("disposed", runtime.isDisposed)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Keep the attach rejection searchable and bounded: MLRN lifecycle investigation needs the
|
|
|
|
|
* exact absent weather object plus enough final-style context, but never an unbounded dump.
|
|
|
|
|
* This executes on the UI queue together with the Style lookup above.
|
|
|
|
|
*/
|
|
|
|
|
private fun weatherObjectsNotReadyDiagnostic(
|
|
|
|
|
reactTag: Int,
|
|
|
|
|
style: Style,
|
|
|
|
|
sourceExists: Boolean,
|
|
|
|
|
weatherColorLayerExists: Boolean,
|
|
|
|
|
particleLayerExists: Boolean,
|
|
|
|
|
): String {
|
|
|
|
|
val sourceIds = style.getSources().map { it.id }
|
|
|
|
|
val layerIds = style.getLayers().map { it.id }
|
|
|
|
|
return buildString {
|
|
|
|
|
append("WEATHER_OBJECTS_NOT_READY reactTag=").append(reactTag)
|
|
|
|
|
append(" mapView=present mapLibreMap=present style=present")
|
|
|
|
|
append(" source[").append(WeatherIds.SOURCE).append("]=")
|
|
|
|
|
.append(presence(sourceExists))
|
|
|
|
|
append(" layer[").append(WeatherIds.COLOR_LAYER).append("]=")
|
|
|
|
|
.append(presence(weatherColorLayerExists))
|
|
|
|
|
append(" layer[").append(WeatherIds.PARTICLE_LAYER).append("]=")
|
|
|
|
|
.append(presence(particleLayerExists))
|
|
|
|
|
append(" styleSourceCount=").append(sourceIds.size)
|
|
|
|
|
append(" styleLayerCount=").append(layerIds.size)
|
|
|
|
|
append(" styleSourceIds=").append(boundedIds(sourceIds))
|
|
|
|
|
append(" styleLayerIds=").append(boundedIds(layerIds))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun presence(exists: Boolean) = if (exists) "present" else "missing"
|
|
|
|
|
|
|
|
|
|
private fun boundedIds(ids: List<String>): String {
|
|
|
|
|
val weatherIds = ids.filter { it in WeatherIds.ALL }.distinct()
|
|
|
|
|
val otherIds = ids.filterNot { it in WeatherIds.ALL }.take(MAX_STYLE_IDS - weatherIds.size)
|
|
|
|
|
val shown = weatherIds + otherIds
|
|
|
|
|
val omitted = ids.size - shown.size
|
|
|
|
|
return buildString {
|
|
|
|
|
append('[').append(shown.joinToString(","))
|
|
|
|
|
if (omitted > 0) append(",…+").append(omitted)
|
|
|
|
|
append(']')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun reject(promise: Promise, code: String, message: String) = promise.reject(code, message)
|
|
|
|
|
private object WeatherIds { const val SOURCE = "rn-weather-fields"; const val COLOR_LAYER = "rn-weather-color"; const val PARTICLE_LAYER = "rn-weather-particles" }
|
|
|
|
|
companion object { const val NAME = "WeatherRuntimeBridge" }
|
|
|
|
|
private object WeatherIds {
|
|
|
|
|
const val SOURCE = "rn-weather-fields"
|
|
|
|
|
const val COLOR_LAYER = "rn-weather-color"
|
|
|
|
|
const val PARTICLE_LAYER = "rn-weather-particles"
|
|
|
|
|
val ALL = setOf(SOURCE, COLOR_LAYER, PARTICLE_LAYER)
|
|
|
|
|
}
|
|
|
|
|
companion object {
|
|
|
|
|
const val NAME = "WeatherRuntimeBridge"
|
|
|
|
|
const val MAX_STYLE_IDS = 24
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|