Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Feature state #3741

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.rnmapbox.rnmbx.components.mapview
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.bridge.WritableMap
import com.facebook.react.bridge.WritableNativeMap
import com.rnmapbox.rnmbx.NativeMapViewModuleSpec
Expand All @@ -12,6 +13,7 @@ import com.rnmapbox.rnmbx.utils.ViewRefTag
import com.rnmapbox.rnmbx.utils.ViewTagResolver
import com.rnmapbox.rnmbx.utils.extensions.toCoordinate
import com.rnmapbox.rnmbx.utils.extensions.toScreenCoordinate
import com.rnmapbox.rnmbx.utils.extensions.toValueHashMap

class NativeMapViewModule(context: ReactApplicationContext, val viewTagResolver: ViewTagResolver) : NativeMapViewModuleSpec(context) {
private fun withMapViewOnUIThread(
Expand Down Expand Up @@ -158,6 +160,44 @@ class NativeMapViewModule(context: ReactApplicationContext, val viewTagResolver:
}
}

override fun setFeatureState(
viewRef: ViewRefTag?,
featureId: String,
state: ReadableMap,
sourceId: String,
sourceLayerId: String?,
promise: Promise
) {
withMapViewOnUIThread(viewRef, promise) {
it.setFeatureState(featureId, state.toValueHashMap(), sourceId, sourceLayerId, createCommandResponse(promise))
}
}

override fun getFeatureState(
viewRef: ViewRefTag?,
featureId: String,
sourceId: String,
sourceLayerId: String?,
promise: Promise
) {
withMapViewOnUIThread(viewRef, promise) {
it.getFeatureState(featureId, sourceId, sourceLayerId, createCommandResponse(promise))
}
}

override fun removeFeatureState(
viewRef: ViewRefTag?,
featureId: String,
stateKey: String?,
sourceId: String,
sourceLayerId: String?,
promise: Promise
) {
withMapViewOnUIThread(viewRef, promise) {
it.removeFeatureState(featureId, stateKey, sourceId, sourceLayerId, createCommandResponse(promise))
}
}

override fun querySourceFeatures(
viewRef: ViewRefTag?,
sourceId: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,24 @@ import com.mapbox.maps.extension.style.layers.properties.generated.ProjectionNam
import com.mapbox.maps.extension.style.layers.properties.generated.Visibility
import com.mapbox.maps.extension.style.projection.generated.Projection
import com.mapbox.maps.extension.style.projection.generated.setProjection
import com.mapbox.maps.logE
import com.mapbox.maps.plugin.attribution.attribution
import com.mapbox.maps.plugin.compass.compass
import com.mapbox.maps.plugin.delegates.listeners.*
import com.mapbox.maps.plugin.gestures.*
import com.mapbox.maps.plugin.logo.logo
import com.mapbox.maps.plugin.scalebar.scalebar
import com.mapbox.maps.toCameraOptions
import com.mapbox.maps.viewannotation.ViewAnnotationManager
import com.rnmapbox.rnmbx.R
import com.rnmapbox.rnmbx.components.AbstractMapFeature
import com.rnmapbox.rnmbx.components.RemovalReason
import com.rnmapbox.rnmbx.components.annotation.RNMBXMarkerView
import com.rnmapbox.rnmbx.components.annotation.RNMBXMarkerViewManager
import com.rnmapbox.rnmbx.components.annotation.RNMBXPointAnnotation
import com.rnmapbox.rnmbx.components.annotation.RNMBXPointAnnotationCoordinator
import com.rnmapbox.rnmbx.components.camera.RNMBXCamera
import com.rnmapbox.rnmbx.components.images.ImageManager
import com.rnmapbox.rnmbx.components.images.RNMBXImages
import com.rnmapbox.rnmbx.components.location.LocationComponentManager
import com.rnmapbox.rnmbx.components.location.RNMBXNativeUserLocation
Expand All @@ -64,17 +68,11 @@ import com.rnmapbox.rnmbx.events.MapClickEvent
import com.rnmapbox.rnmbx.events.constants.EventTypes
import com.rnmapbox.rnmbx.utils.*
import com.rnmapbox.rnmbx.utils.extensions.toReadableArray
import java.util.*

import com.rnmapbox.rnmbx.components.annotation.RNMBXPointAnnotationCoordinator
import com.rnmapbox.rnmbx.components.images.ImageManager

import com.rnmapbox.rnmbx.v11compat.event.*
import com.rnmapbox.rnmbx.v11compat.feature.*
import com.rnmapbox.rnmbx.v11compat.mapboxmap.*
import com.rnmapbox.rnmbx.v11compat.ornamentsettings.*
import org.json.JSONException
import org.json.JSONObject
import java.util.*
import org.json.*


fun <T> MutableList<T>.removeIf21(predicate: (T) -> Boolean): Boolean {
var removed = false
Expand Down Expand Up @@ -1097,6 +1095,50 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie
}
}

fun setFeatureState(
featureId: String,
state: HashMap<String, Value>,
sourceId: String,
sourceLayerId: String?,
response: CommandResponse
) {
mapView.getMapboxMap().setFeatureState(sourceId, sourceLayerId, featureId, Value.valueOf(state))
response.success { }
}

fun getFeatureState(
featureId: String,
sourceId: String,
sourceLayerId: String?,
response: CommandResponse
) {
mapView.getMapboxMap().getFeatureState(sourceId, sourceLayerId, featureId) { expected ->
if (expected.isValue) {
response.success {
val state = expected.value!!.contents
if (state is Map<*,*>) {
it.putMap("featureState", writableMapOf(*state.map { it.key to it.value}.toTypedArray()))
} else {
it.putMap("featureState", Arguments.createMap())
}
}
} else {
response.error(expected.error!!.toString())
}
}
}

fun removeFeatureState(
featureId: String,
stateKey: String?,
sourceId: String,
sourceLayerId: String?,
response: CommandResponse
) {
mapView.getMapboxMap().removeFeatureState(sourceId, sourceLayerId, featureId, stateKey)
response.success { }
}

fun match(layer: Layer, sourceId:String, sourceLayerId: String?) : Boolean {
fun match(actSourceId: String, actSourceLayerId: String?) : Boolean {
return (actSourceId == sourceId && ((sourceLayerId == null) || (sourceLayerId == actSourceLayerId)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,36 @@ package com.rnmapbox.rnmbx.utils
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableArray
import com.facebook.react.bridge.WritableMap
import com.mapbox.bindgen.Value

fun writableMapOf(vararg values: Pair<String, *>): WritableMap {
fun writableMapOf(vararg values: Pair<*, *>): WritableMap {
val map = Arguments.createMap()
for ((key, value) in values) {
if (key !is String) continue;
when (value) {
null -> map.putNull(key)
is Boolean -> map.putBoolean(key, value)
is Double -> map.putDouble(key, value)
is Int -> map.putInt(key, value)
is Long -> map.putInt(key, value.toInt())
is String -> map.putString(key, value)
is Map<*,*> -> map.putMap(key, writableMapOf(*value.map { it.key to it.value }.toTypedArray()))
is Array<*> -> map.putArray(key, writableArrayOf(*value.map{it as Any}.toTypedArray()))
is WritableMap -> map.putMap(key, value)
is WritableArray -> map.putArray(key, value)
is Value -> {
val contents = value.contents
when (contents) {
null -> map.putNull(key)
is Boolean -> map.putBoolean(key, contents)
is Double -> map.putDouble(key, contents)
is Int -> map.putInt(key, contents)
is Long -> map.putInt(key, contents.toInt())
is String -> map.putString(key, contents)
is WritableMap -> map.putMap(key, contents)
is WritableArray -> map.putArray(key, contents)
}
}
else -> throw IllegalArgumentException("Unsupported value type ${value::class.java.name} for key [$key]")
}
}
Expand All @@ -32,8 +49,25 @@ fun writableArrayOf(vararg values: Any): WritableArray {
is Int -> array.pushInt(value)
is Long -> array.pushInt(value.toInt())
is String -> array.pushString(value)
is Map<*,*> -> array.pushMap(writableMapOf(*value.map { it.key to it.value }.toTypedArray()))
is Array<*> -> array.pushArray(writableArrayOf(*value.map{ it as Any }.toTypedArray()))
is WritableMap -> array.pushMap(value)
is WritableArray -> array.pushArray(value)
is Value -> {
val contents = value.contents
when (contents) {
null -> array.pushNull()
is Boolean -> array.pushBoolean(contents)
is Double -> array.pushDouble(contents)
is Int -> array.pushInt(contents)
is Long -> array.pushInt(contents.toInt())
is String -> array.pushString(contents)
is Map<*,*> -> array.pushMap(writableMapOf(*contents.map { it.key to it.value }.toTypedArray()))
is Array<*> -> array.pushArray(writableArrayOf(*contents.map{it as Any}.toTypedArray()))
is WritableMap -> array.pushMap(contents)
is WritableArray -> array.pushArray(contents)
}
}
else -> throw IllegalArgumentException("Unsupported value type ${value::class.java.name}")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReactModuleWithSpec;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
Expand Down Expand Up @@ -83,6 +84,18 @@ public NativeMapViewModuleSpec(ReactApplicationContext reactContext) {
@DoNotStrip
public abstract void clearData(@Nullable Double viewRef, Promise promise);

@ReactMethod
@DoNotStrip
public abstract void setFeatureState(@Nullable Double viewRef, String featureId, ReadableMap state, String sourceId, @Nullable String sourceLayerId, Promise promise);

@ReactMethod
@DoNotStrip
public abstract void getFeatureState(@Nullable Double viewRef, String featureId, String sourceId, @Nullable String sourceLayerId, Promise promise);

@ReactMethod
@DoNotStrip
public abstract void removeFeatureState(@Nullable Double viewRef, String featureId, @Nullable String stateKey, String sourceId, @Nullable String sourceLayerId, Promise promise);

@ReactMethod
@DoNotStrip
public abstract void querySourceFeatures(@Nullable Double viewRef, String sourceId, ReadableArray withFilter, ReadableArray withSourceLayerIDs, Promise promise);
Expand Down
59 changes: 57 additions & 2 deletions docs/MapView.md
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,9 @@ Queries the currently loaded data for elevation at a geographical location.<br/>
| `coordinate` | `Position` | `Yes` | the coordinates to query elevation at |


[Query Terrain Elevation](../examples/V10/QueryTerrainElevation)### setSourceVisibility(visible, sourceId[, sourceLayerId])
[Query Terrain Elevation](../examples/V10/QueryTerrainElevation)

### setSourceVisibility(visible, sourceId[, sourceLayerId])

Sets the visibility of all the layers referencing the specified `sourceLayerId` and/or `sourceId`

Expand All @@ -721,13 +723,66 @@ Sets the visibility of all the layers referencing the specified `sourceLayerId`
| ---- | :--: | :------: | :----------: |
| `visible` | `boolean` | `Yes` | Visibility of the layers |
| `sourceId` | `string` | `Yes` | Identifier of the target source (e.g. 'composite') |
| `sourceLayerId` | `String` | `No` | Identifier of the target source-layer (e.g. 'building') |
| `sourceLayerId` | `string` | `No` | Identifier of the target source-layer (e.g. 'building') |



```javascript
await this._map.setSourceVisibility(false, 'composite', 'building')
```

### setFeatureState(featureId, state, sourceId [, sourceLayerId])

Updates the state map of a feature within a style source.

Updates entries in the state map of a given feature within a style source.

Only entries listed in the `state` will be updated.

An entry in the feature state map that is not listed in `state` will retain its previous value.

#### arguments
| Name | Type | Required | Description |
| ---- | :--: | :------: | :----------: |
| `featureId` | `string` | `Yes` | Identifier of the feature whose state should be updated |
| `state` | `object` | `Yes` | Map of entries to update with their respective new values. |
| `sourceId` | `string` | `Yes` | Style source identifier |
| `sourceLayerId` | `string` | `No` | Style source layer identifier (for multi-layer sources such as vector sources). |

```javascript
await this._map.setFeatureState('my-feature-id', { 'my-feature-state-key': 'my-feature-state-value' }, 'my-source-id', 'my-source-layer-id')
```


### getFeatureState(featureId, sourceId [, sourceLayerId])

Returns the state map of a feature within a style source.

#### arguments
| Name | Type | Required | Description |
| ---- | :--: | :------: | :----------: |
| `featureId` | `string` | `Yes` | Identifier of the feature whose state should be updated. |
| `sourceId` | `string` | `Yes` | Style source identifier. |
| `sourceLayerId` | `string` | `No` | Style source layer identifier (for multi-layer sources such as vector sources). |

```javascript
await this._map.getFeatureState('my-feature-id', 'my-source-id', 'my-source-layer-id')
```

### removeFeatureState(featureId, stateKey, sourceId [, sourceLayerId])

Removes entries from a feature state object.

Removes a specified property or all properties from a feature's state object depending on the value of `stateKey`.

#### arguments
| Name | Type | Required | Description |
| ---- | :--: | :------: | :----------: |
| `featureId` | `string` | `Yes` | Identifier of the feature whose state should be removed. |
| `stateKey` | `string` or `null` | `Yes` | The name of the property to remove. If `null`, all feature’s state object properties are removed. |
| `sourceId` | `string` | `Yes` | Style source identifier. |
| `sourceLayerId` | `string` | `No` | Style source layer identifier (for multi-layer sources such as vector sources). |

```javascript
await this._map.removeFeatureState('my-feature-id', 'my-feature-state-key', 'my-source-id', 'my-source-layer-id')
```
Loading