Skip to content

Commit 85e97a9

Browse files
committed
Implement feature state
1 parent bbd898e commit 85e97a9

13 files changed

Lines changed: 837 additions & 313 deletions

File tree

android/src/main/java/com/rnmapbox/rnmbx/components/mapview/NativeMapViewModule.kt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.rnmapbox.rnmbx.components.mapview
33
import com.facebook.react.bridge.Promise
44
import com.facebook.react.bridge.ReactApplicationContext
55
import com.facebook.react.bridge.ReadableArray
6+
import com.facebook.react.bridge.ReadableMap
67
import com.facebook.react.bridge.WritableMap
78
import com.facebook.react.bridge.WritableNativeMap
89
import com.rnmapbox.rnmbx.NativeMapViewModuleSpec
@@ -12,6 +13,7 @@ import com.rnmapbox.rnmbx.utils.ViewRefTag
1213
import com.rnmapbox.rnmbx.utils.ViewTagResolver
1314
import com.rnmapbox.rnmbx.utils.extensions.toCoordinate
1415
import com.rnmapbox.rnmbx.utils.extensions.toScreenCoordinate
16+
import com.rnmapbox.rnmbx.utils.extensions.toValueHashMap
1517

1618
class NativeMapViewModule(context: ReactApplicationContext, val viewTagResolver: ViewTagResolver) : NativeMapViewModuleSpec(context) {
1719
private fun withMapViewOnUIThread(
@@ -158,6 +160,44 @@ class NativeMapViewModule(context: ReactApplicationContext, val viewTagResolver:
158160
}
159161
}
160162

163+
override fun setFeatureState(
164+
viewRef: ViewRefTag?,
165+
featureId: String,
166+
state: ReadableMap,
167+
sourceId: String,
168+
sourceLayerId: String?,
169+
promise: Promise
170+
) {
171+
withMapViewOnUIThread(viewRef, promise) {
172+
it.setFeatureState(featureId, state.toValueHashMap(), sourceId, sourceLayerId, createCommandResponse(promise))
173+
}
174+
}
175+
176+
override fun getFeatureState(
177+
viewRef: ViewRefTag?,
178+
featureId: String,
179+
sourceId: String,
180+
sourceLayerId: String?,
181+
promise: Promise
182+
) {
183+
withMapViewOnUIThread(viewRef, promise) {
184+
it.getFeatureState(featureId, sourceId, sourceLayerId, createCommandResponse(promise))
185+
}
186+
}
187+
188+
override fun removeFeatureState(
189+
viewRef: ViewRefTag?,
190+
featureId: String,
191+
stateKey: String?,
192+
sourceId: String,
193+
sourceLayerId: String?,
194+
promise: Promise
195+
) {
196+
withMapViewOnUIThread(viewRef, promise) {
197+
it.removeFeatureState(featureId, stateKey, sourceId, sourceLayerId, createCommandResponse(promise))
198+
}
199+
}
200+
161201
override fun querySourceFeatures(
162202
viewRef: ViewRefTag?,
163203
sourceId: String,

android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapView.kt

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,24 @@ import com.mapbox.maps.extension.style.layers.properties.generated.ProjectionNam
3434
import com.mapbox.maps.extension.style.layers.properties.generated.Visibility
3535
import com.mapbox.maps.extension.style.projection.generated.Projection
3636
import com.mapbox.maps.extension.style.projection.generated.setProjection
37+
import com.mapbox.maps.logE
3738
import com.mapbox.maps.plugin.attribution.attribution
3839
import com.mapbox.maps.plugin.compass.compass
3940
import com.mapbox.maps.plugin.delegates.listeners.*
4041
import com.mapbox.maps.plugin.gestures.*
4142
import com.mapbox.maps.plugin.logo.logo
4243
import com.mapbox.maps.plugin.scalebar.scalebar
44+
import com.mapbox.maps.toCameraOptions
4345
import com.mapbox.maps.viewannotation.ViewAnnotationManager
4446
import com.rnmapbox.rnmbx.R
4547
import com.rnmapbox.rnmbx.components.AbstractMapFeature
4648
import com.rnmapbox.rnmbx.components.RemovalReason
4749
import com.rnmapbox.rnmbx.components.annotation.RNMBXMarkerView
4850
import com.rnmapbox.rnmbx.components.annotation.RNMBXMarkerViewManager
4951
import com.rnmapbox.rnmbx.components.annotation.RNMBXPointAnnotation
52+
import com.rnmapbox.rnmbx.components.annotation.RNMBXPointAnnotationCoordinator
5053
import com.rnmapbox.rnmbx.components.camera.RNMBXCamera
54+
import com.rnmapbox.rnmbx.components.images.ImageManager
5155
import com.rnmapbox.rnmbx.components.images.RNMBXImages
5256
import com.rnmapbox.rnmbx.components.location.LocationComponentManager
5357
import com.rnmapbox.rnmbx.components.location.RNMBXNativeUserLocation
@@ -64,17 +68,11 @@ import com.rnmapbox.rnmbx.events.MapClickEvent
6468
import com.rnmapbox.rnmbx.events.constants.EventTypes
6569
import com.rnmapbox.rnmbx.utils.*
6670
import com.rnmapbox.rnmbx.utils.extensions.toReadableArray
67-
import java.util.*
68-
69-
import com.rnmapbox.rnmbx.components.annotation.RNMBXPointAnnotationCoordinator
70-
import com.rnmapbox.rnmbx.components.images.ImageManager
71-
7271
import com.rnmapbox.rnmbx.v11compat.event.*
73-
import com.rnmapbox.rnmbx.v11compat.feature.*
74-
import com.rnmapbox.rnmbx.v11compat.mapboxmap.*
7572
import com.rnmapbox.rnmbx.v11compat.ornamentsettings.*
76-
import org.json.JSONException
77-
import org.json.JSONObject
73+
import java.util.*
74+
import org.json.*
75+
7876

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

1098+
fun setFeatureState(
1099+
featureId: String,
1100+
state: HashMap<String, Value>,
1101+
sourceId: String,
1102+
sourceLayerId: String?,
1103+
response: CommandResponse
1104+
) {
1105+
mapView.getMapboxMap().setFeatureState(sourceId, sourceLayerId, featureId, Value.valueOf(state))
1106+
response.success { }
1107+
}
1108+
1109+
fun getFeatureState(
1110+
featureId: String,
1111+
sourceId: String,
1112+
sourceLayerId: String?,
1113+
response: CommandResponse
1114+
) {
1115+
mapView.getMapboxMap().getFeatureState(sourceId, sourceLayerId, featureId) { expected ->
1116+
if (expected.isValue) {
1117+
response.success {
1118+
val state = expected.value!!.contents
1119+
if (state is Map<*,*>) {
1120+
it.putMap("featureState", writableMapOf(*state.map { it.key to it.value}.toTypedArray()))
1121+
} else {
1122+
it.putMap("featureState", Arguments.createMap())
1123+
}
1124+
}
1125+
} else {
1126+
response.error(expected.error!!.toString())
1127+
}
1128+
}
1129+
}
1130+
1131+
fun removeFeatureState(
1132+
featureId: String,
1133+
stateKey: String?,
1134+
sourceId: String,
1135+
sourceLayerId: String?,
1136+
response: CommandResponse
1137+
) {
1138+
mapView.getMapboxMap().removeFeatureState(sourceId, sourceLayerId, featureId, stateKey)
1139+
response.success { }
1140+
}
1141+
11001142
fun match(layer: Layer, sourceId:String, sourceLayerId: String?) : Boolean {
11011143
fun match(actSourceId: String, actSourceLayerId: String?) : Boolean {
11021144
return (actSourceId == sourceId && ((sourceLayerId == null) || (sourceLayerId == actSourceLayerId)))

android/src/main/java/com/rnmapbox/rnmbx/utils/writeableMapArrayOf.kt

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,36 @@ package com.rnmapbox.rnmbx.utils
33
import com.facebook.react.bridge.Arguments
44
import com.facebook.react.bridge.WritableArray
55
import com.facebook.react.bridge.WritableMap
6+
import com.mapbox.bindgen.Value
67

7-
fun writableMapOf(vararg values: Pair<String, *>): WritableMap {
8+
fun writableMapOf(vararg values: Pair<*, *>): WritableMap {
89
val map = Arguments.createMap()
910
for ((key, value) in values) {
11+
if (key !is String) continue;
1012
when (value) {
1113
null -> map.putNull(key)
1214
is Boolean -> map.putBoolean(key, value)
1315
is Double -> map.putDouble(key, value)
1416
is Int -> map.putInt(key, value)
1517
is Long -> map.putInt(key, value.toInt())
1618
is String -> map.putString(key, value)
19+
is Map<*,*> -> map.putMap(key, writableMapOf(*value.map { it.key to it.value }.toTypedArray()))
20+
is Array<*> -> map.putArray(key, writableArrayOf(*value.map{it as Any}.toTypedArray()))
1721
is WritableMap -> map.putMap(key, value)
1822
is WritableArray -> map.putArray(key, value)
23+
is Value -> {
24+
val contents = value.contents
25+
when (contents) {
26+
null -> map.putNull(key)
27+
is Boolean -> map.putBoolean(key, contents)
28+
is Double -> map.putDouble(key, contents)
29+
is Int -> map.putInt(key, contents)
30+
is Long -> map.putInt(key, contents.toInt())
31+
is String -> map.putString(key, contents)
32+
is WritableMap -> map.putMap(key, contents)
33+
is WritableArray -> map.putArray(key, contents)
34+
}
35+
}
1936
else -> throw IllegalArgumentException("Unsupported value type ${value::class.java.name} for key [$key]")
2037
}
2138
}
@@ -32,8 +49,25 @@ fun writableArrayOf(vararg values: Any): WritableArray {
3249
is Int -> array.pushInt(value)
3350
is Long -> array.pushInt(value.toInt())
3451
is String -> array.pushString(value)
52+
is Map<*,*> -> array.pushMap(writableMapOf(*value.map { it.key to it.value }.toTypedArray()))
53+
is Array<*> -> array.pushArray(writableArrayOf(*value.map{ it as Any }.toTypedArray()))
3554
is WritableMap -> array.pushMap(value)
3655
is WritableArray -> array.pushArray(value)
56+
is Value -> {
57+
val contents = value.contents
58+
when (contents) {
59+
null -> array.pushNull()
60+
is Boolean -> array.pushBoolean(contents)
61+
is Double -> array.pushDouble(contents)
62+
is Int -> array.pushInt(contents)
63+
is Long -> array.pushInt(contents.toInt())
64+
is String -> array.pushString(contents)
65+
is Map<*,*> -> array.pushMap(writableMapOf(*contents.map { it.key to it.value }.toTypedArray()))
66+
is Array<*> -> array.pushArray(writableArrayOf(*contents.map{it as Any}.toTypedArray()))
67+
is WritableMap -> array.pushMap(contents)
68+
is WritableArray -> array.pushArray(contents)
69+
}
70+
}
3771
else -> throw IllegalArgumentException("Unsupported value type ${value::class.java.name}")
3872
}
3973
}

android/src/main/old-arch/com/rnmapbox/rnmbx/NativeMapViewModuleSpec.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.facebook.react.bridge.ReactMethod;
2020
import com.facebook.react.bridge.ReactModuleWithSpec;
2121
import com.facebook.react.bridge.ReadableArray;
22+
import com.facebook.react.bridge.ReadableMap;
2223
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
2324
import javax.annotation.Nonnull;
2425
import javax.annotation.Nullable;
@@ -83,6 +84,18 @@ public NativeMapViewModuleSpec(ReactApplicationContext reactContext) {
8384
@DoNotStrip
8485
public abstract void clearData(@Nullable Double viewRef, Promise promise);
8586

87+
@ReactMethod
88+
@DoNotStrip
89+
public abstract void setFeatureState(@Nullable Double viewRef, String featureId, ReadableMap state, String sourceId, @Nullable String sourceLayerId, Promise promise);
90+
91+
@ReactMethod
92+
@DoNotStrip
93+
public abstract void getFeatureState(@Nullable Double viewRef, String featureId, String sourceId, @Nullable String sourceLayerId, Promise promise);
94+
95+
@ReactMethod
96+
@DoNotStrip
97+
public abstract void removeFeatureState(@Nullable Double viewRef, String featureId, @Nullable String stateKey, String sourceId, @Nullable String sourceLayerId, Promise promise);
98+
8699
@ReactMethod
87100
@DoNotStrip
88101
public abstract void querySourceFeatures(@Nullable Double viewRef, String sourceId, ReadableArray withFilter, ReadableArray withSourceLayerIDs, Promise promise);

docs/MapView.md

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,9 @@ Queries the currently loaded data for elevation at a geographical location.<br/>
712712
| `coordinate` | `Position` | `Yes` | the coordinates to query elevation at |
713713

714714

715-
[Query Terrain Elevation](../examples/V10/QueryTerrainElevation)### setSourceVisibility(visible, sourceId[, sourceLayerId])
715+
[Query Terrain Elevation](../examples/V10/QueryTerrainElevation)
716+
717+
### setSourceVisibility(visible, sourceId[, sourceLayerId])
716718

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

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

726728

727729

728730
```javascript
729731
await this._map.setSourceVisibility(false, 'composite', 'building')
730732
```
731733

734+
### setFeatureState(featureId, state, sourceId [, sourceLayerId])
735+
736+
Updates the state map of a feature within a style source.
737+
738+
Updates entries in the state map of a given feature within a style source.
739+
740+
Only entries listed in the `state` will be updated.
741+
742+
An entry in the feature state map that is not listed in `state` will retain its previous value.
743+
744+
#### arguments
745+
| Name | Type | Required | Description |
746+
| ---- | :--: | :------: | :----------: |
747+
| `featureId` | `string` | `Yes` | Identifier of the feature whose state should be updated |
748+
| `state` | `object` | `Yes` | Map of entries to update with their respective new values. |
749+
| `sourceId` | `string` | `Yes` | Style source identifier |
750+
| `sourceLayerId` | `string` | `No` | Style source layer identifier (for multi-layer sources such as vector sources). |
751+
752+
```javascript
753+
await this._map.setFeatureState('my-feature-id', { 'my-feature-state-key': 'my-feature-state-value' }, 'my-source-id', 'my-source-layer-id')
754+
```
755+
756+
757+
### getFeatureState(featureId, sourceId [, sourceLayerId])
758+
759+
Returns the state map of a feature within a style source.
760+
761+
#### arguments
762+
| Name | Type | Required | Description |
763+
| ---- | :--: | :------: | :----------: |
764+
| `featureId` | `string` | `Yes` | Identifier of the feature whose state should be updated. |
765+
| `sourceId` | `string` | `Yes` | Style source identifier. |
766+
| `sourceLayerId` | `string` | `No` | Style source layer identifier (for multi-layer sources such as vector sources). |
767+
768+
```javascript
769+
await this._map.getFeatureState('my-feature-id', 'my-source-id', 'my-source-layer-id')
770+
```
771+
772+
### removeFeatureState(featureId, stateKey, sourceId [, sourceLayerId])
732773

774+
Removes entries from a feature state object.
733775

776+
Removes a specified property or all properties from a feature's state object depending on the value of `stateKey`.
777+
778+
#### arguments
779+
| Name | Type | Required | Description |
780+
| ---- | :--: | :------: | :----------: |
781+
| `featureId` | `string` | `Yes` | Identifier of the feature whose state should be removed. |
782+
| `stateKey` | `string` or `null` | `Yes` | The name of the property to remove. If `null`, all feature’s state object properties are removed. |
783+
| `sourceId` | `string` | `Yes` | Style source identifier. |
784+
| `sourceLayerId` | `string` | `No` | Style source layer identifier (for multi-layer sources such as vector sources). |
785+
786+
```javascript
787+
await this._map.removeFeatureState('my-feature-id', 'my-feature-state-key', 'my-source-id', 'my-source-layer-id')
788+
```

0 commit comments

Comments
 (0)