Skip to content

Commit

Permalink
Add map.addInteraction, add Standard featuresets
Browse files Browse the repository at this point in the history
  • Loading branch information
Release SDK bot for Maps SDK team committed Jan 8, 2025
1 parent 1636295 commit 0ae5762
Show file tree
Hide file tree
Showing 21 changed files with 1,210 additions and 1,189 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
# main

* Add support for Swift Package Manager.
* Introduce the experimental Interactions API, a toolset that allows you to handle interactions on both layers and basemap features for styles. This API introduces a new concept called `Featureset`, which allows Evolving Basemap styles, such as Standard, to export an abstract set of features, such as POI, buildings, and place labels, regardless of which layers they are rendered on. An `Interaction` can then be targeted to these features, modifying their state when interacted with. For example, you can add a `TapInteraction` to your map which targets the `buildings` `Featureset`. When a user taps on a building, the building will be highlighted and its color will change to blue.

```dart
var tapInteraction = TapInteraction(Featureset.standardBuildings())
mapboxMap.addInteraction(tapInteraction,
(_, FeaturesetFeature feature) {
mapboxMap.setFeatureStateForFeaturesetFeature(feature,
StandardBuildingState(highlight: true));
}
);
```

Specific changes:
* Introduce the experimental `MapboxMap.addInteractions` method, which allows you to add interactions to the map.
* Introduce `TapInteraction` and `LongTapInteraction`, which allow you to add tap and longTap interactions to the map.
* Introduce `FeaturesetDescriptor` -- and convenience descriptors for `StandardBuildings`, `StandardPOIs`, and `StandardPlaceLabels` -- which allow you to describe the featureset you want `Interactions` to target.
* Introduce low-level methods for creating and manipulating interactive features: `queryRenderedFeatures`, `querySourceFeatures`, `setFeatureState`, `getFeatureState`, `removeFeatureState`, `resetFeatureState`
* For more guidance with using these new features see `interactive_features_example.dart`.

### 2.5.0

Expand Down
45 changes: 26 additions & 19 deletions android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import com.mapbox.maps.MapboxExperimental
import com.mapbox.maps.StylePackError
import com.mapbox.maps.applyDefaultParams
import com.mapbox.maps.debugoptions.MapViewDebugOptions
import com.mapbox.maps.extension.style.expressions.generated.Expression
import com.mapbox.maps.extension.style.layers.properties.generated.ProjectionName
import com.mapbox.maps.extension.style.light.LightPosition
import com.mapbox.maps.extension.style.light.generated.ambientLight
Expand Down Expand Up @@ -290,29 +289,27 @@ fun FeaturesetDescriptor.toTypedFeaturesetDescriptor(): TypedFeaturesetDescripto
)
}

fun FeaturesetQueryTarget.toFeaturesetQueryTarget(): com.mapbox.maps.FeaturesetQueryTarget {
return com.mapbox.maps.FeaturesetQueryTarget(featureset.toFeatureSetDescriptor(), filter?.let { Expression.fromRaw(filter) }, id)
}

