diff --git a/__tests__/interface.test.js b/__tests__/interface.test.js index 458d4ed91..d7e729968 100644 --- a/__tests__/interface.test.js +++ b/__tests__/interface.test.js @@ -44,6 +44,8 @@ describe('Public Interface', () => { 'SkyLayer', 'Terrain', 'Atmosphere', + 'Snow', + 'Rain', // sources 'VectorSource', diff --git a/android/src/main/java/com/rnmapbox/rnmbx/RNMBXPackage.kt b/android/src/main/java/com/rnmapbox/rnmbx/RNMBXPackage.kt index b86218ce8..f82fadd32 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/RNMBXPackage.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/RNMBXPackage.kt @@ -26,6 +26,8 @@ import com.rnmapbox.rnmbx.components.mapview.NativeMapViewModule import com.rnmapbox.rnmbx.components.mapview.RNMBXMapViewManager import com.rnmapbox.rnmbx.components.styles.RNMBXStyleImportManager import com.rnmapbox.rnmbx.components.styles.atmosphere.RNMBXAtmosphereManager +import com.rnmapbox.rnmbx.components.styles.snow.RNMBXSnowManager +import com.rnmapbox.rnmbx.components.styles.rain.RNMBXRainManager import com.rnmapbox.rnmbx.components.styles.layers.RNMBXBackgroundLayerManager import com.rnmapbox.rnmbx.components.styles.layers.RNMBXCircleLayerManager import com.rnmapbox.rnmbx.components.styles.layers.RNMBXFillExtrusionLayerManager @@ -169,6 +171,8 @@ class RNMBXPackage : TurboReactPackage() { managers.add(RNMBXSkyLayerManager()) managers.add(RNMBXTerrainManager()) managers.add(RNMBXAtmosphereManager()) + managers.add(RNMBXSnowManager()) + managers.add(RNMBXRainManager()) managers.add(RNMBXBackgroundLayerManager()) managers.add(RNMBXLightManager()) managers.add(RNMBXModelLayerManager()) diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/styles/RNMBXStyleFactory.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/RNMBXStyleFactory.kt index 0fd2d91a2..1420e716d 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/styles/RNMBXStyleFactory.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/RNMBXStyleFactory.kt @@ -18,6 +18,8 @@ import com.mapbox.maps.extension.style.layers.generated.SymbolLayer import com.mapbox.maps.extension.style.layers.generated.HeatmapLayer import com.mapbox.maps.extension.style.layers.generated.HillshadeLayer import com.mapbox.maps.extension.style.atmosphere.generated.Atmosphere +import com.mapbox.maps.extension.style.precipitations.generated.Snow +import com.mapbox.maps.extension.style.precipitations.generated.Rain import com.mapbox.maps.extension.style.terrain.generated.Terrain import com.mapbox.maps.extension.style.layers.generated.ModelLayer // import com.mapbox.maps.extension.style.layers.properties.generated.Visibility @@ -189,6 +191,10 @@ object RNMBXStyleFactory { setLineElevationReference(layer, styleValue) "lineCrossSlope" -> setLineCrossSlope(layer, styleValue) + "lineElevationGroundScale" -> + setLineElevationGroundScale(layer, styleValue) + "lineElevationGroundScaleTransition" -> + setLineElevationGroundScaleTransition(layer, styleValue) "linePatternCrossFade" -> style.addImage(styleValue!!, styleKey, object : OnAllImagesLoaded { override fun onAllImagesLoaded() { @@ -1027,6 +1033,118 @@ object RNMBXStyleFactory { } } } + fun setSnowLayerStyle(layer: Snow, style: RNMBXStyle ) { + val styleKeys = style.allStyleKeys + + if (styleKeys.isEmpty()) { + return + } + + for (styleKey in styleKeys) { + try { + val styleValue = style.getStyleValueForKey(styleKey) + + when (styleKey) { + "density" -> + setDensity(layer, styleValue) + "densityTransition" -> + setDensityTransition(layer, styleValue) + "intensity" -> + setIntensity(layer, styleValue) + "intensityTransition" -> + setIntensityTransition(layer, styleValue) + "color" -> + setColor(layer, styleValue) + "colorTransition" -> + setColorTransition(layer, styleValue) + "opacity" -> + setOpacity(layer, styleValue) + "opacityTransition" -> + setOpacityTransition(layer, styleValue) + "vignette" -> + setVignette(layer, styleValue) + "vignetteTransition" -> + setVignetteTransition(layer, styleValue) + "vignetteColor" -> + setVignetteColor(layer, styleValue) + "vignetteColorTransition" -> + setVignetteColorTransition(layer, styleValue) + "centerThinning" -> + setCenterThinning(layer, styleValue) + "centerThinningTransition" -> + setCenterThinningTransition(layer, styleValue) + "direction" -> + setDirection(layer, styleValue) + "directionTransition" -> + setDirectionTransition(layer, styleValue) + "flakeSize" -> + setFlakeSize(layer, styleValue) + "flakeSizeTransition" -> + setFlakeSizeTransition(layer, styleValue) + } + } catch (e: MapboxStyleException) { + Logger.e(LOG_TAG, "Failed to update: $styleKey ${e.message}") + } + } + } + fun setRainLayerStyle(layer: Rain, style: RNMBXStyle ) { + val styleKeys = style.allStyleKeys + + if (styleKeys.isEmpty()) { + return + } + + for (styleKey in styleKeys) { + try { + val styleValue = style.getStyleValueForKey(styleKey) + + when (styleKey) { + "density" -> + setDensity(layer, styleValue) + "densityTransition" -> + setDensityTransition(layer, styleValue) + "intensity" -> + setIntensity(layer, styleValue) + "intensityTransition" -> + setIntensityTransition(layer, styleValue) + "color" -> + setColor(layer, styleValue) + "colorTransition" -> + setColorTransition(layer, styleValue) + "opacity" -> + setOpacity(layer, styleValue) + "opacityTransition" -> + setOpacityTransition(layer, styleValue) + "vignette" -> + setVignette(layer, styleValue) + "vignetteTransition" -> + setVignetteTransition(layer, styleValue) + "vignetteColor" -> + setVignetteColor(layer, styleValue) + "vignetteColorTransition" -> + setVignetteColorTransition(layer, styleValue) + "centerThinning" -> + setCenterThinning(layer, styleValue) + "centerThinningTransition" -> + setCenterThinningTransition(layer, styleValue) + "direction" -> + setDirection(layer, styleValue) + "directionTransition" -> + setDirectionTransition(layer, styleValue) + "dropletSize" -> + setDropletSize(layer, styleValue) + "dropletSizeTransition" -> + setDropletSizeTransition(layer, styleValue) + "distortionStrength" -> + setDistortionStrength(layer, styleValue) + "distortionStrengthTransition" -> + setDistortionStrengthTransition(layer, styleValue) + } + } catch (e: MapboxStyleException) { + Logger.e(LOG_TAG, "Failed to update: $styleKey ${e.message}") + } + } + } fun setTerrainLayerStyle(layer: Terrain, style: RNMBXStyle ) { val styleKeys = style.allStyleKeys @@ -1732,6 +1850,32 @@ object RNMBXStyleFactory { } } + fun setLineElevationGroundScale(layer: LineLayer, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.lineElevationGroundScale(expression) + } else { + Logger.e("RNMBXLine", "Expression for lineElevationGroundScale is null") + } + } else { + val value = styleValue.getDouble(VALUE_KEY) + if (value != null) { + layer.lineElevationGroundScale(value) + } else { + Logger.e("RNMBXLine", "value for lineElevationGroundScale is null") + } + } + } + + + fun setLineElevationGroundScaleTransition(layer: LineLayer, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.lineElevationGroundScaleTransition(transition); + } + } + fun setLinePatternCrossFade(layer: LineLayer, styleValue: RNMBXStyleValue ) { if (styleValue.isExpression()) { val expression = styleValue.getExpression() @@ -5671,6 +5815,500 @@ object RNMBXStyleFactory { } } + fun setDensity(layer: Snow, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.density(expression) + } else { + Logger.e("RNMBXSnow", "Expression for density is null") + } + } else { + val value = styleValue.getDouble(VALUE_KEY) + if (value != null) { + layer.density(value) + } else { + Logger.e("RNMBXSnow", "value for density is null") + } + } + } + + + fun setDensityTransition(layer: Snow, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.densityTransition(transition); + } + } + + fun setIntensity(layer: Snow, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.intensity(expression) + } else { + Logger.e("RNMBXSnow", "Expression for intensity is null") + } + } else { + val value = styleValue.getDouble(VALUE_KEY) + if (value != null) { + layer.intensity(value) + } else { + Logger.e("RNMBXSnow", "value for intensity is null") + } + } + } + + + fun setIntensityTransition(layer: Snow, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.intensityTransition(transition); + } + } + + fun setColor(layer: Snow, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.color(expression) + } else { + Logger.e("RNMBXSnow", "Expression for color is null") + } + } else { + val value = styleValue.getInt(VALUE_KEY) + if (value != null) { + layer.color(value) + } else { + Logger.e("RNMBXSnow", "value for color is null") + } + } + } + + + fun setColorTransition(layer: Snow, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.colorTransition(transition); + } + } + + fun setOpacity(layer: Snow, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.opacity(expression) + } else { + Logger.e("RNMBXSnow", "Expression for opacity is null") + } + } else { + val value = styleValue.getDouble(VALUE_KEY) + if (value != null) { + layer.opacity(value) + } else { + Logger.e("RNMBXSnow", "value for opacity is null") + } + } + } + + + fun setOpacityTransition(layer: Snow, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.opacityTransition(transition); + } + } + + fun setVignette(layer: Snow, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.vignette(expression) + } else { + Logger.e("RNMBXSnow", "Expression for vignette is null") + } + } else { + val value = styleValue.getDouble(VALUE_KEY) + if (value != null) { + layer.vignette(value) + } else { + Logger.e("RNMBXSnow", "value for vignette is null") + } + } + } + + + fun setVignetteTransition(layer: Snow, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.vignetteTransition(transition); + } + } + + fun setVignetteColor(layer: Snow, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.vignetteColor(expression) + } else { + Logger.e("RNMBXSnow", "Expression for vignetteColor is null") + } + } else { + val value = styleValue.getInt(VALUE_KEY) + if (value != null) { + layer.vignetteColor(value) + } else { + Logger.e("RNMBXSnow", "value for vignetteColor is null") + } + } + } + + + fun setVignetteColorTransition(layer: Snow, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.vignetteColorTransition(transition); + } + } + + fun setCenterThinning(layer: Snow, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.centerThinning(expression) + } else { + Logger.e("RNMBXSnow", "Expression for centerThinning is null") + } + } else { + val value = styleValue.getDouble(VALUE_KEY) + if (value != null) { + layer.centerThinning(value) + } else { + Logger.e("RNMBXSnow", "value for centerThinning is null") + } + } + } + + + fun setCenterThinningTransition(layer: Snow, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.centerThinningTransition(transition); + } + } + + fun setDirection(layer: Snow, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.direction(expression) + } else { + Logger.e("RNMBXSnow", "Expression for direction is null") + } + } else { + val value = styleValue.getFloatArray(VALUE_KEY) + if (value != null) { + layer.direction(value) + } else { + Logger.e("RNMBXSnow", "value for direction is null") + } + } + } + + + fun setDirectionTransition(layer: Snow, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.directionTransition(transition); + } + } + + fun setFlakeSize(layer: Snow, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.flakeSize(expression) + } else { + Logger.e("RNMBXSnow", "Expression for flakeSize is null") + } + } else { + val value = styleValue.getDouble(VALUE_KEY) + if (value != null) { + layer.flakeSize(value) + } else { + Logger.e("RNMBXSnow", "value for flakeSize is null") + } + } + } + + + fun setFlakeSizeTransition(layer: Snow, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.flakeSizeTransition(transition); + } + } + + fun setDensity(layer: Rain, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.density(expression) + } else { + Logger.e("RNMBXRain", "Expression for density is null") + } + } else { + val value = styleValue.getDouble(VALUE_KEY) + if (value != null) { + layer.density(value) + } else { + Logger.e("RNMBXRain", "value for density is null") + } + } + } + + + fun setDensityTransition(layer: Rain, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.densityTransition(transition); + } + } + + fun setIntensity(layer: Rain, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.intensity(expression) + } else { + Logger.e("RNMBXRain", "Expression for intensity is null") + } + } else { + val value = styleValue.getDouble(VALUE_KEY) + if (value != null) { + layer.intensity(value) + } else { + Logger.e("RNMBXRain", "value for intensity is null") + } + } + } + + + fun setIntensityTransition(layer: Rain, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.intensityTransition(transition); + } + } + + fun setColor(layer: Rain, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.color(expression) + } else { + Logger.e("RNMBXRain", "Expression for color is null") + } + } else { + val value = styleValue.getInt(VALUE_KEY) + if (value != null) { + layer.color(value) + } else { + Logger.e("RNMBXRain", "value for color is null") + } + } + } + + + fun setColorTransition(layer: Rain, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.colorTransition(transition); + } + } + + fun setOpacity(layer: Rain, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.opacity(expression) + } else { + Logger.e("RNMBXRain", "Expression for opacity is null") + } + } else { + val value = styleValue.getDouble(VALUE_KEY) + if (value != null) { + layer.opacity(value) + } else { + Logger.e("RNMBXRain", "value for opacity is null") + } + } + } + + + fun setOpacityTransition(layer: Rain, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.opacityTransition(transition); + } + } + + fun setVignette(layer: Rain, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.vignette(expression) + } else { + Logger.e("RNMBXRain", "Expression for vignette is null") + } + } else { + val value = styleValue.getDouble(VALUE_KEY) + if (value != null) { + layer.vignette(value) + } else { + Logger.e("RNMBXRain", "value for vignette is null") + } + } + } + + + fun setVignetteTransition(layer: Rain, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.vignetteTransition(transition); + } + } + + fun setVignetteColor(layer: Rain, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.vignetteColor(expression) + } else { + Logger.e("RNMBXRain", "Expression for vignetteColor is null") + } + } else { + val value = styleValue.getInt(VALUE_KEY) + if (value != null) { + layer.vignetteColor(value) + } else { + Logger.e("RNMBXRain", "value for vignetteColor is null") + } + } + } + + + fun setVignetteColorTransition(layer: Rain, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.vignetteColorTransition(transition); + } + } + + fun setCenterThinning(layer: Rain, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.centerThinning(expression) + } else { + Logger.e("RNMBXRain", "Expression for centerThinning is null") + } + } else { + val value = styleValue.getDouble(VALUE_KEY) + if (value != null) { + layer.centerThinning(value) + } else { + Logger.e("RNMBXRain", "value for centerThinning is null") + } + } + } + + + fun setCenterThinningTransition(layer: Rain, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.centerThinningTransition(transition); + } + } + + fun setDirection(layer: Rain, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.direction(expression) + } else { + Logger.e("RNMBXRain", "Expression for direction is null") + } + } else { + val value = styleValue.getFloatArray(VALUE_KEY) + if (value != null) { + layer.direction(value) + } else { + Logger.e("RNMBXRain", "value for direction is null") + } + } + } + + + fun setDirectionTransition(layer: Rain, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.directionTransition(transition); + } + } + + fun setDropletSize(layer: Rain, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.dropletSize(expression) + } else { + Logger.e("RNMBXRain", "Expression for dropletSize is null") + } + } else { + val value = styleValue.getFloatArray(VALUE_KEY) + if (value != null) { + layer.dropletSize(value) + } else { + Logger.e("RNMBXRain", "value for dropletSize is null") + } + } + } + + + fun setDropletSizeTransition(layer: Rain, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.dropletSizeTransition(transition); + } + } + + fun setDistortionStrength(layer: Rain, styleValue: RNMBXStyleValue ) { + if (styleValue.isExpression()) { + val expression = styleValue.getExpression() + if (expression != null) { + layer.distortionStrength(expression) + } else { + Logger.e("RNMBXRain", "Expression for distortionStrength is null") + } + } else { + val value = styleValue.getDouble(VALUE_KEY) + if (value != null) { + layer.distortionStrength(value) + } else { + Logger.e("RNMBXRain", "value for distortionStrength is null") + } + } + } + + + fun setDistortionStrengthTransition(layer: Rain, styleValue: RNMBXStyleValue) { + val transition = styleValue.transition + if (transition != null) { + layer.distortionStrengthTransition(transition); + } + } + fun setExaggeration(layer: Terrain, styleValue: RNMBXStyleValue ) { if (styleValue.isExpression()) { val expression = styleValue.getExpression() diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/styles/rain/RNMBXRain.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/rain/RNMBXRain.kt new file mode 100644 index 000000000..b1604b207 --- /dev/null +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/rain/RNMBXRain.kt @@ -0,0 +1,83 @@ +package com.rnmapbox.rnmbx.components.styles.rain + +import android.content.Context +import com.facebook.react.bridge.ReadableMap +import com.mapbox.maps.MapboxMap +import com.mapbox.maps.extension.style.precipitations.generated.Rain +import com.mapbox.maps.extension.style.precipitations.generated.removeRain +import com.rnmapbox.rnmbx.components.RemovalReason +import com.rnmapbox.rnmbx.components.mapview.RNMBXMapView +import com.rnmapbox.rnmbx.components.styles.RNMBXStyle +import com.rnmapbox.rnmbx.components.styles.RNMBXStyleFactory +import com.rnmapbox.rnmbx.components.styles.sources.AbstractSourceConsumer +import com.rnmapbox.rnmbx.utils.Logger + +class RNMBXRain(context: Context?) : AbstractSourceConsumer(context) { + override var iD: String? = null + protected var mRain: Rain? = null + + // beginregion RNMBXLayer + @JvmField + protected var mMap: MapboxMap? = null + + @JvmField + protected var mReactStyle: ReadableMap? = null + + fun setReactStyle(reactStyle: ReadableMap?) { + mReactStyle = reactStyle + if (mRain != null) { + addStyles() + } + } + // endregion RNMBXLayer + + override fun addToMap(mapView: RNMBXMapView) { + super.addToMap(mapView) + mMap = mapView.getMapboxMap() + mapView.savedStyle?.let { warnIfMeasureLightUnavailable(it) } + val rain = makeRain() + mRain = rain + addStyles() + mapView.savedStyle?.let { rain.bindTo(it) } + } + + private fun warnIfMeasureLightUnavailable(style: com.mapbox.maps.Style) { + val hasLights = style.getStyleLights().isNotEmpty() + if (hasLights) return + + val affectedProps = listOf("color", "opacity", "vignetteColor") + val missingProps = affectedProps.filter { mReactStyle?.hasKey(it) != true } + if (missingProps.isEmpty()) return + + Logger.w( + "RNMBXRain", + "The current style has no 3D lights, so measure-light(\"brightness\") " + + "expressions used in default rain ${missingProps.joinToString(", ")} will fail. " + + "Use a Standard style or set explicit values for: ${missingProps.joinToString(", ")}" + ) + } + + override fun removeFromMap(mapView: RNMBXMapView, reason: RemovalReason): Boolean { + mapView.savedStyle?.let { it.removeRain() } + mRain = null + mMap = null + return super.removeFromMap(mapView, reason) + } + + fun makeRain(): Rain { + return Rain() + } + + fun addStyles() { + mRain?.also { + RNMBXStyleFactory.setRainLayerStyle( + it, RNMBXStyle( + context, mReactStyle, + mMap!! + ) + ) + } ?: run { + Logger.e("RNMBXRain", "mRain is null") + } + } +} diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/styles/rain/RNMBXRainManager.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/rain/RNMBXRainManager.kt new file mode 100644 index 000000000..5d1d40967 --- /dev/null +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/rain/RNMBXRainManager.kt @@ -0,0 +1,40 @@ +package com.rnmapbox.rnmbx.components.styles.rain + +import com.facebook.react.bridge.Dynamic +import com.facebook.react.uimanager.ThemedReactContext +import com.rnmapbox.rnmbx.utils.extensions.asMapOrNull +import com.facebook.react.uimanager.ViewGroupManager +import com.facebook.react.uimanager.ViewManagerDelegate +import com.facebook.react.uimanager.annotations.ReactProp +import com.facebook.react.viewmanagers.RNMBXRainManagerDelegate +import com.facebook.react.viewmanagers.RNMBXRainManagerInterface + +class RNMBXRainManager : ViewGroupManager(), RNMBXRainManagerInterface { + + private val mDelegate: ViewManagerDelegate + + init { + mDelegate = RNMBXRainManagerDelegate(this) + } + + override fun getDelegate(): ViewManagerDelegate { + return mDelegate + } + + override fun getName(): String { + return REACT_CLASS + } + + override fun createViewInstance(reactContext: ThemedReactContext): RNMBXRain { + return RNMBXRain(reactContext) + } + + @ReactProp(name = "reactStyle") + override fun setReactStyle(rain: RNMBXRain, reactStyle: Dynamic) { + rain.setReactStyle(reactStyle.asMapOrNull()) + } + + companion object { + const val REACT_CLASS = "RNMBXRain" + } +} diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/styles/snow/RNMBXSnow.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/snow/RNMBXSnow.kt new file mode 100644 index 000000000..1f26dd9ab --- /dev/null +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/snow/RNMBXSnow.kt @@ -0,0 +1,66 @@ +package com.rnmapbox.rnmbx.components.styles.snow + +import android.content.Context +import com.facebook.react.bridge.ReadableMap +import com.mapbox.maps.MapboxMap +import com.mapbox.maps.extension.style.precipitations.generated.Snow +import com.mapbox.maps.extension.style.precipitations.generated.removeSnow +import com.rnmapbox.rnmbx.components.RemovalReason +import com.rnmapbox.rnmbx.components.mapview.RNMBXMapView +import com.rnmapbox.rnmbx.components.styles.RNMBXStyle +import com.rnmapbox.rnmbx.components.styles.RNMBXStyleFactory +import com.rnmapbox.rnmbx.components.styles.sources.AbstractSourceConsumer +import com.rnmapbox.rnmbx.utils.Logger + +class RNMBXSnow(context: Context?) : AbstractSourceConsumer(context) { + override var iD: String? = null + protected var mSnow: Snow? = null + + // beginregion RNMBXLayer + @JvmField + protected var mMap: MapboxMap? = null + + @JvmField + protected var mReactStyle: ReadableMap? = null + + fun setReactStyle(reactStyle: ReadableMap?) { + mReactStyle = reactStyle + if (mSnow != null) { + addStyles() + } + } + // endregion RNMBXLayer + + override fun addToMap(mapView: RNMBXMapView) { + super.addToMap(mapView) + mMap = mapView.getMapboxMap() + val snow = makeSnow() + mSnow = snow + addStyles() + mapView.savedStyle?.let { snow.bindTo(it) } + } + + override fun removeFromMap(mapView: RNMBXMapView, reason: RemovalReason): Boolean { + mapView.savedStyle?.let { it.removeSnow() } + mSnow = null + mMap = null + return super.removeFromMap(mapView, reason) + } + + fun makeSnow(): Snow { + return Snow() + } + + fun addStyles() { + mSnow?.also { + RNMBXStyleFactory.setSnowLayerStyle( + it, RNMBXStyle( + context, mReactStyle, + mMap!! + ) + ) + } ?: run { + Logger.e("RNMBXSnow", "mSnow is null") + } + } +} diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/styles/snow/RNMBXSnowManager.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/snow/RNMBXSnowManager.kt new file mode 100644 index 000000000..31edfb506 --- /dev/null +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/styles/snow/RNMBXSnowManager.kt @@ -0,0 +1,40 @@ +package com.rnmapbox.rnmbx.components.styles.snow + +import com.facebook.react.bridge.Dynamic +import com.facebook.react.uimanager.ThemedReactContext +import com.rnmapbox.rnmbx.utils.extensions.asMapOrNull +import com.facebook.react.uimanager.ViewGroupManager +import com.facebook.react.uimanager.ViewManagerDelegate +import com.facebook.react.uimanager.annotations.ReactProp +import com.facebook.react.viewmanagers.RNMBXSnowManagerDelegate +import com.facebook.react.viewmanagers.RNMBXSnowManagerInterface + +class RNMBXSnowManager : ViewGroupManager(), RNMBXSnowManagerInterface { + + private val mDelegate: ViewManagerDelegate + + init { + mDelegate = RNMBXSnowManagerDelegate(this) + } + + override fun getDelegate(): ViewManagerDelegate { + return mDelegate + } + + override fun getName(): String { + return REACT_CLASS + } + + override fun createViewInstance(reactContext: ThemedReactContext): RNMBXSnow { + return RNMBXSnow(reactContext) + } + + @ReactProp(name = "reactStyle") + override fun setReactStyle(snow: RNMBXSnow, reactStyle: Dynamic) { + snow.setReactStyle(reactStyle.asMapOrNull()) + } + + companion object { + const val REACT_CLASS = "RNMBXSnow" + } +} diff --git a/docs/LineLayer.md b/docs/LineLayer.md index 1728e745c..47e96fc68 100644 --- a/docs/LineLayer.md +++ b/docs/LineLayer.md @@ -145,6 +145,7 @@ Customizable style attributes * lineElevationReference
* lineCrossSlope
* visibility
+* lineElevationGroundScale
* lineOpacity
* lineColor
* lineTranslate
@@ -372,6 +373,54 @@ Whether this layer is displayed. Parameters: `` +___ + +### lineElevationGroundScale +Name: `lineElevationGroundScale` + +Mapbox spec: [line-elevation-ground-scale](https://docs.mapbox.com/style-spec/reference/layers/#layout-line-line-elevation-ground-scale) + +#### Description +Controls how much the elevation of lines with `lineElevationReference` set to `sea` scales with terrain exaggeration. A value of 0 keeps the line at a fixed altitude above sea level. A value of 1 scales the elevation proportionally with terrain exaggeration. + +#### Type +`number` +#### Default Value +`0` + +#### Minimum +`0` + + +#### Maximum +`1` + +#### Requires +`lineZOffset` + +#### Expression + +Parameters: `zoom, feature, line-progress` +___ + +### lineElevationGroundScaleTransition +Name: `lineElevationGroundScaleTransition` + +#### Description + +The transition affecting any changes to this layer’s lineElevationGroundScale property. + +#### Type + +`{ duration, delay }` + +#### Units +`milliseconds` + +#### Default Value +`{duration: 300, delay: 0}` + + ___ ### lineOpacity diff --git a/docs/Rain.md b/docs/Rain.md new file mode 100644 index 000000000..6ea88d999 --- /dev/null +++ b/docs/Rain.md @@ -0,0 +1,37 @@ + + + + +```tsx +import { Rain } from '@rnmapbox/maps'; + +Rain + +``` + + +## props + + +### style + +```tsx +RainLayerStyleProps +``` +Rain particle effect style properties. + +@note The default `color` and `vignetteColor` values use `measure-light("brightness")` +expressions that are only available in Mapbox Standard-based styles +(`mapbox://styles/mapbox/standard`, `mapbox://styles/mapbox/standard-satellite`). +When using legacy or custom styles, set `color` and `vignetteColor` explicitly to +avoid "Brightness is unavailable in the current evaluation context" warnings and +invisible rain. For example: `color="#a8adbc" vignetteColor="#464646"`. + + + + + + + + + diff --git a/docs/Snow.md b/docs/Snow.md new file mode 100644 index 000000000..c58b1d3a8 --- /dev/null +++ b/docs/Snow.md @@ -0,0 +1,30 @@ + + + + +```tsx +import { Snow } from '@rnmapbox/maps'; + +Snow + +``` + + +## props + + +### style + +```tsx +SnowLayerStyleProps +``` +FIX ME NO DESCRIPTION + + + + + + + + + diff --git a/docs/docs.json b/docs/docs.json index 3ef281b5a..f11c2b03d 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -3945,6 +3945,34 @@ "namespace": "layout" } }, + { + "name": "lineElevationGroundScale", + "type": "number", + "values": [], + "minimum": 0, + "maximum": 1, + "default": 0, + "description": "Controls how much the elevation of lines with `lineElevationReference` set to `sea` scales with terrain exaggeration. A value of 0 keeps the line at a fixed altitude above sea level. A value of 1 scales the elevation proportionally with terrain exaggeration.", + "requires": [ + "lineZOffset" + ], + "disabledBy": [], + "allowedFunctionTypes": [], + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "feature", + "line-progress" + ] + }, + "transition": true, + "mbx": { + "fullName": "layout-line-line-elevation-ground-scale", + "name": "line-elevation-ground-scale", + "namespace": "layout" + } + }, { "name": "lineOpacity", "type": "number", @@ -6457,6 +6485,23 @@ "relPath": "src/components/PointAnnotation.tsx", "name": "PointAnnotation" }, + "Rain": { + "description": "", + "displayName": "Rain", + "methods": [], + "props": [ + { + "name": "style", + "required": false, + "type": "RainLayerStyleProps", + "default": "none", + "description": "Rain particle effect style properties.\n\n@note The default `color` and `vignetteColor` values use `measure-light(\"brightness\")`\nexpressions that are only available in Mapbox Standard-based styles\n(`mapbox://styles/mapbox/standard`, `mapbox://styles/mapbox/standard-satellite`).\nWhen using legacy or custom styles, set `color` and `vignetteColor` explicitly to\navoid \"Brightness is unavailable in the current evaluation context\" warnings and\ninvisible rain. For example: `color=\"#a8adbc\" vignetteColor=\"#464646\"`." + } + ], + "fileNameWithExt": "Rain.tsx", + "relPath": "src/components/Rain.tsx", + "name": "Rain" + }, "RasterArraySource": { "description": "RasterArraySource is a map content source that supplies raster array image tiles to be shown on the map.\nThis is typically used for particle animations like wind or precipitation patterns.\nThe location of and metadata about the tiles are defined either by an option dictionary\nor by an external file that conforms to the TileJSON specification.\n\n@experimental This component requires Mapbox Maps SDK v11.4.0 or later", "displayName": "RasterArraySource", @@ -7982,6 +8027,23 @@ } ] }, + "Snow": { + "description": "", + "displayName": "Snow", + "methods": [], + "props": [ + { + "name": "style", + "required": false, + "type": "SnowLayerStyleProps", + "default": "none", + "description": "FIX ME NO DESCRIPTION" + } + ], + "fileNameWithExt": "Snow.tsx", + "relPath": "src/components/Snow.tsx", + "name": "Snow" + }, "Style": { "description": "Style is a component that automatically adds sources / layers to the map using Mapbox GL Style Spec.\nOnly [`sources`](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources) & [`layers`](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/) are supported.\nOther fields such as `sprites`, `glyphs` etc. will be ignored. Not all layer / source attributes from the style spec are supported, in general the supported attributes will be mentioned under https://github.com/rnmapbox/maps/tree/main/docs.", "displayName": "Style", diff --git a/docs/examples.json b/docs/examples.json index 935397ae8..6567edf54 100644 --- a/docs/examples.json +++ b/docs/examples.json @@ -602,9 +602,11 @@ "RasterDemSource", "Terrain", "Atmosphere", - "SkyLayer" + "SkyLayer", + "Snow", + "Rain" ], - "docs": "\nDemostrates use of Terrain, Atmosphere and SkyLayer.\n" + "docs": "\nDemonstrates use of Terrain, Atmosphere and SkyLayer. Use the Snow/Rain buttons to toggle experimental particle weather effects.\n" }, "fullPath": "example/src/examples/V10/TerrainSkyAtmosphere.tsx", "relPath": "V10/TerrainSkyAtmosphere.tsx", diff --git a/example/src/examples/V10/TerrainSkyAtmosphere.tsx b/example/src/examples/V10/TerrainSkyAtmosphere.tsx index e74048bba..3bf6fe33a 100644 --- a/example/src/examples/V10/TerrainSkyAtmosphere.tsx +++ b/example/src/examples/V10/TerrainSkyAtmosphere.tsx @@ -1,5 +1,5 @@ -import { useRef } from 'react'; -import { Button } from 'react-native'; +import { useRef, useState } from 'react'; +import { Button, StyleSheet, View } from 'react-native'; import { MapView, SkyLayer, @@ -7,40 +7,48 @@ import { Terrain, RasterDemSource, Atmosphere, + Snow, + Rain, Camera, } from '@rnmapbox/maps'; Logger.setLogLevel('verbose'); +type WeatherEffect = 'none' | 'snow' | 'rain'; + const TerrainSkyAtmosphere = () => { const cameraRef = useRef(null); + const [weather, setWeather] = useState('none'); return ( - <> -