@OptIn(MapboxExperimental::class)
fun Map<String, Any?>.toFeatureState(): com.mapbox.maps.interactions.FeatureState {
val map = this
return FeatureState {
for ((key, value) in map) {
when (value) {
is String -> {
addStringState(key, value)
}
is Long -> {
addLongState(key, value)
}
is Double -> {
addDoubleState(key, value)
}
is Boolean -> {
addBooleanState(key, value)
value?.let {
when (value) {
is String -> {
addStringState(key, value)
}
is Long -> {
addLongState(key, value)
}
is Double -> {
addDoubleState(key, value)
}
is Boolean -> {
addBooleanState(key, value)
}
else -> throw (RuntimeException("Unsupported (key, value): ($key, $value)"))
}
else -> throw (RuntimeException("Unsupported (key, value): ($key, $value)"))
}
}
}
Expand All @@ -321,7 +318,7 @@ fun Map<String, Any?>.toFeatureState(): com.mapbox.maps.interactions.FeatureStat
@OptIn(MapboxExperimental::class)
@SuppressLint("RestrictedApi")
fun FeaturesetFeature.toFeaturesetFeature(): com.mapbox.maps.interactions.FeaturesetFeature<FeatureState> {
val jsonObject: JsonObject = JsonParser.parseString(properties.toString()).getAsJsonObject()
val jsonObject: JsonObject = JsonParser.parseString(Gson().toJson(properties)).getAsJsonObject()
featureset.featuresetId?.let {
return com.mapbox.maps.interactions.FeaturesetFeature(
id?.toFeaturesetFeatureId(),
Expand Down Expand Up @@ -642,6 +639,16 @@ fun com.mapbox.maps.interactions.FeaturesetFeature<FeatureState>.toFLTFeatureset
)
}

@SuppressLint("RestrictedApi")
@OptIn(MapboxExperimental::class)
fun com.mapbox.maps.InteractionContext.toFLTMapContentGestureContext(): MapContentGestureContext {
return MapContentGestureContext(
ScreenCoordinate(screenCoordinate.x, screenCoordinate.y),
coordinateInfo.coordinate,
GestureState.ENDED
)
}

fun com.mapbox.maps.QueriedSourceFeature.toFLTQueriedSourceFeature(): QueriedSourceFeature {
return QueriedSourceFeature(queriedFeature.toFLTQueriedFeature())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import com.mapbox.maps.mapbox_maps.pigeons.FeatureExtensionValue
import com.mapbox.maps.mapbox_maps.pigeons.FeaturesetDescriptor
import com.mapbox.maps.mapbox_maps.pigeons.FeaturesetFeature
import com.mapbox.maps.mapbox_maps.pigeons.FeaturesetFeatureId
import com.mapbox.maps.mapbox_maps.pigeons.FeaturesetQueryTarget
import com.mapbox.maps.mapbox_maps.pigeons.MapDebugOptions
import com.mapbox.maps.mapbox_maps.pigeons.MapOptions
import com.mapbox.maps.mapbox_maps.pigeons.NorthOrientation
Expand Down Expand Up @@ -180,32 +179,6 @@ class MapInterfaceController(
}
}

@OptIn(MapboxExperimental::class, MapboxDelicateApi::class)
override fun queryRenderedFeaturesForTargets(
geometry: _RenderedQueryGeometry,
targets: List<FeaturesetQueryTarget>,
callback: (Result<List<QueriedRenderedFeature>>) -> Unit
) {
mapboxMap.queryRenderedFeatures(
geometry.toRenderedQueryGeometry(context),
targets.map { target ->
target.toFeaturesetQueryTarget()
}
) {
if (it.isError) {
callback(Result.failure(Throwable(it.error)))
} else {
callback(
Result.success(
it.value!!.map { feature ->
feature.toFLTQueriedRenderedFeature()
}.toMutableList()
)
)
}
}
}

@OptIn(MapboxExperimental::class)
override fun queryRenderedFeaturesForFeatureset(
geometry: _RenderedQueryGeometry,
Expand All @@ -214,8 +187,8 @@ class MapInterfaceController(
callback: (Result<List<FeaturesetFeature>>) -> Unit
) {
mapboxMap.queryRenderedFeatures(
geometry.toRenderedQueryGeometry(context),
featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor<*, com.mapbox.maps.interactions.FeaturesetFeature<FeatureState>>,
geometry.toRenderedQueryGeometry(context),
filter?.let { Expression.fromRaw(filter) }
) {
callback(
Expand All @@ -234,8 +207,8 @@ class MapInterfaceController(
) {
val geometry = RenderedQueryGeometry(ScreenBox(ScreenCoordinate(0.0, 0.0), ScreenCoordinate(mapView.width.toDouble(), mapView.height.toDouble())))
mapboxMap.queryRenderedFeatures(
geometry,
featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor<*, com.mapbox.maps.interactions.FeaturesetFeature<FeatureState>>,
geometry,
filter?.let { Expression.fromRaw(filter) }
) {
callback(
Expand Down Expand Up @@ -264,28 +237,6 @@ class MapInterfaceController(
}
}

@OptIn(MapboxExperimental::class)
override fun querySourceFeaturesForTargets(
target: FeaturesetQueryTarget,
callback: (Result<List<QueriedSourceFeature>>) -> Unit
) {
mapboxMap.querySourceFeatures(
target.featureset.toTypedFeaturesetDescriptor(),
target.filter?.let { Expression.fromRaw(target.filter) },
target.id
) {
if (it.isError) {
callback(Result.failure(Throwable(it.error)))
} else {
callback(
Result.success(
it.value!!.map { feature -> feature.toFLTQueriedSourceFeature() }.toMutableList()
)
)
}
}
}

override fun getGeoJsonClusterLeaves(
sourceIdentifier: String,
cluster: Map<String?, Any?>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,20 @@ import androidx.lifecycle.setViewTreeLifecycleOwner
import com.mapbox.bindgen.Value
import com.mapbox.common.SettingsServiceFactory
import com.mapbox.common.SettingsServiceStorageType
import com.mapbox.common.toValue
import com.mapbox.maps.ClickInteraction
import com.mapbox.maps.FeaturesetDescriptor
import com.mapbox.maps.LongClickInteraction
import com.mapbox.maps.MapInitOptions
import com.mapbox.maps.MapView
import com.mapbox.maps.MapboxMap
import com.mapbox.maps.extension.style.expressions.dsl.generated.id
import com.mapbox.maps.mapbox_maps.annotation.AnnotationController
import com.mapbox.maps.mapbox_maps.pigeons.AttributionSettingsInterface
import com.mapbox.maps.mapbox_maps.pigeons.CompassSettingsInterface
import com.mapbox.maps.mapbox_maps.pigeons.GesturesSettingsInterface
import com.mapbox.maps.mapbox_maps.pigeons.InteractionType
import com.mapbox.maps.mapbox_maps.pigeons.InteractionsListener
import com.mapbox.maps.mapbox_maps.pigeons.LogoSettingsInterface
import com.mapbox.maps.mapbox_maps.pigeons.Projection
import com.mapbox.maps.mapbox_maps.pigeons.ScaleBarSettingsInterface
Expand Down Expand Up @@ -235,6 +242,54 @@ class MapboxMapController(
gestureController.removeListeners()
result.success(null)
}
"interactions#add_interaction" -> {
val listener = InteractionsListener(messenger, channelSuffix)
val arguments: HashMap<String, Any> = call.arguments as? HashMap<String, Any> ?: return
val featuresetDescriptorList = arguments["featuresetDescriptor"] as? List<Any?> ?: return
val featuresetDescriptor = com.mapbox.maps.mapbox_maps.pigeons.FeaturesetDescriptor.fromList(featuresetDescriptorList)
val interactionTypeRaw = arguments["interactionType"] as? Int ?: return
val interactionType = InteractionType.ofRaw(interactionTypeRaw)
val stopPropagation = arguments["stopPropagation"] as? Boolean ?: return
val id = arguments["id"] as? Int ?: return
val filter = arguments["filter"] as? String
val radius = arguments["radius"] as? Double

featuresetDescriptor.featuresetId?.let {
when (interactionType) {
InteractionType.TAP -> mapboxMap?.addInteraction(
ClickInteraction.featureset(id = it, importId = featuresetDescriptor.importId, filter = filter.toValue(), radius = radius) { featuresetFeature, context ->
listener.onInteraction(context.toFLTMapContentGestureContext(), featuresetFeature.toFLTFeaturesetFeature(), id.toLong()) { _ -> }
return@featureset stopPropagation
}
)
InteractionType.LONG_TAP -> mapboxMap?.addInteraction(
LongClickInteraction.featureset(id = it, importId = featuresetDescriptor.importId, filter = filter.toValue(), radius = radius) { featuresetFeature, context ->
listener.onInteraction(context.toFLTMapContentGestureContext(), featuresetFeature.toFLTFeaturesetFeature(), id.toLong()) { _ -> }
return@featureset stopPropagation
}
)
null -> return
}
} ?: featuresetDescriptor.layerId?.let {
when (interactionType) {
InteractionType.TAP -> mapboxMap?.addInteraction(
ClickInteraction.layer(id = it, filter = filter.toValue(), radius = radius) { featuresetFeature, context ->
listener.onInteraction(context.toFLTMapContentGestureContext(), featuresetFeature.toFLTFeaturesetFeature(), id.toLong()) { _ -> }
return@layer stopPropagation
}
)
InteractionType.LONG_TAP -> mapboxMap?.addInteraction(
LongClickInteraction.layer(id = it, filter = filter.toValue(), radius = radius) { featuresetFeature, context ->
listener.onInteraction(context.toFLTMapContentGestureContext(), featuresetFeature.toFLTFeaturesetFeature(), id.toLong()) { _ -> }
return@layer stopPropagation
}
)
null -> return
}
}

result.success(null)
}
"platform#releaseMethodChannels" -> {
dispose()
result.success(null)
Expand Down
Loading

0 comments on commit 0ae5762

Please sign in to comment.