From 001bf2317cc5d4c7adffc3f936c31fcb0b788b51 Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Wed, 23 Oct 2024 10:11:26 -0400 Subject: [PATCH 01/28] Initial implementation of QRF and Featurestate --- .../com/mapbox/maps/mapbox_maps/Extentions.kt | 16 + .../mapbox_maps/MapInterfaceController.kt | 65 ++ .../maps/mapbox_maps/pigeons/MapInterfaces.kt | 569 +++++++++++++++- example/assets/featuresetsStyle.json | 166 +++++ example/integration_test/all_test.dart | 2 + .../integration_test/empty_map_widget.dart | 3 +- .../interactive_features_test.dart | 281 ++++++++ example/lib/interactive_features.dart | 173 +++++ .../Classes/Extensions.swift | 133 +++- .../Classes/Generated/MapInterfaces.swift | 623 ++++++++++++++++- .../Classes/MapInterfaceController.swift | 165 ++++- .../Classes/StyleController.swift | 6 + .../Classes/TurfAdapters.swift | 30 + lib/mapbox_maps_flutter.dart | 1 + lib/src/mapbox_map.dart | 81 +++ lib/src/pigeons/map_interfaces.dart | 642 ++++++++++++++++++ lib/src/turf_adapters.dart | 52 +- 17 files changed, 2984 insertions(+), 24 deletions(-) create mode 100644 example/assets/featuresetsStyle.json create mode 100644 example/integration_test/interactive_features_test.dart create mode 100644 example/lib/interactive_features.dart diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt index d7f7d1fa4..abc14c2e1 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt @@ -263,6 +263,22 @@ fun RenderedQueryOptions.toRenderedQueryOptions(): com.mapbox.maps.RenderedQuery return com.mapbox.maps.RenderedQueryOptions(layerIds, filter?.toValue()) } +fun FeaturesetFeatureId.toFeaturesetFeatureId(): com.mapbox.maps.FeaturesetFeatureId { + return com.mapbox.maps.FeaturesetFeatureId(id, namespace) +} + +fun FeaturesetDescriptor.toFeatureSetDescriptor(): com.mapbox.maps.FeaturesetDescriptor { + return com.mapbox.maps.FeaturesetDescriptor(featuresetId, importId, layerId) +} + +fun FeaturesetQueryTarget.toFeaturesetQueryTarget(): com.mapbox.maps.FeaturesetQueryTarget { + return com.mapbox.maps.FeaturesetQueryTarget(featureset.toFeatureSetDescriptor(), filter?.toValue(), id) +} + +// fun FeaturesetFeature.toFeaturesetFeature(): com.mapbox.maps.interactions.FeaturesetFeature<> { +// return com.mapbox.maps.interactions.FeaturesetFeature(id, descriptor, state, originalFeature) +// } + fun MapDebugOptions.toMapDebugOptions(): com.mapbox.maps.MapDebugOptions { return com.mapbox.maps.MapDebugOptions.values()[data.ordinal] } diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt index be10e807b..29d158ef6 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt @@ -5,12 +5,16 @@ import com.google.gson.Gson import com.mapbox.geojson.Feature import com.mapbox.geojson.Point import com.mapbox.maps.MapView +import com.mapbox.maps.MapboxExperimental import com.mapbox.maps.MapboxMap import com.mapbox.maps.TileCacheBudget import com.mapbox.maps.extension.observable.eventdata.MapLoadingErrorEventData +import com.mapbox.maps.interactions.FeatureState import com.mapbox.maps.mapbox_maps.pigeons.CanonicalTileID import com.mapbox.maps.mapbox_maps.pigeons.ConstrainMode import com.mapbox.maps.mapbox_maps.pigeons.FeatureExtensionValue +import com.mapbox.maps.mapbox_maps.pigeons.FeaturesetFeature +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 @@ -22,6 +26,7 @@ import com.mapbox.maps.mapbox_maps.pigeons.SourceQueryOptions import com.mapbox.maps.mapbox_maps.pigeons.TileCacheBudgetInMegabytes import com.mapbox.maps.mapbox_maps.pigeons.TileCacheBudgetInTiles import com.mapbox.maps.mapbox_maps.pigeons.TileCoverOptions +import com.mapbox.maps.mapbox_maps.pigeons.TypedFeaturesetDescriptor import com.mapbox.maps.mapbox_maps.pigeons.ViewportMode import com.mapbox.maps.mapbox_maps.pigeons._MapInterface import com.mapbox.maps.mapbox_maps.pigeons._MapWidgetDebugOptions @@ -166,6 +171,29 @@ class MapInterfaceController( } } + override fun queryRenderedFeaturesForGeometry( + geometry: _RenderedQueryGeometry, + targets: List, + callback: (Result>) -> Unit + ) { + mapboxMap.queryRenderedFeatures( + geometry.toRenderedQueryGeometry(context), + targets.map { + it.toFeaturesetQueryTarget() + } + ) { + if (it.isError) { + callback(Result.failure(Throwable(it.error))) + } else { + callback( + Result.success( + it.value!!.map { feature -> feature.toFLTQueriedRenderedFeature() }.toMutableList() + ) + ) + } + } + } + override fun querySourceFeatures( sourceId: String, options: SourceQueryOptions, @@ -253,6 +281,27 @@ class MapInterfaceController( } } + @OptIn(MapboxExperimental::class) + override fun setFeatureStateForFeatureStateFeature( + feature: FeaturesetFeature, + state: String, + callback: (Result) -> Unit + ) { + mapboxMap.setFeatureState( + com.mapbox.maps.interactions.TypedFeaturesetDescriptor.Featureset( + feature.descriptor!!.descriptor.featuresetId!!, feature.descriptor.descriptor.importId + ), + feature.id!!.toFeaturesetFeatureId(), + FeatureState { feature.state } + ) { + if (it.isError) { + callback(Result.failure(Throwable(it.error))) + } else { + callback(Result.success(Unit)) + } + } + } + override fun getFeatureState( sourceId: String, sourceLayerId: String?, @@ -269,6 +318,22 @@ class MapInterfaceController( } } } + @OptIn(MapboxExperimental::class) + override fun getFeatureStateForFeaturesetFeature( + feature: FeaturesetFeature, + callback: (Result) -> Unit + ) { + mapboxMap.getFeatureState( + com.mapbox.maps.interactions.TypedFeaturesetDescriptor.Featureset( + feature.descriptor!!.descriptor.featuresetId!!, feature.descriptor.descriptor.importId + ), + feature.id!!.toFeaturesetFeatureId() + ) { expected -> + callback.let { + it(Result.success(expected.asJsonString())) + } + } + } override fun removeFeatureState( sourceId: String, diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt index 7e9927303..13c773b77 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt @@ -1263,19 +1263,27 @@ data class QueriedRenderedFeature( * If the feature has been rendered in multiple layers, multiple Ids will be provided. * If the feature is only rendered in one layer, a single Id will be provided. */ - val layers: List + val layers: List, + /** + * An array of feature query targets that correspond to this queried feature. + * + * - Note: Returned query targets will omit the original `filter` data. + */ + val queryTargets: List? = null ) { companion object { fun fromList(pigeonVar_list: List): QueriedRenderedFeature { val queriedFeature = pigeonVar_list[0] as QueriedFeature val layers = pigeonVar_list[1] as List - return QueriedRenderedFeature(queriedFeature, layers) + val queryTargets = pigeonVar_list[2] as List? + return QueriedRenderedFeature(queriedFeature, layers, queryTargets) } } fun toList(): List { return listOf( queriedFeature, layers, + queryTargets, ) } } @@ -1344,6 +1352,167 @@ data class QueriedFeature( } } +/** + * Represents a unique identifier for a feature in one exported featureset or a layer. + * + * Generated class from Pigeon that represents data sent in messages. + */ +data class FeaturesetFeatureId( + /** + * The `featureId` uniquely identifies a feature within a featureset or layer. + * Note: The Identifier of the feature is not guaranteed to be persistent + * and can change depending on the source that is used. + */ + val id: String, + /** + * An optional field that represents the feature namespace defined by + * the Selector within a Featureset to which this feature belongs. + * If the underlying source is the same for multiple selectors within a Featureset, + * the same `featureNamespace` should be used across those selectors. + * This practice ensures the uniqueness of `FeaturesetFeatureId` across the style. + * Defining a `featureNamespace` value for the Selector is recommended, + * especially when multiple selectors exist in a Featureset, + * as it can enhance the efficiency of feature operations. + */ + val namespace: String? = null +) { + companion object { + fun fromList(pigeonVar_list: List): FeaturesetFeatureId { + val id = pigeonVar_list[0] as String + val namespace = pigeonVar_list[1] as String? + return FeaturesetFeatureId(id, namespace) + } + } + fun toList(): List { + return listOf( + id, + namespace, + ) + } +} + +/** + * Represents an identifier for a single exported featureset or a layer. + * + * Generated class from Pigeon that represents data sent in messages. + */ +data class FeaturesetDescriptor( + /** + * An optional unique identifier for the featureset within the style. + * This id is used to reference a specific featureset. + * + * * Note: If `featuresetId` is provided and valid, it takes precedence over `layerId`, + * * meaning `layerId` will not be considered even if it has a valid value. + */ + val featuresetId: String? = null, + /** + * An optional import id that is required if the featureset is defined within an imported style. + * If the featureset belongs to the current style, this field should be set to a null string. + * + * Note: `importId` is only applicable when used in conjunction with `featuresetId` + * and has no effect when used with `layerId`. + */ + val importId: String? = null, + /** + * An optional unique identifier for the layer within the current style. + * + * Note: If `featuresetId` is valid, `layerId` will be ignored even if it has a valid value. + * Additionally, `importId` does not apply when using `layerId`. + */ + val layerId: String? = null +) { + companion object { + fun fromList(pigeonVar_list: List): FeaturesetDescriptor { + val featuresetId = pigeonVar_list[0] as String? + val importId = pigeonVar_list[1] as String? + val layerId = pigeonVar_list[2] as String? + return FeaturesetDescriptor(featuresetId, importId, layerId) + } + } + fun toList(): List { + return listOf( + featuresetId, + importId, + layerId, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class FeaturesetFeature( + /** + * Optional identifier holding feature id and feature namespace. + * It could be NULL when underlying [Feature.id] is null. + */ + val id: FeaturesetFeatureId? = null, + /** + * The [TypedFeaturesetDescriptor] this concrete feature comes from. + * List of supported featuresets could be found in the nested classes + * (e.g. [TypedFeaturesetDescriptor.Featureset], [TypedFeaturesetDescriptor.Layer], etc). + */ + val featureset: FeaturesetDescriptor, + /** + * A feature geometry. + * Feature JSON properties. + * The geoJSON feature. + */ + val geoJSONFeature: Feature, + /** + * Current feature state stored as a concrete instance of [FeatureState]. + * Important: this state is immutable and represents the feature state + * at the precise moment of the interaction callback. + */ + val state: Map +) { + companion object { + fun fromList(pigeonVar_list: List): FeaturesetFeature { + val id = pigeonVar_list[0] as FeaturesetFeatureId? + val featureset = pigeonVar_list[1] as FeaturesetDescriptor + val geoJSONFeature = pigeonVar_list[2] as Feature + val state = pigeonVar_list[3] as Map + return FeaturesetFeature(id, featureset, geoJSONFeature, state) + } + } + fun toList(): List { + return listOf( + id, + featureset, + geoJSONFeature, + state, + ) + } +} + +/** + * Defines the parameters for querying features from a Featureset with an optional filter and id. + * + * Generated class from Pigeon that represents data sent in messages. + */ +data class FeaturesetQueryTarget( + /** The FeaturesetDescriptor that specifies the featureset to be included in the query. */ + val featureset: FeaturesetDescriptor, + /** An optional filter expression used to refine the query results based on conditions related to the specified featureset. */ + val filter: String? = null, + /** An optional unique identifier associated with the FeaturesetQueryTarget. */ + val id: Long? = null +) { + companion object { + fun fromList(pigeonVar_list: List): FeaturesetQueryTarget { + val featureset = pigeonVar_list[0] as FeaturesetDescriptor + val filter = pigeonVar_list[1] as String? + val id = pigeonVar_list[2] as Long? + return FeaturesetQueryTarget(featureset, filter, id) + } + } + fun toList(): List { + return listOf( + featureset, + filter, + id, + ) + } +} + /** * Geometry for querying rendered features. * @@ -3173,6 +3342,69 @@ interface _MapInterface { * @return A `cancelable` object that could be used to cancel the pending query. */ fun queryRenderedFeatures(geometry: _RenderedQueryGeometry, options: RenderedQueryOptions, callback: (Result>) -> Unit) + /** + * Queries the map for rendered features using featureset descriptors. + * + * This method allows to query both featureset from imported styles and user layers in the root style. + * The results can be additionally filtered per-featureset. + * + * ```dart + * let targets = [ + * FeaturesetQueryTarget( + * featureset: .layer("my-layer"), + * filter: Exp(.eq) { + * Exp(.get) { "type" } + * "hotel" + * } + * ), + * FeaturesetQueryTarget(featureset: .featureset("poi", importId: "basemap")) + * ] + * mapView.mapboxMap.queryRenderedFeatures(with: CGPoint(x: 0, y: 0), + * targets: targets) { result in + * // handle features in result + * } + * ``` + * + * - Important: This is a low-level method. If you need to handle basic gestures on map content, please prefer to use Interactions API (see ``MapboxMap/addInteraction(_:)``) or ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)``. + * + * @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. + * @param targets An array of targets to query with. + * @param completion Callback called when the query completes. + */ + fun queryRenderedFeaturesForTargets(geometry: _RenderedQueryGeometry, targets: List, callback: (Result>) -> Unit) + /** + * Queries the map for rendered features with one typed featureset. + * + * The results array will contain features of the type specified by this featureset. + * + * ```swift + * mapView.mapboxMap.queryRenderedFeatures( + * with: CGPoint(x: 0, y: 0), + * featureset: .standardBuildings) { result in + * // handle buildings in result + * } + * ``` + * + * - Important: If you need to handle basic gestures on map content, please prefer to use Interactions API, see ``MapboxMap/addInteraction(_:)``. + * + * @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. + * @param featureset A typed featureset to query with. + * @param filter An additional filter for features. + * @param completion Callback called when the query completes. + */ + fun queryRenderedFeaturesForFeatureset(geometry: _RenderedQueryGeometry, featureset: FeaturesetDescriptor, filter: String?, callback: (Result>) -> Unit) + /** + * Queries all rendered features in current viewport, using one typed featureset. + * + * This is same as ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)`` called with geometry matching the current viewport. + * + * - Important: If you need to handle basic gestures on map content, please prefer to use Interactions API, see ``MapboxMap/addInteraction(_:)``. + * + * @param featureset A typed featureset to query with. + * @param filter An additional filter for features. + * @param completion Callback called when the query completes. + */ + fun queryRenderedFeaturesInViewport(featureset: FeaturesetDescriptor, filter: String?, callback: (Result>) -> Unit) /** * Queries the map for source features. * @@ -3181,6 +3413,13 @@ interface _MapInterface { * @param completion The `query features completion` called when the query completes. */ fun querySourceFeatures(sourceId: String, options: SourceQueryOptions, callback: (Result>) -> Unit) + /** + * Queries the source features for a given featureset. + * + * @param target A featureset query target. + * @param completion Callback called when the query completes. + */ + fun querySourceFeaturesForFeatureset(target: FeaturesetQueryTarget, callback: (Result>) -> Unit) /** * Returns all the leaves (original points) of a cluster (given its cluster_id) from a GeoJsonSource, with pagination support: limit is the number of leaves * to return (set to Infinity for all points), and offset is the amount of points to skip (for pagination). @@ -3235,6 +3474,32 @@ interface _MapInterface { * @param state The `state` object with properties to update with their respective new values. */ fun setFeatureState(sourceId: String, sourceLayerId: String?, featureId: String, state: String, callback: (Result) -> Unit) + /** + * Update the state map of a feature within a featureset. + * Update entries in the state map of a given feature within a style source. Only entries listed in the state map + * will be updated. An entry in the feature state map that is not listed in `state` will retain its previous value. + * + * @param featureset The featureset to look the feature in. + * @param featureId Identifier of the feature whose state should be updated. + * @param state Map of entries to update with their respective new values + * @param callback The `feature state operation callback` called when the operation completes or ends. + * + * @return A `Cancelable` object that could be used to cancel the pending operation. + */ + fun setFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: Map, callback: (Result) -> Unit) + /** + * Update the state map of an individual feature. + * + * The feature should have a non-nil ``FeaturesetFeatureType/id``. Otherwise, + * the operation will be no-op and callback will receive an error. + * + * @param feature The feature to update. + * @param state Map of entries to update with their respective new values + * @param callback The `feature state operation callback` called when the operation completes or ends. + * + * @return A `Cancelable` object that could be used to cancel the pending operation. + */ + fun setFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, state: Map, callback: (Result) -> Unit) /** * Gets the state map of a feature within a style source. * @@ -3247,6 +3512,24 @@ interface _MapInterface { * @param completion The `query feature state completion` called when the query completes. */ fun getFeatureState(sourceId: String, sourceLayerId: String?, featureId: String, callback: (Result) -> Unit) + /** + * Get the state map of a feature within a style source. + * + * @param featureset A featureset the feature belongs to. + * @param featureId Identifier of the feature whose state should be queried. + * + * @return A `Cancelable` object that could be used to cancel the pending query. + */ + fun getFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, callback: (Result>) -> Unit) + /** + * Get the state map of a feature within a style source. + * + * @param feature An interactive feature to query the state from. + * @param completion Feature's state map or an empty map if the feature could not be found. + * + * @return A `Cancelable` object that could be used to cancel the pending query. + */ + fun getFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, callback: (Result>) -> Unit) /** * Removes entries from a feature state object. * @@ -3262,6 +3545,41 @@ interface _MapInterface { * @param stateKey The key of the property to remove. If `null`, all feature's state object properties are removed. */ fun removeFeatureState(sourceId: String, sourceLayerId: String?, featureId: String, stateKey: String?, callback: (Result) -> Unit) + /** + * Removes entries from a feature state object of a feature in the specified featureset. + * Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. + * + * @param featureset A featureset the feature belongs to. + * @param featureId Identifier of the feature whose state should be removed. + * @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. + * @param callback The `feature state operation callback` called when the operation completes or ends. + * + * @return A `Cancelable` object that could be used to cancel the pending operation. + */ + fun removeFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String, callback: (Result) -> Unit) + /** + * Removes entries from a specified Feature. + * Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. + * + * @param feature An interactive feature to update. + * @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. + * @param callback The `feature state operation callback` called when the operation completes or ends. + * + * @return A `Cancelable` object that could be used to cancel the pending operation. + */ + fun removeFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, stateKey: String, callback: (Result) -> Unit) + /** + * Reset all the feature states within a featureset. + * + * Note that updates to feature state are asynchronous, so changes made by this method might not be + * immediately visible using ``MapboxMap/getFeatureState(_:callback:)``. + * + * @param featureset A featureset descriptor + * @param callback The `feature state operation callback` called when the operation completes or ends. + * + * @return A `Cancelable` object that could be used to cancel the pending operation. + */ + fun resetFeatureStatesForFeatureset(featureset: FeaturesetDescriptor, callback: (Result) -> Unit) /** Reduces memory use. Useful to call when the application gets paused or sent to background. */ fun reduceMemoryUse() /** @@ -3669,6 +3987,70 @@ interface _MapInterface { channel.setMessageHandler(null) } } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesForTargets$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val geometryArg = args[0] as _RenderedQueryGeometry + val targetsArg = args[1] as List + api.queryRenderedFeaturesForTargets(geometryArg, targetsArg) { result: Result> -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesForFeatureset$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val geometryArg = args[0] as _RenderedQueryGeometry + val featuresetArg = args[1] as FeaturesetDescriptor + val filterArg = args[2] as String? + api.queryRenderedFeaturesForFeatureset(geometryArg, featuresetArg, filterArg) { result: Result> -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesInViewport$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val featuresetArg = args[0] as FeaturesetDescriptor + val filterArg = args[1] as String? + api.queryRenderedFeaturesInViewport(featuresetArg, filterArg) { result: Result> -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.querySourceFeatures$separatedMessageChannelSuffix", codec) if (api != null) { @@ -3690,6 +4072,26 @@ interface _MapInterface { channel.setMessageHandler(null) } } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.querySourceFeaturesForFeatureset$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val targetArg = args[0] as FeaturesetQueryTarget + api.querySourceFeaturesForFeatureset(targetArg) { result: Result> -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.getGeoJsonClusterLeaves$separatedMessageChannelSuffix", codec) if (api != null) { @@ -3777,6 +4179,47 @@ interface _MapInterface { channel.setMessageHandler(null) } } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.setFeatureStateForFeaturesetFeatureDescriptor$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val featuresetArg = args[0] as FeaturesetDescriptor + val featureIdArg = args[1] as FeaturesetFeatureId + val stateArg = args[2] as Map + api.setFeatureStateForFeaturesetFeatureDescriptor(featuresetArg, featureIdArg, stateArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + reply.reply(wrapResult(null)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.setFeatureStateForFeaturesetFeature$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val featureArg = args[0] as FeaturesetFeature + val stateArg = args[1] as Map + api.setFeatureStateForFeaturesetFeature(featureArg, stateArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + reply.reply(wrapResult(null)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.getFeatureState$separatedMessageChannelSuffix", codec) if (api != null) { @@ -3799,6 +4242,47 @@ interface _MapInterface { channel.setMessageHandler(null) } } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.getFeatureStateForFeaturesetDescriptor$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val featuresetArg = args[0] as FeaturesetDescriptor + val featureIdArg = args[1] as FeaturesetFeatureId + api.getFeatureStateForFeaturesetDescriptor(featuresetArg, featureIdArg) { result: Result> -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.getFeatureStateForFeaturesetFeature$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val featureArg = args[0] as FeaturesetFeature + api.getFeatureStateForFeaturesetFeature(featureArg) { result: Result> -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureState$separatedMessageChannelSuffix", codec) if (api != null) { @@ -3821,6 +4305,66 @@ interface _MapInterface { channel.setMessageHandler(null) } } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureStateForFeaturesetFeatureDescriptor$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val featuresetArg = args[0] as FeaturesetDescriptor + val featureIdArg = args[1] as FeaturesetFeatureId + val stateKeyArg = args[2] as String + api.removeFeatureStateForFeaturesetFeatureDescriptor(featuresetArg, featureIdArg, stateKeyArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + reply.reply(wrapResult(null)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureStateForFeaturesetFeature$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val featureArg = args[0] as FeaturesetFeature + val stateKeyArg = args[1] as String + api.removeFeatureStateForFeaturesetFeature(featureArg, stateKeyArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + reply.reply(wrapResult(null)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.resetFeatureStatesForFeatureset$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val featuresetArg = args[0] as FeaturesetDescriptor + api.resetFeatureStatesForFeatureset(featuresetArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + reply.reply(wrapResult(null)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.reduceMemoryUse$separatedMessageChannelSuffix", codec) if (api != null) { @@ -5025,6 +5569,12 @@ interface StyleManager { * @param layerIds The ids of layers that will localize on, default is null which means will localize all the feasible layers. */ fun localizeLabels(locale: String, layerIds: List?, callback: (Result) -> Unit) + /** + * Returns the available featuresets in the currently loaded style. + * + * - Note: This function should only be called after the style is fully loaded; otherwise, the result may be unreliable. + */ + fun getFeaturesets(): List companion object { /** The codec used by StyleManager. */ @@ -6136,6 +6686,21 @@ interface StyleManager { channel.setMessageHandler(null) } } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter.StyleManager.getFeaturesets$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + listOf(api.getFeaturesets()) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } } } } diff --git a/example/assets/featuresetsStyle.json b/example/assets/featuresetsStyle.json new file mode 100644 index 000000000..d9d37932a --- /dev/null +++ b/example/assets/featuresetsStyle.json @@ -0,0 +1,166 @@ +{ + "version": 8, + "imports": [ + { + "id": "nested", + "url": "", + "config": {}, + "data": { + "version": 8, + "featuresets": { + "poi" : { + "selectors": [ + { + "layer": "poi-label-1", + "properties": { + "type": [ "get", "type" ], + "name": [ "get", "name" ], + "class": "poi" + }, + "featureNamespace": "A" + } + ] + } + }, + "sources": { + "geojson": { + "type": "geojson", + "data": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "filter": "true", + "name": "nest1", + "type": "A" + }, + "geometry": { + "type": "Point", + "coordinates": [ 0.01, 0.01 ] + }, + "id": 11 + }, + { + "type": "Feature", + "properties": { + "name": "nest2", + "type": "B" + }, + "geometry": { + "type": "Point", + "coordinates": [ 0.01, 0.01 ] + }, + "id": 12 + }, + { + "type": "Feature", + "properties": { + "name": "nest3", + "type": "B" + }, + "geometry": { + "type": "Point", + "coordinates": [ -0.05, -0.05 ] + }, + "id": 13 + } + ] + } + } + }, + "layers": [ + { + "id": "poi-label-1", + "type": "circle", + "source": "geojson", + "paint": { + "circle-radius": 5, + "circle-color": "red" + } + } + ] + } + } + ], + "sources": { + "geojson": { + "type": "geojson", + "promoteId": "foo", + "data": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "foo": 1 + }, + "geometry": { + "type": "Point", + "coordinates": [ + 0, + 0 + ] + } + } + ] + } + }, + "geojson-2": { + "type": "geojson", + "promoteId": "bar", + "data": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "bar": 1 + }, + "geometry": { + "type": "Point", + "coordinates": [ 0.01, 0.01 ] + } + }, + { + "type": "Feature", + "properties": { + "bar": 2, + "filter": true, + "name": "qux" + }, + "geometry": { + "type": "Point", + "coordinates": [ 0.01, 0.01 ] + } + } + ] + } + } + }, + "layers": [ + { + "id": "background", + "type": "background", + "background-color": "green" + }, + { + "id": "circle-1", + "type": "circle", + "source": "geojson", + "paint": { + "circle-radius": 5, + "circle-color": "black" + } + }, + { + "id": "circle-2", + "type": "circle", + "source": "geojson-2", + "paint": { + "circle-radius": 3, + "circle-color": "blue" + } + } + ] +} \ No newline at end of file diff --git a/example/integration_test/all_test.dart b/example/integration_test/all_test.dart index ccbe22c80..b51309988 100644 --- a/example/integration_test/all_test.dart +++ b/example/integration_test/all_test.dart @@ -48,6 +48,7 @@ import 'scale_bar_test.dart' as scale_bar_test; import 'offline_test.dart' as offline_test; import 'snapshotter/snapshotter_test.dart' as snapshotter_test; import 'viewport_test.dart' as viewport_test; +import 'interactive_features_test.dart' as interactive_features_test; void main() { animation_test.main(); @@ -74,6 +75,7 @@ void main() { // style tests style_test.main(); + interactive_features_test.main(); // layer tests background_layer_test.main(); diff --git a/example/integration_test/empty_map_widget.dart b/example/integration_test/empty_map_widget.dart index 70a2c921d..ff93d0e14 100644 --- a/example/integration_test/empty_map_widget.dart +++ b/example/integration_test/empty_map_widget.dart @@ -28,7 +28,8 @@ Future main( {double? width, double? height, CameraOptions? camera, - ViewportState? viewport}) { + ViewportState? viewport, + Alignment alignment = Alignment.topLeft}) { final completer = Completer(); MapboxOptions.setAccessToken(ACCESS_TOKEN); diff --git a/example/integration_test/interactive_features_test.dart b/example/integration_test/interactive_features_test.dart new file mode 100644 index 000000000..15fc1ef89 --- /dev/null +++ b/example/integration_test/interactive_features_test.dart @@ -0,0 +1,281 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart'; +import 'package:mapbox_maps_example/empty_map_widget.dart' as app; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('test_featureset_QRF', (WidgetTester tester) async { + // load style and position camera + final mapFuture = app.main( + width: 200, + height: 200, + camera: + CameraOptions(center: Point(coordinates: Position(0, 0)), zoom: 10), + alignment: Alignment(100, 100)); + await tester.pumpAndSettle(); + final mapboxMap = await mapFuture; + var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); + mapboxMap.style.setStyleJSON(styleJson); + + await app.events.onMapLoaded.future; + await Future.delayed(Duration(seconds: 1)); + + // test queryRenderedFeaturesForFeatureset + var coord = await mapboxMap + .pixelForCoordinate(Point(coordinates: Position(0.01, 0.01))); + var featuresetQuery = await mapboxMap.queryRenderedFeaturesForFeatureset( + geometry: RenderedQueryGeometry.fromScreenCoordinate(coord), + featureset: + FeaturesetDescriptor(featuresetId: "poi", importId: "nested")); + + expect(featuresetQuery.length, 2); + expect(featuresetQuery.first.geoJSONFeature.properties?["name"], "nest2"); + expect(featuresetQuery[1].geoJSONFeature.properties?["name"], "nest1"); + expect(featuresetQuery[0].geoJSONFeature.properties?["class"], "poi"); + + // test queryRenderedFeaturesForFeatureset with filter + var filter = '["==",["get", "type"], "A"]'; + var featuresetFilterQuery = + await mapboxMap.queryRenderedFeaturesForFeatureset( + geometry: RenderedQueryGeometry.fromScreenCoordinate(coord), + featureset: + FeaturesetDescriptor(featuresetId: "poi", importId: "nested"), + filter: filter); + + expect(featuresetFilterQuery.length, 1); + expect( + featuresetFilterQuery[0].geoJSONFeature.properties?["name"], "nest1"); + expect(featuresetFilterQuery[0].geoJSONFeature.properties?["class"], "poi"); + + // test queryRenderedFeaturesInViewport + var viewportQuery = await mapboxMap.queryRenderedFeaturesInViewport( + featureset: + FeaturesetDescriptor(featuresetId: "poi", importId: "nested")); + + expect(viewportQuery.length, 3); + expect(viewportQuery[0].geoJSONFeature.properties?["name"], "nest2"); + expect(viewportQuery[1].geoJSONFeature.properties?["name"], "nest1"); + expect(viewportQuery[2].geoJSONFeature.properties?["name"], "nest3"); + expect(viewportQuery[2].geoJSONFeature.properties?["class"], "poi"); + }); + + testWidgets('test_featurestate_methods', (WidgetTester tester) async { + // load style and position camera + final mapFuture = app.main( + width: 200, + height: 200, + camera: + CameraOptions(center: Point(coordinates: Position(0, 0)), zoom: 10), + alignment: Alignment(100, 100)); + await tester.pumpAndSettle(); + final mapboxMap = await mapFuture; + var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); + mapboxMap.style.setStyleJSON(styleJson); + + await app.events.onMapLoaded.future; + + var geoJSONFeature = Feature( + id: "feature", geometry: Point(coordinates: Position(0.01, 0.01))); + var feature = FeaturesetFeature( + id: FeaturesetFeatureId(id: "11", namespace: "A"), + featureset: + FeaturesetDescriptor(featuresetId: "poi", importId: "nested"), + geoJSONFeature: geoJSONFeature, + state: {}); + Map state = { + "highlight": true, + }; + + // test set and get featurestate + await mapboxMap.setFeatureStateForFeaturesetFeature(feature, state); + var returnedFeatureState = + await mapboxMap.getFeatureStateForFeaturesetFeature(feature); + expect(returnedFeatureState, state); + + // test remove featurestate + await mapboxMap.removeFeatureStateForFeaturesetFeature( + feature, "highlight"); + var returnedFeatureState2 = + await mapboxMap.getFeatureStateForFeaturesetFeature(feature); + expect(returnedFeatureState2, {}); + + // test reset featurestate + await Future.delayed(Duration(seconds: 1)); + await mapboxMap.setFeatureStateForFeaturesetFeature(feature, state); + var returnedFeatureState3 = + await mapboxMap.getFeatureStateForFeaturesetFeature(feature); + expect(returnedFeatureState3, state); + + await mapboxMap.resetFeatureStatesForFeatureset( + FeaturesetDescriptor(featuresetId: "poi", importId: "nested")); + var returnedFeatureState4 = + await mapboxMap.getFeatureStateForFeaturesetFeature(feature); + expect(returnedFeatureState4, {}); + }); + + testWidgets('test_featurestate_descriptor_methods', + (WidgetTester tester) async { + // load style and position camera + final mapFuture = app.main( + width: 200, + height: 200, + camera: + CameraOptions(center: Point(coordinates: Position(0, 0)), zoom: 10), + alignment: Alignment(100, 100)); + await tester.pumpAndSettle(); + final mapboxMap = await mapFuture; + var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); + mapboxMap.style.setStyleJSON(styleJson); + + await app.events.onMapLoaded.future; + + var featuresetDescriptor = + FeaturesetDescriptor(featuresetId: "poi", importId: "nested"); + var featuresetID = FeaturesetFeatureId(id: "11", namespace: "A"); + Map state = { + "highlight": true, + }; + + // test set and get featurestate + await mapboxMap.setFeatureStateForFeaturesetFeatureDescriptor( + featuresetDescriptor, featuresetID, state); + var returnedFeatureState = + await mapboxMap.getFeatureStateForFeaturesetDescriptor( + featuresetDescriptor, featuresetID); + expect(returnedFeatureState, state); + + // test remove featurestate + await mapboxMap.removeFeatureStateForFeaturesetFeatureDescriptor( + featuresetDescriptor, featuresetID, "highlight"); + var returnedFeatureState2 = + await mapboxMap.getFeatureStateForFeaturesetDescriptor( + featuresetDescriptor, featuresetID); + expect(returnedFeatureState2, {}); + }); + + testWidgets('test_state_is_queried', (WidgetTester tester) async { + // load style and position camera + final mapFuture = app.main( + width: 200, + height: 200, + camera: + CameraOptions(center: Point(coordinates: Position(0, 0)), zoom: 10), + alignment: Alignment(100, 100)); + await tester.pumpAndSettle(); + final mapboxMap = await mapFuture; + var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); + mapboxMap.style.setStyleJSON(styleJson); + + await app.events.onMapLoaded.future; + + var featuresetID = FeaturesetFeatureId(id: "11", namespace: "A"); + var featuresetDescriptor = + FeaturesetDescriptor(featuresetId: "poi", importId: "nested"); + Map state = { + "hide": true, + }; + var filter = '["==",["get", "type"], "A"]'; + Map expectedProperties = { + "name": "nest1", + "type": "A", + "class": "poi" + }; + + await mapboxMap.setFeatureStateForFeaturesetFeatureDescriptor( + featuresetDescriptor, featuresetID, state); + var queryResult = await mapboxMap.queryRenderedFeaturesInViewport( + featureset: featuresetDescriptor, filter: filter); + var poi = queryResult.first; + var point = Point.fromJson(poi.geoJSONFeature.geometry!.toJson()); + + expect(queryResult.length, 1); + expect(poi.id?.id, featuresetID.id); + expect(poi.id?.namespace, featuresetID.namespace); + expect(poi.state, state); + expect(point.coordinates.lat, closeTo(0.01, 0.05)); + expect(point.coordinates.lng, closeTo(0.01, 0.05)); + expect(poi.geoJSONFeature.properties, expectedProperties); + }); + + testWidgets('test_getFeaturesets', (WidgetTester tester) async { + // load style and position camera + final mapFuture = app.main( + width: 200, + height: 200, + camera: + CameraOptions(center: Point(coordinates: Position(0, 0)), zoom: 10), + alignment: Alignment(100, 100)); + await tester.pumpAndSettle(); + final mapboxMap = await mapFuture; + var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); + mapboxMap.style.setStyleJSON(styleJson); + + await app.events.onMapLoaded.future; + + var returnedFeaturesets = await mapboxMap.style.getFeaturesets(); + + expect(returnedFeaturesets.length, 1); + expect(returnedFeaturesets.first.importId, "nested"); + }); + + testWidgets('test_query_featureset_target', (WidgetTester tester) async { + // load style and position camera + final mapFuture = app.main( + width: 200, + height: 200, + camera: + CameraOptions(center: Point(coordinates: Position(0, 0)), zoom: 10), + alignment: Alignment(100, 100)); + await tester.pumpAndSettle(); + final mapboxMap = await mapFuture; + var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); + mapboxMap.style.setStyleJSON(styleJson); + + await app.events.onMapLoaded.future; + + var featuresetFilter = '["==", ["get", "type"], "B"]'; + var layerFilter = '["==", ["get", "filter"], true]'; + var featuresetPOI = + FeaturesetDescriptor(featuresetId: "poi", importId: "nested"); + var featuresetLayer = FeaturesetDescriptor(layerId: "circle-2"); + var coord = await mapboxMap + .pixelForCoordinate(Point(coordinates: Position(0.01, 0.01))); + var targets = [ + FeaturesetQueryTarget( + featureset: featuresetPOI, filter: featuresetFilter, id: 1), + FeaturesetQueryTarget( + featureset: featuresetLayer, filter: layerFilter, id: 2) + ]; + + var returnedQuery = await mapboxMap.queryRenderedFeaturesForTargets( + RenderedQueryGeometry.fromScreenCoordinate(coord), targets); + var firstFeature = + Feature.fromFeature(returnedQuery[0]!.queriedFeature.feature); + var secondFeature = + Feature.fromFeature(returnedQuery[1]!.queriedFeature.feature); + + expect(returnedQuery.length, 2); + expect(returnedQuery[0]?.queryTargets?.length, 1); + expect(returnedQuery[0]?.queryTargets?.last.id, 2); + expect(returnedQuery[0]?.queryTargets?.last.featureset.layerId, "circle-2"); + expect(returnedQuery[0]?.queryTargets?.last.filter, null); + expect(firstFeature.id, 2); + expect(firstFeature.properties?["name"], "qux"); + expect(returnedQuery[1]?.queryTargets?.length, 1); + expect(returnedQuery[1]?.queryTargets?.last.id, 1); + expect(returnedQuery[1]?.queryTargets?.last.featureset.featuresetId, "poi"); + expect(returnedQuery[1]?.queryTargets?.last.featureset.importId, "nested"); + expect(returnedQuery[1]?.queryTargets?.last.filter, null); + expect(secondFeature.id, 12); + expect(secondFeature.properties?["class"], "poi"); + expect(secondFeature.properties?["name"], "nest2"); + }); +} diff --git a/example/lib/interactive_features.dart b/example/lib/interactive_features.dart new file mode 100644 index 000000000..c2d5de61f --- /dev/null +++ b/example/lib/interactive_features.dart @@ -0,0 +1,173 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart'; +import 'page.dart'; + +class InteractiveFeaturesPage extends ExamplePage { + InteractiveFeaturesPage() + : super(const Icon(Icons.map), 'Interactive features map'); + + @override + Widget build(BuildContext context) { + return const InteractiveFeatures(); + } +} + +class InteractiveFeatures extends StatefulWidget { + const InteractiveFeatures(); + + @override + State createState() => InteractiveFeaturesState(); +} + +class InteractiveFeaturesState extends State { + MapboxMap? mapboxMap; + var isLight = true; + var feature = Feature( + id: "addedFeature", + geometry: + Point(coordinates: Position(24.94180921290157, 60.171227338006844)), + properties: {"test": "data"}); + + _onMapCreated(MapboxMap mapboxMap) { + this.mapboxMap = mapboxMap; + mapboxMap.style; + } + + _onStyleLoadedCallback(StyleLoadedEventData data) { + // ScaffoldMessenger.of(context).showSnackBar(SnackBar( + // content: Text("Style loaded :), time: ${data.timeInterval}"), + // backgroundColor: Theme.of(context).primaryColor, + // duration: Duration(seconds: 1), + // )); + print("styleLoaded"); + } + + _onTap(context) async { + print("tap"); + + // QRF + var clicked = await mapboxMap?.pixelForCoordinate(context.point); + var renderedQueryGeometry = + RenderedQueryGeometry.fromScreenCoordinate(clicked!); + var target = FeaturesetQueryTarget( + featureset: FeaturesetDescriptor( + featuresetId: "buildings", importId: "basemap")); + var targets = [target]; + var query = await mapboxMap?.queryRenderedFeaturesForTargets( + renderedQueryGeometry, targets); + var feature = query?.first?.queriedFeature; + print("feature"); + print(feature?.feature); + + // Get FeatureState + // var typedFeatures = Feature.fromQueriedFeature(feature!); + + Map data = { + "highlight": true, + }; + + String jsonString = jsonEncode(data); + + var geometry = { + "type": "Point", + "coordinates": [1, 2] + }; + + Feature featured = + Feature(id: "id", geometry: Point(coordinates: Position(1, 2))); + + var featureSetFeature = FeaturesetFeature( + id: FeaturesetFeatureId(id: "1225951980"), + featureset: FeaturesetDescriptor( + featuresetId: "buildings", importId: "basemap"), + geoJSONFeature: featured, + state: data); + + // Set FeatureState + await mapboxMap?.setFeatureStateForFeaturesetFeature( + featureSetFeature, data); + + await Future.delayed(Duration(seconds: 2)); + + var featuerState = + await mapboxMap?.getFeatureStateForFeaturesetFeature(featureSetFeature); + print("yello"); + print(featuerState); + } + + _onLongClick(context) async { + var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); + mapboxMap?.style.setStyleJSON(styleJson); + print("longclick"); + + var clicked = await mapboxMap?.pixelForCoordinate(context.point); + + var filter = '["=",["get", "type"],"A"]'; + var featuresetFilterQuery = + await mapboxMap?.queryRenderedFeaturesForFeatureset( + geometry: RenderedQueryGeometry.fromScreenCoordinate(clicked!), + featureset: + FeaturesetDescriptor(featuresetId: "poi", importId: "nested"), + filter: filter); + + print(featuresetFilterQuery?[0].geoJSONFeature.properties?["name"]); + print(featuresetFilterQuery?[0].geoJSONFeature.properties?["class"]); + + var renderedQueryGeometry = + RenderedQueryGeometry.fromScreenCoordinate(clicked!); + + var target = FeaturesetQueryTarget( + featureset: FeaturesetDescriptor( + featuresetId: "buildings", importId: "basemap")); + var targets = [target]; + + var query = await mapboxMap?.queryRenderedFeaturesForTargets( + renderedQueryGeometry, targets); + print(query?.first?.queriedFeature.feature); + } + + @override + Widget build(BuildContext context) { + return new Scaffold( + floatingActionButton: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + FloatingActionButton( + child: Icon(Icons.swap_horiz), + heroTag: null, + onPressed: () { + setState( + () => isLight = !isLight, + ); + if (isLight) { + mapboxMap?.loadStyleURI(MapboxStyles.LIGHT); + } else { + mapboxMap?.loadStyleURI(MapboxStyles.DARK); + } + }), + SizedBox(height: 10), + ], + ), + ), + body: MapWidget( + key: ValueKey("mapWidget"), + cameraOptions: CameraOptions( + center: Point( + coordinates: Position(24.94180921290157, 60.171227338006844)), + zoom: 15.0, + pitch: 30), + styleUri: + "", + textureView: true, + onMapCreated: _onMapCreated, + onStyleLoadedListener: _onStyleLoadedCallback, + onLongTapListener: _onLongClick, + onTapListener: _onTap, + )); + } +} diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift index 407238310..bb105a8ea 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift @@ -154,6 +154,99 @@ extension RenderedQueryOptions { return MapboxCoreMaps.RenderedQueryOptions(layerIds: layerIds?.compactMap { $0 }, filter: filterExp) } } + +extension String { + func toExp() throws -> Exp { + guard let data = self.data(using: .utf8) else { + throw DecodingError.valueNotFound(String.self, DecodingError.Context(codingPath: [], debugDescription: "Could not decode string to Exp")) + } + return try JSONDecoder().decode(Expression.self, from: data) + } +} + +extension _RenderedQueryGeometry: RenderedQueryGeometryConvertible { + var geometry: MapboxMaps.RenderedQueryGeometry { + // TODO + switch type { + case .sCREENBOX: + guard let cgRect = convertValueToCGRect(value) else { fallthrough } + return RenderedQueryGeometry(boundingBox: cgRect) + case .sCREENCOORDINATE: + guard let cgPoint = convertValueToCGPoint(value) else { fallthrough } + return RenderedQueryGeometry(point: cgPoint) + case .lIST: + guard let cgPoints = convertValueToCGPoints(value) else { fallthrough } + return RenderedQueryGeometry(shape: cgPoints) + default: + // TODO Fix this + return RenderedQueryGeometry(shape: []) + } + } +} + +/// Convert to extension? +func convertValueToCGRect(_ value: String) -> CGRect? { + let screenBoxArray = convertStringToDictionary(properties: value) + guard let minCoord = screenBoxArray["min"] as? [String: Double] else { return nil } + guard let maxCoord = screenBoxArray["max"] as? [String: Double] else { return nil } + guard let minX = minCoord["x"], let minY = minCoord["y"], + let maxX = maxCoord["x"], let maxY = maxCoord["y"] else { + return nil + } + let screenBox = ScreenBox(min: ScreenCoordinate(x: minX, y: minY), + max: ScreenCoordinate(x: maxX, y: maxY)) + return screenBox.toCGRect() +} + +func convertValueToCGPoint(_ value: String) -> CGPoint? { + guard let pointDict = convertStringToDictionary(properties: value) as? [String: Double], + let x = pointDict["x"], let y = pointDict["y"] else { + return nil + } + return CGPoint(x: x, y: y) +} + +func convertValueToCGPoints(_ value: String) -> [CGPoint]? { + guard let data = value.data(using: .utf8), + let rawPoints = try? JSONDecoder().decode([[String: Double]].self, from: data) else { + return nil + } + let cgPoints = rawPoints.compactMap { + guard let x = $0["x"], let y = $0["y"] else { return Optional.none } + return CGPoint(x: x, y: y) + } + return cgPoints +} + +extension FeaturesetFeatureId { + func toMapFeaturesetFeatureId() -> MapboxMaps.FeaturesetFeatureId { + return MapboxMaps.FeaturesetFeatureId(id: id, namespace: namespace) + } +} + +extension FeaturesetQueryTarget { + func toMapFeaturesetQueryTarget() -> MapboxMaps.FeaturesetQueryTarget { + let filterExpression = try? filter.flatMap { try $0.toExp() } + return MapboxMaps.FeaturesetQueryTarget(featureset: featureset.toMapFeaturesetDescriptor(), filter: filterExpression, id: id.map { UInt64($0) }) + } +} + +extension FeaturesetDescriptor { + func toMapFeaturesetDescriptor() -> MapboxMaps.FeaturesetDescriptor { + return MapboxMaps.FeaturesetDescriptor(featuresetId2: featuresetId, importId2: importId, layerId2: layerId) + } +} + +extension FeaturesetFeature { + func toMapFeaturesetFeature() -> MapboxMaps.FeaturesetFeature { + return MapboxMaps.FeaturesetFeature( + id: id?.toMapFeaturesetFeatureId(), + featureset: featureset.toMapFeaturesetDescriptor(), + geoJsonFeature: geoJSONFeature, + state: JSONObject.init(turfRawValue: state)!) + } +} + extension MercatorCoordinate { func toMercatorCoordinate() -> MapboxMaps.MercatorCoordinate { return MapboxMaps.MercatorCoordinate(x: x, y: y) @@ -374,9 +467,17 @@ extension MapboxMaps.QueriedSourceFeature { return QueriedSourceFeature(queriedFeature: queriedFeature.toFLTQueriedFeature()) } } +extension MapboxMaps.FeaturesetQueryTarget { + func toFLTFeaturesetQueryTarget() -> FeaturesetQueryTarget { + return FeaturesetQueryTarget(featureset: featureset.toFLTFeaturesetDescriptor(), filter: filter?.description, id: id.map { Int64($0) }) + } +} extension MapboxMaps.QueriedRenderedFeature { func toFLTQueriedRenderedFeature() -> QueriedRenderedFeature { - return QueriedRenderedFeature(queriedFeature: queriedFeature.toFLTQueriedFeature(), layers: layers) + return QueriedRenderedFeature( + queriedFeature: queriedFeature.toFLTQueriedFeature(), + layers: layers, + queryTargets: queryTargets.map({$0.toFLTFeaturesetQueryTarget()})) } } extension MapboxMaps.QueriedFeature { @@ -423,6 +524,36 @@ extension MapboxMaps.MapOptions { ) } } +extension MapboxMaps.FeaturesetFeatureId { + func toFLTFeaturesetFeatureId() -> FeaturesetFeatureId { + return FeaturesetFeatureId( + id: self.id, + namespace: self.namespace) + } +} + +extension MapboxMaps.FeaturesetDescriptor { + func toFLTFeaturesetDescriptor() -> FeaturesetDescriptor { + return FeaturesetDescriptor( + featuresetId: featuresetId, + importId: importId, + layerId: layerId) + } +} + +extension MapboxMaps.FeaturesetFeature { + func toFLTFeaturesetFeature() -> FeaturesetFeature { + var feature = Feature(geometry: geometry) + feature.properties = properties + + return FeaturesetFeature( + id: id?.toFLTFeaturesetFeatureId(), + featureset: featureset.toFLTFeaturesetDescriptor(), + geoJSONFeature: feature, + state: state.turfRawValue) + } +} + extension MapboxMaps.CameraBounds { func toFLTCameraBounds() -> CameraBounds { return CameraBounds( diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift index ad171d08f..2a64b2b19 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift @@ -1114,21 +1114,28 @@ struct QueriedRenderedFeature { /// If the feature has been rendered in multiple layers, multiple Ids will be provided. /// If the feature is only rendered in one layer, a single Id will be provided. var layers: [String?] + /// An array of feature query targets that correspond to this queried feature. + /// + /// - Note: Returned query targets will omit the original `filter` data. + var queryTargets: [FeaturesetQueryTarget]? // swift-format-ignore: AlwaysUseLowerCamelCase static func fromList(_ pigeonVar_list: [Any?]) -> QueriedRenderedFeature? { let queriedFeature = pigeonVar_list[0] as! QueriedFeature let layers = pigeonVar_list[1] as! [String?] + let queryTargets: [FeaturesetQueryTarget]? = nilOrValue(pigeonVar_list[2]) return QueriedRenderedFeature( queriedFeature: queriedFeature, - layers: layers + layers: layers, + queryTargets: queryTargets ) } func toList() -> [Any?] { return [ queriedFeature, layers, + queryTargets, ] } } @@ -1196,6 +1203,159 @@ struct QueriedFeature { } } +/// Represents a unique identifier for a feature in one exported featureset or a layer. +/// +/// Generated class from Pigeon that represents data sent in messages. +struct FeaturesetFeatureId { + /// The `featureId` uniquely identifies a feature within a featureset or layer. + /// Note: The Identifier of the feature is not guaranteed to be persistent + /// and can change depending on the source that is used. + var id: String + /// An optional field that represents the feature namespace defined by + /// the Selector within a Featureset to which this feature belongs. + /// If the underlying source is the same for multiple selectors within a Featureset, + /// the same `featureNamespace` should be used across those selectors. + /// This practice ensures the uniqueness of `FeaturesetFeatureId` across the style. + /// Defining a `featureNamespace` value for the Selector is recommended, + /// especially when multiple selectors exist in a Featureset, + /// as it can enhance the efficiency of feature operations. + var namespace: String? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> FeaturesetFeatureId? { + let id = pigeonVar_list[0] as! String + let namespace: String? = nilOrValue(pigeonVar_list[1]) + + return FeaturesetFeatureId( + id: id, + namespace: namespace + ) + } + func toList() -> [Any?] { + return [ + id, + namespace, + ] + } +} + +/// Represents an identifier for a single exported featureset or a layer. +/// +/// Generated class from Pigeon that represents data sent in messages. +struct FeaturesetDescriptor { + /// An optional unique identifier for the featureset within the style. + /// This id is used to reference a specific featureset. + /// + /// * Note: If `featuresetId` is provided and valid, it takes precedence over `layerId`, + /// * meaning `layerId` will not be considered even if it has a valid value. + var featuresetId: String? + /// An optional import id that is required if the featureset is defined within an imported style. + /// If the featureset belongs to the current style, this field should be set to a null string. + /// + /// Note: `importId` is only applicable when used in conjunction with `featuresetId` + /// and has no effect when used with `layerId`. + var importId: String? + /// An optional unique identifier for the layer within the current style. + /// + /// Note: If `featuresetId` is valid, `layerId` will be ignored even if it has a valid value. + /// Additionally, `importId` does not apply when using `layerId`. + var layerId: String? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> FeaturesetDescriptor? { + let featuresetId: String? = nilOrValue(pigeonVar_list[0]) + let importId: String? = nilOrValue(pigeonVar_list[1]) + let layerId: String? = nilOrValue(pigeonVar_list[2]) + + return FeaturesetDescriptor( + featuresetId: featuresetId, + importId: importId, + layerId: layerId + ) + } + func toList() -> [Any?] { + return [ + featuresetId, + importId, + layerId, + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct FeaturesetFeature { + /// Optional identifier holding feature id and feature namespace. + /// It could be NULL when underlying [Feature.id] is null. + var id: FeaturesetFeatureId? + /// The [TypedFeaturesetDescriptor] this concrete feature comes from. + /// List of supported featuresets could be found in the nested classes + /// (e.g. [TypedFeaturesetDescriptor.Featureset], [TypedFeaturesetDescriptor.Layer], etc). + var featureset: FeaturesetDescriptor + /// A feature geometry. + /// Feature JSON properties. + /// The geoJSON feature. + var geoJSONFeature: Feature + /// Current feature state stored as a concrete instance of [FeatureState]. + /// Important: this state is immutable and represents the feature state + /// at the precise moment of the interaction callback. + var state: [String: Any?] + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> FeaturesetFeature? { + let id: FeaturesetFeatureId? = nilOrValue(pigeonVar_list[0]) + let featureset = pigeonVar_list[1] as! FeaturesetDescriptor + let geoJSONFeature = pigeonVar_list[2] as! Feature + let state = pigeonVar_list[3] as! [String: Any?] + + return FeaturesetFeature( + id: id, + featureset: featureset, + geoJSONFeature: geoJSONFeature, + state: state + ) + } + func toList() -> [Any?] { + return [ + id, + featureset, + geoJSONFeature, + state, + ] + } +} + +/// Defines the parameters for querying features from a Featureset with an optional filter and id. +/// +/// Generated class from Pigeon that represents data sent in messages. +struct FeaturesetQueryTarget { + /// The FeaturesetDescriptor that specifies the featureset to be included in the query. + var featureset: FeaturesetDescriptor + /// An optional filter expression used to refine the query results based on conditions related to the specified featureset. + var filter: String? + /// An optional unique identifier associated with the FeaturesetQueryTarget. + var id: Int64? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> FeaturesetQueryTarget? { + let featureset = pigeonVar_list[0] as! FeaturesetDescriptor + let filter: String? = nilOrValue(pigeonVar_list[1]) + let id: Int64? = nilOrValue(pigeonVar_list[2]) + + return FeaturesetQueryTarget( + featureset: featureset, + filter: filter, + id: id + ) + } + func toList() -> [Any?] { + return [ + featureset, + filter, + id, + ] + } +} + /// Geometry for querying rendered features. /// /// Generated class from Pigeon that represents data sent in messages. @@ -2931,12 +3091,74 @@ protocol _MapInterface { /// @param completion The `query features completion` called when the query completes. /// @return A `cancelable` object that could be used to cancel the pending query. func queryRenderedFeatures(geometry: _RenderedQueryGeometry, options: RenderedQueryOptions, completion: @escaping (Result<[QueriedRenderedFeature?], Error>) -> Void) + /// Queries the map for rendered features using featureset descriptors. + /// + /// This method allows to query both featureset from imported styles and user layers in the root style. + /// The results can be additionally filtered per-featureset. + /// + /// ```dart + /// let targets = [ + /// FeaturesetQueryTarget( + /// featureset: .layer("my-layer"), + /// filter: Exp(.eq) { + /// Exp(.get) { "type" } + /// "hotel" + /// } + /// ), + /// FeaturesetQueryTarget(featureset: .featureset("poi", importId: "basemap")) + /// ] + /// mapView.mapboxMap.queryRenderedFeatures(with: CGPoint(x: 0, y: 0), + /// targets: targets) { result in + /// // handle features in result + /// } + /// ``` + /// + /// - Important: This is a low-level method. If you need to handle basic gestures on map content, please prefer to use Interactions API (see ``MapboxMap/addInteraction(_:)``) or ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)``. + /// + /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. + /// @param targets An array of targets to query with. + /// @param completion Callback called when the query completes. + func queryRenderedFeaturesForTargets(geometry: _RenderedQueryGeometry, targets: [FeaturesetQueryTarget], completion: @escaping (Result<[QueriedRenderedFeature?], Error>) -> Void) + /// Queries the map for rendered features with one typed featureset. + /// + /// The results array will contain features of the type specified by this featureset. + /// + /// ```swift + /// mapView.mapboxMap.queryRenderedFeatures( + /// with: CGPoint(x: 0, y: 0), + /// featureset: .standardBuildings) { result in + /// // handle buildings in result + /// } + /// ``` + /// + /// - Important: If you need to handle basic gestures on map content, please prefer to use Interactions API, see ``MapboxMap/addInteraction(_:)``. + /// + /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. + /// @param featureset A typed featureset to query with. + /// @param filter An additional filter for features. + /// @param completion Callback called when the query completes. + func queryRenderedFeaturesForFeatureset(geometry: _RenderedQueryGeometry, featureset: FeaturesetDescriptor, filter: String?, completion: @escaping (Result<[FeaturesetFeature], Error>) -> Void) + /// Queries all rendered features in current viewport, using one typed featureset. + /// + /// This is same as ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)`` called with geometry matching the current viewport. + /// + /// - Important: If you need to handle basic gestures on map content, please prefer to use Interactions API, see ``MapboxMap/addInteraction(_:)``. + /// + /// @param featureset A typed featureset to query with. + /// @param filter An additional filter for features. + /// @param completion Callback called when the query completes. + func queryRenderedFeaturesInViewport(featureset: FeaturesetDescriptor, filter: String?, completion: @escaping (Result<[FeaturesetFeature], Error>) -> Void) /// Queries the map for source features. /// /// @param sourceId The style source identifier used to query for source features. /// @param options The `source query options` for querying source features. /// @param completion The `query features completion` called when the query completes. func querySourceFeatures(sourceId: String, options: SourceQueryOptions, completion: @escaping (Result<[QueriedSourceFeature?], Error>) -> Void) + /// Queries the source features for a given featureset. + /// + /// @param target A featureset query target. + /// @param completion Callback called when the query completes. + func querySourceFeaturesForFeatureset(target: FeaturesetQueryTarget, completion: @escaping (Result<[QueriedSourceFeature?], Error>) -> Void) /// Returns all the leaves (original points) of a cluster (given its cluster_id) from a GeoJsonSource, with pagination support: limit is the number of leaves /// to return (set to Infinity for all points), and offset is the amount of points to skip (for pagination). /// @@ -2983,6 +3205,28 @@ protocol _MapInterface { /// @param featureId The feature identifier of the feature whose state should be updated. /// @param state The `state` object with properties to update with their respective new values. func setFeatureState(sourceId: String, sourceLayerId: String?, featureId: String, state: String, completion: @escaping (Result) -> Void) + /// Update the state map of a feature within a featureset. + /// Update entries in the state map of a given feature within a style source. Only entries listed in the state map + /// will be updated. An entry in the feature state map that is not listed in `state` will retain its previous value. + /// + /// @param featureset The featureset to look the feature in. + /// @param featureId Identifier of the feature whose state should be updated. + /// @param state Map of entries to update with their respective new values + /// @param callback The `feature state operation callback` called when the operation completes or ends. + /// + /// @return A `Cancelable` object that could be used to cancel the pending operation. + func setFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: [String: Any?], completion: @escaping (Result) -> Void) + /// Update the state map of an individual feature. + /// + /// The feature should have a non-nil ``FeaturesetFeatureType/id``. Otherwise, + /// the operation will be no-op and callback will receive an error. + /// + /// @param feature The feature to update. + /// @param state Map of entries to update with their respective new values + /// @param callback The `feature state operation callback` called when the operation completes or ends. + /// + /// @return A `Cancelable` object that could be used to cancel the pending operation. + func setFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, state: [String: Any?], completion: @escaping (Result) -> Void) /// Gets the state map of a feature within a style source. /// /// Note that updates to feature state are asynchronous, so changes made by other methods might not be @@ -2993,6 +3237,20 @@ protocol _MapInterface { /// @param featureId The feature identifier of the feature whose state should be queried. /// @param completion The `query feature state completion` called when the query completes. func getFeatureState(sourceId: String, sourceLayerId: String?, featureId: String, completion: @escaping (Result) -> Void) + /// Get the state map of a feature within a style source. + /// + /// @param featureset A featureset the feature belongs to. + /// @param featureId Identifier of the feature whose state should be queried. + /// + /// @return A `Cancelable` object that could be used to cancel the pending query. + func getFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, completion: @escaping (Result<[String: Any?], Error>) -> Void) + /// Get the state map of a feature within a style source. + /// + /// @param feature An interactive feature to query the state from. + /// @param completion Feature's state map or an empty map if the feature could not be found. + /// + /// @return A `Cancelable` object that could be used to cancel the pending query. + func getFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, completion: @escaping (Result<[String: Any?], Error>) -> Void) /// Removes entries from a feature state object. /// /// Remove a specified property or all property from a feature's state object, depending on the value of @@ -3006,6 +3264,35 @@ protocol _MapInterface { /// @param featureId The feature identifier of the feature whose state should be removed. /// @param stateKey The key of the property to remove. If `null`, all feature's state object properties are removed. func removeFeatureState(sourceId: String, sourceLayerId: String?, featureId: String, stateKey: String?, completion: @escaping (Result) -> Void) + /// Removes entries from a feature state object of a feature in the specified featureset. + /// Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. + /// + /// @param featureset A featureset the feature belongs to. + /// @param featureId Identifier of the feature whose state should be removed. + /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. + /// @param callback The `feature state operation callback` called when the operation completes or ends. + /// + /// @return A `Cancelable` object that could be used to cancel the pending operation. + func removeFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String, completion: @escaping (Result) -> Void) + /// Removes entries from a specified Feature. + /// Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. + /// + /// @param feature An interactive feature to update. + /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. + /// @param callback The `feature state operation callback` called when the operation completes or ends. + /// + /// @return A `Cancelable` object that could be used to cancel the pending operation. + func removeFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, stateKey: String, completion: @escaping (Result) -> Void) + /// Reset all the feature states within a featureset. + /// + /// Note that updates to feature state are asynchronous, so changes made by this method might not be + /// immediately visible using ``MapboxMap/getFeatureState(_:callback:)``. + /// + /// @param featureset A featureset descriptor + /// @param callback The `feature state operation callback` called when the operation completes or ends. + /// + /// @return A `Cancelable` object that could be used to cancel the pending operation. + func resetFeatureStatesForFeatureset(featureset: FeaturesetDescriptor, completion: @escaping (Result) -> Void) /// Reduces memory use. Useful to call when the application gets paused or sent to background. func reduceMemoryUse() throws /// Gets elevation for the given coordinate. @@ -3398,6 +3685,115 @@ class _MapInterfaceSetup { } else { queryRenderedFeaturesChannel.setMessageHandler(nil) } + /// Queries the map for rendered features using featureset descriptors. + /// + /// This method allows to query both featureset from imported styles and user layers in the root style. + /// The results can be additionally filtered per-featureset. + /// + /// ```dart + /// let targets = [ + /// FeaturesetQueryTarget( + /// featureset: .layer("my-layer"), + /// filter: Exp(.eq) { + /// Exp(.get) { "type" } + /// "hotel" + /// } + /// ), + /// FeaturesetQueryTarget(featureset: .featureset("poi", importId: "basemap")) + /// ] + /// mapView.mapboxMap.queryRenderedFeatures(with: CGPoint(x: 0, y: 0), + /// targets: targets) { result in + /// // handle features in result + /// } + /// ``` + /// + /// - Important: This is a low-level method. If you need to handle basic gestures on map content, please prefer to use Interactions API (see ``MapboxMap/addInteraction(_:)``) or ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)``. + /// + /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. + /// @param targets An array of targets to query with. + /// @param completion Callback called when the query completes. + let queryRenderedFeaturesForTargetsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesForTargets\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + queryRenderedFeaturesForTargetsChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let geometryArg = args[0] as! _RenderedQueryGeometry + let targetsArg = args[1] as! [FeaturesetQueryTarget] + api.queryRenderedFeaturesForTargets(geometry: geometryArg, targets: targetsArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + queryRenderedFeaturesForTargetsChannel.setMessageHandler(nil) + } + /// Queries the map for rendered features with one typed featureset. + /// + /// The results array will contain features of the type specified by this featureset. + /// + /// ```swift + /// mapView.mapboxMap.queryRenderedFeatures( + /// with: CGPoint(x: 0, y: 0), + /// featureset: .standardBuildings) { result in + /// // handle buildings in result + /// } + /// ``` + /// + /// - Important: If you need to handle basic gestures on map content, please prefer to use Interactions API, see ``MapboxMap/addInteraction(_:)``. + /// + /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. + /// @param featureset A typed featureset to query with. + /// @param filter An additional filter for features. + /// @param completion Callback called when the query completes. + let queryRenderedFeaturesForFeaturesetChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesForFeatureset\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + queryRenderedFeaturesForFeaturesetChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let geometryArg = args[0] as! _RenderedQueryGeometry + let featuresetArg = args[1] as! FeaturesetDescriptor + let filterArg: String? = nilOrValue(args[2]) + api.queryRenderedFeaturesForFeatureset(geometry: geometryArg, featureset: featuresetArg, filter: filterArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + queryRenderedFeaturesForFeaturesetChannel.setMessageHandler(nil) + } + /// Queries all rendered features in current viewport, using one typed featureset. + /// + /// This is same as ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)`` called with geometry matching the current viewport. + /// + /// - Important: If you need to handle basic gestures on map content, please prefer to use Interactions API, see ``MapboxMap/addInteraction(_:)``. + /// + /// @param featureset A typed featureset to query with. + /// @param filter An additional filter for features. + /// @param completion Callback called when the query completes. + let queryRenderedFeaturesInViewportChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesInViewport\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + queryRenderedFeaturesInViewportChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let featuresetArg = args[0] as! FeaturesetDescriptor + let filterArg: String? = nilOrValue(args[1]) + api.queryRenderedFeaturesInViewport(featureset: featuresetArg, filter: filterArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + queryRenderedFeaturesInViewportChannel.setMessageHandler(nil) + } /// Queries the map for source features. /// /// @param sourceId The style source identifier used to query for source features. @@ -3421,6 +3817,27 @@ class _MapInterfaceSetup { } else { querySourceFeaturesChannel.setMessageHandler(nil) } + /// Queries the source features for a given featureset. + /// + /// @param target A featureset query target. + /// @param completion Callback called when the query completes. + let querySourceFeaturesForFeaturesetChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.querySourceFeaturesForFeatureset\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + querySourceFeaturesForFeaturesetChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let targetArg = args[0] as! FeaturesetQueryTarget + api.querySourceFeaturesForFeatureset(target: targetArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + querySourceFeaturesForFeaturesetChannel.setMessageHandler(nil) + } /// Returns all the leaves (original points) of a cluster (given its cluster_id) from a GeoJsonSource, with pagination support: limit is the number of leaves /// to return (set to Infinity for all points), and offset is the amount of points to skip (for pagination). /// @@ -3539,6 +3956,63 @@ class _MapInterfaceSetup { } else { setFeatureStateChannel.setMessageHandler(nil) } + /// Update the state map of a feature within a featureset. + /// Update entries in the state map of a given feature within a style source. Only entries listed in the state map + /// will be updated. An entry in the feature state map that is not listed in `state` will retain its previous value. + /// + /// @param featureset The featureset to look the feature in. + /// @param featureId Identifier of the feature whose state should be updated. + /// @param state Map of entries to update with their respective new values + /// @param callback The `feature state operation callback` called when the operation completes or ends. + /// + /// @return A `Cancelable` object that could be used to cancel the pending operation. + let setFeatureStateForFeaturesetFeatureDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.setFeatureStateForFeaturesetFeatureDescriptor\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setFeatureStateForFeaturesetFeatureDescriptorChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let featuresetArg = args[0] as! FeaturesetDescriptor + let featureIdArg = args[1] as! FeaturesetFeatureId + let stateArg = args[2] as! [String: Any?] + api.setFeatureStateForFeaturesetFeatureDescriptor(featureset: featuresetArg, featureId: featureIdArg, state: stateArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + setFeatureStateForFeaturesetFeatureDescriptorChannel.setMessageHandler(nil) + } + /// Update the state map of an individual feature. + /// + /// The feature should have a non-nil ``FeaturesetFeatureType/id``. Otherwise, + /// the operation will be no-op and callback will receive an error. + /// + /// @param feature The feature to update. + /// @param state Map of entries to update with their respective new values + /// @param callback The `feature state operation callback` called when the operation completes or ends. + /// + /// @return A `Cancelable` object that could be used to cancel the pending operation. + let setFeatureStateForFeaturesetFeatureChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.setFeatureStateForFeaturesetFeature\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setFeatureStateForFeaturesetFeatureChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let featureArg = args[0] as! FeaturesetFeature + let stateArg = args[1] as! [String: Any?] + api.setFeatureStateForFeaturesetFeature(feature: featureArg, state: stateArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + setFeatureStateForFeaturesetFeatureChannel.setMessageHandler(nil) + } /// Gets the state map of a feature within a style source. /// /// Note that updates to feature state are asynchronous, so changes made by other methods might not be @@ -3567,6 +4041,53 @@ class _MapInterfaceSetup { } else { getFeatureStateChannel.setMessageHandler(nil) } + /// Get the state map of a feature within a style source. + /// + /// @param featureset A featureset the feature belongs to. + /// @param featureId Identifier of the feature whose state should be queried. + /// + /// @return A `Cancelable` object that could be used to cancel the pending query. + let getFeatureStateForFeaturesetDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.getFeatureStateForFeaturesetDescriptor\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getFeatureStateForFeaturesetDescriptorChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let featuresetArg = args[0] as! FeaturesetDescriptor + let featureIdArg = args[1] as! FeaturesetFeatureId + api.getFeatureStateForFeaturesetDescriptor(featureset: featuresetArg, featureId: featureIdArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + getFeatureStateForFeaturesetDescriptorChannel.setMessageHandler(nil) + } + /// Get the state map of a feature within a style source. + /// + /// @param feature An interactive feature to query the state from. + /// @param completion Feature's state map or an empty map if the feature could not be found. + /// + /// @return A `Cancelable` object that could be used to cancel the pending query. + let getFeatureStateForFeaturesetFeatureChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.getFeatureStateForFeaturesetFeature\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getFeatureStateForFeaturesetFeatureChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let featureArg = args[0] as! FeaturesetFeature + api.getFeatureStateForFeaturesetFeature(feature: featureArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + getFeatureStateForFeaturesetFeatureChannel.setMessageHandler(nil) + } /// Removes entries from a feature state object. /// /// Remove a specified property or all property from a feature's state object, depending on the value of @@ -3599,6 +4120,86 @@ class _MapInterfaceSetup { } else { removeFeatureStateChannel.setMessageHandler(nil) } + /// Removes entries from a feature state object of a feature in the specified featureset. + /// Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. + /// + /// @param featureset A featureset the feature belongs to. + /// @param featureId Identifier of the feature whose state should be removed. + /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. + /// @param callback The `feature state operation callback` called when the operation completes or ends. + /// + /// @return A `Cancelable` object that could be used to cancel the pending operation. + let removeFeatureStateForFeaturesetFeatureDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureStateForFeaturesetFeatureDescriptor\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + removeFeatureStateForFeaturesetFeatureDescriptorChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let featuresetArg = args[0] as! FeaturesetDescriptor + let featureIdArg = args[1] as! FeaturesetFeatureId + let stateKeyArg = args[2] as! String + api.removeFeatureStateForFeaturesetFeatureDescriptor(featureset: featuresetArg, featureId: featureIdArg, stateKey: stateKeyArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + removeFeatureStateForFeaturesetFeatureDescriptorChannel.setMessageHandler(nil) + } + /// Removes entries from a specified Feature. + /// Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. + /// + /// @param feature An interactive feature to update. + /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. + /// @param callback The `feature state operation callback` called when the operation completes or ends. + /// + /// @return A `Cancelable` object that could be used to cancel the pending operation. + let removeFeatureStateForFeaturesetFeatureChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureStateForFeaturesetFeature\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + removeFeatureStateForFeaturesetFeatureChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let featureArg = args[0] as! FeaturesetFeature + let stateKeyArg = args[1] as! String + api.removeFeatureStateForFeaturesetFeature(feature: featureArg, stateKey: stateKeyArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + removeFeatureStateForFeaturesetFeatureChannel.setMessageHandler(nil) + } + /// Reset all the feature states within a featureset. + /// + /// Note that updates to feature state are asynchronous, so changes made by this method might not be + /// immediately visible using ``MapboxMap/getFeatureState(_:callback:)``. + /// + /// @param featureset A featureset descriptor + /// @param callback The `feature state operation callback` called when the operation completes or ends. + /// + /// @return A `Cancelable` object that could be used to cancel the pending operation. + let resetFeatureStatesForFeaturesetChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.resetFeatureStatesForFeatureset\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + resetFeatureStatesForFeaturesetChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let featuresetArg = args[0] as! FeaturesetDescriptor + api.resetFeatureStatesForFeatureset(featureset: featuresetArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + resetFeatureStatesForFeaturesetChannel.setMessageHandler(nil) + } /// Reduces memory use. Useful to call when the application gets paused or sent to background. let reduceMemoryUseChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.reduceMemoryUse\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { @@ -4660,6 +5261,10 @@ protocol StyleManager { /// @param locale The locale to apply for localization /// @param layerIds The ids of layers that will localize on, default is null which means will localize all the feasible layers. func localizeLabels(locale: String, layerIds: [String]?, completion: @escaping (Result) -> Void) + /// Returns the available featuresets in the currently loaded style. + /// + /// - Note: This function should only be called after the style is fully loaded; otherwise, the result may be unreliable. + func getFeaturesets() throws -> [FeaturesetDescriptor] } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. @@ -6017,6 +6622,22 @@ class StyleManagerSetup { } else { localizeLabelsChannel.setMessageHandler(nil) } + /// Returns the available featuresets in the currently loaded style. + /// + /// - Note: This function should only be called after the style is fully loaded; otherwise, the result may be unreliable. + let getFeaturesetsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter.StyleManager.getFeaturesets\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getFeaturesetsChannel.setMessageHandler { _, reply in + do { + let result = try api.getFeaturesets() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getFeaturesetsChannel.setMessageHandler(nil) + } } } /// Allows to cancel the associated asynchronous operation diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift index e96c1cf33..2ec96b7c2 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift @@ -153,17 +153,10 @@ final class MapInterfaceController: _MapInterface { do { switch geometry.type { case .sCREENBOX: - let screenBoxArray = convertStringToDictionary(properties: geometry.value) - guard let minCoord = screenBoxArray["min"] as? [String: Double] else { return } - guard let maxCoord = screenBoxArray["max"] as? [String: Double] else { return } - guard let minX = minCoord["x"], let minY = minCoord["y"], - let maxX = maxCoord["x"], let maxY = maxCoord["y"] else { + guard let cgRect = convertValueToCGRect(geometry.value) else { completion(.failure(FlutterError(code: MapInterfaceController.errorCode, message: "Geometry format error", details: geometry.value))) return } - let screenBox = ScreenBox(min: ScreenCoordinate(x: minX, y: minY), - max: ScreenCoordinate(x: maxX, y: maxY)) - let cgRect = screenBox.toCGRect() let queryOptions = try options.toRenderedQueryOptions() self.mapboxMap.queryRenderedFeatures(with: cgRect, options: queryOptions) { result in switch result { @@ -174,13 +167,10 @@ final class MapInterfaceController: _MapInterface { } } case .sCREENCOORDINATE: - guard let pointDict = convertStringToDictionary(properties: geometry.value) as? [String: Double], - let x = pointDict["x"], let y = pointDict["y"] else { + guard let cgPoint = convertValueToCGPoint(geometry.value) else { completion(.failure(FlutterError(code: MapInterfaceController.errorCode, message: "Geometry format error", details: geometry.value))) return } - let cgPoint = CGPoint(x: x, y: y) - try self.mapboxMap.queryRenderedFeatures(with: cgPoint, options: options.toRenderedQueryOptions()) { result in switch result { case .success(let features): @@ -190,15 +180,10 @@ final class MapInterfaceController: _MapInterface { } } case .lIST: - guard let data = geometry.value.data(using: .utf8), - let rawPoints = try? JSONDecoder().decode([[String: Double]].self, from: data) else { + guard let cgPoints = convertValueToCGPoints(geometry.value) else { completion(.failure(FlutterError(code: MapInterfaceController.errorCode, message: "Geometry format error", details: geometry.value))) return } - let cgPoints = rawPoints.compactMap { - guard let x = $0["x"], let y = $0["y"] else { return Optional.none } - return CGPoint(x: x, y: y) - } try self.mapboxMap.queryRenderedFeatures(with: cgPoints, options: options.toRenderedQueryOptions()) { result in switch result { case .success(let features): @@ -213,6 +198,41 @@ final class MapInterfaceController: _MapInterface { } } + func queryRenderedFeaturesForTargets(geometry: _RenderedQueryGeometry, targets: [FeaturesetQueryTarget], completion: @escaping (Result<[QueriedRenderedFeature?], any Error>) -> Void) { + self.mapboxMap.queryRenderedFeatures(with: geometry, targets: targets.map({$0.toMapFeaturesetQueryTarget()})) { result in + switch result { + case .success(let features): + completion(.success(features.map({$0.toFLTQueriedRenderedFeature()}))) + case .failure(let error): + completion(.failure(FlutterError(code: MapInterfaceController.errorCode, message: "\(error)", details: nil))) + } + } + } + + func queryRenderedFeaturesForFeatureset(geometry: _RenderedQueryGeometry, featureset: FeaturesetDescriptor, filter: String?, completion: @escaping (Result<[FeaturesetFeature], any Error>) -> Void) { + let filterExpression = try? filter.flatMap { try $0.toExp() } + self.mapboxMap.queryRenderedFeatures(with: geometry, featureset: featureset.toMapFeaturesetDescriptor(), filter: filterExpression) { result in + switch result { + case .success(let features): + completion(.success(features.map({$0.toFLTFeaturesetFeature()}))) + case .failure(let error): + completion(.failure(FlutterError(code: MapInterfaceController.errorCode, message: "\(error)", details: nil))) + } + } + } + + func queryRenderedFeaturesInViewport(featureset: FeaturesetDescriptor, filter: String?, completion: @escaping (Result<[FeaturesetFeature], any Error>) -> Void) { + let filterExpression = try? filter.flatMap { try $0.toExp() } + self.mapboxMap.queryRenderedFeatures(featureset: featureset.toMapFeaturesetDescriptor(), filter: filterExpression) { result in + switch result { + case .success(let features): + completion(.success(features.map({$0.toFLTFeaturesetFeature()}))) + case .failure(let error): + completion(.failure(FlutterError(code: MapInterfaceController.errorCode, message: "\(error)", details: nil))) + } + } + } + func querySourceFeatures(sourceId: String, options: SourceQueryOptions, completion: @escaping (Result<[QueriedSourceFeature?], Error>) -> Void) { do { try self.mapboxMap.querySourceFeatures(for: sourceId, options: options.toSourceQueryOptions()) { result in @@ -228,6 +248,17 @@ final class MapInterfaceController: _MapInterface { } } + func querySourceFeaturesForFeatureset(target: FeaturesetQueryTarget, completion: @escaping (Result<[QueriedSourceFeature?], any Error>) -> Void) { + self.mapboxMap.querySourceFeatures(for: target.toMapFeaturesetQueryTarget()) { result in + switch result { + case .success(let features): + completion(.success(features.map({$0.toFLTQueriedSourceFeature()}))) + case .failure(let error): + completion(.failure(FlutterError(code: MapInterfaceController.errorCode, message: "\(error)", details: nil))) + } + } + } + func getGeoJsonClusterLeaves(sourceIdentifier: String, cluster: [String?: Any?], limit: Int64?, offset: Int64?, completion: @escaping (Result) -> Void) { guard let feature = convertDictionaryToFeature(dict: cluster) else { completion(.failure(FlutterError(code: MapInterfaceController.errorCode, message: "Feature format error", details: convertDictionaryToString(dict: cluster)))) @@ -284,6 +315,40 @@ final class MapInterfaceController: _MapInterface { } } + func setFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: [String: Any?], completion: @escaping (Result) -> Void) { + guard let state = JSONObject.init(turfRawValue: state) else { + return + } + self.mapboxMap.setFeatureState(featureset: featureset.toMapFeaturesetDescriptor(), featureId: featureId.toMapFeaturesetFeatureId(), state: state) { error in + if let error { + completion(.failure(FlutterError( + code: "setFeatureStateError", + message: error.localizedDescription, + details: nil + ))) + } else { + completion(.success(())) + } + } + } + + func setFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, state: [String: Any?], completion: @escaping (Result) -> Void) { + guard let state = JSONObject.init(turfRawValue: state) else { + return + } + self.mapboxMap.setFeatureState(feature.toMapFeaturesetFeature(), state: state) { error in + if let error { + completion(.failure(FlutterError( + code: "setFeatureStateError", + message: error.localizedDescription, + details: nil + ))) + } else { + completion(.success(())) + } + } + } + func getFeatureState(sourceId: String, sourceLayerId: String?, featureId: String, completion: @escaping (Result) -> Void) { self.mapboxMap.getFeatureState(sourceId: sourceId, sourceLayerId: sourceLayerId, featureId: featureId) { result in switch result { @@ -295,6 +360,28 @@ final class MapInterfaceController: _MapInterface { } } + func getFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, completion: @escaping (Result<[String: Any?], any Error>) -> Void) { + self.mapboxMap.getFeatureState(featureset: featureset.toMapFeaturesetDescriptor(), featureId: featureId.toMapFeaturesetFeatureId()) { result in + switch result { + case .success(let state): + completion(.success(state.mapValues { $0?.rawValue })) + case .failure(let error): + completion(.failure(FlutterError(code: MapInterfaceController.errorCode, message: "\(error)", details: nil))) + } + } + } + + func getFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, completion: @escaping (Result<[String: Any?], any Error>) -> Void) { + self.mapboxMap.getFeatureState(feature.toMapFeaturesetFeature()) { result in + switch result { + case .success(let state): + completion(.success(state.mapValues { $0?.rawValue })) + case .failure(let error): + completion(.failure(FlutterError(code: MapInterfaceController.errorCode, message: "\(error)", details: nil))) + } + } + } + func removeFeatureState(sourceId: String, sourceLayerId: String?, featureId: String, stateKey: String?, completion: @escaping (Result) -> Void) { self.mapboxMap.removeFeatureState(sourceId: sourceId, sourceLayerId: sourceLayerId, featureId: featureId, stateKey: stateKey) { result in switch result { @@ -306,6 +393,48 @@ final class MapInterfaceController: _MapInterface { } } + func removeFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String, completion: @escaping (Result) -> Void) { + self.mapboxMap.removeFeatureState(featureset: featureset.toMapFeaturesetDescriptor(), featureId: featureId.toMapFeaturesetFeatureId(), stateKey: stateKey) { error in + if let error { + completion(.failure(FlutterError( + code: "removeFeatureStateError", + message: error.localizedDescription, + details: nil + ))) + } else { + completion(.success(())) + } + } + } + + func removeFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, stateKey: String, completion: @escaping (Result) -> Void) { + self.mapboxMap.removeFeatureState(feature.toMapFeaturesetFeature(), stateKey: stateKey) { error in + if let error { + completion(.failure(FlutterError( + code: "removeFeatureStateError", + message: error.localizedDescription, + details: nil + ))) + } else { + completion(.success(())) + } + } + } + + func resetFeatureStatesForFeatureset(featureset: FeaturesetDescriptor, completion: @escaping (Result) -> Void) { + self.mapboxMap.resetFeatureStates(featureset: featureset.toMapFeaturesetDescriptor()) { error in + if let error { + completion(.failure(FlutterError( + code: "resetFeatureStateError", + message: error.localizedDescription, + details: nil + ))) + } else { + completion(.success(())) + } + } + } + func reduceMemoryUse() throws { mapboxMap.reduceMemoryUse() } diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/StyleController.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/StyleController.swift index 98c4fa665..9c7b35f4c 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/StyleController.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/StyleController.swift @@ -427,6 +427,12 @@ final class StyleController: StyleManager { } } + func getFeaturesets() throws -> [FeaturesetDescriptor] { + return styleManager.featuresets.map { + $0.toFLTFeaturesetDescriptor() + } + } + // MARK: Style Lights func getStyleLights() throws -> [StyleObjectInfo?] { diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/TurfAdapters.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/TurfAdapters.swift index 415146274..ed1a54639 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/TurfAdapters.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/TurfAdapters.swift @@ -70,6 +70,36 @@ extension Feature { } } +extension Geometry { + static func fromList(_ list: [Any?]) -> Geometry? { + guard let raw = list.first as? [String: Any], + let jsonData = try? JSONSerialization.data(withJSONObject: raw, options: []), + let geometry = try? JSONDecoder().decode(Geometry.self, from: jsonData) else { return nil } + + return geometry + } + + func toList() -> [Any?] { + return [ + self.toMap() + ] + } +} + +extension JSONObject { + static func fromList(_ list: [Any?]) -> JSONObject? { + guard let raw = list.first as? [String: Any] else { return nil } + + return JSONObject(turfRawValue: raw) + } + + static func fromString(_ string: String) -> JSONObject? { + guard let data = string.data(using: .utf8) else { return nil } + + return try? JSONDecoder().decode(JSONObject.self, from: data) + } +} + extension LocationCoordinate2D { fileprivate init(values: [Double]) { diff --git a/lib/mapbox_maps_flutter.dart b/lib/mapbox_maps_flutter.dart index 0078a9c6f..4b64ad564 100644 --- a/lib/mapbox_maps_flutter.dart +++ b/lib/mapbox_maps_flutter.dart @@ -9,6 +9,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; +import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart'; import 'package:meta/meta.dart'; import 'package:turf/turf.dart' as turf; diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index 248442f3a..c3a5aadef 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -443,11 +443,40 @@ class MapboxMap extends ChangeNotifier { _RenderedQueryGeometry(value: geometry.value, type: geometry.type), options); + /// Queries the map for rendered features using featureset descriptors. + Future> queryRenderedFeaturesForTargets( + RenderedQueryGeometry geometry, + List targets) async => + _mapInterface.queryRenderedFeaturesForTargets( + _RenderedQueryGeometry(value: geometry.value, type: geometry.type), + targets); + + /// Queries the map for rendered features with one typed featureset. + Future> queryRenderedFeaturesForFeatureset( + {required RenderedQueryGeometry geometry, + required FeaturesetDescriptor featureset, + String? filter = null}) async => + _mapInterface.queryRenderedFeaturesForFeatureset( + _RenderedQueryGeometry(value: geometry.value, type: geometry.type), + featureset, + filter); + + /// Queries all rendered features in current viewport, using one typed featureset. + Future> queryRenderedFeaturesInViewport( + {required FeaturesetDescriptor featureset, + String? filter = null}) async => + _mapInterface.queryRenderedFeaturesInViewport(featureset, filter); + /// Queries the map for source features. Future> querySourceFeatures( String sourceId, SourceQueryOptions options) => _mapInterface.querySourceFeatures(sourceId, options); + /// Queries the source features for a given featureset. + Future> querySourceFeaturesForFeatureset( + FeaturesetQueryTarget target) async => + _mapInterface.querySourceFeaturesForFeatureset(target); + /// Returns all the leaves (original points) of a cluster (given its cluster_id) from a GeoJsonSource, with pagination support: limit is the number of leaves /// to return (set to Infinity for all points), and offset is the amount of points to skip (for pagination). /// @@ -485,6 +514,24 @@ class MapboxMap extends ChangeNotifier { String featureId, String state) => _mapInterface.setFeatureState(sourceId, sourceLayerId, featureId, state); + /// Update the state map of a feature within a featureset. + /// Update entries in the state map of a given feature within a style source. Only entries listed in the state map + /// will be updated. An entry in the feature state map that is not listed in `state` will retain its previous value. + Future setFeatureStateForFeaturesetFeatureDescriptor( + FeaturesetDescriptor featureset, + FeaturesetFeatureId featureId, + Map state) => + _mapInterface.setFeatureStateForFeaturesetFeatureDescriptor( + featureset, featureId, state); + + /// Update the state map of an individual feature. + /// + /// The feature should have a non-nil ``FeaturesetFeatureType/id``. Otherwise, + /// the operation will be no-op and callback will receive an error. + Future setFeatureStateForFeaturesetFeature( + FeaturesetFeature feature, Map state) => + _mapInterface.setFeatureStateForFeaturesetFeature(feature, state); + /// Gets the state map of a feature within a style source. /// /// Note that updates to feature state are asynchronous, so changes made by other methods might not be @@ -493,6 +540,17 @@ class MapboxMap extends ChangeNotifier { String sourceId, String? sourceLayerId, String featureId) => _mapInterface.getFeatureState(sourceId, sourceLayerId, featureId); + /// Get the state map of a feature within a style source. + Future> getFeatureStateForFeaturesetDescriptor( + FeaturesetDescriptor featureset, FeaturesetFeatureId featureId) => + _mapInterface.getFeatureStateForFeaturesetDescriptor( + featureset, featureId); + + /// Get the state map of a feature within a style source. + Future> getFeatureStateForFeaturesetFeature( + FeaturesetFeature feature) => + _mapInterface.getFeatureStateForFeaturesetFeature(feature); + /// Removes entries from a feature state object. /// /// Remove a specified property or all property from a feature's state object, depending on the value of @@ -505,6 +563,29 @@ class MapboxMap extends ChangeNotifier { _mapInterface.removeFeatureState( sourceId, sourceLayerId, featureId, stateKey); + /// Removes entries from a feature state object of a feature in the specified featureset. + /// Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. + Future removeFeatureStateForFeaturesetFeatureDescriptor( + FeaturesetDescriptor featureset, + FeaturesetFeatureId featureId, + String stateKey) => + _mapInterface.removeFeatureStateForFeaturesetFeatureDescriptor( + featureset, featureId, stateKey); + + /// Removes entries from a specified Feature. + /// Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. + Future removeFeatureStateForFeaturesetFeature( + FeaturesetFeature feature, String stateKey) => + _mapInterface.removeFeatureStateForFeaturesetFeature(feature, stateKey); + + /// Reset all the feature states within a featureset. + /// + /// Note that updates to feature state are asynchronous, so changes made by this method might not be + /// immediately visible using ``MapboxMap/getFeatureState(_:callback:)``. + Future resetFeatureStatesForFeatureset( + FeaturesetDescriptor featureset) => + _mapInterface.resetFeatureStatesForFeatureset(featureset); + /// Reduces memory use. Useful to call when the application gets paused or sent to background. Future reduceMemoryUse() => _mapInterface.reduceMemoryUse(); diff --git a/lib/src/pigeons/map_interfaces.dart b/lib/src/pigeons/map_interfaces.dart index e25306cf1..c881360a2 100644 --- a/lib/src/pigeons/map_interfaces.dart +++ b/lib/src/pigeons/map_interfaces.dart @@ -1195,6 +1195,7 @@ class QueriedRenderedFeature { QueriedRenderedFeature({ required this.queriedFeature, required this.layers, + this.queryTargets, }); /// Feature returned by the query. @@ -1205,10 +1206,16 @@ class QueriedRenderedFeature { /// If the feature is only rendered in one layer, a single Id will be provided. List layers; + /// An array of feature query targets that correspond to this queried feature. + /// + /// - Note: Returned query targets will omit the original `filter` data. + List? queryTargets; + Object encode() { return [ queriedFeature, layers, + queryTargets, ]; } @@ -1217,6 +1224,8 @@ class QueriedRenderedFeature { return QueriedRenderedFeature( queriedFeature: result[0]! as QueriedFeature, layers: (result[1] as List?)!.cast(), + queryTargets: + (result[2] as List?)?.cast(), ); } } @@ -1289,6 +1298,172 @@ class QueriedFeature { } } +/// Represents a unique identifier for a feature in one exported featureset or a layer. +class FeaturesetFeatureId { + FeaturesetFeatureId({ + required this.id, + this.namespace, + }); + + /// The `featureId` uniquely identifies a feature within a featureset or layer. + /// Note: The Identifier of the feature is not guaranteed to be persistent + /// and can change depending on the source that is used. + String id; + + /// An optional field that represents the feature namespace defined by + /// the Selector within a Featureset to which this feature belongs. + /// If the underlying source is the same for multiple selectors within a Featureset, + /// the same `featureNamespace` should be used across those selectors. + /// This practice ensures the uniqueness of `FeaturesetFeatureId` across the style. + /// Defining a `featureNamespace` value for the Selector is recommended, + /// especially when multiple selectors exist in a Featureset, + /// as it can enhance the efficiency of feature operations. + String? namespace; + + Object encode() { + return [ + id, + namespace, + ]; + } + + static FeaturesetFeatureId decode(Object result) { + result as List; + return FeaturesetFeatureId( + id: result[0]! as String, + namespace: result[1] as String?, + ); + } +} + +/// Represents an identifier for a single exported featureset or a layer. +class FeaturesetDescriptor { + FeaturesetDescriptor({ + this.featuresetId, + this.importId, + this.layerId, + }); + + /// An optional unique identifier for the featureset within the style. + /// This id is used to reference a specific featureset. + /// + /// * Note: If `featuresetId` is provided and valid, it takes precedence over `layerId`, + /// * meaning `layerId` will not be considered even if it has a valid value. + String? featuresetId; + + /// An optional import id that is required if the featureset is defined within an imported style. + /// If the featureset belongs to the current style, this field should be set to a null string. + /// + /// Note: `importId` is only applicable when used in conjunction with `featuresetId` + /// and has no effect when used with `layerId`. + String? importId; + + /// An optional unique identifier for the layer within the current style. + /// + /// Note: If `featuresetId` is valid, `layerId` will be ignored even if it has a valid value. + /// Additionally, `importId` does not apply when using `layerId`. + String? layerId; + + Object encode() { + return [ + featuresetId, + importId, + layerId, + ]; + } + + static FeaturesetDescriptor decode(Object result) { + result as List; + return FeaturesetDescriptor( + featuresetId: result[0] as String?, + importId: result[1] as String?, + layerId: result[2] as String?, + ); + } +} + +class FeaturesetFeature { + FeaturesetFeature({ + this.id, + required this.featureset, + required this.geoJSONFeature, + required this.state, + }); + + /// Optional identifier holding feature id and feature namespace. + /// It could be NULL when underlying [Feature.id] is null. + FeaturesetFeatureId? id; + + /// The [TypedFeaturesetDescriptor] this concrete feature comes from. + /// List of supported featuresets could be found in the nested classes + /// (e.g. [TypedFeaturesetDescriptor.Featureset], [TypedFeaturesetDescriptor.Layer], etc). + FeaturesetDescriptor featureset; + + /// A feature geometry. + /// Feature JSON properties. + /// The geoJSON feature. + Feature geoJSONFeature; + + /// Current feature state stored as a concrete instance of [FeatureState]. + /// Important: this state is immutable and represents the feature state + /// at the precise moment of the interaction callback. + Map state; + + Object encode() { + return [ + id, + featureset, + geoJSONFeature, + state, + ]; + } + + static FeaturesetFeature decode(Object result) { + result as List; + return FeaturesetFeature( + id: result[0] as FeaturesetFeatureId?, + featureset: result[1]! as FeaturesetDescriptor, + geoJSONFeature: result[2]! as Feature, + state: (result[3] as Map?)!.cast(), + ); + } +} + +/// Defines the parameters for querying features from a Featureset with an optional filter and id. +class FeaturesetQueryTarget { + FeaturesetQueryTarget({ + required this.featureset, + this.filter, + this.id, + }); + + /// The FeaturesetDescriptor that specifies the featureset to be included in the query. + FeaturesetDescriptor featureset; + + /// An optional filter expression used to refine the query results based on conditions related to the specified featureset. + String? filter; + + /// An optional unique identifier associated with the FeaturesetQueryTarget. + int? id; + + Object encode() { + return [ + featureset, + filter, + id, + ]; + } + + static FeaturesetQueryTarget decode(Object result) { + result as List; + return FeaturesetQueryTarget( + featureset: result[0]! as FeaturesetDescriptor, + filter: result[1] as String?, + id: result[2] as int?, + ); + } +} + /// Geometry for querying rendered features. class _RenderedQueryGeometry { _RenderedQueryGeometry({ @@ -3688,6 +3863,156 @@ class _MapInterface { } } + /// Queries the map for rendered features using featureset descriptors. + /// + /// This method allows to query both featureset from imported styles and user layers in the root style. + /// The results can be additionally filtered per-featureset. + /// + /// ```dart + /// let targets = [ + /// FeaturesetQueryTarget( + /// featureset: .layer("my-layer"), + /// filter: Exp(.eq) { + /// Exp(.get) { "type" } + /// "hotel" + /// } + /// ), + /// FeaturesetQueryTarget(featureset: .featureset("poi", importId: "basemap")) + /// ] + /// mapView.mapboxMap.queryRenderedFeatures(with: CGPoint(x: 0, y: 0), + /// targets: targets) { result in + /// // handle features in result + /// } + /// ``` + /// + /// - Important: This is a low-level method. If you need to handle basic gestures on map content, please prefer to use Interactions API (see ``MapboxMap/addInteraction(_:)``) or ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)``. + /// + /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. + /// @param targets An array of targets to query with. + /// @param completion Callback called when the query completes. + Future> queryRenderedFeaturesForTargets( + _RenderedQueryGeometry geometry, + List targets) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesForTargets$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([geometry, targets]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as List?)! + .cast(); + } + } + + /// Queries the map for rendered features with one typed featureset. + /// + /// The results array will contain features of the type specified by this featureset. + /// + /// ```swift + /// mapView.mapboxMap.queryRenderedFeatures( + /// with: CGPoint(x: 0, y: 0), + /// featureset: .standardBuildings) { result in + /// // handle buildings in result + /// } + /// ``` + /// + /// - Important: If you need to handle basic gestures on map content, please prefer to use Interactions API, see ``MapboxMap/addInteraction(_:)``. + /// + /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. + /// @param featureset A typed featureset to query with. + /// @param filter An additional filter for features. + /// @param completion Callback called when the query completes. + Future> queryRenderedFeaturesForFeatureset( + _RenderedQueryGeometry geometry, + FeaturesetDescriptor featureset, + String? filter) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesForFeatureset$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([geometry, featureset, filter]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as List?)! + .cast(); + } + } + + /// Queries all rendered features in current viewport, using one typed featureset. + /// + /// This is same as ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)`` called with geometry matching the current viewport. + /// + /// - Important: If you need to handle basic gestures on map content, please prefer to use Interactions API, see ``MapboxMap/addInteraction(_:)``. + /// + /// @param featureset A typed featureset to query with. + /// @param filter An additional filter for features. + /// @param completion Callback called when the query completes. + Future> queryRenderedFeaturesInViewport( + FeaturesetDescriptor featureset, String? filter) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesInViewport$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([featureset, filter]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as List?)! + .cast(); + } + } + /// Queries the map for source features. /// /// @param sourceId The style source identifier used to query for source features. @@ -3724,6 +4049,41 @@ class _MapInterface { } } + /// Queries the source features for a given featureset. + /// + /// @param target A featureset query target. + /// @param completion Callback called when the query completes. + Future> querySourceFeaturesForFeatureset( + FeaturesetQueryTarget target) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.querySourceFeaturesForFeatureset$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([target]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as List?)! + .cast(); + } + } + /// Returns all the leaves (original points) of a cluster (given its cluster_id) from a GeoJsonSource, with pagination support: limit is the number of leaves /// to return (set to Infinity for all points), and offset is the amount of points to skip (for pagination). /// @@ -3883,6 +4243,78 @@ class _MapInterface { } } + /// Update the state map of a feature within a featureset. + /// Update entries in the state map of a given feature within a style source. Only entries listed in the state map + /// will be updated. An entry in the feature state map that is not listed in `state` will retain its previous value. + /// + /// @param featureset The featureset to look the feature in. + /// @param featureId Identifier of the feature whose state should be updated. + /// @param state Map of entries to update with their respective new values + /// @param callback The `feature state operation callback` called when the operation completes or ends. + /// + /// @return A `Cancelable` object that could be used to cancel the pending operation. + Future setFeatureStateForFeaturesetFeatureDescriptor( + FeaturesetDescriptor featureset, + FeaturesetFeatureId featureId, + Map state) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.setFeatureStateForFeaturesetFeatureDescriptor$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([featureset, featureId, state]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + /// Update the state map of an individual feature. + /// + /// The feature should have a non-nil ``FeaturesetFeatureType/id``. Otherwise, + /// the operation will be no-op and callback will receive an error. + /// + /// @param feature The feature to update. + /// @param state Map of entries to update with their respective new values + /// @param callback The `feature state operation callback` called when the operation completes or ends. + /// + /// @return A `Cancelable` object that could be used to cancel the pending operation. + Future setFeatureStateForFeaturesetFeature( + FeaturesetFeature feature, Map state) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.setFeatureStateForFeaturesetFeature$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([feature, state]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + /// Gets the state map of a feature within a style source. /// /// Note that updates to feature state are asynchronous, so changes made by other methods might not be @@ -3922,6 +4354,80 @@ class _MapInterface { } } + /// Get the state map of a feature within a style source. + /// + /// @param featureset A featureset the feature belongs to. + /// @param featureId Identifier of the feature whose state should be queried. + /// + /// @return A `Cancelable` object that could be used to cancel the pending query. + Future> getFeatureStateForFeaturesetDescriptor( + FeaturesetDescriptor featureset, FeaturesetFeatureId featureId) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.getFeatureStateForFeaturesetDescriptor$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([featureset, featureId]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as Map?)! + .cast(); + } + } + + /// Get the state map of a feature within a style source. + /// + /// @param feature An interactive feature to query the state from. + /// @param completion Feature's state map or an empty map if the feature could not be found. + /// + /// @return A `Cancelable` object that could be used to cancel the pending query. + Future> getFeatureStateForFeaturesetFeature( + FeaturesetFeature feature) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.getFeatureStateForFeaturesetFeature$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([feature]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as Map?)! + .cast(); + } + } + /// Removes entries from a feature state object. /// /// Remove a specified property or all property from a feature's state object, depending on the value of @@ -3960,6 +4466,109 @@ class _MapInterface { } } + /// Removes entries from a feature state object of a feature in the specified featureset. + /// Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. + /// + /// @param featureset A featureset the feature belongs to. + /// @param featureId Identifier of the feature whose state should be removed. + /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. + /// @param callback The `feature state operation callback` called when the operation completes or ends. + /// + /// @return A `Cancelable` object that could be used to cancel the pending operation. + Future removeFeatureStateForFeaturesetFeatureDescriptor( + FeaturesetDescriptor featureset, + FeaturesetFeatureId featureId, + String stateKey) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureStateForFeaturesetFeatureDescriptor$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([featureset, featureId, stateKey]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + /// Removes entries from a specified Feature. + /// Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. + /// + /// @param feature An interactive feature to update. + /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. + /// @param callback The `feature state operation callback` called when the operation completes or ends. + /// + /// @return A `Cancelable` object that could be used to cancel the pending operation. + Future removeFeatureStateForFeaturesetFeature( + FeaturesetFeature feature, String stateKey) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureStateForFeaturesetFeature$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([feature, stateKey]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + /// Reset all the feature states within a featureset. + /// + /// Note that updates to feature state are asynchronous, so changes made by this method might not be + /// immediately visible using ``MapboxMap/getFeatureState(_:callback:)``. + /// + /// @param featureset A featureset descriptor + /// @param callback The `feature state operation callback` called when the operation completes or ends. + /// + /// @return A `Cancelable` object that could be used to cancel the pending operation. + Future resetFeatureStatesForFeatureset( + FeaturesetDescriptor featureset) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.resetFeatureStatesForFeatureset$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([featureset]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + /// Reduces memory use. Useful to call when the application gets paused or sent to background. Future reduceMemoryUse() async { final String pigeonVar_channelName = @@ -6778,6 +7387,39 @@ class StyleManager { return; } } + + /// Returns the available featuresets in the currently loaded style. + /// + /// - Note: This function should only be called after the style is fully loaded; otherwise, the result may be unreliable. + Future> getFeaturesets() async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.mapbox_maps_flutter.StyleManager.getFeaturesets$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send(null) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as List?)! + .cast(); + } + } } /// Allows to cancel the associated asynchronous operation diff --git a/lib/src/turf_adapters.dart b/lib/src/turf_adapters.dart index c3173e6b9..08a22ee89 100644 --- a/lib/src/turf_adapters.dart +++ b/lib/src/turf_adapters.dart @@ -74,7 +74,8 @@ final class Feature extends turf.Feature { static Feature decode(Object result) { result as List; - return Feature.fromJson((result.first as Map).cast()); + var valid = convertToValidMap(result.first as Map); + return Feature.fromJson(valid); } factory Feature.fromJson(Map json) { @@ -86,4 +87,53 @@ final class Feature extends turf.Feature { geometry: feature.geometry, fields: feature.fields); } + + factory Feature.fromFeature(Map feature) { + var valid = convertToValidMap(feature as Map); + return Feature.fromJson(valid); + } + + // factory Feature.fromQueriedFeature(QueriedFeature queriedFeature) { + // final feature = + // queriedFeature.feature.convertToValidMap(queriedFeature.feature); + // // print(queriedFeature.feature); + // // print(feature); + // return Feature.fromJson(feature); + // } } + +// extension on List { +// Map convertToValidMap(List input) { +// return +// return input.map((key, value) { +// // print(value.runtimeType); +// if (key is! String) { +// throw Exception( +// "Invalid key type. Expected String but got ${key.runtimeType}"); +// } +// if (value is Map) { +// // Recursively convert nested maps +// return MapEntry(key, convertToValidMap(value)); +// } +// return MapEntry(key, value); +// }); +// } +// } + +//extension on Map { +Map convertToValidMap(Map input) { + return input.map((key, value) { + if (key is! String) { + throw Exception( + "Invalid key type. Expected String but got ${key.runtimeType}"); + } + if (value is Map) { + // Recursively convert nested maps + return MapEntry(key, convertToValidMap(value)); + } + return MapEntry(key, value); + }); +} +//} + +typedef JSONObject = Map; From 62c1984127a445690cf1386fbaea6fcde13889ff Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Wed, 23 Oct 2024 22:01:56 -0400 Subject: [PATCH 02/28] Add Standard experimental, build out Android --- .../com/mapbox/maps/mapbox_maps/Extentions.kt | 50 +++++- .../mapbox_maps/MapInterfaceController.kt | 160 ++++++++++++++---- .../maps/mapbox_maps/StyleController.kt | 5 + .../interactive_features_test.dart | 6 +- ...dart => interactive_features_example.dart} | 85 ++-------- example/lib/main.dart | 2 + lib/src/style/mapbox_styles.dart | 4 + 7 files changed, 202 insertions(+), 110 deletions(-) rename example/lib/{interactive_features.dart => interactive_features_example.dart} (57%) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt index abc14c2e1..bc6dcc19a 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt @@ -1,5 +1,6 @@ package com.mapbox.maps.mapbox_maps +import android.annotation.SuppressLint import android.content.Context import android.graphics.Bitmap import com.google.gson.Gson @@ -7,11 +8,14 @@ import com.mapbox.bindgen.Expected import com.mapbox.bindgen.None import com.mapbox.bindgen.Value import com.mapbox.common.TileRegionError +import com.mapbox.common.toValue import com.mapbox.geojson.* import com.mapbox.maps.EdgeInsets +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.dsl.generated.id 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 @@ -19,6 +23,8 @@ import com.mapbox.maps.extension.style.light.generated.directionalLight import com.mapbox.maps.extension.style.light.generated.flatLight import com.mapbox.maps.extension.style.projection.generated.Projection import com.mapbox.maps.extension.style.types.StyleTransition +import com.mapbox.maps.interactions.FeatureState +import com.mapbox.maps.interactions.TypedFeaturesetDescriptor import com.mapbox.maps.logE import com.mapbox.maps.mapbox_maps.pigeons.* import org.json.JSONArray @@ -275,9 +281,21 @@ fun FeaturesetQueryTarget.toFeaturesetQueryTarget(): com.mapbox.maps.FeaturesetQ return com.mapbox.maps.FeaturesetQueryTarget(featureset.toFeatureSetDescriptor(), filter?.toValue(), id) } -// fun FeaturesetFeature.toFeaturesetFeature(): com.mapbox.maps.interactions.FeaturesetFeature<> { -// return com.mapbox.maps.interactions.FeaturesetFeature(id, descriptor, state, originalFeature) -// } +//@OptIn(MapboxExperimental::class) +//@SuppressLint("RestrictedApi") +//fun FeaturesetFeature.toFeaturesetFeature(): com.mapbox.maps.interactions.FeaturesetFeature { +// // TODO: layers +// featureset.featuresetId?.let { +// return com.mapbox.maps.interactions.FeaturesetFeature( +// id?.toFeaturesetFeatureId(), +// TypedFeaturesetDescriptor.Featureset( +// featureset.featuresetId, featureset.importId +// ), +// state = FeatureState { state }, +// originalFeature = geoJSONFeature +// ) +// } +//} fun MapDebugOptions.toMapDebugOptions(): com.mapbox.maps.MapDebugOptions { return com.mapbox.maps.MapDebugOptions.values()[data.ordinal] @@ -520,6 +538,32 @@ fun com.mapbox.maps.QueriedRenderedFeature.toFLTQueriedRenderedFeature(): Querie return QueriedRenderedFeature(queriedFeature.toFLTQueriedFeature(), layers) } +fun com.mapbox.maps.FeaturesetFeatureId.toFLTFeaturesetFeatureId(): FeaturesetFeatureId { + return FeaturesetFeatureId(featureId, featureNamespace) +} + +fun com.mapbox.maps.FeaturesetDescriptor.toFLTFeaturesetDescriptor(): FeaturesetDescriptor { + return FeaturesetDescriptor(featuresetId, importId, layerId) +} + +@SuppressLint("RestrictedApi") +@OptIn(MapboxExperimental::class) +fun com.mapbox.maps.interactions.FeaturesetFeature.toFltFeaturesetFeature(): FeaturesetFeature { + // TODO: need access to original feature or properties + val feature = Feature.fromGeometry(geometry); + + val map: Map = JSONObject(state.asJsonString()).toMap() + .filterKeys { it != null } // Filter out null keys + .mapKeys { it.key!! } + + return FeaturesetFeature( + id?.toFLTFeaturesetFeatureId(), + descriptor.toFeaturesetDescriptor().toFLTFeaturesetDescriptor(), + feature, + map + ) +} + fun com.mapbox.maps.QueriedSourceFeature.toFLTQueriedSourceFeature(): QueriedSourceFeature { return QueriedSourceFeature(queriedFeature.toFLTQueriedFeature()) } diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt index 29d158ef6..7eb4b3080 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt @@ -2,18 +2,22 @@ package com.mapbox.maps.mapbox_maps import android.content.Context import com.google.gson.Gson +import com.mapbox.common.toValue import com.mapbox.geojson.Feature import com.mapbox.geojson.Point import com.mapbox.maps.MapView +import com.mapbox.maps.MapboxDelicateApi import com.mapbox.maps.MapboxExperimental import com.mapbox.maps.MapboxMap import com.mapbox.maps.TileCacheBudget import com.mapbox.maps.extension.observable.eventdata.MapLoadingErrorEventData -import com.mapbox.maps.interactions.FeatureState +import com.mapbox.maps.interactions.TypedFeaturesetDescriptor import com.mapbox.maps.mapbox_maps.pigeons.CanonicalTileID import com.mapbox.maps.mapbox_maps.pigeons.ConstrainMode 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 @@ -26,7 +30,6 @@ import com.mapbox.maps.mapbox_maps.pigeons.SourceQueryOptions import com.mapbox.maps.mapbox_maps.pigeons.TileCacheBudgetInMegabytes import com.mapbox.maps.mapbox_maps.pigeons.TileCacheBudgetInTiles import com.mapbox.maps.mapbox_maps.pigeons.TileCoverOptions -import com.mapbox.maps.mapbox_maps.pigeons.TypedFeaturesetDescriptor import com.mapbox.maps.mapbox_maps.pigeons.ViewportMode import com.mapbox.maps.mapbox_maps.pigeons._MapInterface import com.mapbox.maps.mapbox_maps.pigeons._MapWidgetDebugOptions @@ -171,7 +174,76 @@ class MapInterfaceController( } } - override fun queryRenderedFeaturesForGeometry( + @OptIn(MapboxExperimental::class, MapboxDelicateApi::class) + override fun queryRenderedFeaturesForTargets( + geometry: _RenderedQueryGeometry, + targets: List, + callback: (Result>) -> 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, + featureset: FeaturesetDescriptor, + filter: String?, + callback: (Result>) -> Unit + ) { + featureset.featuresetId?.let { + mapboxMap.queryRenderedFeatures( + geometry.toRenderedQueryGeometry(context), + TypedFeaturesetDescriptor.Featureset( + featureset.featuresetId, featureset.importId + ), + filter.toValue() + ) { + callback( + Result.success( + it.map { feature -> feature.toFltFeaturesetFeature() }.toMutableList() + ) + ) + } + } ?: { + featureset.layerId?.let { it -> + mapboxMap.queryRenderedFeatures( + geometry.toRenderedQueryGeometry(context), + TypedFeaturesetDescriptor.Layer( + featureset.layerId + ), + filter.toValue() + ) { + callback( + Result.success( + it.map { feature -> feature.toFltFeaturesetFeature() }.toMutableList() + ) + ) + } + } + } + } + + override fun queryRenderedFeaturesInViewport( + featureset: FeaturesetDescriptor, + filter: String?, + callback: (Result>) -> Unit + ) { + TODO(reason = "convenience") + } + + fun queryRenderedFeaturesForGeometry( geometry: _RenderedQueryGeometry, targets: List, callback: (Result>) -> Unit @@ -212,6 +284,13 @@ class MapInterfaceController( } } + override fun querySourceFeaturesForFeatureset( + target: FeaturesetQueryTarget, + callback: (Result>) -> Unit + ) { + TODO("Not yet implemented") + } + override fun getGeoJsonClusterLeaves( sourceIdentifier: String, cluster: Map, @@ -281,25 +360,21 @@ class MapInterfaceController( } } - @OptIn(MapboxExperimental::class) - override fun setFeatureStateForFeatureStateFeature( + override fun setFeatureStateForFeaturesetFeatureDescriptor( + featureset: FeaturesetDescriptor, + featureId: FeaturesetFeatureId, + state: Map, + callback: (Result) -> Unit + ) { + TODO("Not yet implemented") + } + + override fun setFeatureStateForFeaturesetFeature( feature: FeaturesetFeature, - state: String, + state: Map, callback: (Result) -> Unit ) { - mapboxMap.setFeatureState( - com.mapbox.maps.interactions.TypedFeaturesetDescriptor.Featureset( - feature.descriptor!!.descriptor.featuresetId!!, feature.descriptor.descriptor.importId - ), - feature.id!!.toFeaturesetFeatureId(), - FeatureState { feature.state } - ) { - if (it.isError) { - callback(Result.failure(Throwable(it.error))) - } else { - callback(Result.success(Unit)) - } - } + TODO("Not yet implemented") } override fun getFeatureState( @@ -318,21 +393,20 @@ class MapInterfaceController( } } } - @OptIn(MapboxExperimental::class) + + override fun getFeatureStateForFeaturesetDescriptor( + featureset: FeaturesetDescriptor, + featureId: FeaturesetFeatureId, + callback: (Result>) -> Unit + ) { + TODO("Not yet implemented") + } + override fun getFeatureStateForFeaturesetFeature( feature: FeaturesetFeature, - callback: (Result) -> Unit + callback: (Result>) -> Unit ) { - mapboxMap.getFeatureState( - com.mapbox.maps.interactions.TypedFeaturesetDescriptor.Featureset( - feature.descriptor!!.descriptor.featuresetId!!, feature.descriptor.descriptor.importId - ), - feature.id!!.toFeaturesetFeatureId() - ) { expected -> - callback.let { - it(Result.success(expected.asJsonString())) - } - } + TODO("Not yet implemented") } override fun removeFeatureState( @@ -351,6 +425,30 @@ class MapInterfaceController( } } + override fun removeFeatureStateForFeaturesetFeatureDescriptor( + featureset: FeaturesetDescriptor, + featureId: FeaturesetFeatureId, + stateKey: String, + callback: (Result) -> Unit + ) { + TODO("Not yet implemented") + } + + override fun removeFeatureStateForFeaturesetFeature( + feature: FeaturesetFeature, + stateKey: String, + callback: (Result) -> Unit + ) { + TODO("Not yet implemented") + } + + override fun resetFeatureStatesForFeatureset( + featureset: FeaturesetDescriptor, + callback: (Result) -> Unit + ) { + TODO("Not yet implemented") + } + override fun reduceMemoryUse() { mapboxMap.reduceMemoryUse() } diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/StyleController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/StyleController.kt index 255bf482a..a9dd1314e 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/StyleController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/StyleController.kt @@ -19,6 +19,7 @@ import com.mapbox.maps.mapbox_maps.pigeons.CameraOptions import com.mapbox.maps.mapbox_maps.pigeons.CanonicalTileID import com.mapbox.maps.mapbox_maps.pigeons.CoordinateBounds import com.mapbox.maps.mapbox_maps.pigeons.DirectionalLight +import com.mapbox.maps.mapbox_maps.pigeons.FeaturesetDescriptor import com.mapbox.maps.mapbox_maps.pigeons.FlatLight import com.mapbox.maps.mapbox_maps.pigeons.ImageContent import com.mapbox.maps.mapbox_maps.pigeons.ImageStretches @@ -588,6 +589,10 @@ class StyleController(private val context: Context, private val styleManager: Ma callback(Result.success(Unit)) } + override fun getFeaturesets(): List { + TODO("no implemented on Android") + } + override fun addStyleImage( imageId: String, scale: Double, diff --git a/example/integration_test/interactive_features_test.dart b/example/integration_test/interactive_features_test.dart index 15fc1ef89..389fd2982 100644 --- a/example/integration_test/interactive_features_test.dart +++ b/example/integration_test/interactive_features_test.dart @@ -1,13 +1,9 @@ -import 'dart:convert'; -import 'dart:io'; -import 'dart:math'; - import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart'; -import 'package:mapbox_maps_example/empty_map_widget.dart' as app; +import 'empty_map_widget.dart' as app; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); diff --git a/example/lib/interactive_features.dart b/example/lib/interactive_features_example.dart similarity index 57% rename from example/lib/interactive_features.dart rename to example/lib/interactive_features_example.dart index c2d5de61f..a0882b5ea 100644 --- a/example/lib/interactive_features.dart +++ b/example/lib/interactive_features_example.dart @@ -3,26 +3,23 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart'; -import 'page.dart'; - -class InteractiveFeaturesPage extends ExamplePage { - InteractiveFeaturesPage() - : super(const Icon(Icons.map), 'Interactive features map'); +import 'example.dart'; +class InteractiveFeaturesExample extends StatefulWidget implements Example { @override - Widget build(BuildContext context) { - return const InteractiveFeatures(); - } -} - -class InteractiveFeatures extends StatefulWidget { - const InteractiveFeatures(); + final Widget leading = const Icon(Icons.map); + @override + final String title = 'Interactive Features'; + @override + final String? subtitle = null; @override - State createState() => InteractiveFeaturesState(); + State createState() => InteractiveFeaturesState(); } -class InteractiveFeaturesState extends State { +class InteractiveFeaturesState extends State { + InteractiveFeaturesState(); + MapboxMap? mapboxMap; var isLight = true; var feature = Feature( @@ -36,15 +33,6 @@ class InteractiveFeaturesState extends State { mapboxMap.style; } - _onStyleLoadedCallback(StyleLoadedEventData data) { - // ScaffoldMessenger.of(context).showSnackBar(SnackBar( - // content: Text("Style loaded :), time: ${data.timeInterval}"), - // backgroundColor: Theme.of(context).primaryColor, - // duration: Duration(seconds: 1), - // )); - print("styleLoaded"); - } - _onTap(context) async { print("tap"); @@ -62,20 +50,10 @@ class InteractiveFeaturesState extends State { print("feature"); print(feature?.feature); - // Get FeatureState - // var typedFeatures = Feature.fromQueriedFeature(feature!); - Map data = { "highlight": true, }; - String jsonString = jsonEncode(data); - - var geometry = { - "type": "Point", - "coordinates": [1, 2] - }; - Feature featured = Feature(id: "id", geometry: Point(coordinates: Position(1, 2))); @@ -92,41 +70,9 @@ class InteractiveFeaturesState extends State { await Future.delayed(Duration(seconds: 2)); - var featuerState = + var featureState = await mapboxMap?.getFeatureStateForFeaturesetFeature(featureSetFeature); - print("yello"); - print(featuerState); - } - - _onLongClick(context) async { - var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); - mapboxMap?.style.setStyleJSON(styleJson); - print("longclick"); - - var clicked = await mapboxMap?.pixelForCoordinate(context.point); - - var filter = '["=",["get", "type"],"A"]'; - var featuresetFilterQuery = - await mapboxMap?.queryRenderedFeaturesForFeatureset( - geometry: RenderedQueryGeometry.fromScreenCoordinate(clicked!), - featureset: - FeaturesetDescriptor(featuresetId: "poi", importId: "nested"), - filter: filter); - - print(featuresetFilterQuery?[0].geoJSONFeature.properties?["name"]); - print(featuresetFilterQuery?[0].geoJSONFeature.properties?["class"]); - - var renderedQueryGeometry = - RenderedQueryGeometry.fromScreenCoordinate(clicked!); - - var target = FeaturesetQueryTarget( - featureset: FeaturesetDescriptor( - featuresetId: "buildings", importId: "basemap")); - var targets = [target]; - - var query = await mapboxMap?.queryRenderedFeaturesForTargets( - renderedQueryGeometry, targets); - print(query?.first?.queriedFeature.feature); + print(featureState); } @override @@ -161,12 +107,9 @@ class InteractiveFeaturesState extends State { coordinates: Position(24.94180921290157, 60.171227338006844)), zoom: 15.0, pitch: 30), - styleUri: - "", + styleUri: MapboxStyles.STANDARD_EXPERIMENTAL, textureView: true, onMapCreated: _onMapCreated, - onStyleLoadedListener: _onStyleLoadedCallback, - onLongTapListener: _onLongClick, onTapListener: _onTap, )); } diff --git a/example/lib/main.dart b/example/lib/main.dart index 04f97880f..b64bb6b35 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -10,6 +10,7 @@ import 'package:mapbox_maps_example/ornaments_example.dart'; import 'package:mapbox_maps_example/geojson_line_example.dart'; import 'package:mapbox_maps_example/image_source_example.dart'; import 'package:mapbox_maps_example/map_interface_example.dart'; +import 'package:mapbox_maps_example/interactive_features_example.dart'; import 'package:mapbox_maps_example/polygon_annotations_example.dart'; import 'package:mapbox_maps_example/polyline_annotations_example.dart'; import 'package:mapbox_maps_example/simple_map_example.dart'; @@ -41,6 +42,7 @@ final List _allPages = [ SpinningGlobeExample(), FullMapExample(), StyleExample(), + InteractiveFeaturesExample(), CameraExample(), ProjectionExample(), MapInterfaceExample(), diff --git a/lib/src/style/mapbox_styles.dart b/lib/src/style/mapbox_styles.dart index c599ee519..e10c06e62 100644 --- a/lib/src/style/mapbox_styles.dart +++ b/lib/src/style/mapbox_styles.dart @@ -36,6 +36,10 @@ class MapboxStyles { /// improve the style. static const String SATELLITE_STREETS = "mapbox://styles/mapbox/satellite-streets-v12"; + + /// NOT FOR PRODUCTION USE. An experimental version of the Mapbox Standard style. + /// This style is used for testing new features and changes to the Mapbox Standard style. The style may change or be removed at any time. + static const String STANDARD_EXPERIMENTAL = "mapbox://styles/mapbox-map-design/standard-experimental-ime"; } /// A pre-specified location in the style where layer will be added to. From aa9373347bdb94e4e2a9c8c6479afb29c896647d Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Thu, 24 Oct 2024 22:20:53 -0400 Subject: [PATCH 03/28] Finish up Android interfaces, general cleanup --- .../com/mapbox/maps/mapbox_maps/Extentions.kt | 104 +++++++--- .../mapbox_maps/MapInterfaceController.kt | 183 ++++++++++++------ .../interactive_features_test.dart | 157 +++++++-------- .../Classes/Extensions.swift | 6 +- lib/src/mapbox_map.dart | 4 +- lib/src/turf_adapters.dart | 31 +-- 6 files changed, 290 insertions(+), 195 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt index bc6dcc19a..1c94c97d9 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt @@ -3,7 +3,10 @@ package com.mapbox.maps.mapbox_maps import android.annotation.SuppressLint import android.content.Context import android.graphics.Bitmap +import android.util.Log import com.google.gson.Gson +import com.google.gson.JsonObject +import com.google.gson.JsonParser import com.mapbox.bindgen.Expected import com.mapbox.bindgen.None import com.mapbox.bindgen.Value @@ -16,6 +19,7 @@ import com.mapbox.maps.StylePackError import com.mapbox.maps.applyDefaultParams import com.mapbox.maps.debugoptions.MapViewDebugOptions import com.mapbox.maps.extension.style.expressions.dsl.generated.id +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 @@ -277,25 +281,64 @@ fun FeaturesetDescriptor.toFeatureSetDescriptor(): com.mapbox.maps.FeaturesetDes return com.mapbox.maps.FeaturesetDescriptor(featuresetId, importId, layerId) } +@OptIn(MapboxExperimental::class) +fun FeaturesetDescriptor.toTypedFeaturesetDescriptor(): TypedFeaturesetDescriptor<*, *> { + featuresetId?.let { + return TypedFeaturesetDescriptor.Featureset( + featuresetId, importId + ) + } + return TypedFeaturesetDescriptor.Layer( + layerId!! // If there is not a featuresetId there will be a layerId + ) +} + fun FeaturesetQueryTarget.toFeaturesetQueryTarget(): com.mapbox.maps.FeaturesetQueryTarget { - return com.mapbox.maps.FeaturesetQueryTarget(featureset.toFeatureSetDescriptor(), filter?.toValue(), id) -} - -//@OptIn(MapboxExperimental::class) -//@SuppressLint("RestrictedApi") -//fun FeaturesetFeature.toFeaturesetFeature(): com.mapbox.maps.interactions.FeaturesetFeature { -// // TODO: layers -// featureset.featuresetId?.let { -// return com.mapbox.maps.interactions.FeaturesetFeature( -// id?.toFeaturesetFeatureId(), -// TypedFeaturesetDescriptor.Featureset( -// featureset.featuresetId, featureset.importId -// ), -// state = FeatureState { state }, -// originalFeature = geoJSONFeature -// ) -// } -//} + return com.mapbox.maps.FeaturesetQueryTarget(featureset.toFeatureSetDescriptor(), filter?.let { Expression.fromRaw(filter) }, id) +} + +@OptIn(MapboxExperimental::class) +fun Map.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) + } + else -> throw (RuntimeException("Unsupported (key, value): ($key, $value)")) + } + } + } +} + +@OptIn(MapboxExperimental::class) +@SuppressLint("RestrictedApi") +fun FeaturesetFeature.toFeaturesetFeature(): com.mapbox.maps.interactions.FeaturesetFeature { + featureset.featuresetId?.let { + return com.mapbox.maps.interactions.FeaturesetFeature( + id?.toFeaturesetFeatureId(), + featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor.Featureset, + state.toFeatureState(), + geoJSONFeature + ) + } + return com.mapbox.maps.interactions.FeaturesetFeature( + id?.toFeaturesetFeatureId(), + featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor.Layer, + state.toFeatureState(), + geoJSONFeature + ) +} fun MapDebugOptions.toMapDebugOptions(): com.mapbox.maps.MapDebugOptions { return com.mapbox.maps.MapDebugOptions.values()[data.ordinal] @@ -534,8 +577,13 @@ fun com.mapbox.maps.QueriedFeature.toFLTQueriedFeature(): QueriedFeature { return QueriedFeature(JSONObject(this.feature.toJson()).toMap(), source, sourceLayer, state.toJson()) } +fun com.mapbox.maps.FeaturesetQueryTarget.toFLTFeaturesetQueryTarget(): FeaturesetQueryTarget { + return FeaturesetQueryTarget(featureset.toFLTFeaturesetDescriptor(), filter?.toString(), id) +} + fun com.mapbox.maps.QueriedRenderedFeature.toFLTQueriedRenderedFeature(): QueriedRenderedFeature { - return QueriedRenderedFeature(queriedFeature.toFLTQueriedFeature(), layers) + val queryTargets = targets.map { it.toFLTFeaturesetQueryTarget() } + return QueriedRenderedFeature(queriedFeature.toFLTQueriedFeature(), layers, queryTargets) } fun com.mapbox.maps.FeaturesetFeatureId.toFLTFeaturesetFeatureId(): FeaturesetFeatureId { @@ -546,15 +594,21 @@ fun com.mapbox.maps.FeaturesetDescriptor.toFLTFeaturesetDescriptor(): Featureset return FeaturesetDescriptor(featuresetId, importId, layerId) } -@SuppressLint("RestrictedApi") @OptIn(MapboxExperimental::class) -fun com.mapbox.maps.interactions.FeaturesetFeature.toFltFeaturesetFeature(): FeaturesetFeature { - // TODO: need access to original feature or properties - val feature = Feature.fromGeometry(geometry); - - val map: Map = JSONObject(state.asJsonString()).toMap() +fun com.mapbox.maps.interactions.FeatureState.toMap(): Map { + return JSONObject(this.asJsonString()).toMap() .filterKeys { it != null } // Filter out null keys .mapKeys { it.key!! } +} + +@SuppressLint("RestrictedApi") +@OptIn(MapboxExperimental::class) +fun com.mapbox.maps.interactions.FeaturesetFeature.toFltFeaturesetFeature(): FeaturesetFeature { +// TODO: need access to original feature or better conversion to JsonObject from JSONObject + val jsonObject: JsonObject = JsonParser.parseString(properties.toString()).getAsJsonObject(); + val feature = Feature.fromGeometry(geometry, jsonObject); + properties.toMap() + val map: Map = state.toMap() return FeaturesetFeature( id?.toFLTFeaturesetFeatureId(), diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt index 7eb4b3080..a8ca0e638 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt @@ -1,7 +1,10 @@ package com.mapbox.maps.mapbox_maps import android.content.Context +import android.util.Log import com.google.gson.Gson +import com.google.gson.JsonObject +import com.mapbox.bindgen.Value import com.mapbox.common.toValue import com.mapbox.geojson.Feature import com.mapbox.geojson.Point @@ -9,8 +12,14 @@ import com.mapbox.maps.MapView import com.mapbox.maps.MapboxDelicateApi import com.mapbox.maps.MapboxExperimental import com.mapbox.maps.MapboxMap +import com.mapbox.maps.RenderedQueryGeometry +import com.mapbox.maps.ScreenBox +import com.mapbox.maps.ScreenCoordinate import com.mapbox.maps.TileCacheBudget import com.mapbox.maps.extension.observable.eventdata.MapLoadingErrorEventData +import com.mapbox.maps.extension.style.expressions.generated.Expression +import com.mapbox.maps.interactions.FeatureState +import com.mapbox.maps.interactions.FeatureStateKey import com.mapbox.maps.interactions.TypedFeaturesetDescriptor import com.mapbox.maps.mapbox_maps.pigeons.CanonicalTileID import com.mapbox.maps.mapbox_maps.pigeons.ConstrainMode @@ -35,6 +44,7 @@ import com.mapbox.maps.mapbox_maps.pigeons._MapInterface import com.mapbox.maps.mapbox_maps.pigeons._MapWidgetDebugOptions import com.mapbox.maps.mapbox_maps.pigeons._RenderedQueryGeometry import com.mapbox.maps.plugin.delegates.listeners.OnMapLoadErrorListener +import org.json.JSONObject class MapInterfaceController( private val mapboxMap: MapboxMap, @@ -182,13 +192,18 @@ class MapInterfaceController( ) { mapboxMap.queryRenderedFeatures( geometry.toRenderedQueryGeometry(context), - targets.map { target -> target.toFeaturesetQueryTarget() }) { + 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() + it.value!!.map { feature -> + feature.toFLTQueriedRenderedFeature() + }.toMutableList() ) ) } @@ -202,67 +217,38 @@ class MapInterfaceController( filter: String?, callback: (Result>) -> Unit ) { - featureset.featuresetId?.let { - mapboxMap.queryRenderedFeatures( - geometry.toRenderedQueryGeometry(context), - TypedFeaturesetDescriptor.Featureset( - featureset.featuresetId, featureset.importId - ), - filter.toValue() - ) { - callback( - Result.success( - it.map { feature -> feature.toFltFeaturesetFeature() }.toMutableList() - ) + mapboxMap.queryRenderedFeatures( + geometry.toRenderedQueryGeometry(context), + featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor<*, com.mapbox.maps.interactions.FeaturesetFeature>, + filter?.let { Expression.fromRaw(filter) } + ) { + callback( + Result.success( + it.map { feature -> feature.toFltFeaturesetFeature() }.toMutableList() ) - } - } ?: { - featureset.layerId?.let { it -> - mapboxMap.queryRenderedFeatures( - geometry.toRenderedQueryGeometry(context), - TypedFeaturesetDescriptor.Layer( - featureset.layerId - ), - filter.toValue() - ) { - callback( - Result.success( - it.map { feature -> feature.toFltFeaturesetFeature() }.toMutableList() - ) - ) - } - } + ) + } } + @OptIn(MapboxExperimental::class) override fun queryRenderedFeaturesInViewport( featureset: FeaturesetDescriptor, filter: String?, callback: (Result>) -> Unit ) { - TODO(reason = "convenience") - } - - fun queryRenderedFeaturesForGeometry( - geometry: _RenderedQueryGeometry, - targets: List, - callback: (Result>) -> Unit - ) { + val size = getSize() + val geometry = RenderedQueryGeometry(ScreenBox(ScreenCoordinate(0.0,0.0), ScreenCoordinate(size.width, size.height))) mapboxMap.queryRenderedFeatures( - geometry.toRenderedQueryGeometry(context), - targets.map { - it.toFeaturesetQueryTarget() - } + geometry, + featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor<*, com.mapbox.maps.interactions.FeaturesetFeature>, + filter?.let { Expression.fromRaw(filter) } ) { - if (it.isError) { - callback(Result.failure(Throwable(it.error))) - } else { - callback( - Result.success( - it.value!!.map { feature -> feature.toFLTQueriedRenderedFeature() }.toMutableList() - ) + callback( + Result.success( + it.map { feature -> feature.toFltFeaturesetFeature() }.toMutableList() ) - } + ) } } @@ -284,11 +270,26 @@ class MapInterfaceController( } } + @OptIn(MapboxExperimental::class) override fun querySourceFeaturesForFeatureset( target: FeaturesetQueryTarget, callback: (Result>) -> Unit ) { - TODO("Not yet implemented") + + 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( @@ -360,21 +361,34 @@ class MapInterfaceController( } } + @OptIn(MapboxExperimental::class, MapboxDelicateApi::class) override fun setFeatureStateForFeaturesetFeatureDescriptor( featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: Map, callback: (Result) -> Unit ) { - TODO("Not yet implemented") + val statee = state.toFeatureState(); + Log.d("jkl", statee.toString()); + mapboxMap.setFeatureState( + // TODO: Allow for TypedFeaturesetDescriptor.Layer + featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor.Featureset, + featureId.toFeaturesetFeatureId(), + statee + ) { callback(Result.success(Unit)) } } + @OptIn(MapboxExperimental::class) override fun setFeatureStateForFeaturesetFeature( feature: FeaturesetFeature, state: Map, callback: (Result) -> Unit ) { - TODO("Not yet implemented") + val mapsFeature = feature.toFeaturesetFeature() + mapboxMap.setFeatureState( + mapsFeature, + state.toFeatureState() + ) { callback(Result.success(Unit)) } } override fun getFeatureState( @@ -394,19 +408,38 @@ class MapInterfaceController( } } + @OptIn(MapboxExperimental::class, MapboxDelicateApi::class) override fun getFeatureStateForFeaturesetDescriptor( featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, callback: (Result>) -> Unit ) { - TODO("Not yet implemented") + mapboxMap.getFeatureState( + featureset.toTypedFeaturesetDescriptor(), + featureId.toFeaturesetFeatureId() + ) { + callback( + Result.success( + it.toMap() + ) + ) + } } + @OptIn(MapboxExperimental::class) override fun getFeatureStateForFeaturesetFeature( feature: FeaturesetFeature, callback: (Result>) -> Unit ) { - TODO("Not yet implemented") + mapboxMap.getFeatureState( + feature.toFeaturesetFeature() + ) { + callback( + Result.success( + it.toMap() + ) + ) + } } override fun removeFeatureState( @@ -425,28 +458,60 @@ class MapInterfaceController( } } + @OptIn(MapboxExperimental::class, MapboxDelicateApi::class) override fun removeFeatureStateForFeaturesetFeatureDescriptor( featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String, callback: (Result) -> Unit ) { - TODO("Not yet implemented") + mapboxMap.removeFeatureState( + // TODO: Allow for TypedFeaturesetDescriptor.Layer + featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor.Featureset, + featureId.toFeaturesetFeatureId(), + FeatureStateKey.create(stateKey) + ) { + if (it.isError) { + callback(Result.failure(Throwable(it.error))) + } else { + callback(Result.success(Unit)) + } + } } + @OptIn(MapboxExperimental::class) override fun removeFeatureStateForFeaturesetFeature( feature: FeaturesetFeature, stateKey: String, callback: (Result) -> Unit ) { - TODO("Not yet implemented") + mapboxMap.removeFeatureState( + feature.toFeaturesetFeature(), + FeatureStateKey.create(stateKey) + ) { + if (it.isError) { + callback(Result.failure(Throwable(it.error))) + } else { + callback(Result.success(Unit)) + } + } } + @OptIn(MapboxExperimental::class) override fun resetFeatureStatesForFeatureset( featureset: FeaturesetDescriptor, callback: (Result) -> Unit ) { - TODO("Not yet implemented") + mapboxMap.resetFeatureStates( + // TODO: Allow for TypedFeaturesetDescriptor.Layer + featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor.Featureset, + ) { + if (it.isError) { + callback(Result.failure(Throwable(it.error))) + } else { + callback(Result.success(Unit)) + } + } } override fun reduceMemoryUse() { diff --git a/example/integration_test/interactive_features_test.dart b/example/integration_test/interactive_features_test.dart index 389fd2982..09259a407 100644 --- a/example/integration_test/interactive_features_test.dart +++ b/example/integration_test/interactive_features_test.dart @@ -47,20 +47,19 @@ void main() { filter: filter); expect(featuresetFilterQuery.length, 1); - expect( - featuresetFilterQuery[0].geoJSONFeature.properties?["name"], "nest1"); + expect(featuresetFilterQuery[0].geoJSONFeature.properties?["name"], "nest1"); expect(featuresetFilterQuery[0].geoJSONFeature.properties?["class"], "poi"); // test queryRenderedFeaturesInViewport - var viewportQuery = await mapboxMap.queryRenderedFeaturesInViewport( - featureset: - FeaturesetDescriptor(featuresetId: "poi", importId: "nested")); - - expect(viewportQuery.length, 3); - expect(viewportQuery[0].geoJSONFeature.properties?["name"], "nest2"); - expect(viewportQuery[1].geoJSONFeature.properties?["name"], "nest1"); - expect(viewportQuery[2].geoJSONFeature.properties?["name"], "nest3"); - expect(viewportQuery[2].geoJSONFeature.properties?["class"], "poi"); + // var viewportQuery = await mapboxMap.queryRenderedFeaturesInViewport( + // featureset: + // FeaturesetDescriptor(featuresetId: "poi", importId: "nested")); + + // expect(viewportQuery.length, 3); + // expect(viewportQuery[0].geoJSONFeature.properties?["name"], "nest2"); + // expect(viewportQuery[1].geoJSONFeature.properties?["name"], "nest1"); + // expect(viewportQuery[2].geoJSONFeature.properties?["name"], "nest3"); + // expect(viewportQuery[2].geoJSONFeature.properties?["class"], "poi"); }); testWidgets('test_featurestate_methods', (WidgetTester tester) async { @@ -104,7 +103,7 @@ void main() { expect(returnedFeatureState2, {}); // test reset featurestate - await Future.delayed(Duration(seconds: 1)); + await Future.delayed(Duration(seconds: 5)); await mapboxMap.setFeatureStateForFeaturesetFeature(feature, state); var returnedFeatureState3 = await mapboxMap.getFeatureStateForFeaturesetFeature(feature); @@ -140,6 +139,8 @@ void main() { "highlight": true, }; + await Future.delayed(Duration(seconds: 1)); + // test set and get featurestate await mapboxMap.setFeatureStateForFeaturesetFeatureDescriptor( featuresetDescriptor, featuresetID, state); @@ -157,70 +158,70 @@ void main() { expect(returnedFeatureState2, {}); }); - testWidgets('test_state_is_queried', (WidgetTester tester) async { - // load style and position camera - final mapFuture = app.main( - width: 200, - height: 200, - camera: - CameraOptions(center: Point(coordinates: Position(0, 0)), zoom: 10), - alignment: Alignment(100, 100)); - await tester.pumpAndSettle(); - final mapboxMap = await mapFuture; - var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); - mapboxMap.style.setStyleJSON(styleJson); - - await app.events.onMapLoaded.future; - - var featuresetID = FeaturesetFeatureId(id: "11", namespace: "A"); - var featuresetDescriptor = - FeaturesetDescriptor(featuresetId: "poi", importId: "nested"); - Map state = { - "hide": true, - }; - var filter = '["==",["get", "type"], "A"]'; - Map expectedProperties = { - "name": "nest1", - "type": "A", - "class": "poi" - }; - - await mapboxMap.setFeatureStateForFeaturesetFeatureDescriptor( - featuresetDescriptor, featuresetID, state); - var queryResult = await mapboxMap.queryRenderedFeaturesInViewport( - featureset: featuresetDescriptor, filter: filter); - var poi = queryResult.first; - var point = Point.fromJson(poi.geoJSONFeature.geometry!.toJson()); - - expect(queryResult.length, 1); - expect(poi.id?.id, featuresetID.id); - expect(poi.id?.namespace, featuresetID.namespace); - expect(poi.state, state); - expect(point.coordinates.lat, closeTo(0.01, 0.05)); - expect(point.coordinates.lng, closeTo(0.01, 0.05)); - expect(poi.geoJSONFeature.properties, expectedProperties); - }); - - testWidgets('test_getFeaturesets', (WidgetTester tester) async { - // load style and position camera - final mapFuture = app.main( - width: 200, - height: 200, - camera: - CameraOptions(center: Point(coordinates: Position(0, 0)), zoom: 10), - alignment: Alignment(100, 100)); - await tester.pumpAndSettle(); - final mapboxMap = await mapFuture; - var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); - mapboxMap.style.setStyleJSON(styleJson); - - await app.events.onMapLoaded.future; - - var returnedFeaturesets = await mapboxMap.style.getFeaturesets(); - - expect(returnedFeaturesets.length, 1); - expect(returnedFeaturesets.first.importId, "nested"); - }); + // testWidgets('test_state_is_queried', (WidgetTester tester) async { + // // load style and position camera + // final mapFuture = app.main( + // width: 200, + // height: 200, + // camera: + // CameraOptions(center: Point(coordinates: Position(0, 0)), zoom: 10), + // alignment: Alignment(100, 100)); + // await tester.pumpAndSettle(); + // final mapboxMap = await mapFuture; + // var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); + // mapboxMap.style.setStyleJSON(styleJson); + + // await app.events.onMapLoaded.future; + + // var featuresetID = FeaturesetFeatureId(id: "11", namespace: "A"); + // var featuresetDescriptor = + // FeaturesetDescriptor(featuresetId: "poi", importId: "nested"); + // Map state = { + // "hide": true, + // }; + // var filter = '["==",["get", "type"], "A"]'; + // Map expectedProperties = { + // "name": "nest1", + // "type": "A", + // "class": "poi" + // }; + + // await mapboxMap.setFeatureStateForFeaturesetFeatureDescriptor( + // featuresetDescriptor, featuresetID, state); + // var queryResult = await mapboxMap.queryRenderedFeaturesInViewport( + // featureset: featuresetDescriptor, filter: filter); + // var poi = queryResult.first; + // var point = Point.fromJson(poi.geoJSONFeature.geometry!.toJson()); + + // expect(queryResult.length, 1); + // expect(poi.id?.id, featuresetID.id); + // expect(poi.id?.namespace, featuresetID.namespace); + // expect(poi.state, state); + // expect(point.coordinates.lat, closeTo(0.01, 0.05)); + // expect(point.coordinates.lng, closeTo(0.01, 0.05)); + // expect(poi.geoJSONFeature.properties, expectedProperties); + // }); + + // testWidgets('test_getFeaturesets', (WidgetTester tester) async { + // // load style and position camera + // final mapFuture = app.main( + // width: 200, + // height: 200, + // camera: + // CameraOptions(center: Point(coordinates: Position(0, 0)), zoom: 10), + // alignment: Alignment(100, 100)); + // await tester.pumpAndSettle(); + // final mapboxMap = await mapFuture; + // var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); + // mapboxMap.style.setStyleJSON(styleJson); + + // await app.events.onMapLoaded.future; + + // var returnedFeaturesets = await mapboxMap.style.getFeaturesets(); + + // expect(returnedFeaturesets.length, 1); + // expect(returnedFeaturesets.first.importId, "nested"); + // }); testWidgets('test_query_featureset_target', (WidgetTester tester) async { // load style and position camera @@ -263,14 +264,14 @@ void main() { expect(returnedQuery[0]?.queryTargets?.last.id, 2); expect(returnedQuery[0]?.queryTargets?.last.featureset.layerId, "circle-2"); expect(returnedQuery[0]?.queryTargets?.last.filter, null); - expect(firstFeature.id, 2); + expect(firstFeature.id, "2"); expect(firstFeature.properties?["name"], "qux"); expect(returnedQuery[1]?.queryTargets?.length, 1); expect(returnedQuery[1]?.queryTargets?.last.id, 1); expect(returnedQuery[1]?.queryTargets?.last.featureset.featuresetId, "poi"); expect(returnedQuery[1]?.queryTargets?.last.featureset.importId, "nested"); expect(returnedQuery[1]?.queryTargets?.last.filter, null); - expect(secondFeature.id, 12); + expect(secondFeature.id, "12"); expect(secondFeature.properties?["class"], "poi"); expect(secondFeature.properties?["name"], "nest2"); }); diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift index bb105a8ea..e332f5eac 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift @@ -233,7 +233,11 @@ extension FeaturesetQueryTarget { extension FeaturesetDescriptor { func toMapFeaturesetDescriptor() -> MapboxMaps.FeaturesetDescriptor { - return MapboxMaps.FeaturesetDescriptor(featuresetId2: featuresetId, importId2: importId, layerId2: layerId) + if let featuresetId { + return MapboxMaps.FeaturesetDescriptor.featureset(featuresetId, importId: importId) + } else { + return MapboxMaps.FeaturesetDescriptor.layer(layerId ?? "layer") + } } } diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index c3a5aadef..31b1d10af 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -455,7 +455,7 @@ class MapboxMap extends ChangeNotifier { Future> queryRenderedFeaturesForFeatureset( {required RenderedQueryGeometry geometry, required FeaturesetDescriptor featureset, - String? filter = null}) async => + String? filter}) async => _mapInterface.queryRenderedFeaturesForFeatureset( _RenderedQueryGeometry(value: geometry.value, type: geometry.type), featureset, @@ -464,7 +464,7 @@ class MapboxMap extends ChangeNotifier { /// Queries all rendered features in current viewport, using one typed featureset. Future> queryRenderedFeaturesInViewport( {required FeaturesetDescriptor featureset, - String? filter = null}) async => + String? filter}) async => _mapInterface.queryRenderedFeaturesInViewport(featureset, filter); /// Queries the map for source features. diff --git a/lib/src/turf_adapters.dart b/lib/src/turf_adapters.dart index 08a22ee89..aeae7e345 100644 --- a/lib/src/turf_adapters.dart +++ b/lib/src/turf_adapters.dart @@ -74,8 +74,7 @@ final class Feature extends turf.Feature { static Feature decode(Object result) { result as List; - var valid = convertToValidMap(result.first as Map); - return Feature.fromJson(valid); + return Feature.fromJson(jsonDecode(result.first as String)); } factory Feature.fromJson(Map json) { @@ -92,35 +91,8 @@ final class Feature extends turf.Feature { var valid = convertToValidMap(feature as Map); return Feature.fromJson(valid); } - - // factory Feature.fromQueriedFeature(QueriedFeature queriedFeature) { - // final feature = - // queriedFeature.feature.convertToValidMap(queriedFeature.feature); - // // print(queriedFeature.feature); - // // print(feature); - // return Feature.fromJson(feature); - // } } -// extension on List { -// Map convertToValidMap(List input) { -// return -// return input.map((key, value) { -// // print(value.runtimeType); -// if (key is! String) { -// throw Exception( -// "Invalid key type. Expected String but got ${key.runtimeType}"); -// } -// if (value is Map) { -// // Recursively convert nested maps -// return MapEntry(key, convertToValidMap(value)); -// } -// return MapEntry(key, value); -// }); -// } -// } - -//extension on Map { Map convertToValidMap(Map input) { return input.map((key, value) { if (key is! String) { @@ -134,6 +106,5 @@ Map convertToValidMap(Map input) { return MapEntry(key, value); }); } -//} typedef JSONObject = Map; From 3edbcfa9c7a211d3a8d612c6808aa819c50f9df0 Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Thu, 24 Oct 2024 22:37:44 -0400 Subject: [PATCH 04/28] Linting updates --- .../com/mapbox/maps/mapbox_maps/Extentions.kt | 6 ++---- .../maps/mapbox_maps/MapInterfaceController.kt | 18 ++++++------------ .../Classes/Extensions.swift | 3 +-- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt index 1c94c97d9..8952d2c0f 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt @@ -3,7 +3,6 @@ package com.mapbox.maps.mapbox_maps import android.annotation.SuppressLint import android.content.Context import android.graphics.Bitmap -import android.util.Log import com.google.gson.Gson import com.google.gson.JsonObject import com.google.gson.JsonParser @@ -18,7 +17,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.dsl.generated.id 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 @@ -605,8 +603,8 @@ fun com.mapbox.maps.interactions.FeatureState.toMap(): Map { @OptIn(MapboxExperimental::class) fun com.mapbox.maps.interactions.FeaturesetFeature.toFltFeaturesetFeature(): FeaturesetFeature { // TODO: need access to original feature or better conversion to JsonObject from JSONObject - val jsonObject: JsonObject = JsonParser.parseString(properties.toString()).getAsJsonObject(); - val feature = Feature.fromGeometry(geometry, jsonObject); + val jsonObject: JsonObject = JsonParser.parseString(properties.toString()).getAsJsonObject() + val feature = Feature.fromGeometry(geometry, jsonObject) properties.toMap() val map: Map = state.toMap() diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt index a8ca0e638..08af44da8 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt @@ -3,8 +3,6 @@ package com.mapbox.maps.mapbox_maps import android.content.Context import android.util.Log import com.google.gson.Gson -import com.google.gson.JsonObject -import com.mapbox.bindgen.Value import com.mapbox.common.toValue import com.mapbox.geojson.Feature import com.mapbox.geojson.Point @@ -44,7 +42,6 @@ import com.mapbox.maps.mapbox_maps.pigeons._MapInterface import com.mapbox.maps.mapbox_maps.pigeons._MapWidgetDebugOptions import com.mapbox.maps.mapbox_maps.pigeons._RenderedQueryGeometry import com.mapbox.maps.plugin.delegates.listeners.OnMapLoadErrorListener -import org.json.JSONObject class MapInterfaceController( private val mapboxMap: MapboxMap, @@ -227,7 +224,6 @@ class MapInterfaceController( it.map { feature -> feature.toFltFeaturesetFeature() }.toMutableList() ) ) - } } @@ -238,7 +234,7 @@ class MapInterfaceController( callback: (Result>) -> Unit ) { val size = getSize() - val geometry = RenderedQueryGeometry(ScreenBox(ScreenCoordinate(0.0,0.0), ScreenCoordinate(size.width, size.height))) + val geometry = RenderedQueryGeometry(ScreenBox(ScreenCoordinate(0.0, 0.0), ScreenCoordinate(size.width, size.height))) mapboxMap.queryRenderedFeatures( geometry, featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor<*, com.mapbox.maps.interactions.FeaturesetFeature>, @@ -275,16 +271,16 @@ class MapInterfaceController( target: FeaturesetQueryTarget, callback: (Result>) -> Unit ) { - mapboxMap.querySourceFeatures( target.featureset.toTypedFeaturesetDescriptor(), target.filter?.let { Expression.fromRaw(target.filter) }, - target.id) { + target.id + ) { if (it.isError) { callback(Result.failure(Throwable(it.error))) } else { - callback( - Result.success( + callback ( + Result.success ( it.value!!.map { feature -> feature.toFLTQueriedSourceFeature() }.toMutableList() ) ) @@ -368,13 +364,11 @@ class MapInterfaceController( state: Map, callback: (Result) -> Unit ) { - val statee = state.toFeatureState(); - Log.d("jkl", statee.toString()); mapboxMap.setFeatureState( // TODO: Allow for TypedFeaturesetDescriptor.Layer featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor.Featureset, featureId.toFeaturesetFeatureId(), - statee + state.toFeatureState() ) { callback(Result.success(Unit)) } } diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift index e332f5eac..c3818ceaa 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift @@ -574,8 +574,7 @@ extension CGPoint { } } -extension MapboxMaps.MapContentGestureContext { - +extension MapboxMaps.InteractionContext { func toFLTMapContentGestureContext() -> MapContentGestureContext { MapContentGestureContext( touchPosition: point.toFLTScreenCoordinate(), From b06416ea7c112cc62f34d002a1ca20e90bb9f5d8 Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Thu, 24 Oct 2024 22:43:43 -0400 Subject: [PATCH 05/28] Additional lint edits --- .../com/mapbox/maps/mapbox_maps/MapInterfaceController.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt index 08af44da8..9894a774b 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt @@ -1,7 +1,6 @@ package com.mapbox.maps.mapbox_maps import android.content.Context -import android.util.Log import com.google.gson.Gson import com.mapbox.common.toValue import com.mapbox.geojson.Feature @@ -279,8 +278,8 @@ class MapInterfaceController( if (it.isError) { callback(Result.failure(Throwable(it.error))) } else { - callback ( - Result.success ( + callback( + Result.success( it.value!!.map { feature -> feature.toFLTQueriedSourceFeature() }.toMutableList() ) ) From 9400d40efe855a589992d7752d98de9988088dd3 Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Fri, 25 Oct 2024 17:02:51 -0400 Subject: [PATCH 06/28] Clean up type conversions, restore tests --- .../com/mapbox/maps/mapbox_maps/Extentions.kt | 64 ++++-- .../mapbox_maps/MapInterfaceController.kt | 23 +-- .../maps/mapbox_maps/StyleController.kt | 2 +- .../maps/mapbox_maps/pigeons/MapInterfaces.kt | 129 +++++------- .../interactive_features_test.dart | 192 +++++++++--------- example/lib/interactive_features_example.dart | 8 +- .../Classes/Extensions.swift | 10 +- .../Classes/Generated/MapInterfaces.swift | 168 +++++---------- .../Classes/TurfAdapters.swift | 6 +- lib/mapbox_maps_flutter.dart | 1 - lib/src/mapbox_map.dart | 3 +- lib/src/pigeons/map_interfaces.dart | 119 ++++------- lib/src/style/mapbox_styles.dart | 5 +- lib/src/turf_adapters.dart | 35 +--- 14 files changed, 318 insertions(+), 447 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt index 8952d2c0f..d96696c66 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt @@ -322,19 +322,20 @@ fun Map.toFeatureState(): com.mapbox.maps.interactions.FeatureStat @OptIn(MapboxExperimental::class) @SuppressLint("RestrictedApi") fun FeaturesetFeature.toFeaturesetFeature(): com.mapbox.maps.interactions.FeaturesetFeature { + val jsonObject: JsonObject = JsonParser.parseString(properties.toString()).getAsJsonObject() featureset.featuresetId?.let { return com.mapbox.maps.interactions.FeaturesetFeature( id?.toFeaturesetFeatureId(), featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor.Featureset, state.toFeatureState(), - geoJSONFeature + Feature.fromGeometry(geometry.toGeometry(), jsonObject) ) } return com.mapbox.maps.interactions.FeaturesetFeature( id?.toFeaturesetFeatureId(), featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor.Layer, state.toFeatureState(), - geoJSONFeature + Feature.fromGeometry(geometry.toGeometry(), jsonObject) ) } @@ -454,6 +455,38 @@ fun CameraBoundsOptions.toCameraBoundsOptions(): com.mapbox.maps.CameraBoundsOpt .minZoom(minZoom) .build() +fun Geometry.toMap(): Map { + return when (this) { + is Point -> mapOf( + "coordinates" to listOf(this.latitude(), this.longitude()) + ) + is LineString -> mapOf( + "coordinates" to this.coordinates().map { listOf(it.latitude(), it.longitude()) } + ) + is Polygon -> mapOf( + "coordinates" to this.coordinates().map { ring -> + ring.map { listOf(it.latitude(), it.longitude()) } + } + ) + is MultiPoint -> mapOf( + "coordinates" to this.coordinates().map { listOf(it.latitude(), it.longitude()) } + ) + is MultiLineString -> mapOf( + "coordinates" to this.coordinates().map { line -> + line.map { listOf(it.latitude(), it.longitude()) } + } + ) + is MultiPolygon -> mapOf( + "coordinates" to this.coordinates().map { polygon -> + polygon.map { ring -> + ring.map { listOf(it.latitude(), it.longitude()) } + } + } + ) + else -> throw IllegalArgumentException("Unsupported geometry type") + } +} + fun Map.toGeometry(): Geometry { when { this["type"] == "Point" -> { @@ -592,27 +625,15 @@ fun com.mapbox.maps.FeaturesetDescriptor.toFLTFeaturesetDescriptor(): Featureset return FeaturesetDescriptor(featuresetId, importId, layerId) } -@OptIn(MapboxExperimental::class) -fun com.mapbox.maps.interactions.FeatureState.toMap(): Map { - return JSONObject(this.asJsonString()).toMap() - .filterKeys { it != null } // Filter out null keys - .mapKeys { it.key!! } -} - @SuppressLint("RestrictedApi") @OptIn(MapboxExperimental::class) -fun com.mapbox.maps.interactions.FeaturesetFeature.toFltFeaturesetFeature(): FeaturesetFeature { -// TODO: need access to original feature or better conversion to JsonObject from JSONObject - val jsonObject: JsonObject = JsonParser.parseString(properties.toString()).getAsJsonObject() - val feature = Feature.fromGeometry(geometry, jsonObject) - properties.toMap() - val map: Map = state.toMap() - +fun com.mapbox.maps.interactions.FeaturesetFeature.toFLTFeaturesetFeature(): FeaturesetFeature { return FeaturesetFeature( id?.toFLTFeaturesetFeatureId(), descriptor.toFeaturesetDescriptor().toFLTFeaturesetDescriptor(), - feature, - map + geometry.toMap(), + properties.toFilteredMap(), + JSONObject(state.asJsonString()).toFilteredMap() ) } @@ -706,6 +727,13 @@ fun JSONObject.toMap(): Map = keys().asSequence().associateWith { } } +@OptIn(MapboxExperimental::class) +fun JSONObject.toFilteredMap(): Map { + return this.toMap() + .filterKeys { it != null } // Filter out null keys + .mapKeys { it.key!! } +} + fun Number.toLogicalPixels(context: Context): Double { return this.toDouble() / context.resources.displayMetrics.density } diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt index 9894a774b..3e2690f46 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt @@ -41,6 +41,7 @@ import com.mapbox.maps.mapbox_maps.pigeons._MapInterface import com.mapbox.maps.mapbox_maps.pigeons._MapWidgetDebugOptions import com.mapbox.maps.mapbox_maps.pigeons._RenderedQueryGeometry import com.mapbox.maps.plugin.delegates.listeners.OnMapLoadErrorListener +import org.json.JSONObject class MapInterfaceController( private val mapboxMap: MapboxMap, @@ -220,7 +221,7 @@ class MapInterfaceController( ) { callback( Result.success( - it.map { feature -> feature.toFltFeaturesetFeature() }.toMutableList() + it.map { feature -> feature.toFLTFeaturesetFeature() }.toMutableList() ) ) } @@ -232,8 +233,7 @@ class MapInterfaceController( filter: String?, callback: (Result>) -> Unit ) { - val size = getSize() - val geometry = RenderedQueryGeometry(ScreenBox(ScreenCoordinate(0.0, 0.0), ScreenCoordinate(size.width, size.height))) + 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>, @@ -241,7 +241,7 @@ class MapInterfaceController( ) { callback( Result.success( - it.map { feature -> feature.toFltFeaturesetFeature() }.toMutableList() + it.map { feature -> feature.toFLTFeaturesetFeature() }.toMutableList() ) ) } @@ -364,8 +364,7 @@ class MapInterfaceController( callback: (Result) -> Unit ) { mapboxMap.setFeatureState( - // TODO: Allow for TypedFeaturesetDescriptor.Layer - featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor.Featureset, + featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor>, featureId.toFeaturesetFeatureId(), state.toFeatureState() ) { callback(Result.success(Unit)) } @@ -413,7 +412,7 @@ class MapInterfaceController( ) { callback( Result.success( - it.toMap() + JSONObject(it.asJsonString()).toFilteredMap() ) ) } @@ -429,7 +428,7 @@ class MapInterfaceController( ) { callback( Result.success( - it.toMap() + JSONObject(it.asJsonString()).toFilteredMap() ) ) } @@ -459,8 +458,7 @@ class MapInterfaceController( callback: (Result) -> Unit ) { mapboxMap.removeFeatureState( - // TODO: Allow for TypedFeaturesetDescriptor.Layer - featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor.Featureset, + featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor>, featureId.toFeaturesetFeatureId(), FeatureStateKey.create(stateKey) ) { @@ -496,9 +494,8 @@ class MapInterfaceController( callback: (Result) -> Unit ) { mapboxMap.resetFeatureStates( - // TODO: Allow for TypedFeaturesetDescriptor.Layer - featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor.Featureset, - ) { + featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor>, + ) { if (it.isError) { callback(Result.failure(Throwable(it.error))) } else { diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/StyleController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/StyleController.kt index a9dd1314e..7485a0167 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/StyleController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/StyleController.kt @@ -590,7 +590,7 @@ class StyleController(private val context: Context, private val styleManager: Ma } override fun getFeaturesets(): List { - TODO("no implemented on Android") + return styleManager.styleManager.styleFeaturesets.map { it.toFLTFeaturesetDescriptor() } } override fun addStyleImage( diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt index 13c773b77..3c306965c 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt @@ -1353,27 +1353,22 @@ data class QueriedFeature( } /** - * Represents a unique identifier for a feature in one exported featureset or a layer. + * Identifies a feature in a featureset. + * + * Knowing the feature identifier allows to set the feature states to a particular feature, see ``MapboxMap/setFeatureState(featureset:featureId:state:callback:)``. + * + * In a featureset a feature can come from different underlying sources. In that case their IDs are not guaranteed to be unique in the featureset. + * The ``FeaturesetFeatureId/namespace`` is used to disambiguate from which source the feature is coming. + * + * - Warning: There is no guarantee of identifier persistency. This depends on the underlying source of the features and may vary from style to style. + * If you want to store the identifiers persistently, please make sure that the style or source provides this guarantee. * * Generated class from Pigeon that represents data sent in messages. */ data class FeaturesetFeatureId( - /** - * The `featureId` uniquely identifies a feature within a featureset or layer. - * Note: The Identifier of the feature is not guaranteed to be persistent - * and can change depending on the source that is used. - */ + /** A feature id coming from the feature itself.exp */ val id: String, - /** - * An optional field that represents the feature namespace defined by - * the Selector within a Featureset to which this feature belongs. - * If the underlying source is the same for multiple selectors within a Featureset, - * the same `featureNamespace` should be used across those selectors. - * This practice ensures the uniqueness of `FeaturesetFeatureId` across the style. - * Defining a `featureNamespace` value for the Selector is recommended, - * especially when multiple selectors exist in a Featureset, - * as it can enhance the efficiency of feature operations. - */ + /** A namespace of the feature */ val namespace: String? = null ) { companion object { @@ -1392,7 +1387,10 @@ data class FeaturesetFeatureId( } /** - * Represents an identifier for a single exported featureset or a layer. + * A featureset descriptor. + * + * The descriptor instance acts as a universal target for interactions or querying rendered features (see + * ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)``). * * Generated class from Pigeon that represents data sent in messages. */ @@ -1438,29 +1436,30 @@ data class FeaturesetDescriptor( } } -/** Generated class from Pigeon that represents data sent in messages. */ +/** + * A basic feature of a featureset. + * + * The featureset feature is different to the `Turf.Feature`. The latter represents any GeoJSON feature, while the former is a high level representation of features. + * + * Generated class from Pigeon that represents data sent in messages. + */ data class FeaturesetFeature( /** - * Optional identifier holding feature id and feature namespace. - * It could be NULL when underlying [Feature.id] is null. + * An identifier of the feature. + * + * The identifier can be `nil` if the underlying source doesn't have identifiers for features. + * In this case it's impossible to set a feature state for an individual feature. */ val id: FeaturesetFeatureId? = null, - /** - * The [TypedFeaturesetDescriptor] this concrete feature comes from. - * List of supported featuresets could be found in the nested classes - * (e.g. [TypedFeaturesetDescriptor.Featureset], [TypedFeaturesetDescriptor.Layer], etc). - */ + /** A featureset descriptor denoting a featureset this feature belongs to. */ val featureset: FeaturesetDescriptor, + val geometry: Map, + val properties: Map, /** - * A feature geometry. - * Feature JSON properties. - * The geoJSON feature. - */ - val geoJSONFeature: Feature, - /** - * Current feature state stored as a concrete instance of [FeatureState]. - * Important: this state is immutable and represents the feature state - * at the precise moment of the interaction callback. + * A feature state. + * + * This is a **snapshot** of the state that the feature had when it was interacted with. + * To update and read the original state, use ``MapboxMap/setFeatureState()`` and ``MapboxMap/getFeatureState()``. */ val state: Map ) { @@ -1468,16 +1467,18 @@ data class FeaturesetFeature( fun fromList(pigeonVar_list: List): FeaturesetFeature { val id = pigeonVar_list[0] as FeaturesetFeatureId? val featureset = pigeonVar_list[1] as FeaturesetDescriptor - val geoJSONFeature = pigeonVar_list[2] as Feature - val state = pigeonVar_list[3] as Map - return FeaturesetFeature(id, featureset, geoJSONFeature, state) + val geometry = pigeonVar_list[2] as Map + val properties = pigeonVar_list[3] as Map + val state = pigeonVar_list[4] as Map + return FeaturesetFeature(id, featureset, geometry, properties, state) } } fun toList(): List { return listOf( id, featureset, - geoJSONFeature, + geometry, + properties, state, ) } @@ -1489,11 +1490,11 @@ data class FeaturesetFeature( * Generated class from Pigeon that represents data sent in messages. */ data class FeaturesetQueryTarget( - /** The FeaturesetDescriptor that specifies the featureset to be included in the query. */ + /** A `FeaturesetDescriptor` that specifies the featureset to be included in the query. */ val featureset: FeaturesetDescriptor, /** An optional filter expression used to refine the query results based on conditions related to the specified featureset. */ val filter: String? = null, - /** An optional unique identifier associated with the FeaturesetQueryTarget. */ + /** An optional unique identifier associated with the target. */ val id: Long? = null ) { companion object { @@ -3348,28 +3349,10 @@ interface _MapInterface { * This method allows to query both featureset from imported styles and user layers in the root style. * The results can be additionally filtered per-featureset. * - * ```dart - * let targets = [ - * FeaturesetQueryTarget( - * featureset: .layer("my-layer"), - * filter: Exp(.eq) { - * Exp(.get) { "type" } - * "hotel" - * } - * ), - * FeaturesetQueryTarget(featureset: .featureset("poi", importId: "basemap")) - * ] - * mapView.mapboxMap.queryRenderedFeatures(with: CGPoint(x: 0, y: 0), - * targets: targets) { result in - * // handle features in result - * } - * ``` - * - * - Important: This is a low-level method. If you need to handle basic gestures on map content, please prefer to use Interactions API (see ``MapboxMap/addInteraction(_:)``) or ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)``. + * - Important: This is a low-level method. If you need to handle basic gestures on map content, please prefer ``MapboxMap/ queryRenderedFeaturesForFeatureset()``. * * @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. * @param targets An array of targets to query with. - * @param completion Callback called when the query completes. */ fun queryRenderedFeaturesForTargets(geometry: _RenderedQueryGeometry, targets: List, callback: (Result>) -> Unit) /** @@ -3377,32 +3360,18 @@ interface _MapInterface { * * The results array will contain features of the type specified by this featureset. * - * ```swift - * mapView.mapboxMap.queryRenderedFeatures( - * with: CGPoint(x: 0, y: 0), - * featureset: .standardBuildings) { result in - * // handle buildings in result - * } - * ``` - * - * - Important: If you need to handle basic gestures on map content, please prefer to use Interactions API, see ``MapboxMap/addInteraction(_:)``. - * * @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. * @param featureset A typed featureset to query with. * @param filter An additional filter for features. - * @param completion Callback called when the query completes. */ fun queryRenderedFeaturesForFeatureset(geometry: _RenderedQueryGeometry, featureset: FeaturesetDescriptor, filter: String?, callback: (Result>) -> Unit) /** * Queries all rendered features in current viewport, using one typed featureset. * - * This is same as ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)`` called with geometry matching the current viewport. - * - * - Important: If you need to handle basic gestures on map content, please prefer to use Interactions API, see ``MapboxMap/addInteraction(_:)``. + * This is same as `MapboxMap/ queryRenderedFeaturesForFeatureset()`` called with geometry matching the current viewport. * * @param featureset A typed featureset to query with. * @param filter An additional filter for features. - * @param completion Callback called when the query completes. */ fun queryRenderedFeaturesInViewport(featureset: FeaturesetDescriptor, filter: String?, callback: (Result>) -> Unit) /** @@ -3417,7 +3386,6 @@ interface _MapInterface { * Queries the source features for a given featureset. * * @param target A featureset query target. - * @param completion Callback called when the query completes. */ fun querySourceFeaturesForFeatureset(target: FeaturesetQueryTarget, callback: (Result>) -> Unit) /** @@ -3482,7 +3450,6 @@ interface _MapInterface { * @param featureset The featureset to look the feature in. * @param featureId Identifier of the feature whose state should be updated. * @param state Map of entries to update with their respective new values - * @param callback The `feature state operation callback` called when the operation completes or ends. * * @return A `Cancelable` object that could be used to cancel the pending operation. */ @@ -3495,7 +3462,6 @@ interface _MapInterface { * * @param feature The feature to update. * @param state Map of entries to update with their respective new values - * @param callback The `feature state operation callback` called when the operation completes or ends. * * @return A `Cancelable` object that could be used to cancel the pending operation. */ @@ -3509,7 +3475,8 @@ interface _MapInterface { * @param sourceId The style source identifier. * @param sourceLayerId The style source layer identifier (for multi-layer sources such as vector sources). * @param featureId The feature identifier of the feature whose state should be queried. - * @param completion The `query feature state completion` called when the query completes. + * + * @return A `Cancelable` object that could be used to cancel the pending operation. */ fun getFeatureState(sourceId: String, sourceLayerId: String?, featureId: String, callback: (Result) -> Unit) /** @@ -3525,7 +3492,6 @@ interface _MapInterface { * Get the state map of a feature within a style source. * * @param feature An interactive feature to query the state from. - * @param completion Feature's state map or an empty map if the feature could not be found. * * @return A `Cancelable` object that could be used to cancel the pending query. */ @@ -3552,7 +3518,6 @@ interface _MapInterface { * @param featureset A featureset the feature belongs to. * @param featureId Identifier of the feature whose state should be removed. * @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. - * @param callback The `feature state operation callback` called when the operation completes or ends. * * @return A `Cancelable` object that could be used to cancel the pending operation. */ @@ -3563,7 +3528,6 @@ interface _MapInterface { * * @param feature An interactive feature to update. * @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. - * @param callback The `feature state operation callback` called when the operation completes or ends. * * @return A `Cancelable` object that could be used to cancel the pending operation. */ @@ -3572,10 +3536,9 @@ interface _MapInterface { * Reset all the feature states within a featureset. * * Note that updates to feature state are asynchronous, so changes made by this method might not be - * immediately visible using ``MapboxMap/getFeatureState(_:callback:)``. + * immediately visible using ``MapboxMap/getFeatureState()``. * * @param featureset A featureset descriptor - * @param callback The `feature state operation callback` called when the operation completes or ends. * * @return A `Cancelable` object that could be used to cancel the pending operation. */ diff --git a/example/integration_test/interactive_features_test.dart b/example/integration_test/interactive_features_test.dart index 09259a407..e76c9dca0 100644 --- a/example/integration_test/interactive_features_test.dart +++ b/example/integration_test/interactive_features_test.dart @@ -21,8 +21,7 @@ void main() { var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); mapboxMap.style.setStyleJSON(styleJson); - await app.events.onMapLoaded.future; - await Future.delayed(Duration(seconds: 1)); + await Future.delayed(Duration(seconds: 3)); // test queryRenderedFeaturesForFeatureset var coord = await mapboxMap @@ -33,9 +32,9 @@ void main() { FeaturesetDescriptor(featuresetId: "poi", importId: "nested")); expect(featuresetQuery.length, 2); - expect(featuresetQuery.first.geoJSONFeature.properties?["name"], "nest2"); - expect(featuresetQuery[1].geoJSONFeature.properties?["name"], "nest1"); - expect(featuresetQuery[0].geoJSONFeature.properties?["class"], "poi"); + expect(featuresetQuery.first.properties["name"], "nest2"); + expect(featuresetQuery[1].properties["name"], "nest1"); + expect(featuresetQuery[0].properties["class"], "poi"); // test queryRenderedFeaturesForFeatureset with filter var filter = '["==",["get", "type"], "A"]'; @@ -47,19 +46,19 @@ void main() { filter: filter); expect(featuresetFilterQuery.length, 1); - expect(featuresetFilterQuery[0].geoJSONFeature.properties?["name"], "nest1"); - expect(featuresetFilterQuery[0].geoJSONFeature.properties?["class"], "poi"); - - // test queryRenderedFeaturesInViewport - // var viewportQuery = await mapboxMap.queryRenderedFeaturesInViewport( - // featureset: - // FeaturesetDescriptor(featuresetId: "poi", importId: "nested")); - - // expect(viewportQuery.length, 3); - // expect(viewportQuery[0].geoJSONFeature.properties?["name"], "nest2"); - // expect(viewportQuery[1].geoJSONFeature.properties?["name"], "nest1"); - // expect(viewportQuery[2].geoJSONFeature.properties?["name"], "nest3"); - // expect(viewportQuery[2].geoJSONFeature.properties?["class"], "poi"); + expect(featuresetFilterQuery[0].properties["name"], "nest1"); + expect(featuresetFilterQuery[0].properties["class"], "poi"); + + //test queryRenderedFeaturesInViewport + var viewportQuery = await mapboxMap.queryRenderedFeaturesInViewport( + featureset: + FeaturesetDescriptor(featuresetId: "poi", importId: "nested")); + + expect(viewportQuery.length, 3); + expect(viewportQuery[0].properties["name"], "nest2"); + expect(viewportQuery[1].properties["name"], "nest1"); + expect(viewportQuery[2].properties["name"], "nest3"); + expect(viewportQuery[2].properties["class"], "poi"); }); testWidgets('test_featurestate_methods', (WidgetTester tester) async { @@ -77,13 +76,12 @@ void main() { await app.events.onMapLoaded.future; - var geoJSONFeature = Feature( - id: "feature", geometry: Point(coordinates: Position(0.01, 0.01))); var feature = FeaturesetFeature( id: FeaturesetFeatureId(id: "11", namespace: "A"), featureset: FeaturesetDescriptor(featuresetId: "poi", importId: "nested"), - geoJSONFeature: geoJSONFeature, + geometry: Point(coordinates: Position(0.01, 0.01)).toJson(), + properties: {}, state: {}); Map state = { "highlight": true, @@ -158,70 +156,70 @@ void main() { expect(returnedFeatureState2, {}); }); - // testWidgets('test_state_is_queried', (WidgetTester tester) async { - // // load style and position camera - // final mapFuture = app.main( - // width: 200, - // height: 200, - // camera: - // CameraOptions(center: Point(coordinates: Position(0, 0)), zoom: 10), - // alignment: Alignment(100, 100)); - // await tester.pumpAndSettle(); - // final mapboxMap = await mapFuture; - // var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); - // mapboxMap.style.setStyleJSON(styleJson); - - // await app.events.onMapLoaded.future; - - // var featuresetID = FeaturesetFeatureId(id: "11", namespace: "A"); - // var featuresetDescriptor = - // FeaturesetDescriptor(featuresetId: "poi", importId: "nested"); - // Map state = { - // "hide": true, - // }; - // var filter = '["==",["get", "type"], "A"]'; - // Map expectedProperties = { - // "name": "nest1", - // "type": "A", - // "class": "poi" - // }; - - // await mapboxMap.setFeatureStateForFeaturesetFeatureDescriptor( - // featuresetDescriptor, featuresetID, state); - // var queryResult = await mapboxMap.queryRenderedFeaturesInViewport( - // featureset: featuresetDescriptor, filter: filter); - // var poi = queryResult.first; - // var point = Point.fromJson(poi.geoJSONFeature.geometry!.toJson()); - - // expect(queryResult.length, 1); - // expect(poi.id?.id, featuresetID.id); - // expect(poi.id?.namespace, featuresetID.namespace); - // expect(poi.state, state); - // expect(point.coordinates.lat, closeTo(0.01, 0.05)); - // expect(point.coordinates.lng, closeTo(0.01, 0.05)); - // expect(poi.geoJSONFeature.properties, expectedProperties); - // }); - - // testWidgets('test_getFeaturesets', (WidgetTester tester) async { - // // load style and position camera - // final mapFuture = app.main( - // width: 200, - // height: 200, - // camera: - // CameraOptions(center: Point(coordinates: Position(0, 0)), zoom: 10), - // alignment: Alignment(100, 100)); - // await tester.pumpAndSettle(); - // final mapboxMap = await mapFuture; - // var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); - // mapboxMap.style.setStyleJSON(styleJson); - - // await app.events.onMapLoaded.future; - - // var returnedFeaturesets = await mapboxMap.style.getFeaturesets(); - - // expect(returnedFeaturesets.length, 1); - // expect(returnedFeaturesets.first.importId, "nested"); - // }); + testWidgets('test_state_is_queried', (WidgetTester tester) async { + // load style and position camera + final mapFuture = app.main( + width: 200, + height: 200, + camera: + CameraOptions(center: Point(coordinates: Position(0, 0)), zoom: 10), + alignment: Alignment(100, 100)); + await tester.pumpAndSettle(); + final mapboxMap = await mapFuture; + var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); + mapboxMap.style.setStyleJSON(styleJson); + + await app.events.onMapLoaded.future; + + var featuresetID = FeaturesetFeatureId(id: "11", namespace: "A"); + var featuresetDescriptor = + FeaturesetDescriptor(featuresetId: "poi", importId: "nested"); + Map state = { + "hide": true, + }; + var filter = '["==",["get", "type"], "A"]'; + Map expectedProperties = { + "name": "nest1", + "type": "A", + "class": "poi" + }; + + await mapboxMap.setFeatureStateForFeaturesetFeatureDescriptor( + featuresetDescriptor, featuresetID, state); + var queryResult = await mapboxMap.queryRenderedFeaturesInViewport( + featureset: featuresetDescriptor, filter: filter); + var poi = queryResult.first; + var point = Point.decode(poi.geometry); + + expect(queryResult.length, 1); + expect(poi.id?.id, featuresetID.id); + expect(poi.id?.namespace, featuresetID.namespace); + expect(poi.state, state); + expect(point.coordinates.lat, closeTo(0.01, 0.05)); + expect(point.coordinates.lng, closeTo(0.01, 0.05)); + expect(poi.properties, expectedProperties); + }); + + testWidgets('test_getFeaturesets', (WidgetTester tester) async { + // load style and position camera + final mapFuture = app.main( + width: 200, + height: 200, + camera: + CameraOptions(center: Point(coordinates: Position(0, 0)), zoom: 10), + alignment: Alignment(100, 100)); + await tester.pumpAndSettle(); + final mapboxMap = await mapFuture; + var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); + mapboxMap.style.setStyleJSON(styleJson); + + await app.events.onMapLoaded.future; + + var returnedFeaturesets = await mapboxMap.style.getFeaturesets(); + + expect(returnedFeaturesets.length, 1); + expect(returnedFeaturesets.first.importId, "nested"); + }); testWidgets('test_query_featureset_target', (WidgetTester tester) async { // load style and position camera @@ -237,6 +235,7 @@ void main() { mapboxMap.style.setStyleJSON(styleJson); await app.events.onMapLoaded.future; + await Future.delayed(Duration(seconds: 1)); var featuresetFilter = '["==", ["get", "type"], "B"]'; var layerFilter = '["==", ["get", "filter"], true]'; @@ -251,28 +250,33 @@ void main() { FeaturesetQueryTarget( featureset: featuresetLayer, filter: layerFilter, id: 2) ]; + Map expectedProperties = { + "name": "qux", + "filter": true, + "bar": 2 + }; + Map expectedProperties2 = { + "type": "B", + "class": "poi", + "name": "nest2" + }; var returnedQuery = await mapboxMap.queryRenderedFeaturesForTargets( RenderedQueryGeometry.fromScreenCoordinate(coord), targets); - var firstFeature = - Feature.fromFeature(returnedQuery[0]!.queriedFeature.feature); - var secondFeature = - Feature.fromFeature(returnedQuery[1]!.queriedFeature.feature); expect(returnedQuery.length, 2); expect(returnedQuery[0]?.queryTargets?.length, 1); expect(returnedQuery[0]?.queryTargets?.last.id, 2); expect(returnedQuery[0]?.queryTargets?.last.featureset.layerId, "circle-2"); expect(returnedQuery[0]?.queryTargets?.last.filter, null); - expect(firstFeature.id, "2"); - expect(firstFeature.properties?["name"], "qux"); + //expect(returnedQuery[0]!.queriedFeature.feature["id"], 2); + expect(returnedQuery[0]!.queriedFeature.feature["properties"], expectedProperties); expect(returnedQuery[1]?.queryTargets?.length, 1); expect(returnedQuery[1]?.queryTargets?.last.id, 1); expect(returnedQuery[1]?.queryTargets?.last.featureset.featuresetId, "poi"); expect(returnedQuery[1]?.queryTargets?.last.featureset.importId, "nested"); expect(returnedQuery[1]?.queryTargets?.last.filter, null); - expect(secondFeature.id, "12"); - expect(secondFeature.properties?["class"], "poi"); - expect(secondFeature.properties?["name"], "nest2"); + //expect(returnedQuery[1]!.queriedFeature.feature["id"], 12); + expect(returnedQuery[1]!.queriedFeature.feature["properties"], expectedProperties2); }); } diff --git a/example/lib/interactive_features_example.dart b/example/lib/interactive_features_example.dart index a0882b5ea..014319ec8 100644 --- a/example/lib/interactive_features_example.dart +++ b/example/lib/interactive_features_example.dart @@ -1,7 +1,5 @@ -import 'dart:convert'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart'; import 'example.dart'; @@ -54,14 +52,14 @@ class InteractiveFeaturesState extends State { "highlight": true, }; - Feature featured = - Feature(id: "id", geometry: Point(coordinates: Position(1, 2))); + var geometry = (Point(coordinates: Position(1, 2))).toJson(); var featureSetFeature = FeaturesetFeature( id: FeaturesetFeatureId(id: "1225951980"), featureset: FeaturesetDescriptor( featuresetId: "buildings", importId: "basemap"), - geoJSONFeature: featured, + geometry: geometry, + properties: {}, state: data); // Set FeatureState diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift index c3818ceaa..4cd040d9d 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift @@ -243,10 +243,12 @@ extension FeaturesetDescriptor { extension FeaturesetFeature { func toMapFeaturesetFeature() -> MapboxMaps.FeaturesetFeature { + var feature = Feature(geometry: convertDictionaryToGeometry(dict: geometry)) + feature.properties = JSONObject.init(turfRawValue: properties) return MapboxMaps.FeaturesetFeature( id: id?.toMapFeaturesetFeatureId(), featureset: featureset.toMapFeaturesetDescriptor(), - geoJsonFeature: geoJSONFeature, + geoJsonFeature: feature, state: JSONObject.init(turfRawValue: state)!) } } @@ -547,13 +549,11 @@ extension MapboxMaps.FeaturesetDescriptor { extension MapboxMaps.FeaturesetFeature { func toFLTFeaturesetFeature() -> FeaturesetFeature { - var feature = Feature(geometry: geometry) - feature.properties = properties - return FeaturesetFeature( id: id?.toFLTFeaturesetFeatureId(), featureset: featureset.toFLTFeaturesetDescriptor(), - geoJSONFeature: feature, + geometry: geometry.toMap(), + properties: properties.turfRawValue, state: state.turfRawValue) } } diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift index 2a64b2b19..faa77ac52 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift @@ -1203,22 +1203,21 @@ struct QueriedFeature { } } -/// Represents a unique identifier for a feature in one exported featureset or a layer. +/// Identifies a feature in a featureset. +/// +/// Knowing the feature identifier allows to set the feature states to a particular feature, see ``MapboxMap/setFeatureState(featureset:featureId:state:callback:)``. +/// +/// In a featureset a feature can come from different underlying sources. In that case their IDs are not guaranteed to be unique in the featureset. +/// The ``FeaturesetFeatureId/namespace`` is used to disambiguate from which source the feature is coming. +/// +/// - Warning: There is no guarantee of identifier persistency. This depends on the underlying source of the features and may vary from style to style. +/// If you want to store the identifiers persistently, please make sure that the style or source provides this guarantee. /// /// Generated class from Pigeon that represents data sent in messages. struct FeaturesetFeatureId { - /// The `featureId` uniquely identifies a feature within a featureset or layer. - /// Note: The Identifier of the feature is not guaranteed to be persistent - /// and can change depending on the source that is used. + /// A feature id coming from the feature itself.exp var id: String - /// An optional field that represents the feature namespace defined by - /// the Selector within a Featureset to which this feature belongs. - /// If the underlying source is the same for multiple selectors within a Featureset, - /// the same `featureNamespace` should be used across those selectors. - /// This practice ensures the uniqueness of `FeaturesetFeatureId` across the style. - /// Defining a `featureNamespace` value for the Selector is recommended, - /// especially when multiple selectors exist in a Featureset, - /// as it can enhance the efficiency of feature operations. + /// A namespace of the feature var namespace: String? // swift-format-ignore: AlwaysUseLowerCamelCase @@ -1239,7 +1238,10 @@ struct FeaturesetFeatureId { } } -/// Represents an identifier for a single exported featureset or a layer. +/// A featureset descriptor. +/// +/// The descriptor instance acts as a universal target for interactions or querying rendered features (see +/// ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)``). /// /// Generated class from Pigeon that represents data sent in messages. struct FeaturesetDescriptor { @@ -1282,35 +1284,40 @@ struct FeaturesetDescriptor { } } +/// A basic feature of a featureset. +/// +/// The featureset feature is different to the `Turf.Feature`. The latter represents any GeoJSON feature, while the former is a high level representation of features. +/// /// Generated class from Pigeon that represents data sent in messages. struct FeaturesetFeature { - /// Optional identifier holding feature id and feature namespace. - /// It could be NULL when underlying [Feature.id] is null. + /// An identifier of the feature. + /// + /// The identifier can be `nil` if the underlying source doesn't have identifiers for features. + /// In this case it's impossible to set a feature state for an individual feature. var id: FeaturesetFeatureId? - /// The [TypedFeaturesetDescriptor] this concrete feature comes from. - /// List of supported featuresets could be found in the nested classes - /// (e.g. [TypedFeaturesetDescriptor.Featureset], [TypedFeaturesetDescriptor.Layer], etc). + /// A featureset descriptor denoting a featureset this feature belongs to. var featureset: FeaturesetDescriptor - /// A feature geometry. - /// Feature JSON properties. - /// The geoJSON feature. - var geoJSONFeature: Feature - /// Current feature state stored as a concrete instance of [FeatureState]. - /// Important: this state is immutable and represents the feature state - /// at the precise moment of the interaction callback. + var geometry: [String?: Any?] + var properties: [String: Any?] + /// A feature state. + /// + /// This is a **snapshot** of the state that the feature had when it was interacted with. + /// To update and read the original state, use ``MapboxMap/setFeatureState()`` and ``MapboxMap/getFeatureState()``. var state: [String: Any?] // swift-format-ignore: AlwaysUseLowerCamelCase static func fromList(_ pigeonVar_list: [Any?]) -> FeaturesetFeature? { let id: FeaturesetFeatureId? = nilOrValue(pigeonVar_list[0]) let featureset = pigeonVar_list[1] as! FeaturesetDescriptor - let geoJSONFeature = pigeonVar_list[2] as! Feature - let state = pigeonVar_list[3] as! [String: Any?] + let geometry = pigeonVar_list[2] as! [String?: Any?] + let properties = pigeonVar_list[3] as! [String: Any?] + let state = pigeonVar_list[4] as! [String: Any?] return FeaturesetFeature( id: id, featureset: featureset, - geoJSONFeature: geoJSONFeature, + geometry: geometry, + properties: properties, state: state ) } @@ -1318,21 +1325,22 @@ struct FeaturesetFeature { return [ id, featureset, - geoJSONFeature, + geometry, + properties, state, ] } } -/// Defines the parameters for querying features from a Featureset with an optional filter and id. +/// Defines the parameters for querying features from a Featureset with an optional filter and id. /// /// Generated class from Pigeon that represents data sent in messages. struct FeaturesetQueryTarget { - /// The FeaturesetDescriptor that specifies the featureset to be included in the query. + /// A `FeaturesetDescriptor` that specifies the featureset to be included in the query. var featureset: FeaturesetDescriptor /// An optional filter expression used to refine the query results based on conditions related to the specified featureset. var filter: String? - /// An optional unique identifier associated with the FeaturesetQueryTarget. + /// An optional unique identifier associated with the target. var id: Int64? // swift-format-ignore: AlwaysUseLowerCamelCase @@ -3096,57 +3104,25 @@ protocol _MapInterface { /// This method allows to query both featureset from imported styles and user layers in the root style. /// The results can be additionally filtered per-featureset. /// - /// ```dart - /// let targets = [ - /// FeaturesetQueryTarget( - /// featureset: .layer("my-layer"), - /// filter: Exp(.eq) { - /// Exp(.get) { "type" } - /// "hotel" - /// } - /// ), - /// FeaturesetQueryTarget(featureset: .featureset("poi", importId: "basemap")) - /// ] - /// mapView.mapboxMap.queryRenderedFeatures(with: CGPoint(x: 0, y: 0), - /// targets: targets) { result in - /// // handle features in result - /// } - /// ``` - /// - /// - Important: This is a low-level method. If you need to handle basic gestures on map content, please prefer to use Interactions API (see ``MapboxMap/addInteraction(_:)``) or ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)``. + /// - Important: This is a low-level method. If you need to handle basic gestures on map content, please prefer ``MapboxMap/ queryRenderedFeaturesForFeatureset()``. /// /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param targets An array of targets to query with. - /// @param completion Callback called when the query completes. func queryRenderedFeaturesForTargets(geometry: _RenderedQueryGeometry, targets: [FeaturesetQueryTarget], completion: @escaping (Result<[QueriedRenderedFeature?], Error>) -> Void) /// Queries the map for rendered features with one typed featureset. /// /// The results array will contain features of the type specified by this featureset. /// - /// ```swift - /// mapView.mapboxMap.queryRenderedFeatures( - /// with: CGPoint(x: 0, y: 0), - /// featureset: .standardBuildings) { result in - /// // handle buildings in result - /// } - /// ``` - /// - /// - Important: If you need to handle basic gestures on map content, please prefer to use Interactions API, see ``MapboxMap/addInteraction(_:)``. - /// /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param featureset A typed featureset to query with. /// @param filter An additional filter for features. - /// @param completion Callback called when the query completes. func queryRenderedFeaturesForFeatureset(geometry: _RenderedQueryGeometry, featureset: FeaturesetDescriptor, filter: String?, completion: @escaping (Result<[FeaturesetFeature], Error>) -> Void) /// Queries all rendered features in current viewport, using one typed featureset. /// - /// This is same as ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)`` called with geometry matching the current viewport. - /// - /// - Important: If you need to handle basic gestures on map content, please prefer to use Interactions API, see ``MapboxMap/addInteraction(_:)``. + /// This is same as `MapboxMap/ queryRenderedFeaturesForFeatureset()`` called with geometry matching the current viewport. /// /// @param featureset A typed featureset to query with. /// @param filter An additional filter for features. - /// @param completion Callback called when the query completes. func queryRenderedFeaturesInViewport(featureset: FeaturesetDescriptor, filter: String?, completion: @escaping (Result<[FeaturesetFeature], Error>) -> Void) /// Queries the map for source features. /// @@ -3157,7 +3133,6 @@ protocol _MapInterface { /// Queries the source features for a given featureset. /// /// @param target A featureset query target. - /// @param completion Callback called when the query completes. func querySourceFeaturesForFeatureset(target: FeaturesetQueryTarget, completion: @escaping (Result<[QueriedSourceFeature?], Error>) -> Void) /// Returns all the leaves (original points) of a cluster (given its cluster_id) from a GeoJsonSource, with pagination support: limit is the number of leaves /// to return (set to Infinity for all points), and offset is the amount of points to skip (for pagination). @@ -3212,7 +3187,6 @@ protocol _MapInterface { /// @param featureset The featureset to look the feature in. /// @param featureId Identifier of the feature whose state should be updated. /// @param state Map of entries to update with their respective new values - /// @param callback The `feature state operation callback` called when the operation completes or ends. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. func setFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: [String: Any?], completion: @escaping (Result) -> Void) @@ -3223,7 +3197,6 @@ protocol _MapInterface { /// /// @param feature The feature to update. /// @param state Map of entries to update with their respective new values - /// @param callback The `feature state operation callback` called when the operation completes or ends. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. func setFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, state: [String: Any?], completion: @escaping (Result) -> Void) @@ -3235,7 +3208,8 @@ protocol _MapInterface { /// @param sourceId The style source identifier. /// @param sourceLayerId The style source layer identifier (for multi-layer sources such as vector sources). /// @param featureId The feature identifier of the feature whose state should be queried. - /// @param completion The `query feature state completion` called when the query completes. + /// + /// @return A `Cancelable` object that could be used to cancel the pending operation. func getFeatureState(sourceId: String, sourceLayerId: String?, featureId: String, completion: @escaping (Result) -> Void) /// Get the state map of a feature within a style source. /// @@ -3247,7 +3221,6 @@ protocol _MapInterface { /// Get the state map of a feature within a style source. /// /// @param feature An interactive feature to query the state from. - /// @param completion Feature's state map or an empty map if the feature could not be found. /// /// @return A `Cancelable` object that could be used to cancel the pending query. func getFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, completion: @escaping (Result<[String: Any?], Error>) -> Void) @@ -3270,7 +3243,6 @@ protocol _MapInterface { /// @param featureset A featureset the feature belongs to. /// @param featureId Identifier of the feature whose state should be removed. /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. - /// @param callback The `feature state operation callback` called when the operation completes or ends. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. func removeFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String, completion: @escaping (Result) -> Void) @@ -3279,17 +3251,15 @@ protocol _MapInterface { /// /// @param feature An interactive feature to update. /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. - /// @param callback The `feature state operation callback` called when the operation completes or ends. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. func removeFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, stateKey: String, completion: @escaping (Result) -> Void) /// Reset all the feature states within a featureset. /// /// Note that updates to feature state are asynchronous, so changes made by this method might not be - /// immediately visible using ``MapboxMap/getFeatureState(_:callback:)``. + /// immediately visible using ``MapboxMap/getFeatureState()``. /// /// @param featureset A featureset descriptor - /// @param callback The `feature state operation callback` called when the operation completes or ends. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. func resetFeatureStatesForFeatureset(featureset: FeaturesetDescriptor, completion: @escaping (Result) -> Void) @@ -3690,28 +3660,10 @@ class _MapInterfaceSetup { /// This method allows to query both featureset from imported styles and user layers in the root style. /// The results can be additionally filtered per-featureset. /// - /// ```dart - /// let targets = [ - /// FeaturesetQueryTarget( - /// featureset: .layer("my-layer"), - /// filter: Exp(.eq) { - /// Exp(.get) { "type" } - /// "hotel" - /// } - /// ), - /// FeaturesetQueryTarget(featureset: .featureset("poi", importId: "basemap")) - /// ] - /// mapView.mapboxMap.queryRenderedFeatures(with: CGPoint(x: 0, y: 0), - /// targets: targets) { result in - /// // handle features in result - /// } - /// ``` - /// - /// - Important: This is a low-level method. If you need to handle basic gestures on map content, please prefer to use Interactions API (see ``MapboxMap/addInteraction(_:)``) or ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)``. + /// - Important: This is a low-level method. If you need to handle basic gestures on map content, please prefer ``MapboxMap/ queryRenderedFeaturesForFeatureset()``. /// /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param targets An array of targets to query with. - /// @param completion Callback called when the query completes. let queryRenderedFeaturesForTargetsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesForTargets\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { queryRenderedFeaturesForTargetsChannel.setMessageHandler { message, reply in @@ -3734,20 +3686,9 @@ class _MapInterfaceSetup { /// /// The results array will contain features of the type specified by this featureset. /// - /// ```swift - /// mapView.mapboxMap.queryRenderedFeatures( - /// with: CGPoint(x: 0, y: 0), - /// featureset: .standardBuildings) { result in - /// // handle buildings in result - /// } - /// ``` - /// - /// - Important: If you need to handle basic gestures on map content, please prefer to use Interactions API, see ``MapboxMap/addInteraction(_:)``. - /// /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param featureset A typed featureset to query with. /// @param filter An additional filter for features. - /// @param completion Callback called when the query completes. let queryRenderedFeaturesForFeaturesetChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesForFeatureset\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { queryRenderedFeaturesForFeaturesetChannel.setMessageHandler { message, reply in @@ -3769,13 +3710,10 @@ class _MapInterfaceSetup { } /// Queries all rendered features in current viewport, using one typed featureset. /// - /// This is same as ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)`` called with geometry matching the current viewport. - /// - /// - Important: If you need to handle basic gestures on map content, please prefer to use Interactions API, see ``MapboxMap/addInteraction(_:)``. + /// This is same as `MapboxMap/ queryRenderedFeaturesForFeatureset()`` called with geometry matching the current viewport. /// /// @param featureset A typed featureset to query with. /// @param filter An additional filter for features. - /// @param completion Callback called when the query completes. let queryRenderedFeaturesInViewportChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesInViewport\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { queryRenderedFeaturesInViewportChannel.setMessageHandler { message, reply in @@ -3820,7 +3758,6 @@ class _MapInterfaceSetup { /// Queries the source features for a given featureset. /// /// @param target A featureset query target. - /// @param completion Callback called when the query completes. let querySourceFeaturesForFeaturesetChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.querySourceFeaturesForFeatureset\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { querySourceFeaturesForFeaturesetChannel.setMessageHandler { message, reply in @@ -3963,7 +3900,6 @@ class _MapInterfaceSetup { /// @param featureset The featureset to look the feature in. /// @param featureId Identifier of the feature whose state should be updated. /// @param state Map of entries to update with their respective new values - /// @param callback The `feature state operation callback` called when the operation completes or ends. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. let setFeatureStateForFeaturesetFeatureDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.setFeatureStateForFeaturesetFeatureDescriptor\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) @@ -3992,7 +3928,6 @@ class _MapInterfaceSetup { /// /// @param feature The feature to update. /// @param state Map of entries to update with their respective new values - /// @param callback The `feature state operation callback` called when the operation completes or ends. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. let setFeatureStateForFeaturesetFeatureChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.setFeatureStateForFeaturesetFeature\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) @@ -4021,7 +3956,8 @@ class _MapInterfaceSetup { /// @param sourceId The style source identifier. /// @param sourceLayerId The style source layer identifier (for multi-layer sources such as vector sources). /// @param featureId The feature identifier of the feature whose state should be queried. - /// @param completion The `query feature state completion` called when the query completes. + /// + /// @return A `Cancelable` object that could be used to cancel the pending operation. let getFeatureStateChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.getFeatureState\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { getFeatureStateChannel.setMessageHandler { message, reply in @@ -4068,7 +4004,6 @@ class _MapInterfaceSetup { /// Get the state map of a feature within a style source. /// /// @param feature An interactive feature to query the state from. - /// @param completion Feature's state map or an empty map if the feature could not be found. /// /// @return A `Cancelable` object that could be used to cancel the pending query. let getFeatureStateForFeaturesetFeatureChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.getFeatureStateForFeaturesetFeature\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) @@ -4126,7 +4061,6 @@ class _MapInterfaceSetup { /// @param featureset A featureset the feature belongs to. /// @param featureId Identifier of the feature whose state should be removed. /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. - /// @param callback The `feature state operation callback` called when the operation completes or ends. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. let removeFeatureStateForFeaturesetFeatureDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureStateForFeaturesetFeatureDescriptor\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) @@ -4153,7 +4087,6 @@ class _MapInterfaceSetup { /// /// @param feature An interactive feature to update. /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. - /// @param callback The `feature state operation callback` called when the operation completes or ends. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. let removeFeatureStateForFeaturesetFeatureChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureStateForFeaturesetFeature\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) @@ -4177,10 +4110,9 @@ class _MapInterfaceSetup { /// Reset all the feature states within a featureset. /// /// Note that updates to feature state are asynchronous, so changes made by this method might not be - /// immediately visible using ``MapboxMap/getFeatureState(_:callback:)``. + /// immediately visible using ``MapboxMap/getFeatureState()``. /// /// @param featureset A featureset descriptor - /// @param callback The `feature state operation callback` called when the operation completes or ends. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. let resetFeatureStatesForFeaturesetChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.resetFeatureStatesForFeatureset\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/TurfAdapters.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/TurfAdapters.swift index ed1a54639..5abaaea56 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/TurfAdapters.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/TurfAdapters.swift @@ -70,11 +70,11 @@ extension Feature { } } -extension Geometry { - static func fromList(_ list: [Any?]) -> Geometry? { +extension Turf.Geometry { + static func fromList(_ list: [Any?]) -> Turf.Geometry? { guard let raw = list.first as? [String: Any], let jsonData = try? JSONSerialization.data(withJSONObject: raw, options: []), - let geometry = try? JSONDecoder().decode(Geometry.self, from: jsonData) else { return nil } + let geometry = try? JSONDecoder().decode(Turf.Geometry.self, from: jsonData) else { return nil } return geometry } diff --git a/lib/mapbox_maps_flutter.dart b/lib/mapbox_maps_flutter.dart index 4b64ad564..0078a9c6f 100644 --- a/lib/mapbox_maps_flutter.dart +++ b/lib/mapbox_maps_flutter.dart @@ -9,7 +9,6 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; -import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart'; import 'package:meta/meta.dart'; import 'package:turf/turf.dart' as turf; diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index 31b1d10af..388b88282 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -463,8 +463,7 @@ class MapboxMap extends ChangeNotifier { /// Queries all rendered features in current viewport, using one typed featureset. Future> queryRenderedFeaturesInViewport( - {required FeaturesetDescriptor featureset, - String? filter}) async => + {required FeaturesetDescriptor featureset, String? filter}) async => _mapInterface.queryRenderedFeaturesInViewport(featureset, filter); /// Queries the map for source features. diff --git a/lib/src/pigeons/map_interfaces.dart b/lib/src/pigeons/map_interfaces.dart index c881360a2..cd5559bcc 100644 --- a/lib/src/pigeons/map_interfaces.dart +++ b/lib/src/pigeons/map_interfaces.dart @@ -1298,26 +1298,25 @@ class QueriedFeature { } } -/// Represents a unique identifier for a feature in one exported featureset or a layer. +/// Identifies a feature in a featureset. +/// +/// Knowing the feature identifier allows to set the feature states to a particular feature, see ``MapboxMap/setFeatureState(featureset:featureId:state:callback:)``. +/// +/// In a featureset a feature can come from different underlying sources. In that case their IDs are not guaranteed to be unique in the featureset. +/// The ``FeaturesetFeatureId/namespace`` is used to disambiguate from which source the feature is coming. +/// +/// - Warning: There is no guarantee of identifier persistency. This depends on the underlying source of the features and may vary from style to style. +/// If you want to store the identifiers persistently, please make sure that the style or source provides this guarantee. class FeaturesetFeatureId { FeaturesetFeatureId({ required this.id, this.namespace, }); - /// The `featureId` uniquely identifies a feature within a featureset or layer. - /// Note: The Identifier of the feature is not guaranteed to be persistent - /// and can change depending on the source that is used. + /// A feature id coming from the feature itself.exp String id; - /// An optional field that represents the feature namespace defined by - /// the Selector within a Featureset to which this feature belongs. - /// If the underlying source is the same for multiple selectors within a Featureset, - /// the same `featureNamespace` should be used across those selectors. - /// This practice ensures the uniqueness of `FeaturesetFeatureId` across the style. - /// Defining a `featureNamespace` value for the Selector is recommended, - /// especially when multiple selectors exist in a Featureset, - /// as it can enhance the efficiency of feature operations. + /// A namespace of the feature String? namespace; Object encode() { @@ -1336,7 +1335,10 @@ class FeaturesetFeatureId { } } -/// Represents an identifier for a single exported featureset or a layer. +/// A featureset descriptor. +/// +/// The descriptor instance acts as a universal target for interactions or querying rendered features (see +/// ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)``). class FeaturesetDescriptor { FeaturesetDescriptor({ this.featuresetId, @@ -1382,38 +1384,43 @@ class FeaturesetDescriptor { } } +/// A basic feature of a featureset. +/// +/// The featureset feature is different to the `Turf.Feature`. The latter represents any GeoJSON feature, while the former is a high level representation of features. class FeaturesetFeature { FeaturesetFeature({ this.id, required this.featureset, - required this.geoJSONFeature, + required this.geometry, + required this.properties, required this.state, }); - /// Optional identifier holding feature id and feature namespace. - /// It could be NULL when underlying [Feature.id] is null. + /// An identifier of the feature. + /// + /// The identifier can be `nil` if the underlying source doesn't have identifiers for features. + /// In this case it's impossible to set a feature state for an individual feature. FeaturesetFeatureId? id; - /// The [TypedFeaturesetDescriptor] this concrete feature comes from. - /// List of supported featuresets could be found in the nested classes - /// (e.g. [TypedFeaturesetDescriptor.Featureset], [TypedFeaturesetDescriptor.Layer], etc). + /// A featureset descriptor denoting a featureset this feature belongs to. FeaturesetDescriptor featureset; - /// A feature geometry. - /// Feature JSON properties. - /// The geoJSON feature. - Feature geoJSONFeature; + Map geometry; + + Map properties; - /// Current feature state stored as a concrete instance of [FeatureState]. - /// Important: this state is immutable and represents the feature state - /// at the precise moment of the interaction callback. + /// A feature state. + /// + /// This is a **snapshot** of the state that the feature had when it was interacted with. + /// To update and read the original state, use ``MapboxMap/setFeatureState()`` and ``MapboxMap/getFeatureState()``. Map state; Object encode() { return [ id, featureset, - geoJSONFeature, + geometry, + properties, state, ]; } @@ -1423,8 +1430,10 @@ class FeaturesetFeature { return FeaturesetFeature( id: result[0] as FeaturesetFeatureId?, featureset: result[1]! as FeaturesetDescriptor, - geoJSONFeature: result[2]! as Feature, - state: (result[3] as Map?)!.cast(), + geometry: (result[2] as Map?)!.cast(), + properties: + (result[3] as Map?)!.cast(), + state: (result[4] as Map?)!.cast(), ); } } @@ -1437,13 +1446,13 @@ class FeaturesetQueryTarget { this.id, }); - /// The FeaturesetDescriptor that specifies the featureset to be included in the query. + /// A `FeaturesetDescriptor` that specifies the featureset to be included in the query. FeaturesetDescriptor featureset; /// An optional filter expression used to refine the query results based on conditions related to the specified featureset. String? filter; - /// An optional unique identifier associated with the FeaturesetQueryTarget. + /// An optional unique identifier associated with the target. int? id; Object encode() { @@ -3868,28 +3877,10 @@ class _MapInterface { /// This method allows to query both featureset from imported styles and user layers in the root style. /// The results can be additionally filtered per-featureset. /// - /// ```dart - /// let targets = [ - /// FeaturesetQueryTarget( - /// featureset: .layer("my-layer"), - /// filter: Exp(.eq) { - /// Exp(.get) { "type" } - /// "hotel" - /// } - /// ), - /// FeaturesetQueryTarget(featureset: .featureset("poi", importId: "basemap")) - /// ] - /// mapView.mapboxMap.queryRenderedFeatures(with: CGPoint(x: 0, y: 0), - /// targets: targets) { result in - /// // handle features in result - /// } - /// ``` - /// - /// - Important: This is a low-level method. If you need to handle basic gestures on map content, please prefer to use Interactions API (see ``MapboxMap/addInteraction(_:)``) or ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)``. + /// - Important: This is a low-level method. If you need to handle basic gestures on map content, please prefer ``MapboxMap/ queryRenderedFeaturesForFeatureset()``. /// /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param targets An array of targets to query with. - /// @param completion Callback called when the query completes. Future> queryRenderedFeaturesForTargets( _RenderedQueryGeometry geometry, List targets) async { @@ -3926,20 +3917,9 @@ class _MapInterface { /// /// The results array will contain features of the type specified by this featureset. /// - /// ```swift - /// mapView.mapboxMap.queryRenderedFeatures( - /// with: CGPoint(x: 0, y: 0), - /// featureset: .standardBuildings) { result in - /// // handle buildings in result - /// } - /// ``` - /// - /// - Important: If you need to handle basic gestures on map content, please prefer to use Interactions API, see ``MapboxMap/addInteraction(_:)``. - /// /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param featureset A typed featureset to query with. /// @param filter An additional filter for features. - /// @param completion Callback called when the query completes. Future> queryRenderedFeaturesForFeatureset( _RenderedQueryGeometry geometry, FeaturesetDescriptor featureset, @@ -3975,13 +3955,10 @@ class _MapInterface { /// Queries all rendered features in current viewport, using one typed featureset. /// - /// This is same as ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)`` called with geometry matching the current viewport. - /// - /// - Important: If you need to handle basic gestures on map content, please prefer to use Interactions API, see ``MapboxMap/addInteraction(_:)``. + /// This is same as `MapboxMap/ queryRenderedFeaturesForFeatureset()`` called with geometry matching the current viewport. /// /// @param featureset A typed featureset to query with. /// @param filter An additional filter for features. - /// @param completion Callback called when the query completes. Future> queryRenderedFeaturesInViewport( FeaturesetDescriptor featureset, String? filter) async { final String pigeonVar_channelName = @@ -4052,7 +4029,6 @@ class _MapInterface { /// Queries the source features for a given featureset. /// /// @param target A featureset query target. - /// @param completion Callback called when the query completes. Future> querySourceFeaturesForFeatureset( FeaturesetQueryTarget target) async { final String pigeonVar_channelName = @@ -4250,7 +4226,6 @@ class _MapInterface { /// @param featureset The featureset to look the feature in. /// @param featureId Identifier of the feature whose state should be updated. /// @param state Map of entries to update with their respective new values - /// @param callback The `feature state operation callback` called when the operation completes or ends. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. Future setFeatureStateForFeaturesetFeatureDescriptor( @@ -4287,7 +4262,6 @@ class _MapInterface { /// /// @param feature The feature to update. /// @param state Map of entries to update with their respective new values - /// @param callback The `feature state operation callback` called when the operation completes or ends. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. Future setFeatureStateForFeaturesetFeature( @@ -4323,7 +4297,8 @@ class _MapInterface { /// @param sourceId The style source identifier. /// @param sourceLayerId The style source layer identifier (for multi-layer sources such as vector sources). /// @param featureId The feature identifier of the feature whose state should be queried. - /// @param completion The `query feature state completion` called when the query completes. + /// + /// @return A `Cancelable` object that could be used to cancel the pending operation. Future getFeatureState( String sourceId, String? sourceLayerId, String featureId) async { final String pigeonVar_channelName = @@ -4394,7 +4369,6 @@ class _MapInterface { /// Get the state map of a feature within a style source. /// /// @param feature An interactive feature to query the state from. - /// @param completion Feature's state map or an empty map if the feature could not be found. /// /// @return A `Cancelable` object that could be used to cancel the pending query. Future> getFeatureStateForFeaturesetFeature( @@ -4472,7 +4446,6 @@ class _MapInterface { /// @param featureset A featureset the feature belongs to. /// @param featureId Identifier of the feature whose state should be removed. /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. - /// @param callback The `feature state operation callback` called when the operation completes or ends. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. Future removeFeatureStateForFeaturesetFeatureDescriptor( @@ -4507,7 +4480,6 @@ class _MapInterface { /// /// @param feature An interactive feature to update. /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. - /// @param callback The `feature state operation callback` called when the operation completes or ends. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. Future removeFeatureStateForFeaturesetFeature( @@ -4538,10 +4510,9 @@ class _MapInterface { /// Reset all the feature states within a featureset. /// /// Note that updates to feature state are asynchronous, so changes made by this method might not be - /// immediately visible using ``MapboxMap/getFeatureState(_:callback:)``. + /// immediately visible using ``MapboxMap/getFeatureState()``. /// /// @param featureset A featureset descriptor - /// @param callback The `feature state operation callback` called when the operation completes or ends. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. Future resetFeatureStatesForFeatureset( diff --git a/lib/src/style/mapbox_styles.dart b/lib/src/style/mapbox_styles.dart index e10c06e62..6db058e27 100644 --- a/lib/src/style/mapbox_styles.dart +++ b/lib/src/style/mapbox_styles.dart @@ -36,10 +36,11 @@ class MapboxStyles { /// improve the style. static const String SATELLITE_STREETS = "mapbox://styles/mapbox/satellite-streets-v12"; - + /// NOT FOR PRODUCTION USE. An experimental version of the Mapbox Standard style. /// This style is used for testing new features and changes to the Mapbox Standard style. The style may change or be removed at any time. - static const String STANDARD_EXPERIMENTAL = "mapbox://styles/mapbox-map-design/standard-experimental-ime"; + static const String STANDARD_EXPERIMENTAL = + "mapbox://styles/mapbox-map-design/standard-experimental-ime"; } /// A pre-specified location in the style where layer will be added to. diff --git a/lib/src/turf_adapters.dart b/lib/src/turf_adapters.dart index aeae7e345..1bc5c0a7d 100644 --- a/lib/src/turf_adapters.dart +++ b/lib/src/turf_adapters.dart @@ -9,8 +9,8 @@ final class Point extends turf.Point { } static Point decode(Object result) { - result as List; - return Point.fromJson((result.first as Map).cast()); + var map = (result is List) ? result.first : result; + return Point.fromJson((map as Map).cast()); } Object encode() { @@ -26,8 +26,8 @@ final class Polygon extends turf.Polygon { } static Polygon decode(Object result) { - result as List; - return Polygon.fromJson((result.first as Map).cast()); + var map = (result is List) ? result.first : result; + return Polygon.fromJson((map as Map).cast()); } factory Polygon.fromJson(Map json) { @@ -47,8 +47,8 @@ final class LineString extends turf.LineString { } static LineString decode(Object result) { - result as List; - return LineString.fromJson((result.first as Map).cast()); + var map = (result is List) ? result.first : result; + return LineString.fromJson((map as Map).cast()); } factory LineString.fromJson(Map json) { @@ -86,25 +86,4 @@ final class Feature extends turf.Feature { geometry: feature.geometry, fields: feature.fields); } - - factory Feature.fromFeature(Map feature) { - var valid = convertToValidMap(feature as Map); - return Feature.fromJson(valid); - } -} - -Map convertToValidMap(Map input) { - return input.map((key, value) { - if (key is! String) { - throw Exception( - "Invalid key type. Expected String but got ${key.runtimeType}"); - } - if (value is Map) { - // Recursively convert nested maps - return MapEntry(key, convertToValidMap(value)); - } - return MapEntry(key, value); - }); -} - -typedef JSONObject = Map; +} \ No newline at end of file From 6118ca2471da8e431e8e7d474f1879cec06ecf7c Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Fri, 25 Oct 2024 17:10:10 -0400 Subject: [PATCH 07/28] Update for linting --- .../com/mapbox/maps/mapbox_maps/MapInterfaceController.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt index 3e2690f46..525d67c45 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt @@ -494,8 +494,8 @@ class MapInterfaceController( callback: (Result) -> Unit ) { mapboxMap.resetFeatureStates( - featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor>, - ) { + featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor> + ) { if (it.isError) { callback(Result.failure(Throwable(it.error))) } else { From ad78eb7ef55c3ad15160c7a1eb1fcf5dd921e378 Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Fri, 25 Oct 2024 18:08:28 -0400 Subject: [PATCH 08/28] Change toValue back --- .../src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt | 1 - .../kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt | 1 - 2 files changed, 2 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt index d96696c66..7e37d765f 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt @@ -10,7 +10,6 @@ import com.mapbox.bindgen.Expected import com.mapbox.bindgen.None import com.mapbox.bindgen.Value import com.mapbox.common.TileRegionError -import com.mapbox.common.toValue import com.mapbox.geojson.* import com.mapbox.maps.EdgeInsets import com.mapbox.maps.MapboxExperimental diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt index 525d67c45..b0837057c 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt @@ -2,7 +2,6 @@ package com.mapbox.maps.mapbox_maps import android.content.Context import com.google.gson.Gson -import com.mapbox.common.toValue import com.mapbox.geojson.Feature import com.mapbox.geojson.Point import com.mapbox.maps.MapView From 15397b01d57183ca18897c703d0c5c0cd2b819cb Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Sun, 27 Oct 2024 12:51:03 -0400 Subject: [PATCH 09/28] Update example, some docs cleanup --- .../mapbox_maps/MapInterfaceController.kt | 8 +- .../maps/mapbox_maps/pigeons/MapInterfaces.kt | 12 +- .../interactive_features_test.dart | 12 +- example/lib/interactive_features_example.dart | 124 +++++++----------- .../Classes/Generated/MapInterfaces.swift | 12 +- .../Classes/MapInterfaceController.swift | 4 +- lib/src/mapbox_map.dart | 15 ++- lib/src/pigeons/map_interfaces.dart | 8 +- lib/src/turf_adapters.dart | 2 +- 9 files changed, 94 insertions(+), 103 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt index b0837057c..0e9d649c6 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt @@ -453,13 +453,13 @@ class MapInterfaceController( override fun removeFeatureStateForFeaturesetFeatureDescriptor( featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, - stateKey: String, + stateKey: String?, callback: (Result) -> Unit ) { mapboxMap.removeFeatureState( featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor>, featureId.toFeaturesetFeatureId(), - FeatureStateKey.create(stateKey) + stateKey?.let { FeatureStateKey.create(it) } ) { if (it.isError) { callback(Result.failure(Throwable(it.error))) @@ -472,12 +472,12 @@ class MapInterfaceController( @OptIn(MapboxExperimental::class) override fun removeFeatureStateForFeaturesetFeature( feature: FeaturesetFeature, - stateKey: String, + stateKey: String?, callback: (Result) -> Unit ) { mapboxMap.removeFeatureState( feature.toFeaturesetFeature(), - FeatureStateKey.create(stateKey) + stateKey?.let { FeatureStateKey.create(it) } ) { if (it.isError) { callback(Result.failure(Throwable(it.error))) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt index 3c306965c..da83382d8 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt @@ -1451,9 +1451,11 @@ data class FeaturesetFeature( * In this case it's impossible to set a feature state for an individual feature. */ val id: FeaturesetFeatureId? = null, - /** A featureset descriptor denoting a featureset this feature belongs to. */ + /** A featureset descriptor denoting the featureset this feature belongs to. */ val featureset: FeaturesetDescriptor, + /** A feature geometry. */ val geometry: Map, + /** Feature JSON properties. */ val properties: Map, /** * A feature state. @@ -3521,7 +3523,7 @@ interface _MapInterface { * * @return A `Cancelable` object that could be used to cancel the pending operation. */ - fun removeFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String, callback: (Result) -> Unit) + fun removeFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String?, callback: (Result) -> Unit) /** * Removes entries from a specified Feature. * Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. @@ -3531,7 +3533,7 @@ interface _MapInterface { * * @return A `Cancelable` object that could be used to cancel the pending operation. */ - fun removeFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, stateKey: String, callback: (Result) -> Unit) + fun removeFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, stateKey: String?, callback: (Result) -> Unit) /** * Reset all the feature states within a featureset. * @@ -4275,7 +4277,7 @@ interface _MapInterface { val args = message as List val featuresetArg = args[0] as FeaturesetDescriptor val featureIdArg = args[1] as FeaturesetFeatureId - val stateKeyArg = args[2] as String + val stateKeyArg = args[2] as String? api.removeFeatureStateForFeaturesetFeatureDescriptor(featuresetArg, featureIdArg, stateKeyArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { @@ -4295,7 +4297,7 @@ interface _MapInterface { channel.setMessageHandler { message, reply -> val args = message as List val featureArg = args[0] as FeaturesetFeature - val stateKeyArg = args[1] as String + val stateKeyArg = args[1] as String? api.removeFeatureStateForFeaturesetFeature(featureArg, stateKeyArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { diff --git a/example/integration_test/interactive_features_test.dart b/example/integration_test/interactive_features_test.dart index e76c9dca0..6ca1c8768 100644 --- a/example/integration_test/interactive_features_test.dart +++ b/example/integration_test/interactive_features_test.dart @@ -95,7 +95,7 @@ void main() { // test remove featurestate await mapboxMap.removeFeatureStateForFeaturesetFeature( - feature, "highlight"); + feature: feature, stateKey: "highlight"); var returnedFeatureState2 = await mapboxMap.getFeatureStateForFeaturesetFeature(feature); expect(returnedFeatureState2, {}); @@ -149,7 +149,9 @@ void main() { // test remove featurestate await mapboxMap.removeFeatureStateForFeaturesetFeatureDescriptor( - featuresetDescriptor, featuresetID, "highlight"); + featureset: featuresetDescriptor, + featureId: featuresetID, + stateKey: "highlight"); var returnedFeatureState2 = await mapboxMap.getFeatureStateForFeaturesetDescriptor( featuresetDescriptor, featuresetID); @@ -270,13 +272,15 @@ void main() { expect(returnedQuery[0]?.queryTargets?.last.featureset.layerId, "circle-2"); expect(returnedQuery[0]?.queryTargets?.last.filter, null); //expect(returnedQuery[0]!.queriedFeature.feature["id"], 2); - expect(returnedQuery[0]!.queriedFeature.feature["properties"], expectedProperties); + expect(returnedQuery[0]!.queriedFeature.feature["properties"], + expectedProperties); expect(returnedQuery[1]?.queryTargets?.length, 1); expect(returnedQuery[1]?.queryTargets?.last.id, 1); expect(returnedQuery[1]?.queryTargets?.last.featureset.featuresetId, "poi"); expect(returnedQuery[1]?.queryTargets?.last.featureset.importId, "nested"); expect(returnedQuery[1]?.queryTargets?.last.filter, null); //expect(returnedQuery[1]!.queriedFeature.feature["id"], 12); - expect(returnedQuery[1]!.queriedFeature.feature["properties"], expectedProperties2); + expect(returnedQuery[1]!.queriedFeature.feature["properties"], + expectedProperties2); }); } diff --git a/example/lib/interactive_features_example.dart b/example/lib/interactive_features_example.dart index 014319ec8..91961a31e 100644 --- a/example/lib/interactive_features_example.dart +++ b/example/lib/interactive_features_example.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart'; import 'example.dart'; @@ -9,7 +8,7 @@ class InteractiveFeaturesExample extends StatefulWidget implements Example { @override final String title = 'Interactive Features'; @override - final String? subtitle = null; + final String? subtitle = 'Click to select buildings, long-click to unselect'; @override State createState() => InteractiveFeaturesState(); @@ -17,14 +16,7 @@ class InteractiveFeaturesExample extends StatefulWidget implements Example { class InteractiveFeaturesState extends State { InteractiveFeaturesState(); - MapboxMap? mapboxMap; - var isLight = true; - var feature = Feature( - id: "addedFeature", - geometry: - Point(coordinates: Position(24.94180921290157, 60.171227338006844)), - properties: {"test": "data"}); _onMapCreated(MapboxMap mapboxMap) { this.mapboxMap = mapboxMap; @@ -32,83 +24,69 @@ class InteractiveFeaturesState extends State { } _onTap(context) async { - print("tap"); - - // QRF + // Define the geometry to query, in this case the point where the user clicked. var clicked = await mapboxMap?.pixelForCoordinate(context.point); var renderedQueryGeometry = RenderedQueryGeometry.fromScreenCoordinate(clicked!); - var target = FeaturesetQueryTarget( - featureset: FeaturesetDescriptor( - featuresetId: "buildings", importId: "basemap")); - var targets = [target]; - var query = await mapboxMap?.queryRenderedFeaturesForTargets( - renderedQueryGeometry, targets); - var feature = query?.first?.queriedFeature; - print("feature"); - print(feature?.feature); - Map data = { - "highlight": true, - }; + // Define the featureset to query. In this case "buildings", which is defined in the + // Standard Experimental style. + var featureset = + FeaturesetDescriptor(featuresetId: "buildings", importId: "basemap"); - var geometry = (Point(coordinates: Position(1, 2))).toJson(); + // Query the featureset for the geometry + var queriedFeatures = await mapboxMap?.queryRenderedFeaturesForFeatureset( + geometry: renderedQueryGeometry, featureset: featureset); + var featuresetFeatureId = queriedFeatures?.first.id; - var featureSetFeature = FeaturesetFeature( - id: FeaturesetFeatureId(id: "1225951980"), - featureset: FeaturesetDescriptor( - featuresetId: "buildings", importId: "basemap"), - geometry: geometry, - properties: {}, - state: data); + if (featuresetFeatureId != null) { + // Define the state to set for the feature, in this case highlighting + // Set that featurestate on that featuresetFeature + Map state = { + "highlight": true, + }; + await mapboxMap?.setFeatureStateForFeaturesetFeatureDescriptor( + featureset, featuresetFeatureId, state); + } + } - // Set FeatureState - await mapboxMap?.setFeatureStateForFeaturesetFeature( - featureSetFeature, data); + _onLongTap(context) async { + // Define the geometry to query, in this case the point where the user clicked. + var clicked = await mapboxMap?.pixelForCoordinate(context.point); + var renderedQueryGeometry = + RenderedQueryGeometry.fromScreenCoordinate(clicked!); - await Future.delayed(Duration(seconds: 2)); + // Query the featureset for the geometry + var featureset = FeaturesetDescriptor(featuresetId: "buildings", importId: "basemap"); + var queriedFeatures = await mapboxMap?.queryRenderedFeaturesForFeatureset( + geometry: renderedQueryGeometry, + featureset: featureset); + var featuresetFeatureId = queriedFeatures?.first.id; - var featureState = - await mapboxMap?.getFeatureStateForFeaturesetFeature(featureSetFeature); - print(featureState); + // Remove that feature state + if (featuresetFeatureId != null) { + mapboxMap?.removeFeatureStateForFeaturesetFeatureDescriptor(featureId: featuresetFeatureId, featureset: featureset); + } } @override Widget build(BuildContext context) { - return new Scaffold( - floatingActionButton: Padding( - padding: const EdgeInsets.all(12.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - FloatingActionButton( - child: Icon(Icons.swap_horiz), - heroTag: null, - onPressed: () { - setState( - () => isLight = !isLight, - ); - if (isLight) { - mapboxMap?.loadStyleURI(MapboxStyles.LIGHT); - } else { - mapboxMap?.loadStyleURI(MapboxStyles.DARK); - } - }), - SizedBox(height: 10), - ], - ), - ), + return Scaffold( body: MapWidget( - key: ValueKey("mapWidget"), - cameraOptions: CameraOptions( - center: Point( - coordinates: Position(24.94180921290157, 60.171227338006844)), - zoom: 15.0, - pitch: 30), - styleUri: MapboxStyles.STANDARD_EXPERIMENTAL, - textureView: true, - onMapCreated: _onMapCreated, - onTapListener: _onTap, - )); + key: ValueKey("mapWidget"), + cameraOptions: CameraOptions( + center: Point(coordinates: Position(24.9453, 60.1718)), + bearing: 49.92, + zoom: 16.35, + pitch: 40), + + /// DON'T USE Standard Experimental style in production, it will break over time. + /// Currently this feature is in preview. + styleUri: MapboxStyles.STANDARD_EXPERIMENTAL, + textureView: true, + onMapCreated: _onMapCreated, + onTapListener: _onTap, + onLongTapListener: _onLongTap, + )); } } diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift index faa77ac52..a24e64418 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift @@ -1295,9 +1295,11 @@ struct FeaturesetFeature { /// The identifier can be `nil` if the underlying source doesn't have identifiers for features. /// In this case it's impossible to set a feature state for an individual feature. var id: FeaturesetFeatureId? - /// A featureset descriptor denoting a featureset this feature belongs to. + /// A featureset descriptor denoting the featureset this feature belongs to. var featureset: FeaturesetDescriptor + /// A feature geometry. var geometry: [String?: Any?] + /// Feature JSON properties. var properties: [String: Any?] /// A feature state. /// @@ -3245,7 +3247,7 @@ protocol _MapInterface { /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. - func removeFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String, completion: @escaping (Result) -> Void) + func removeFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String?, completion: @escaping (Result) -> Void) /// Removes entries from a specified Feature. /// Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. /// @@ -3253,7 +3255,7 @@ protocol _MapInterface { /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. - func removeFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, stateKey: String, completion: @escaping (Result) -> Void) + func removeFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, stateKey: String?, completion: @escaping (Result) -> Void) /// Reset all the feature states within a featureset. /// /// Note that updates to feature state are asynchronous, so changes made by this method might not be @@ -4069,7 +4071,7 @@ class _MapInterfaceSetup { let args = message as! [Any?] let featuresetArg = args[0] as! FeaturesetDescriptor let featureIdArg = args[1] as! FeaturesetFeatureId - let stateKeyArg = args[2] as! String + let stateKeyArg: String? = nilOrValue(args[2]) api.removeFeatureStateForFeaturesetFeatureDescriptor(featureset: featuresetArg, featureId: featureIdArg, stateKey: stateKeyArg) { result in switch result { case .success: @@ -4094,7 +4096,7 @@ class _MapInterfaceSetup { removeFeatureStateForFeaturesetFeatureChannel.setMessageHandler { message, reply in let args = message as! [Any?] let featureArg = args[0] as! FeaturesetFeature - let stateKeyArg = args[1] as! String + let stateKeyArg: String? = nilOrValue(args[1]) api.removeFeatureStateForFeaturesetFeature(feature: featureArg, stateKey: stateKeyArg) { result in switch result { case .success: diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift index 2ec96b7c2..77b931a99 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift @@ -393,7 +393,7 @@ final class MapInterfaceController: _MapInterface { } } - func removeFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String, completion: @escaping (Result) -> Void) { + func removeFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String?, completion: @escaping (Result) -> Void) { self.mapboxMap.removeFeatureState(featureset: featureset.toMapFeaturesetDescriptor(), featureId: featureId.toMapFeaturesetFeatureId(), stateKey: stateKey) { error in if let error { completion(.failure(FlutterError( @@ -407,7 +407,7 @@ final class MapInterfaceController: _MapInterface { } } - func removeFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, stateKey: String, completion: @escaping (Result) -> Void) { + func removeFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, stateKey: String?, completion: @escaping (Result) -> Void) { self.mapboxMap.removeFeatureState(feature.toMapFeaturesetFeature(), stateKey: stateKey) { error in if let error { completion(.failure(FlutterError( diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index 388b88282..6340a1019 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -557,24 +557,27 @@ class MapboxMap extends ChangeNotifier { /// /// Note that updates to feature state are asynchronous, so changes made by this method might not be /// immediately visible using `getStateFeature`. - Future removeFeatureState(String sourceId, String? sourceLayerId, - String featureId, String? stateKey) => + Future removeFeatureState( + {required String sourceId, + required String? sourceLayerId, + required String featureId, + String? stateKey}) => _mapInterface.removeFeatureState( sourceId, sourceLayerId, featureId, stateKey); /// Removes entries from a feature state object of a feature in the specified featureset. /// Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. Future removeFeatureStateForFeaturesetFeatureDescriptor( - FeaturesetDescriptor featureset, - FeaturesetFeatureId featureId, - String stateKey) => + {required FeaturesetDescriptor featureset, + required FeaturesetFeatureId featureId, + String? stateKey}) => _mapInterface.removeFeatureStateForFeaturesetFeatureDescriptor( featureset, featureId, stateKey); /// Removes entries from a specified Feature. /// Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. Future removeFeatureStateForFeaturesetFeature( - FeaturesetFeature feature, String stateKey) => + {required FeaturesetFeature feature, String? stateKey}) => _mapInterface.removeFeatureStateForFeaturesetFeature(feature, stateKey); /// Reset all the feature states within a featureset. diff --git a/lib/src/pigeons/map_interfaces.dart b/lib/src/pigeons/map_interfaces.dart index cd5559bcc..5072a5e32 100644 --- a/lib/src/pigeons/map_interfaces.dart +++ b/lib/src/pigeons/map_interfaces.dart @@ -1402,11 +1402,13 @@ class FeaturesetFeature { /// In this case it's impossible to set a feature state for an individual feature. FeaturesetFeatureId? id; - /// A featureset descriptor denoting a featureset this feature belongs to. + /// A featureset descriptor denoting the featureset this feature belongs to. FeaturesetDescriptor featureset; + /// A feature geometry. Map geometry; + /// Feature JSON properties. Map properties; /// A feature state. @@ -4451,7 +4453,7 @@ class _MapInterface { Future removeFeatureStateForFeaturesetFeatureDescriptor( FeaturesetDescriptor featureset, FeaturesetFeatureId featureId, - String stateKey) async { + String? stateKey) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureStateForFeaturesetFeatureDescriptor$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = @@ -4483,7 +4485,7 @@ class _MapInterface { /// /// @return A `Cancelable` object that could be used to cancel the pending operation. Future removeFeatureStateForFeaturesetFeature( - FeaturesetFeature feature, String stateKey) async { + FeaturesetFeature feature, String? stateKey) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureStateForFeaturesetFeature$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = diff --git a/lib/src/turf_adapters.dart b/lib/src/turf_adapters.dart index 1bc5c0a7d..03b705ca5 100644 --- a/lib/src/turf_adapters.dart +++ b/lib/src/turf_adapters.dart @@ -86,4 +86,4 @@ final class Feature extends turf.Feature { geometry: feature.geometry, fields: feature.fields); } -} \ No newline at end of file +} From 7d1cd5c13b55bfbcd089ecaa8174c19d0eb6f649 Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Sun, 27 Oct 2024 13:22:26 -0400 Subject: [PATCH 10/28] Revert --- lib/src/mapbox_map.dart | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index 6340a1019..1de334af1 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -557,11 +557,8 @@ class MapboxMap extends ChangeNotifier { /// /// Note that updates to feature state are asynchronous, so changes made by this method might not be /// immediately visible using `getStateFeature`. - Future removeFeatureState( - {required String sourceId, - required String? sourceLayerId, - required String featureId, - String? stateKey}) => + Future removeFeatureState(String sourceId, String? sourceLayerId, + String featureId, String? stateKey) => _mapInterface.removeFeatureState( sourceId, sourceLayerId, featureId, stateKey); From 3a1cc8af288ecf49c89371f0223cf54ef3aeaa6f Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Mon, 28 Oct 2024 19:34:03 -0400 Subject: [PATCH 11/28] Build out geometries --- .../com/mapbox/maps/mapbox_maps/Extentions.kt | 6 ++ .../mapbox_maps/MapInterfaceController.kt | 4 +- .../maps/mapbox_maps/pigeons/MapInterfaces.kt | 8 +-- .../interactive_features_test.dart | 48 ++++++++++----- example/lib/interactive_features_example.dart | 16 ++--- .../Classes/Extensions.swift | 61 ++++++++++++++++--- .../Classes/Generated/MapInterfaces.swift | 4 +- .../Classes/MapInterfaceController.swift | 4 +- lib/src/pigeons/map_interfaces.dart | 8 +-- lib/src/style/mapbox_styles.dart | 1 + lib/src/turf_adapters.dart | 19 ++++++ 11 files changed, 133 insertions(+), 46 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt index 7e37d765f..9bb93df88 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt @@ -457,25 +457,31 @@ fun CameraBoundsOptions.toCameraBoundsOptions(): com.mapbox.maps.CameraBoundsOpt fun Geometry.toMap(): Map { return when (this) { is Point -> mapOf( + "type" to "Point", "coordinates" to listOf(this.latitude(), this.longitude()) ) is LineString -> mapOf( + "type" to "LineString", "coordinates" to this.coordinates().map { listOf(it.latitude(), it.longitude()) } ) is Polygon -> mapOf( + "type" to "Polygon", "coordinates" to this.coordinates().map { ring -> ring.map { listOf(it.latitude(), it.longitude()) } } ) is MultiPoint -> mapOf( + "type" to "MultiPoint", "coordinates" to this.coordinates().map { listOf(it.latitude(), it.longitude()) } ) is MultiLineString -> mapOf( + "type" to "MultiLineString", "coordinates" to this.coordinates().map { line -> line.map { listOf(it.latitude(), it.longitude()) } } ) is MultiPolygon -> mapOf( + "type" to "MultiPolygon", "coordinates" to this.coordinates().map { polygon -> polygon.map { ring -> ring.map { listOf(it.latitude(), it.longitude()) } diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt index 0e9d649c6..6c7e3eb9d 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt @@ -184,7 +184,7 @@ class MapInterfaceController( override fun queryRenderedFeaturesForTargets( geometry: _RenderedQueryGeometry, targets: List, - callback: (Result>) -> Unit + callback: (Result>) -> Unit ) { mapboxMap.queryRenderedFeatures( geometry.toRenderedQueryGeometry(context), @@ -267,7 +267,7 @@ class MapInterfaceController( @OptIn(MapboxExperimental::class) override fun querySourceFeaturesForFeatureset( target: FeaturesetQueryTarget, - callback: (Result>) -> Unit + callback: (Result>) -> Unit ) { mapboxMap.querySourceFeatures( target.featureset.toTypedFeaturesetDescriptor(), diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt index da83382d8..5803c34ec 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt @@ -3356,7 +3356,7 @@ interface _MapInterface { * @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. * @param targets An array of targets to query with. */ - fun queryRenderedFeaturesForTargets(geometry: _RenderedQueryGeometry, targets: List, callback: (Result>) -> Unit) + fun queryRenderedFeaturesForTargets(geometry: _RenderedQueryGeometry, targets: List, callback: (Result>) -> Unit) /** * Queries the map for rendered features with one typed featureset. * @@ -3389,7 +3389,7 @@ interface _MapInterface { * * @param target A featureset query target. */ - fun querySourceFeaturesForFeatureset(target: FeaturesetQueryTarget, callback: (Result>) -> Unit) + fun querySourceFeaturesForFeatureset(target: FeaturesetQueryTarget, callback: (Result>) -> Unit) /** * Returns all the leaves (original points) of a cluster (given its cluster_id) from a GeoJsonSource, with pagination support: limit is the number of leaves * to return (set to Infinity for all points), and offset is the amount of points to skip (for pagination). @@ -3959,7 +3959,7 @@ interface _MapInterface { val args = message as List val geometryArg = args[0] as _RenderedQueryGeometry val targetsArg = args[1] as List - api.queryRenderedFeaturesForTargets(geometryArg, targetsArg) { result: Result> -> + api.queryRenderedFeaturesForTargets(geometryArg, targetsArg) { result: Result> -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -4043,7 +4043,7 @@ interface _MapInterface { channel.setMessageHandler { message, reply -> val args = message as List val targetArg = args[0] as FeaturesetQueryTarget - api.querySourceFeaturesForFeatureset(targetArg) { result: Result> -> + api.querySourceFeaturesForFeatureset(targetArg) { result: Result> -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) diff --git a/example/integration_test/interactive_features_test.dart b/example/integration_test/interactive_features_test.dart index 6ca1c8768..33b530628 100644 --- a/example/integration_test/interactive_features_test.dart +++ b/example/integration_test/interactive_features_test.dart @@ -252,35 +252,51 @@ void main() { FeaturesetQueryTarget( featureset: featuresetLayer, filter: layerFilter, id: 2) ]; - Map expectedProperties = { - "name": "qux", - "filter": true, - "bar": 2 + + Map expectedFeatureRaw = { + "id": "2", + "type": "Feature", + "properties": { + "bar": 2, + "filter": true, + "name": "qux" + }, + "geometry": { + "type": "Point", + "coordinates": [ 0.01, 0.01 ] + } }; - Map expectedProperties2 = { - "type": "B", - "class": "poi", - "name": "nest2" + var expectedFeature = Feature.fromFeature(expectedFeatureRaw); + Map expectedFeatureRaw2 = { + "type": "Feature", + "properties": { + "name": "nest2", + "type": "B" + }, + "geometry": { + "type": "Point", + "coordinates": [ 0.01, 0.01 ] + }, + "id": "12" }; + var expectedFeature2 = Feature.fromFeature(expectedFeatureRaw2); var returnedQuery = await mapboxMap.queryRenderedFeaturesForTargets( - RenderedQueryGeometry.fromScreenCoordinate(coord), targets); - + RenderedQueryGeometry.fromScreenCoordinate(coord), targets); + var firstFeature = Feature.fromFeature(returnedQuery[0]!.queriedFeature.feature); + var secondFeature = Feature.fromFeature(returnedQuery[1]!.queriedFeature.feature); + expect(returnedQuery.length, 2); expect(returnedQuery[0]?.queryTargets?.length, 1); expect(returnedQuery[0]?.queryTargets?.last.id, 2); expect(returnedQuery[0]?.queryTargets?.last.featureset.layerId, "circle-2"); expect(returnedQuery[0]?.queryTargets?.last.filter, null); - //expect(returnedQuery[0]!.queriedFeature.feature["id"], 2); - expect(returnedQuery[0]!.queriedFeature.feature["properties"], - expectedProperties); + expect(expectedFeature, firstFeature); expect(returnedQuery[1]?.queryTargets?.length, 1); expect(returnedQuery[1]?.queryTargets?.last.id, 1); expect(returnedQuery[1]?.queryTargets?.last.featureset.featuresetId, "poi"); expect(returnedQuery[1]?.queryTargets?.last.featureset.importId, "nested"); expect(returnedQuery[1]?.queryTargets?.last.filter, null); - //expect(returnedQuery[1]!.queriedFeature.feature["id"], 12); - expect(returnedQuery[1]!.queriedFeature.feature["properties"], - expectedProperties2); + expect(expectedFeature2, secondFeature); }); } diff --git a/example/lib/interactive_features_example.dart b/example/lib/interactive_features_example.dart index 91961a31e..963627af2 100644 --- a/example/lib/interactive_features_example.dart +++ b/example/lib/interactive_features_example.dart @@ -37,16 +37,15 @@ class InteractiveFeaturesState extends State { // Query the featureset for the geometry var queriedFeatures = await mapboxMap?.queryRenderedFeaturesForFeatureset( geometry: renderedQueryGeometry, featureset: featureset); - var featuresetFeatureId = queriedFeatures?.first.id; + var featuresetFeature = queriedFeatures?.first; - if (featuresetFeatureId != null) { + if (featuresetFeature != null) { // Define the state to set for the feature, in this case highlighting // Set that featurestate on that featuresetFeature Map state = { "highlight": true, }; - await mapboxMap?.setFeatureStateForFeaturesetFeatureDescriptor( - featureset, featuresetFeatureId, state); + mapboxMap?.setFeatureStateForFeaturesetFeature(featuresetFeature, state); } } @@ -57,15 +56,16 @@ class InteractiveFeaturesState extends State { RenderedQueryGeometry.fromScreenCoordinate(clicked!); // Query the featureset for the geometry - var featureset = FeaturesetDescriptor(featuresetId: "buildings", importId: "basemap"); + var featureset = + FeaturesetDescriptor(featuresetId: "buildings", importId: "basemap"); var queriedFeatures = await mapboxMap?.queryRenderedFeaturesForFeatureset( - geometry: renderedQueryGeometry, - featureset: featureset); + geometry: renderedQueryGeometry, featureset: featureset); var featuresetFeatureId = queriedFeatures?.first.id; // Remove that feature state if (featuresetFeatureId != null) { - mapboxMap?.removeFeatureStateForFeaturesetFeatureDescriptor(featureId: featuresetFeatureId, featureset: featureset); + mapboxMap?.removeFeatureStateForFeaturesetFeatureDescriptor( + featureId: featuresetFeatureId, featureset: featureset); } } diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift index 4cd040d9d..4b6adc572 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift @@ -166,7 +166,6 @@ extension String { extension _RenderedQueryGeometry: RenderedQueryGeometryConvertible { var geometry: MapboxMaps.RenderedQueryGeometry { - // TODO switch type { case .sCREENBOX: guard let cgRect = convertValueToCGRect(value) else { fallthrough } @@ -178,13 +177,11 @@ extension _RenderedQueryGeometry: RenderedQueryGeometryConvertible { guard let cgPoints = convertValueToCGPoints(value) else { fallthrough } return RenderedQueryGeometry(shape: cgPoints) default: - // TODO Fix this return RenderedQueryGeometry(shape: []) } } } -/// Convert to extension? func convertValueToCGRect(_ value: String) -> CGRect? { let screenBoxArray = convertStringToDictionary(properties: value) guard let minCoord = screenBoxArray["min"] as? [String: Double] else { return nil } @@ -646,7 +643,6 @@ extension MapboxMaps.StylePropertyValue { } extension MapboxMaps.Geometry { - func toMap() -> [String: Any] { switch self { case .point(let point): @@ -655,7 +651,13 @@ extension MapboxMaps.Geometry { return line.toMap() case .polygon(let polygon): return polygon.toMap() - case .multiPoint, .multiLineString, .multiPolygon, .geometryCollection: + case .multiPoint(let multiPoint): + return multiPoint.toMap() + case .multiLineString(let multiLineString): + return multiLineString.toMap() + case .multiPolygon(let multiPolygon): + return multiPolygon.toMap() + case .geometryCollection: return [:] } } @@ -663,19 +665,62 @@ extension MapboxMaps.Geometry { extension Point { func toMap() -> [String: Any] { - return [COORDINATES: [coordinates.longitude, coordinates.latitude]] + return [ + "type": "Point", + COORDINATES: [coordinates.longitude, coordinates.latitude] + ] } } + extension LineString { func toMap() -> [String: Any] { - return [COORDINATES: coordinates.map({[$0.longitude, $0.latitude]})] + return [ + "type": "LineString", + COORDINATES: coordinates.map({[$0.longitude, $0.latitude]}) + ] } } + extension Polygon { func toMap() -> [String: Any] { - return [COORDINATES: coordinates.map({$0.map {[$0.longitude, $0.latitude]}})] + return [ + "type": "Polygon", + COORDINATES: coordinates.map({$0.map {[$0.longitude, $0.latitude]}}) + ] } } + +extension MultiPoint { + func toMap() -> [String: Any] { + return [ + "type": "MultiPoint", + COORDINATES: coordinates.map({[$0.longitude, $0.latitude]}) + ] + } +} + +extension MultiLineString { + func toMap() -> [String: Any] { + return [ + "type": "MultiLineString", + COORDINATES: coordinates.map({$0.map {[$0.longitude, $0.latitude]}}) + ] + } +} + +extension MultiPolygon { + func toMap() -> [String: Any] { + return [ + "type": "MultiPolygon", + COORDINATES: coordinates.map({ polygon in + polygon.map({ ring in + ring.map({[$0.longitude, $0.latitude]}) + }) + }) + ] + } +} + extension CLLocationCoordinate2D { func toDict() -> [String: Any] { return [COORDINATES: [self.longitude, self.latitude]] diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift index a24e64418..3f78c5ef5 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift @@ -3110,7 +3110,7 @@ protocol _MapInterface { /// /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param targets An array of targets to query with. - func queryRenderedFeaturesForTargets(geometry: _RenderedQueryGeometry, targets: [FeaturesetQueryTarget], completion: @escaping (Result<[QueriedRenderedFeature?], Error>) -> Void) + func queryRenderedFeaturesForTargets(geometry: _RenderedQueryGeometry, targets: [FeaturesetQueryTarget], completion: @escaping (Result<[QueriedRenderedFeature], Error>) -> Void) /// Queries the map for rendered features with one typed featureset. /// /// The results array will contain features of the type specified by this featureset. @@ -3135,7 +3135,7 @@ protocol _MapInterface { /// Queries the source features for a given featureset. /// /// @param target A featureset query target. - func querySourceFeaturesForFeatureset(target: FeaturesetQueryTarget, completion: @escaping (Result<[QueriedSourceFeature?], Error>) -> Void) + func querySourceFeaturesForFeatureset(target: FeaturesetQueryTarget, completion: @escaping (Result<[QueriedSourceFeature], Error>) -> Void) /// Returns all the leaves (original points) of a cluster (given its cluster_id) from a GeoJsonSource, with pagination support: limit is the number of leaves /// to return (set to Infinity for all points), and offset is the amount of points to skip (for pagination). /// diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift index 77b931a99..73f756def 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift @@ -198,7 +198,7 @@ final class MapInterfaceController: _MapInterface { } } - func queryRenderedFeaturesForTargets(geometry: _RenderedQueryGeometry, targets: [FeaturesetQueryTarget], completion: @escaping (Result<[QueriedRenderedFeature?], any Error>) -> Void) { + func queryRenderedFeaturesForTargets(geometry: _RenderedQueryGeometry, targets: [FeaturesetQueryTarget], completion: @escaping (Result<[QueriedRenderedFeature], any Error>) -> Void) { self.mapboxMap.queryRenderedFeatures(with: geometry, targets: targets.map({$0.toMapFeaturesetQueryTarget()})) { result in switch result { case .success(let features): @@ -248,7 +248,7 @@ final class MapInterfaceController: _MapInterface { } } - func querySourceFeaturesForFeatureset(target: FeaturesetQueryTarget, completion: @escaping (Result<[QueriedSourceFeature?], any Error>) -> Void) { + func querySourceFeaturesForFeatureset(target: FeaturesetQueryTarget, completion: @escaping (Result<[QueriedSourceFeature], any Error>) -> Void) { self.mapboxMap.querySourceFeatures(for: target.toMapFeaturesetQueryTarget()) { result in switch result { case .success(let features): diff --git a/lib/src/pigeons/map_interfaces.dart b/lib/src/pigeons/map_interfaces.dart index 5072a5e32..38cba987f 100644 --- a/lib/src/pigeons/map_interfaces.dart +++ b/lib/src/pigeons/map_interfaces.dart @@ -3883,7 +3883,7 @@ class _MapInterface { /// /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param targets An array of targets to query with. - Future> queryRenderedFeaturesForTargets( + Future> queryRenderedFeaturesForTargets( _RenderedQueryGeometry geometry, List targets) async { final String pigeonVar_channelName = @@ -3911,7 +3911,7 @@ class _MapInterface { ); } else { return (pigeonVar_replyList[0] as List?)! - .cast(); + .cast(); } } @@ -4031,7 +4031,7 @@ class _MapInterface { /// Queries the source features for a given featureset. /// /// @param target A featureset query target. - Future> querySourceFeaturesForFeatureset( + Future> querySourceFeaturesForFeatureset( FeaturesetQueryTarget target) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.querySourceFeaturesForFeatureset$pigeonVar_messageChannelSuffix'; @@ -4058,7 +4058,7 @@ class _MapInterface { ); } else { return (pigeonVar_replyList[0] as List?)! - .cast(); + .cast(); } } diff --git a/lib/src/style/mapbox_styles.dart b/lib/src/style/mapbox_styles.dart index 6db058e27..b046a9622 100644 --- a/lib/src/style/mapbox_styles.dart +++ b/lib/src/style/mapbox_styles.dart @@ -39,6 +39,7 @@ class MapboxStyles { /// NOT FOR PRODUCTION USE. An experimental version of the Mapbox Standard style. /// This style is used for testing new features and changes to the Mapbox Standard style. The style may change or be removed at any time. + @experimental static const String STANDARD_EXPERIMENTAL = "mapbox://styles/mapbox-map-design/standard-experimental-ime"; } diff --git a/lib/src/turf_adapters.dart b/lib/src/turf_adapters.dart index 03b705ca5..cca2d3a53 100644 --- a/lib/src/turf_adapters.dart +++ b/lib/src/turf_adapters.dart @@ -77,6 +77,11 @@ final class Feature extends turf.Feature { return Feature.fromJson(jsonDecode(result.first as String)); } + factory Feature.fromFeature(Map feature) { + var valid = convertToValidMap(feature as Map); + return Feature.fromJson(valid); + } + factory Feature.fromJson(Map json) { final feature = turf.Feature.fromJson(json); return Feature( @@ -87,3 +92,17 @@ final class Feature extends turf.Feature { fields: feature.fields); } } + +Map convertToValidMap(Map input) { + return input.map((key, value) { + if (key is! String) { + throw Exception( + "Invalid key type. Expected String but got ${key.runtimeType}"); + } + if (value is Map) { + // Recursively convert nested maps + return MapEntry(key, convertToValidMap(value)); + } + return MapEntry(key, value); + }); +} \ No newline at end of file From 76a83e931dffcd540d5afbaaedaacc6faa4c25de Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Tue, 29 Oct 2024 18:53:02 -0400 Subject: [PATCH 12/28] Address review feedback --- .../mapbox_maps/MapInterfaceController.kt | 6 +- .../maps/mapbox_maps/pigeons/MapInterfaces.kt | 18 +++--- .../interactive_features_test.dart | 58 +++++++------------ example/lib/interactive_features_example.dart | 4 +- .../Classes/Generated/MapInterfaces.swift | 30 +++++----- .../Classes/MapInterfaceController.swift | 6 +- lib/src/mapbox_map.dart | 23 ++++++-- lib/src/pigeons/map_interfaces.dart | 29 ++++++++-- lib/src/turf_adapters.dart | 2 +- 9 files changed, 95 insertions(+), 81 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt index 6c7e3eb9d..f889203c5 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt @@ -265,7 +265,7 @@ class MapInterfaceController( } @OptIn(MapboxExperimental::class) - override fun querySourceFeaturesForFeatureset( + override fun querySourceFeaturesForTargets( target: FeaturesetQueryTarget, callback: (Result>) -> Unit ) { @@ -356,7 +356,7 @@ class MapInterfaceController( } @OptIn(MapboxExperimental::class, MapboxDelicateApi::class) - override fun setFeatureStateForFeaturesetFeatureDescriptor( + override fun setFeatureStateForFeaturesetDescriptor( featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: Map, @@ -450,7 +450,7 @@ class MapInterfaceController( } @OptIn(MapboxExperimental::class, MapboxDelicateApi::class) - override fun removeFeatureStateForFeaturesetFeatureDescriptor( + override fun removeFeatureStateForFeaturesetDescriptor( featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String?, diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt index 5803c34ec..af47141cd 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt @@ -3389,7 +3389,7 @@ interface _MapInterface { * * @param target A featureset query target. */ - fun querySourceFeaturesForFeatureset(target: FeaturesetQueryTarget, callback: (Result>) -> Unit) + fun querySourceFeaturesForTargets(target: FeaturesetQueryTarget, callback: (Result>) -> Unit) /** * Returns all the leaves (original points) of a cluster (given its cluster_id) from a GeoJsonSource, with pagination support: limit is the number of leaves * to return (set to Infinity for all points), and offset is the amount of points to skip (for pagination). @@ -3455,7 +3455,7 @@ interface _MapInterface { * * @return A `Cancelable` object that could be used to cancel the pending operation. */ - fun setFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: Map, callback: (Result) -> Unit) + fun setFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: Map, callback: (Result) -> Unit) /** * Update the state map of an individual feature. * @@ -3523,7 +3523,7 @@ interface _MapInterface { * * @return A `Cancelable` object that could be used to cancel the pending operation. */ - fun removeFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String?, callback: (Result) -> Unit) + fun removeFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String?, callback: (Result) -> Unit) /** * Removes entries from a specified Feature. * Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. @@ -4038,12 +4038,12 @@ interface _MapInterface { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.querySourceFeaturesForFeatureset$separatedMessageChannelSuffix", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.querySourceFeaturesForTargets$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List val targetArg = args[0] as FeaturesetQueryTarget - api.querySourceFeaturesForFeatureset(targetArg) { result: Result> -> + api.querySourceFeaturesForTargets(targetArg) { result: Result> -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -4145,14 +4145,14 @@ interface _MapInterface { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.setFeatureStateForFeaturesetFeatureDescriptor$separatedMessageChannelSuffix", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.setFeatureStateForFeaturesetDescriptor$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List val featuresetArg = args[0] as FeaturesetDescriptor val featureIdArg = args[1] as FeaturesetFeatureId val stateArg = args[2] as Map - api.setFeatureStateForFeaturesetFeatureDescriptor(featuresetArg, featureIdArg, stateArg) { result: Result -> + api.setFeatureStateForFeaturesetDescriptor(featuresetArg, featureIdArg, stateArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -4271,14 +4271,14 @@ interface _MapInterface { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureStateForFeaturesetFeatureDescriptor$separatedMessageChannelSuffix", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureStateForFeaturesetDescriptor$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List val featuresetArg = args[0] as FeaturesetDescriptor val featureIdArg = args[1] as FeaturesetFeatureId val stateKeyArg = args[2] as String? - api.removeFeatureStateForFeaturesetFeatureDescriptor(featuresetArg, featureIdArg, stateKeyArg) { result: Result -> + api.removeFeatureStateForFeaturesetDescriptor(featuresetArg, featureIdArg, stateKeyArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) diff --git a/example/integration_test/interactive_features_test.dart b/example/integration_test/interactive_features_test.dart index 33b530628..cd98eeec3 100644 --- a/example/integration_test/interactive_features_test.dart +++ b/example/integration_test/interactive_features_test.dart @@ -21,7 +21,7 @@ void main() { var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); mapboxMap.style.setStyleJSON(styleJson); - await Future.delayed(Duration(seconds: 3)); + await Future.delayed(Duration(seconds: 4)); // test queryRenderedFeaturesForFeatureset var coord = await mapboxMap @@ -140,7 +140,7 @@ void main() { await Future.delayed(Duration(seconds: 1)); // test set and get featurestate - await mapboxMap.setFeatureStateForFeaturesetFeatureDescriptor( + await mapboxMap.setFeatureStateForFeaturesetDescriptor( featuresetDescriptor, featuresetID, state); var returnedFeatureState = await mapboxMap.getFeatureStateForFeaturesetDescriptor( @@ -148,7 +148,7 @@ void main() { expect(returnedFeatureState, state); // test remove featurestate - await mapboxMap.removeFeatureStateForFeaturesetFeatureDescriptor( + await mapboxMap.removeFeatureStateForFeaturesetDescriptor( featureset: featuresetDescriptor, featureId: featuresetID, stateKey: "highlight"); @@ -186,7 +186,7 @@ void main() { "class": "poi" }; - await mapboxMap.setFeatureStateForFeaturesetFeatureDescriptor( + await mapboxMap.setFeatureStateForFeaturesetDescriptor( featuresetDescriptor, featuresetID, state); var queryResult = await mapboxMap.queryRenderedFeaturesInViewport( featureset: featuresetDescriptor, filter: filter); @@ -252,51 +252,37 @@ void main() { FeaturesetQueryTarget( featureset: featuresetLayer, filter: layerFilter, id: 2) ]; - - Map expectedFeatureRaw = { - "id": "2", - "type": "Feature", - "properties": { - "bar": 2, - "filter": true, - "name": "qux" - }, - "geometry": { - "type": "Point", - "coordinates": [ 0.01, 0.01 ] - } + Map expectedProperties = { + "name": "qux", + "filter": true, + "bar": 2 }; - var expectedFeature = Feature.fromFeature(expectedFeatureRaw); - Map expectedFeatureRaw2 = { - "type": "Feature", - "properties": { - "name": "nest2", - "type": "B" - }, - "geometry": { - "type": "Point", - "coordinates": [ 0.01, 0.01 ] - }, - "id": "12" + Map expectedProperties2 = { + "type": "B", + "class": "poi", + "name": "nest2" }; - var expectedFeature2 = Feature.fromFeature(expectedFeatureRaw2); var returnedQuery = await mapboxMap.queryRenderedFeaturesForTargets( - RenderedQueryGeometry.fromScreenCoordinate(coord), targets); - var firstFeature = Feature.fromFeature(returnedQuery[0]!.queriedFeature.feature); - var secondFeature = Feature.fromFeature(returnedQuery[1]!.queriedFeature.feature); - + RenderedQueryGeometry.fromScreenCoordinate(coord), targets); + var firstFeature = + Feature.fromFeature(returnedQuery[0]!.queriedFeature.feature); + var secondFeature = + Feature.fromFeature(returnedQuery[1]!.queriedFeature.feature); + expect(returnedQuery.length, 2); expect(returnedQuery[0]?.queryTargets?.length, 1); expect(returnedQuery[0]?.queryTargets?.last.id, 2); expect(returnedQuery[0]?.queryTargets?.last.featureset.layerId, "circle-2"); expect(returnedQuery[0]?.queryTargets?.last.filter, null); - expect(expectedFeature, firstFeature); + expect(firstFeature.id.toString(), "2"); + expect(firstFeature.properties, expectedProperties); expect(returnedQuery[1]?.queryTargets?.length, 1); expect(returnedQuery[1]?.queryTargets?.last.id, 1); expect(returnedQuery[1]?.queryTargets?.last.featureset.featuresetId, "poi"); expect(returnedQuery[1]?.queryTargets?.last.featureset.importId, "nested"); expect(returnedQuery[1]?.queryTargets?.last.filter, null); - expect(expectedFeature2, secondFeature); + expect(secondFeature.id.toString(), "12"); + expect(secondFeature.properties, expectedProperties2); }); } diff --git a/example/lib/interactive_features_example.dart b/example/lib/interactive_features_example.dart index 963627af2..307c215c3 100644 --- a/example/lib/interactive_features_example.dart +++ b/example/lib/interactive_features_example.dart @@ -64,8 +64,8 @@ class InteractiveFeaturesState extends State { // Remove that feature state if (featuresetFeatureId != null) { - mapboxMap?.removeFeatureStateForFeaturesetFeatureDescriptor( - featureId: featuresetFeatureId, featureset: featureset); + mapboxMap?.removeFeatureStateForFeaturesetDescriptor( + featureset: featureset, featureId: featuresetFeatureId); } } diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift index 3f78c5ef5..04c57400e 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift @@ -3135,7 +3135,7 @@ protocol _MapInterface { /// Queries the source features for a given featureset. /// /// @param target A featureset query target. - func querySourceFeaturesForFeatureset(target: FeaturesetQueryTarget, completion: @escaping (Result<[QueriedSourceFeature], Error>) -> Void) + func querySourceFeaturesForTargets(target: FeaturesetQueryTarget, completion: @escaping (Result<[QueriedSourceFeature], Error>) -> Void) /// Returns all the leaves (original points) of a cluster (given its cluster_id) from a GeoJsonSource, with pagination support: limit is the number of leaves /// to return (set to Infinity for all points), and offset is the amount of points to skip (for pagination). /// @@ -3191,7 +3191,7 @@ protocol _MapInterface { /// @param state Map of entries to update with their respective new values /// /// @return A `Cancelable` object that could be used to cancel the pending operation. - func setFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: [String: Any?], completion: @escaping (Result) -> Void) + func setFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: [String: Any?], completion: @escaping (Result) -> Void) /// Update the state map of an individual feature. /// /// The feature should have a non-nil ``FeaturesetFeatureType/id``. Otherwise, @@ -3247,7 +3247,7 @@ protocol _MapInterface { /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. - func removeFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String?, completion: @escaping (Result) -> Void) + func removeFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String?, completion: @escaping (Result) -> Void) /// Removes entries from a specified Feature. /// Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. /// @@ -3760,12 +3760,12 @@ class _MapInterfaceSetup { /// Queries the source features for a given featureset. /// /// @param target A featureset query target. - let querySourceFeaturesForFeaturesetChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.querySourceFeaturesForFeatureset\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + let querySourceFeaturesForTargetsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.querySourceFeaturesForTargets\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { - querySourceFeaturesForFeaturesetChannel.setMessageHandler { message, reply in + querySourceFeaturesForTargetsChannel.setMessageHandler { message, reply in let args = message as! [Any?] let targetArg = args[0] as! FeaturesetQueryTarget - api.querySourceFeaturesForFeatureset(target: targetArg) { result in + api.querySourceFeaturesForTargets(target: targetArg) { result in switch result { case .success(let res): reply(wrapResult(res)) @@ -3775,7 +3775,7 @@ class _MapInterfaceSetup { } } } else { - querySourceFeaturesForFeaturesetChannel.setMessageHandler(nil) + querySourceFeaturesForTargetsChannel.setMessageHandler(nil) } /// Returns all the leaves (original points) of a cluster (given its cluster_id) from a GeoJsonSource, with pagination support: limit is the number of leaves /// to return (set to Infinity for all points), and offset is the amount of points to skip (for pagination). @@ -3904,14 +3904,14 @@ class _MapInterfaceSetup { /// @param state Map of entries to update with their respective new values /// /// @return A `Cancelable` object that could be used to cancel the pending operation. - let setFeatureStateForFeaturesetFeatureDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.setFeatureStateForFeaturesetFeatureDescriptor\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + let setFeatureStateForFeaturesetDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.setFeatureStateForFeaturesetDescriptor\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { - setFeatureStateForFeaturesetFeatureDescriptorChannel.setMessageHandler { message, reply in + setFeatureStateForFeaturesetDescriptorChannel.setMessageHandler { message, reply in let args = message as! [Any?] let featuresetArg = args[0] as! FeaturesetDescriptor let featureIdArg = args[1] as! FeaturesetFeatureId let stateArg = args[2] as! [String: Any?] - api.setFeatureStateForFeaturesetFeatureDescriptor(featureset: featuresetArg, featureId: featureIdArg, state: stateArg) { result in + api.setFeatureStateForFeaturesetDescriptor(featureset: featuresetArg, featureId: featureIdArg, state: stateArg) { result in switch result { case .success: reply(wrapResult(nil)) @@ -3921,7 +3921,7 @@ class _MapInterfaceSetup { } } } else { - setFeatureStateForFeaturesetFeatureDescriptorChannel.setMessageHandler(nil) + setFeatureStateForFeaturesetDescriptorChannel.setMessageHandler(nil) } /// Update the state map of an individual feature. /// @@ -4065,14 +4065,14 @@ class _MapInterfaceSetup { /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. - let removeFeatureStateForFeaturesetFeatureDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureStateForFeaturesetFeatureDescriptor\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + let removeFeatureStateForFeaturesetDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureStateForFeaturesetDescriptor\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { - removeFeatureStateForFeaturesetFeatureDescriptorChannel.setMessageHandler { message, reply in + removeFeatureStateForFeaturesetDescriptorChannel.setMessageHandler { message, reply in let args = message as! [Any?] let featuresetArg = args[0] as! FeaturesetDescriptor let featureIdArg = args[1] as! FeaturesetFeatureId let stateKeyArg: String? = nilOrValue(args[2]) - api.removeFeatureStateForFeaturesetFeatureDescriptor(featureset: featuresetArg, featureId: featureIdArg, stateKey: stateKeyArg) { result in + api.removeFeatureStateForFeaturesetDescriptor(featureset: featuresetArg, featureId: featureIdArg, stateKey: stateKeyArg) { result in switch result { case .success: reply(wrapResult(nil)) @@ -4082,7 +4082,7 @@ class _MapInterfaceSetup { } } } else { - removeFeatureStateForFeaturesetFeatureDescriptorChannel.setMessageHandler(nil) + removeFeatureStateForFeaturesetDescriptorChannel.setMessageHandler(nil) } /// Removes entries from a specified Feature. /// Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift index 73f756def..0cbdc2f1c 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift @@ -248,7 +248,7 @@ final class MapInterfaceController: _MapInterface { } } - func querySourceFeaturesForFeatureset(target: FeaturesetQueryTarget, completion: @escaping (Result<[QueriedSourceFeature], any Error>) -> Void) { + func querySourceFeaturesForTargets(target: FeaturesetQueryTarget, completion: @escaping (Result<[QueriedSourceFeature], any Error>) -> Void) { self.mapboxMap.querySourceFeatures(for: target.toMapFeaturesetQueryTarget()) { result in switch result { case .success(let features): @@ -315,7 +315,7 @@ final class MapInterfaceController: _MapInterface { } } - func setFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: [String: Any?], completion: @escaping (Result) -> Void) { + func setFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: [String: Any?], completion: @escaping (Result) -> Void) { guard let state = JSONObject.init(turfRawValue: state) else { return } @@ -393,7 +393,7 @@ final class MapInterfaceController: _MapInterface { } } - func removeFeatureStateForFeaturesetFeatureDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String?, completion: @escaping (Result) -> Void) { + func removeFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String?, completion: @escaping (Result) -> Void) { self.mapboxMap.removeFeatureState(featureset: featureset.toMapFeaturesetDescriptor(), featureId: featureId.toMapFeaturesetFeatureId(), stateKey: stateKey) { error in if let error { completion(.failure(FlutterError( diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index 1de334af1..3e59d7e78 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -444,6 +444,7 @@ class MapboxMap extends ChangeNotifier { options); /// Queries the map for rendered features using featureset descriptors. + @experimental Future> queryRenderedFeaturesForTargets( RenderedQueryGeometry geometry, List targets) async => @@ -452,6 +453,7 @@ class MapboxMap extends ChangeNotifier { targets); /// Queries the map for rendered features with one typed featureset. + @experimental Future> queryRenderedFeaturesForFeatureset( {required RenderedQueryGeometry geometry, required FeaturesetDescriptor featureset, @@ -462,6 +464,7 @@ class MapboxMap extends ChangeNotifier { filter); /// Queries all rendered features in current viewport, using one typed featureset. + @experimental Future> queryRenderedFeaturesInViewport( {required FeaturesetDescriptor featureset, String? filter}) async => _mapInterface.queryRenderedFeaturesInViewport(featureset, filter); @@ -472,9 +475,10 @@ class MapboxMap extends ChangeNotifier { _mapInterface.querySourceFeatures(sourceId, options); /// Queries the source features for a given featureset. - Future> querySourceFeaturesForFeatureset( + @experimental + Future> querySourceFeaturesForTargets( FeaturesetQueryTarget target) async => - _mapInterface.querySourceFeaturesForFeatureset(target); + _mapInterface.querySourceFeaturesForTargets(target); /// Returns all the leaves (original points) of a cluster (given its cluster_id) from a GeoJsonSource, with pagination support: limit is the number of leaves /// to return (set to Infinity for all points), and offset is the amount of points to skip (for pagination). @@ -516,17 +520,19 @@ class MapboxMap extends ChangeNotifier { /// Update the state map of a feature within a featureset. /// Update entries in the state map of a given feature within a style source. Only entries listed in the state map /// will be updated. An entry in the feature state map that is not listed in `state` will retain its previous value. - Future setFeatureStateForFeaturesetFeatureDescriptor( + @experimental + Future setFeatureStateForFeaturesetDescriptor( FeaturesetDescriptor featureset, FeaturesetFeatureId featureId, Map state) => - _mapInterface.setFeatureStateForFeaturesetFeatureDescriptor( + _mapInterface.setFeatureStateForFeaturesetDescriptor( featureset, featureId, state); /// Update the state map of an individual feature. /// /// The feature should have a non-nil ``FeaturesetFeatureType/id``. Otherwise, /// the operation will be no-op and callback will receive an error. + @experimental Future setFeatureStateForFeaturesetFeature( FeaturesetFeature feature, Map state) => _mapInterface.setFeatureStateForFeaturesetFeature(feature, state); @@ -540,12 +546,14 @@ class MapboxMap extends ChangeNotifier { _mapInterface.getFeatureState(sourceId, sourceLayerId, featureId); /// Get the state map of a feature within a style source. + @experimental Future> getFeatureStateForFeaturesetDescriptor( FeaturesetDescriptor featureset, FeaturesetFeatureId featureId) => _mapInterface.getFeatureStateForFeaturesetDescriptor( featureset, featureId); /// Get the state map of a feature within a style source. + @experimental Future> getFeatureStateForFeaturesetFeature( FeaturesetFeature feature) => _mapInterface.getFeatureStateForFeaturesetFeature(feature); @@ -564,15 +572,17 @@ class MapboxMap extends ChangeNotifier { /// Removes entries from a feature state object of a feature in the specified featureset. /// Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. - Future removeFeatureStateForFeaturesetFeatureDescriptor( + @experimental + Future removeFeatureStateForFeaturesetDescriptor( {required FeaturesetDescriptor featureset, required FeaturesetFeatureId featureId, String? stateKey}) => - _mapInterface.removeFeatureStateForFeaturesetFeatureDescriptor( + _mapInterface.removeFeatureStateForFeaturesetDescriptor( featureset, featureId, stateKey); /// Removes entries from a specified Feature. /// Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. + @experimental Future removeFeatureStateForFeaturesetFeature( {required FeaturesetFeature feature, String? stateKey}) => _mapInterface.removeFeatureStateForFeaturesetFeature(feature, stateKey); @@ -581,6 +591,7 @@ class MapboxMap extends ChangeNotifier { /// /// Note that updates to feature state are asynchronous, so changes made by this method might not be /// immediately visible using ``MapboxMap/getFeatureState(_:callback:)``. + @experimental Future resetFeatureStatesForFeatureset( FeaturesetDescriptor featureset) => _mapInterface.resetFeatureStatesForFeatureset(featureset); diff --git a/lib/src/pigeons/map_interfaces.dart b/lib/src/pigeons/map_interfaces.dart index 38cba987f..d7474c47a 100644 --- a/lib/src/pigeons/map_interfaces.dart +++ b/lib/src/pigeons/map_interfaces.dart @@ -1209,6 +1209,7 @@ class QueriedRenderedFeature { /// An array of feature query targets that correspond to this queried feature. /// /// - Note: Returned query targets will omit the original `filter` data. + @experimental List? queryTargets; Object encode() { @@ -1307,6 +1308,7 @@ class QueriedFeature { /// /// - Warning: There is no guarantee of identifier persistency. This depends on the underlying source of the features and may vary from style to style. /// If you want to store the identifiers persistently, please make sure that the style or source provides this guarantee. +@experimental class FeaturesetFeatureId { FeaturesetFeatureId({ required this.id, @@ -1339,6 +1341,7 @@ class FeaturesetFeatureId { /// /// The descriptor instance acts as a universal target for interactions or querying rendered features (see /// ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)``). +@experimental class FeaturesetDescriptor { FeaturesetDescriptor({ this.featuresetId, @@ -1387,6 +1390,7 @@ class FeaturesetDescriptor { /// A basic feature of a featureset. /// /// The featureset feature is different to the `Turf.Feature`. The latter represents any GeoJSON feature, while the former is a high level representation of features. +@experimental class FeaturesetFeature { FeaturesetFeature({ this.id, @@ -1441,6 +1445,7 @@ class FeaturesetFeature { } /// Defines the parameters for querying features from a Featureset with an optional filter and id. +@experimental class FeaturesetQueryTarget { FeaturesetQueryTarget({ required this.featureset, @@ -3883,6 +3888,7 @@ class _MapInterface { /// /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param targets An array of targets to query with. + @experimental Future> queryRenderedFeaturesForTargets( _RenderedQueryGeometry geometry, List targets) async { @@ -3922,6 +3928,7 @@ class _MapInterface { /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param featureset A typed featureset to query with. /// @param filter An additional filter for features. + @experimental Future> queryRenderedFeaturesForFeatureset( _RenderedQueryGeometry geometry, FeaturesetDescriptor featureset, @@ -3961,6 +3968,7 @@ class _MapInterface { /// /// @param featureset A typed featureset to query with. /// @param filter An additional filter for features. + @experimental Future> queryRenderedFeaturesInViewport( FeaturesetDescriptor featureset, String? filter) async { final String pigeonVar_channelName = @@ -4031,10 +4039,11 @@ class _MapInterface { /// Queries the source features for a given featureset. /// /// @param target A featureset query target. - Future> querySourceFeaturesForFeatureset( + @experimental + Future> querySourceFeaturesForTargets( FeaturesetQueryTarget target) async { final String pigeonVar_channelName = - 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.querySourceFeaturesForFeatureset$pigeonVar_messageChannelSuffix'; + 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.querySourceFeaturesForTargets$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, @@ -4230,12 +4239,13 @@ class _MapInterface { /// @param state Map of entries to update with their respective new values /// /// @return A `Cancelable` object that could be used to cancel the pending operation. - Future setFeatureStateForFeaturesetFeatureDescriptor( + @experimental + Future setFeatureStateForFeaturesetDescriptor( FeaturesetDescriptor featureset, FeaturesetFeatureId featureId, Map state) async { final String pigeonVar_channelName = - 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.setFeatureStateForFeaturesetFeatureDescriptor$pigeonVar_messageChannelSuffix'; + 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.setFeatureStateForFeaturesetDescriptor$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, @@ -4266,6 +4276,7 @@ class _MapInterface { /// @param state Map of entries to update with their respective new values /// /// @return A `Cancelable` object that could be used to cancel the pending operation. + @experimental Future setFeatureStateForFeaturesetFeature( FeaturesetFeature feature, Map state) async { final String pigeonVar_channelName = @@ -4337,6 +4348,7 @@ class _MapInterface { /// @param featureId Identifier of the feature whose state should be queried. /// /// @return A `Cancelable` object that could be used to cancel the pending query. + @experimental Future> getFeatureStateForFeaturesetDescriptor( FeaturesetDescriptor featureset, FeaturesetFeatureId featureId) async { final String pigeonVar_channelName = @@ -4373,6 +4385,7 @@ class _MapInterface { /// @param feature An interactive feature to query the state from. /// /// @return A `Cancelable` object that could be used to cancel the pending query. + @experimental Future> getFeatureStateForFeaturesetFeature( FeaturesetFeature feature) async { final String pigeonVar_channelName = @@ -4450,12 +4463,13 @@ class _MapInterface { /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. - Future removeFeatureStateForFeaturesetFeatureDescriptor( + @experimental + Future removeFeatureStateForFeaturesetDescriptor( FeaturesetDescriptor featureset, FeaturesetFeatureId featureId, String? stateKey) async { final String pigeonVar_channelName = - 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureStateForFeaturesetFeatureDescriptor$pigeonVar_messageChannelSuffix'; + 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureStateForFeaturesetDescriptor$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, @@ -4484,6 +4498,7 @@ class _MapInterface { /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. + @experimental Future removeFeatureStateForFeaturesetFeature( FeaturesetFeature feature, String? stateKey) async { final String pigeonVar_channelName = @@ -4517,6 +4532,7 @@ class _MapInterface { /// @param featureset A featureset descriptor /// /// @return A `Cancelable` object that could be used to cancel the pending operation. + @experimental Future resetFeatureStatesForFeatureset( FeaturesetDescriptor featureset) async { final String pigeonVar_channelName = @@ -7364,6 +7380,7 @@ class StyleManager { /// Returns the available featuresets in the currently loaded style. /// /// - Note: This function should only be called after the style is fully loaded; otherwise, the result may be unreliable. + @experimental Future> getFeaturesets() async { final String pigeonVar_channelName = 'dev.flutter.pigeon.mapbox_maps_flutter.StyleManager.getFeaturesets$pigeonVar_messageChannelSuffix'; diff --git a/lib/src/turf_adapters.dart b/lib/src/turf_adapters.dart index cca2d3a53..be76d48ba 100644 --- a/lib/src/turf_adapters.dart +++ b/lib/src/turf_adapters.dart @@ -105,4 +105,4 @@ Map convertToValidMap(Map input) { } return MapEntry(key, value); }); -} \ No newline at end of file +} From f39abf8431b1c806f72bed232f8e56ba2781f398 Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Wed, 30 Oct 2024 20:13:33 -0400 Subject: [PATCH 13/28] Add framework for typed interactions --- .../maps/mapbox_maps/pigeons/MapInterfaces.kt | 110 +++++++++++++++- example/lib/interactive_features_example.dart | 20 ++- .../Classes/Extensions.swift | 21 +++ .../Classes/Generated/MapInterfaces.swift | 90 ++++++++++++- .../Classes/MapInterfaceController.swift | 10 +- lib/src/mapbox_map.dart | 5 +- lib/src/pigeons/map_interfaces.dart | 123 +++++++++++++++--- 7 files changed, 347 insertions(+), 32 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt index af47141cd..5c79a252b 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt @@ -229,6 +229,17 @@ enum class ViewAnnotationAnchor(val raw: Int) { } } +enum class InteractionType(val raw: Int) { + CLICK(0), + LONG_CLICK(1); + + companion object { + fun ofRaw(raw: Int): InteractionType? { + return values().firstOrNull { it.raw == raw } + } + } +} + /** Type information of the variant's content */ enum class Type(val raw: Int) { SCREEN_BOX(0), @@ -1386,6 +1397,49 @@ data class FeaturesetFeatureId( } } +/** Generated class from Pigeon that represents data sent in messages. */ +data class Interaction( + val typedFeaturesetDescriptor: TypedFeaturesetDescriptor, + val interactionType: InteractionType, + val filter: String? = null +) { + companion object { + fun fromList(pigeonVar_list: List): Interaction { + val typedFeaturesetDescriptor = pigeonVar_list[0] as TypedFeaturesetDescriptor + val interactionType = pigeonVar_list[1] as InteractionType + val filter = pigeonVar_list[2] as String? + return Interaction(typedFeaturesetDescriptor, interactionType, filter) + } + } + fun toList(): List { + return listOf( + typedFeaturesetDescriptor, + interactionType, + filter, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class TypedFeaturesetDescriptor( + val featuresetDescriptor: FeaturesetDescriptor, + val featuresetType: String +) { + companion object { + fun fromList(pigeonVar_list: List): TypedFeaturesetDescriptor { + val featuresetDescriptor = pigeonVar_list[0] as FeaturesetDescriptor + val featuresetType = pigeonVar_list[1] as String + return TypedFeaturesetDescriptor(featuresetDescriptor, featuresetType) + } + } + fun toList(): List { + return listOf( + featuresetDescriptor, + featuresetType, + ) + } +} + /** * A featureset descriptor. * @@ -2025,7 +2079,7 @@ private open class MapInterfacesPigeonCodec : StandardMessageCodec() { } 137.toByte() -> { return (readValue(buffer) as Long?)?.let { - Type.ofRaw(it.toInt()) + InteractionType.ofRaw(it.toInt()) } } 138.toByte() -> { @@ -2304,6 +2358,21 @@ private open class MapInterfacesPigeonCodec : StandardMessageCodec() { } } 193.toByte() -> { + return (readValue(buffer) as? List)?.let { + ImageContent.fromList(it) + } + } + 195.toByte() -> { + return (readValue(buffer) as? List)?.let { + TransitionOptions.fromList(it) + } + } + 196.toByte() -> { + return (readValue(buffer) as? List)?.let { + CanonicalTileID.fromList(it) + } + } + 197.toByte() -> { return (readValue(buffer) as? List)?.let { StylePropertyValue.fromList(it) } @@ -2345,7 +2414,7 @@ private open class MapInterfacesPigeonCodec : StandardMessageCodec() { stream.write(136) writeValue(stream, value.raw) } - is Type -> { + is InteractionType -> { stream.write(137) writeValue(stream, value.raw) } @@ -2573,6 +2642,18 @@ private open class MapInterfacesPigeonCodec : StandardMessageCodec() { stream.write(193) writeValue(stream, value.toList()) } + is TransitionOptions -> { + stream.write(195) + writeValue(stream, value.toList()) + } + is CanonicalTileID -> { + stream.write(196) + writeValue(stream, value.toList()) + } + is StylePropertyValue -> { + stream.write(197) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } @@ -3455,7 +3536,7 @@ interface _MapInterface { * * @return A `Cancelable` object that could be used to cancel the pending operation. */ - fun setFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: Map, callback: (Result) -> Unit) + fun setFeatureStateForFeaturesetDescriptor(featureset: TypedFeaturesetDescriptor, featureId: FeaturesetFeatureId, state: Map, callback: (Result) -> Unit) /** * Update the state map of an individual feature. * @@ -3545,6 +3626,7 @@ interface _MapInterface { * @return A `Cancelable` object that could be used to cancel the pending operation. */ fun resetFeatureStatesForFeatureset(featureset: FeaturesetDescriptor, callback: (Result) -> Unit) + fun addInteraction(interaction: Interaction, callback: (Result) -> Unit) /** Reduces memory use. Useful to call when the application gets paused or sent to background. */ fun reduceMemoryUse() /** @@ -4149,7 +4231,7 @@ interface _MapInterface { if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val featuresetArg = args[0] as FeaturesetDescriptor + val featuresetArg = args[0] as TypedFeaturesetDescriptor val featureIdArg = args[1] as FeaturesetFeatureId val stateArg = args[2] as Map api.setFeatureStateForFeaturesetDescriptor(featuresetArg, featureIdArg, stateArg) { result: Result -> @@ -4330,6 +4412,26 @@ interface _MapInterface { channel.setMessageHandler(null) } } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.addInteraction$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val interactionArg = args[0] as Interaction + api.addInteraction(interactionArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.reduceMemoryUse$separatedMessageChannelSuffix", codec) if (api != null) { diff --git a/example/lib/interactive_features_example.dart b/example/lib/interactive_features_example.dart index 307c215c3..46f51b426 100644 --- a/example/lib/interactive_features_example.dart +++ b/example/lib/interactive_features_example.dart @@ -21,6 +21,19 @@ class InteractiveFeaturesState extends State { _onMapCreated(MapboxMap mapboxMap) { this.mapboxMap = mapboxMap; mapboxMap.style; + + var featureset = + FeaturesetDescriptor(featuresetId: "buildings", importId: "basemap"); + + var typedFeaturesetDescriptor = TypedFeaturesetDescriptor( + featuresetDescriptor: featureset, featuresetType: "featureset"); + + var interactionType = InteractionType.CLICK; + + var interaction = Interaction( + typedFeaturesetDescriptor: typedFeaturesetDescriptor, + interactionType: interactionType); + mapboxMap.addInteraction(interaction); } _onTap(context) async { @@ -39,13 +52,18 @@ class InteractiveFeaturesState extends State { geometry: renderedQueryGeometry, featureset: featureset); var featuresetFeature = queriedFeatures?.first; + var typedFeaturesetDescriptor = TypedFeaturesetDescriptor( + featuresetDescriptor: featureset, featuresetType: "featureset"); + if (featuresetFeature != null) { // Define the state to set for the feature, in this case highlighting // Set that featurestate on that featuresetFeature Map state = { "highlight": true, }; - mapboxMap?.setFeatureStateForFeaturesetFeature(featuresetFeature, state); + mapboxMap?.setFeatureStateForFeaturesetDescriptor( + typedFeaturesetDescriptor, featuresetFeature.id!, state); + //mapboxMap?.setFeatureStateForFeaturesetFeature(featuresetFeature, state); } } diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift index 4b6adc572..1ab74db94 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift @@ -250,6 +250,27 @@ extension FeaturesetFeature { } } +extension Interaction { + func toMapInteraction(completion: @escaping (FeaturesetFeature) -> Void) -> MapboxMaps.Interaction { + let filterExpression = try? filter.flatMap { try $0.toExp() } + + switch interactionType { + case .cLICK: + return TapInteraction.init(typedFeaturesetDescriptor.featuresetDescriptor.toMapFeaturesetDescriptor(), filter: filterExpression) { _, _ in + // TODO: figure out how to pass typed completion with pigeon + print("tapped") + return true + } + case .lONGCLICK: + return LongPressInteraction.init(typedFeaturesetDescriptor.featuresetDescriptor.toMapFeaturesetDescriptor(), filter: filterExpression) { featuresetFeature, _ in + + completion(featuresetFeature.toFLTFeaturesetFeature()) + return true + } + } + } +} + extension MercatorCoordinate { func toMercatorCoordinate() -> MapboxMaps.MercatorCoordinate { return MapboxMaps.MercatorCoordinate(x: x, y: y) diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift index 04c57400e..8c3467aab 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift @@ -186,6 +186,11 @@ enum ViewAnnotationAnchor: Int { case cENTER = 8 } +enum InteractionType: Int { + case cLICK = 0 + case lONGCLICK = 1 +} + /// Type information of the variant's content enum Type: Int { case sCREENBOX = 0 @@ -1238,6 +1243,56 @@ struct FeaturesetFeatureId { } } +/// Generated class from Pigeon that represents data sent in messages. +struct Interaction { + var typedFeaturesetDescriptor: TypedFeaturesetDescriptor + var interactionType: InteractionType + var filter: String? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> Interaction? { + let typedFeaturesetDescriptor = pigeonVar_list[0] as! TypedFeaturesetDescriptor + let interactionType = pigeonVar_list[1] as! InteractionType + let filter: String? = nilOrValue(pigeonVar_list[2]) + + return Interaction( + typedFeaturesetDescriptor: typedFeaturesetDescriptor, + interactionType: interactionType, + filter: filter + ) + } + func toList() -> [Any?] { + return [ + typedFeaturesetDescriptor, + interactionType, + filter, + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct TypedFeaturesetDescriptor { + var featuresetDescriptor: FeaturesetDescriptor + var featuresetType: String + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> TypedFeaturesetDescriptor? { + let featuresetDescriptor = pigeonVar_list[0] as! FeaturesetDescriptor + let featuresetType = pigeonVar_list[1] as! String + + return TypedFeaturesetDescriptor( + featuresetDescriptor: featuresetDescriptor, + featuresetType: featuresetType + ) + } + func toList() -> [Any?] { + return [ + featuresetDescriptor, + featuresetType, + ] + } +} + /// A featureset descriptor. /// /// The descriptor instance acts as a universal target for interactions or querying rendered features (see @@ -1920,7 +1975,7 @@ private class MapInterfacesPigeonCodecReader: FlutterStandardReader { case 137: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return Type(rawValue: enumResultAsInt) + return InteractionType(rawValue: enumResultAsInt) } return nil case 138: @@ -2131,7 +2186,7 @@ private class MapInterfacesPigeonCodecWriter: FlutterStandardWriter { } else if let value = value as? ViewAnnotationAnchor { super.writeByte(136) super.writeValue(value.rawValue) - } else if let value = value as? Type { + } else if let value = value as? InteractionType { super.writeByte(137) super.writeValue(value.rawValue) } else if let value = value as? FillExtrusionBaseAlignment { @@ -2302,6 +2357,15 @@ private class MapInterfacesPigeonCodecWriter: FlutterStandardWriter { } else if let value = value as? StylePropertyValue { super.writeByte(193) super.writeValue(value.toList()) + } else if let value = value as? TransitionOptions { + super.writeByte(195) + super.writeValue(value.toList()) + } else if let value = value as? CanonicalTileID { + super.writeByte(196) + super.writeValue(value.toList()) + } else if let value = value as? StylePropertyValue { + super.writeByte(197) + super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -3191,7 +3255,7 @@ protocol _MapInterface { /// @param state Map of entries to update with their respective new values /// /// @return A `Cancelable` object that could be used to cancel the pending operation. - func setFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: [String: Any?], completion: @escaping (Result) -> Void) + func setFeatureStateForFeaturesetDescriptor(featureset: TypedFeaturesetDescriptor, featureId: FeaturesetFeatureId, state: [String: Any?], completion: @escaping (Result) -> Void) /// Update the state map of an individual feature. /// /// The feature should have a non-nil ``FeaturesetFeatureType/id``. Otherwise, @@ -3265,6 +3329,7 @@ protocol _MapInterface { /// /// @return A `Cancelable` object that could be used to cancel the pending operation. func resetFeatureStatesForFeatureset(featureset: FeaturesetDescriptor, completion: @escaping (Result) -> Void) + func addInteraction(interaction: Interaction, completion: @escaping (Result) -> Void) /// Reduces memory use. Useful to call when the application gets paused or sent to background. func reduceMemoryUse() throws /// Gets elevation for the given coordinate. @@ -3908,7 +3973,7 @@ class _MapInterfaceSetup { if let api = api { setFeatureStateForFeaturesetDescriptorChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let featuresetArg = args[0] as! FeaturesetDescriptor + let featuresetArg = args[0] as! TypedFeaturesetDescriptor let featureIdArg = args[1] as! FeaturesetFeatureId let stateArg = args[2] as! [String: Any?] api.setFeatureStateForFeaturesetDescriptor(featureset: featuresetArg, featureId: featureIdArg, state: stateArg) { result in @@ -4134,6 +4199,23 @@ class _MapInterfaceSetup { } else { resetFeatureStatesForFeaturesetChannel.setMessageHandler(nil) } + let addInteractionChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.addInteraction\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + addInteractionChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let interactionArg = args[0] as! Interaction + api.addInteraction(interaction: interactionArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + addInteractionChannel.setMessageHandler(nil) + } /// Reduces memory use. Useful to call when the application gets paused or sent to background. let reduceMemoryUseChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.reduceMemoryUse\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift index 0cbdc2f1c..c13980e7c 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift @@ -315,11 +315,11 @@ final class MapInterfaceController: _MapInterface { } } - func setFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: [String: Any?], completion: @escaping (Result) -> Void) { + func setFeatureStateForFeaturesetDescriptor(featureset: TypedFeaturesetDescriptor, featureId: FeaturesetFeatureId, state: [String: Any?], completion: @escaping (Result) -> Void) { guard let state = JSONObject.init(turfRawValue: state) else { return } - self.mapboxMap.setFeatureState(featureset: featureset.toMapFeaturesetDescriptor(), featureId: featureId.toMapFeaturesetFeatureId(), state: state) { error in + self.mapboxMap.setFeatureState(featureset: featureset.featuresetDescriptor.toMapFeaturesetDescriptor(), featureId: featureId.toMapFeaturesetFeatureId(), state: state) { error in if let error { completion(.failure(FlutterError( code: "setFeatureStateError", @@ -435,6 +435,12 @@ final class MapInterfaceController: _MapInterface { } } + func addInteraction(interaction: Interaction, completion: @escaping (Result) -> Void) { + mapboxMap.addInteraction(interaction.toMapInteraction(completion: { _ in + print("no op") + })) + } + func reduceMemoryUse() throws { mapboxMap.reduceMemoryUse() } diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index 3e59d7e78..0bf513820 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -522,7 +522,7 @@ class MapboxMap extends ChangeNotifier { /// will be updated. An entry in the feature state map that is not listed in `state` will retain its previous value. @experimental Future setFeatureStateForFeaturesetDescriptor( - FeaturesetDescriptor featureset, + TypedFeaturesetDescriptor featureset, FeaturesetFeatureId featureId, Map state) => _mapInterface.setFeatureStateForFeaturesetDescriptor( @@ -596,6 +596,9 @@ class MapboxMap extends ChangeNotifier { FeaturesetDescriptor featureset) => _mapInterface.resetFeatureStatesForFeatureset(featureset); + Future addInteraction(Interaction interaction) => + _mapInterface.addInteraction(interaction); + /// Reduces memory use. Useful to call when the application gets paused or sent to background. Future reduceMemoryUse() => _mapInterface.reduceMemoryUse(); diff --git a/lib/src/pigeons/map_interfaces.dart b/lib/src/pigeons/map_interfaces.dart index d7474c47a..fc14af720 100644 --- a/lib/src/pigeons/map_interfaces.dart +++ b/lib/src/pigeons/map_interfaces.dart @@ -168,6 +168,11 @@ enum ViewAnnotationAnchor { CENTER, } +enum InteractionType { + CLICK, + LONG_CLICK, +} + /// Type information of the variant's content enum Type { SCREEN_BOX, @@ -1209,7 +1214,6 @@ class QueriedRenderedFeature { /// An array of feature query targets that correspond to this queried feature. /// /// - Note: Returned query targets will omit the original `filter` data. - @experimental List? queryTargets; Object encode() { @@ -1308,7 +1312,6 @@ class QueriedFeature { /// /// - Warning: There is no guarantee of identifier persistency. This depends on the underlying source of the features and may vary from style to style. /// If you want to store the identifiers persistently, please make sure that the style or source provides this guarantee. -@experimental class FeaturesetFeatureId { FeaturesetFeatureId({ required this.id, @@ -1337,11 +1340,67 @@ class FeaturesetFeatureId { } } +class Interaction { + Interaction({ + required this.typedFeaturesetDescriptor, + required this.interactionType, + this.filter, + }); + + TypedFeaturesetDescriptor typedFeaturesetDescriptor; + + InteractionType interactionType; + + String? filter; + + Object encode() { + return [ + typedFeaturesetDescriptor, + interactionType, + filter, + ]; + } + + static Interaction decode(Object result) { + result as List; + return Interaction( + typedFeaturesetDescriptor: result[0]! as TypedFeaturesetDescriptor, + interactionType: result[1]! as InteractionType, + filter: result[2] as String?, + ); + } +} + +class TypedFeaturesetDescriptor { + TypedFeaturesetDescriptor({ + required this.featuresetDescriptor, + required this.featuresetType, + }); + + FeaturesetDescriptor featuresetDescriptor; + + String featuresetType; + + Object encode() { + return [ + featuresetDescriptor, + featuresetType, + ]; + } + + static TypedFeaturesetDescriptor decode(Object result) { + result as List; + return TypedFeaturesetDescriptor( + featuresetDescriptor: result[0]! as FeaturesetDescriptor, + featuresetType: result[1]! as String, + ); + } +} + /// A featureset descriptor. /// /// The descriptor instance acts as a universal target for interactions or querying rendered features (see /// ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)``). -@experimental class FeaturesetDescriptor { FeaturesetDescriptor({ this.featuresetId, @@ -1390,7 +1449,6 @@ class FeaturesetDescriptor { /// A basic feature of a featureset. /// /// The featureset feature is different to the `Turf.Feature`. The latter represents any GeoJSON feature, while the former is a high level representation of features. -@experimental class FeaturesetFeature { FeaturesetFeature({ this.id, @@ -1445,7 +1503,6 @@ class FeaturesetFeature { } /// Defines the parameters for querying features from a Featureset with an optional filter and id. -@experimental class FeaturesetQueryTarget { FeaturesetQueryTarget({ required this.featureset, @@ -2061,7 +2118,7 @@ class MapInterfaces_PigeonCodec extends StandardMessageCodec { } else if (value is ViewAnnotationAnchor) { buffer.putUint8(136); writeValue(buffer, value.index); - } else if (value is Type) { + } else if (value is InteractionType) { buffer.putUint8(137); writeValue(buffer, value.index); } else if (value is FillExtrusionBaseAlignment) { @@ -2232,6 +2289,15 @@ class MapInterfaces_PigeonCodec extends StandardMessageCodec { } else if (value is StylePropertyValue) { buffer.putUint8(193); writeValue(buffer, value.encode()); + } else if (value is TransitionOptions) { + buffer.putUint8(195); + writeValue(buffer, value.encode()); + } else if (value is CanonicalTileID) { + buffer.putUint8(196); + writeValue(buffer, value.encode()); + } else if (value is StylePropertyValue) { + buffer.putUint8(197); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -2266,7 +2332,7 @@ class MapInterfaces_PigeonCodec extends StandardMessageCodec { return value == null ? null : ViewAnnotationAnchor.values[value]; case 137: final int? value = readValue(buffer) as int?; - return value == null ? null : Type.values[value]; + return value == null ? null : InteractionType.values[value]; case 138: final int? value = readValue(buffer) as int?; return value == null ? null : FillExtrusionBaseAlignment.values[value]; @@ -3888,7 +3954,6 @@ class _MapInterface { /// /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param targets An array of targets to query with. - @experimental Future> queryRenderedFeaturesForTargets( _RenderedQueryGeometry geometry, List targets) async { @@ -3928,7 +3993,6 @@ class _MapInterface { /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param featureset A typed featureset to query with. /// @param filter An additional filter for features. - @experimental Future> queryRenderedFeaturesForFeatureset( _RenderedQueryGeometry geometry, FeaturesetDescriptor featureset, @@ -3968,7 +4032,6 @@ class _MapInterface { /// /// @param featureset A typed featureset to query with. /// @param filter An additional filter for features. - @experimental Future> queryRenderedFeaturesInViewport( FeaturesetDescriptor featureset, String? filter) async { final String pigeonVar_channelName = @@ -4039,7 +4102,6 @@ class _MapInterface { /// Queries the source features for a given featureset. /// /// @param target A featureset query target. - @experimental Future> querySourceFeaturesForTargets( FeaturesetQueryTarget target) async { final String pigeonVar_channelName = @@ -4239,9 +4301,8 @@ class _MapInterface { /// @param state Map of entries to update with their respective new values /// /// @return A `Cancelable` object that could be used to cancel the pending operation. - @experimental Future setFeatureStateForFeaturesetDescriptor( - FeaturesetDescriptor featureset, + TypedFeaturesetDescriptor featureset, FeaturesetFeatureId featureId, Map state) async { final String pigeonVar_channelName = @@ -4276,7 +4337,6 @@ class _MapInterface { /// @param state Map of entries to update with their respective new values /// /// @return A `Cancelable` object that could be used to cancel the pending operation. - @experimental Future setFeatureStateForFeaturesetFeature( FeaturesetFeature feature, Map state) async { final String pigeonVar_channelName = @@ -4348,7 +4408,6 @@ class _MapInterface { /// @param featureId Identifier of the feature whose state should be queried. /// /// @return A `Cancelable` object that could be used to cancel the pending query. - @experimental Future> getFeatureStateForFeaturesetDescriptor( FeaturesetDescriptor featureset, FeaturesetFeatureId featureId) async { final String pigeonVar_channelName = @@ -4385,7 +4444,6 @@ class _MapInterface { /// @param feature An interactive feature to query the state from. /// /// @return A `Cancelable` object that could be used to cancel the pending query. - @experimental Future> getFeatureStateForFeaturesetFeature( FeaturesetFeature feature) async { final String pigeonVar_channelName = @@ -4463,7 +4521,6 @@ class _MapInterface { /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. - @experimental Future removeFeatureStateForFeaturesetDescriptor( FeaturesetDescriptor featureset, FeaturesetFeatureId featureId, @@ -4498,7 +4555,6 @@ class _MapInterface { /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. /// /// @return A `Cancelable` object that could be used to cancel the pending operation. - @experimental Future removeFeatureStateForFeaturesetFeature( FeaturesetFeature feature, String? stateKey) async { final String pigeonVar_channelName = @@ -4532,7 +4588,6 @@ class _MapInterface { /// @param featureset A featureset descriptor /// /// @return A `Cancelable` object that could be used to cancel the pending operation. - @experimental Future resetFeatureStatesForFeatureset( FeaturesetDescriptor featureset) async { final String pigeonVar_channelName = @@ -4558,6 +4613,35 @@ class _MapInterface { } } + Future addInteraction(Interaction interaction) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.addInteraction$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([interaction]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as FeaturesetFeature?)!; + } + } + /// Reduces memory use. Useful to call when the application gets paused or sent to background. Future reduceMemoryUse() async { final String pigeonVar_channelName = @@ -7380,7 +7464,6 @@ class StyleManager { /// Returns the available featuresets in the currently loaded style. /// /// - Note: This function should only be called after the style is fully loaded; otherwise, the result may be unreliable. - @experimental Future> getFeaturesets() async { final String pigeonVar_channelName = 'dev.flutter.pigeon.mapbox_maps_flutter.StyleManager.getFeaturesets$pigeonVar_messageChannelSuffix'; From f78a1ebaeb813663ff4c25e14d1ef038e2eb2966 Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Thu, 19 Dec 2024 13:54:53 -0500 Subject: [PATCH 14/28] Update generated code --- .../maps/mapbox_maps/pigeons/MapInterfaces.kt | 274 ++++++++++-------- .../Classes/Generated/MapInterfaces.swift | 260 +++++++++-------- lib/src/pigeons/map_interfaces.dart | 257 ++++++++-------- 3 files changed, 442 insertions(+), 349 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt index 5c79a252b..c788fbc3e 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt @@ -2084,295 +2084,315 @@ private open class MapInterfacesPigeonCodec : StandardMessageCodec() { } 138.toByte() -> { return (readValue(buffer) as Long?)?.let { - FillExtrusionBaseAlignment.ofRaw(it.toInt()) + Type.ofRaw(it.toInt()) } } 139.toByte() -> { return (readValue(buffer) as Long?)?.let { - FillExtrusionHeightAlignment.ofRaw(it.toInt()) + FillExtrusionBaseAlignment.ofRaw(it.toInt()) } } 140.toByte() -> { return (readValue(buffer) as Long?)?.let { - BackgroundPitchAlignment.ofRaw(it.toInt()) + FillExtrusionHeightAlignment.ofRaw(it.toInt()) } } 141.toByte() -> { return (readValue(buffer) as Long?)?.let { - StylePackErrorType.ofRaw(it.toInt()) + BackgroundPitchAlignment.ofRaw(it.toInt()) } } 142.toByte() -> { return (readValue(buffer) as Long?)?.let { - ResponseErrorReason.ofRaw(it.toInt()) + StylePackErrorType.ofRaw(it.toInt()) } } 143.toByte() -> { return (readValue(buffer) as Long?)?.let { - OfflineRegionDownloadState.ofRaw(it.toInt()) + ResponseErrorReason.ofRaw(it.toInt()) } } 144.toByte() -> { return (readValue(buffer) as Long?)?.let { - TileStoreUsageMode.ofRaw(it.toInt()) + OfflineRegionDownloadState.ofRaw(it.toInt()) } } 145.toByte() -> { return (readValue(buffer) as Long?)?.let { - StylePropertyValueKind.ofRaw(it.toInt()) + TileStoreUsageMode.ofRaw(it.toInt()) } } 146.toByte() -> { return (readValue(buffer) as Long?)?.let { - StyleProjectionName.ofRaw(it.toInt()) + StylePropertyValueKind.ofRaw(it.toInt()) } } 147.toByte() -> { return (readValue(buffer) as Long?)?.let { - Anchor.ofRaw(it.toInt()) + StyleProjectionName.ofRaw(it.toInt()) } } 148.toByte() -> { return (readValue(buffer) as Long?)?.let { - HttpMethod.ofRaw(it.toInt()) + Anchor.ofRaw(it.toInt()) } } 149.toByte() -> { return (readValue(buffer) as Long?)?.let { - HttpRequestErrorType.ofRaw(it.toInt()) + HttpMethod.ofRaw(it.toInt()) } } 150.toByte() -> { return (readValue(buffer) as Long?)?.let { - DownloadErrorCode.ofRaw(it.toInt()) + HttpRequestErrorType.ofRaw(it.toInt()) } } 151.toByte() -> { return (readValue(buffer) as Long?)?.let { - DownloadState.ofRaw(it.toInt()) + DownloadErrorCode.ofRaw(it.toInt()) } } 152.toByte() -> { return (readValue(buffer) as Long?)?.let { - TileRegionErrorType.ofRaw(it.toInt()) + DownloadState.ofRaw(it.toInt()) } } 153.toByte() -> { return (readValue(buffer) as Long?)?.let { - _MapEvent.ofRaw(it.toInt()) + TileRegionErrorType.ofRaw(it.toInt()) } } 154.toByte() -> { - return (readValue(buffer) as? List)?.let { - PointDecoder.fromList(it) + return (readValue(buffer) as Long?)?.let { + _MapEvent.ofRaw(it.toInt()) } } 155.toByte() -> { return (readValue(buffer) as? List)?.let { - FeatureDecoder.fromList(it) + PointDecoder.fromList(it) } } 156.toByte() -> { return (readValue(buffer) as? List)?.let { - GlyphsRasterizationOptions.fromList(it) + FeatureDecoder.fromList(it) } } 157.toByte() -> { return (readValue(buffer) as? List)?.let { - TileCoverOptions.fromList(it) + GlyphsRasterizationOptions.fromList(it) } } 158.toByte() -> { return (readValue(buffer) as? List)?.let { - MbxEdgeInsets.fromList(it) + TileCoverOptions.fromList(it) } } 159.toByte() -> { return (readValue(buffer) as? List)?.let { - CameraOptions.fromList(it) + MbxEdgeInsets.fromList(it) } } 160.toByte() -> { return (readValue(buffer) as? List)?.let { - CameraState.fromList(it) + CameraOptions.fromList(it) } } 161.toByte() -> { return (readValue(buffer) as? List)?.let { - CameraBoundsOptions.fromList(it) + CameraState.fromList(it) } } 162.toByte() -> { return (readValue(buffer) as? List)?.let { - CameraBounds.fromList(it) + CameraBoundsOptions.fromList(it) } } 163.toByte() -> { return (readValue(buffer) as? List)?.let { - MapAnimationOptions.fromList(it) + CameraBounds.fromList(it) } } 164.toByte() -> { return (readValue(buffer) as? List)?.let { - CoordinateBounds.fromList(it) + MapAnimationOptions.fromList(it) } } 165.toByte() -> { return (readValue(buffer) as? List)?.let { - MapDebugOptions.fromList(it) + CoordinateBounds.fromList(it) } } 166.toByte() -> { return (readValue(buffer) as? List)?.let { - TileCacheBudgetInMegabytes.fromList(it) + MapDebugOptions.fromList(it) } } 167.toByte() -> { return (readValue(buffer) as? List)?.let { - TileCacheBudgetInTiles.fromList(it) + TileCacheBudgetInMegabytes.fromList(it) } } 168.toByte() -> { return (readValue(buffer) as? List)?.let { - MapOptions.fromList(it) + TileCacheBudgetInTiles.fromList(it) } } 169.toByte() -> { return (readValue(buffer) as? List)?.let { - ScreenCoordinate.fromList(it) + MapOptions.fromList(it) } } 170.toByte() -> { return (readValue(buffer) as? List)?.let { - ScreenBox.fromList(it) + ScreenCoordinate.fromList(it) } } 171.toByte() -> { return (readValue(buffer) as? List)?.let { - CoordinateBoundsZoom.fromList(it) + ScreenBox.fromList(it) } } 172.toByte() -> { return (readValue(buffer) as? List)?.let { - Size.fromList(it) + CoordinateBoundsZoom.fromList(it) } } 173.toByte() -> { return (readValue(buffer) as? List)?.let { - RenderedQueryOptions.fromList(it) + Size.fromList(it) } } 174.toByte() -> { return (readValue(buffer) as? List)?.let { - SourceQueryOptions.fromList(it) + RenderedQueryOptions.fromList(it) } } 175.toByte() -> { return (readValue(buffer) as? List)?.let { - FeatureExtensionValue.fromList(it) + SourceQueryOptions.fromList(it) } } 176.toByte() -> { return (readValue(buffer) as? List)?.let { - LayerPosition.fromList(it) + FeatureExtensionValue.fromList(it) } } 177.toByte() -> { return (readValue(buffer) as? List)?.let { - QueriedRenderedFeature.fromList(it) + LayerPosition.fromList(it) } } 178.toByte() -> { return (readValue(buffer) as? List)?.let { - QueriedSourceFeature.fromList(it) + QueriedRenderedFeature.fromList(it) } } 179.toByte() -> { return (readValue(buffer) as? List)?.let { - QueriedFeature.fromList(it) + QueriedSourceFeature.fromList(it) } } 180.toByte() -> { return (readValue(buffer) as? List)?.let { - _RenderedQueryGeometry.fromList(it) + QueriedFeature.fromList(it) } } 181.toByte() -> { return (readValue(buffer) as? List)?.let { - ProjectedMeters.fromList(it) + FeaturesetFeatureId.fromList(it) } } 182.toByte() -> { return (readValue(buffer) as? List)?.let { - MercatorCoordinate.fromList(it) + Interaction.fromList(it) } } 183.toByte() -> { return (readValue(buffer) as? List)?.let { - StyleObjectInfo.fromList(it) + TypedFeaturesetDescriptor.fromList(it) } } 184.toByte() -> { return (readValue(buffer) as? List)?.let { - StyleProjection.fromList(it) + FeaturesetDescriptor.fromList(it) } } 185.toByte() -> { return (readValue(buffer) as? List)?.let { - FlatLight.fromList(it) + FeaturesetFeature.fromList(it) } } 186.toByte() -> { return (readValue(buffer) as? List)?.let { - DirectionalLight.fromList(it) + FeaturesetQueryTarget.fromList(it) } } 187.toByte() -> { return (readValue(buffer) as? List)?.let { - AmbientLight.fromList(it) + _RenderedQueryGeometry.fromList(it) } } 188.toByte() -> { return (readValue(buffer) as? List)?.let { - MbxImage.fromList(it) + ProjectedMeters.fromList(it) } } 189.toByte() -> { return (readValue(buffer) as? List)?.let { - ImageStretches.fromList(it) + MercatorCoordinate.fromList(it) } } 190.toByte() -> { return (readValue(buffer) as? List)?.let { - ImageContent.fromList(it) + StyleObjectInfo.fromList(it) } } 191.toByte() -> { return (readValue(buffer) as? List)?.let { - TransitionOptions.fromList(it) + StyleProjection.fromList(it) } } 192.toByte() -> { return (readValue(buffer) as? List)?.let { - CanonicalTileID.fromList(it) + FlatLight.fromList(it) } } 193.toByte() -> { return (readValue(buffer) as? List)?.let { - ImageContent.fromList(it) + DirectionalLight.fromList(it) + } + } + 194.toByte() -> { + return (readValue(buffer) as? List)?.let { + AmbientLight.fromList(it) } } 195.toByte() -> { return (readValue(buffer) as? List)?.let { - TransitionOptions.fromList(it) + MbxImage.fromList(it) } } 196.toByte() -> { return (readValue(buffer) as? List)?.let { - CanonicalTileID.fromList(it) + ImageStretches.fromList(it) } } 197.toByte() -> { + return (readValue(buffer) as? List)?.let { + ImageContent.fromList(it) + } + } + 198.toByte() -> { + return (readValue(buffer) as? List)?.let { + TransitionOptions.fromList(it) + } + } + 199.toByte() -> { + return (readValue(buffer) as? List)?.let { + CanonicalTileID.fromList(it) + } + } + 200.toByte() -> { return (readValue(buffer) as? List)?.let { StylePropertyValue.fromList(it) } @@ -2418,242 +2438,258 @@ private open class MapInterfacesPigeonCodec : StandardMessageCodec() { stream.write(137) writeValue(stream, value.raw) } - is FillExtrusionBaseAlignment -> { + is Type -> { stream.write(138) writeValue(stream, value.raw) } - is FillExtrusionHeightAlignment -> { + is FillExtrusionBaseAlignment -> { stream.write(139) writeValue(stream, value.raw) } - is BackgroundPitchAlignment -> { + is FillExtrusionHeightAlignment -> { stream.write(140) writeValue(stream, value.raw) } - is StylePackErrorType -> { + is BackgroundPitchAlignment -> { stream.write(141) writeValue(stream, value.raw) } - is ResponseErrorReason -> { + is StylePackErrorType -> { stream.write(142) writeValue(stream, value.raw) } - is OfflineRegionDownloadState -> { + is ResponseErrorReason -> { stream.write(143) writeValue(stream, value.raw) } - is TileStoreUsageMode -> { + is OfflineRegionDownloadState -> { stream.write(144) writeValue(stream, value.raw) } - is StylePropertyValueKind -> { + is TileStoreUsageMode -> { stream.write(145) writeValue(stream, value.raw) } - is StyleProjectionName -> { + is StylePropertyValueKind -> { stream.write(146) writeValue(stream, value.raw) } - is Anchor -> { + is StyleProjectionName -> { stream.write(147) writeValue(stream, value.raw) } - is HttpMethod -> { + is Anchor -> { stream.write(148) writeValue(stream, value.raw) } - is HttpRequestErrorType -> { + is HttpMethod -> { stream.write(149) writeValue(stream, value.raw) } - is DownloadErrorCode -> { + is HttpRequestErrorType -> { stream.write(150) writeValue(stream, value.raw) } - is DownloadState -> { + is DownloadErrorCode -> { stream.write(151) writeValue(stream, value.raw) } - is TileRegionErrorType -> { + is DownloadState -> { stream.write(152) writeValue(stream, value.raw) } - is _MapEvent -> { + is TileRegionErrorType -> { stream.write(153) writeValue(stream, value.raw) } - is Point -> { + is _MapEvent -> { stream.write(154) - writeValue(stream, value.toList()) + writeValue(stream, value.raw) } - is Feature -> { + is Point -> { stream.write(155) writeValue(stream, value.toList()) } - is GlyphsRasterizationOptions -> { + is Feature -> { stream.write(156) writeValue(stream, value.toList()) } - is TileCoverOptions -> { + is GlyphsRasterizationOptions -> { stream.write(157) writeValue(stream, value.toList()) } - is MbxEdgeInsets -> { + is TileCoverOptions -> { stream.write(158) writeValue(stream, value.toList()) } - is CameraOptions -> { + is MbxEdgeInsets -> { stream.write(159) writeValue(stream, value.toList()) } - is CameraState -> { + is CameraOptions -> { stream.write(160) writeValue(stream, value.toList()) } - is CameraBoundsOptions -> { + is CameraState -> { stream.write(161) writeValue(stream, value.toList()) } - is CameraBounds -> { + is CameraBoundsOptions -> { stream.write(162) writeValue(stream, value.toList()) } - is MapAnimationOptions -> { + is CameraBounds -> { stream.write(163) writeValue(stream, value.toList()) } - is CoordinateBounds -> { + is MapAnimationOptions -> { stream.write(164) writeValue(stream, value.toList()) } - is MapDebugOptions -> { + is CoordinateBounds -> { stream.write(165) writeValue(stream, value.toList()) } - is TileCacheBudgetInMegabytes -> { + is MapDebugOptions -> { stream.write(166) writeValue(stream, value.toList()) } - is TileCacheBudgetInTiles -> { + is TileCacheBudgetInMegabytes -> { stream.write(167) writeValue(stream, value.toList()) } - is MapOptions -> { + is TileCacheBudgetInTiles -> { stream.write(168) writeValue(stream, value.toList()) } - is ScreenCoordinate -> { + is MapOptions -> { stream.write(169) writeValue(stream, value.toList()) } - is ScreenBox -> { + is ScreenCoordinate -> { stream.write(170) writeValue(stream, value.toList()) } - is CoordinateBoundsZoom -> { + is ScreenBox -> { stream.write(171) writeValue(stream, value.toList()) } - is Size -> { + is CoordinateBoundsZoom -> { stream.write(172) writeValue(stream, value.toList()) } - is RenderedQueryOptions -> { + is Size -> { stream.write(173) writeValue(stream, value.toList()) } - is SourceQueryOptions -> { + is RenderedQueryOptions -> { stream.write(174) writeValue(stream, value.toList()) } - is FeatureExtensionValue -> { + is SourceQueryOptions -> { stream.write(175) writeValue(stream, value.toList()) } - is LayerPosition -> { + is FeatureExtensionValue -> { stream.write(176) writeValue(stream, value.toList()) } - is QueriedRenderedFeature -> { + is LayerPosition -> { stream.write(177) writeValue(stream, value.toList()) } - is QueriedSourceFeature -> { + is QueriedRenderedFeature -> { stream.write(178) writeValue(stream, value.toList()) } - is QueriedFeature -> { + is QueriedSourceFeature -> { stream.write(179) writeValue(stream, value.toList()) } - is _RenderedQueryGeometry -> { + is QueriedFeature -> { stream.write(180) writeValue(stream, value.toList()) } - is ProjectedMeters -> { + is FeaturesetFeatureId -> { stream.write(181) writeValue(stream, value.toList()) } - is MercatorCoordinate -> { + is Interaction -> { stream.write(182) writeValue(stream, value.toList()) } - is StyleObjectInfo -> { + is TypedFeaturesetDescriptor -> { stream.write(183) writeValue(stream, value.toList()) } - is StyleProjection -> { + is FeaturesetDescriptor -> { stream.write(184) writeValue(stream, value.toList()) } - is FlatLight -> { + is FeaturesetFeature -> { stream.write(185) writeValue(stream, value.toList()) } - is DirectionalLight -> { + is FeaturesetQueryTarget -> { stream.write(186) writeValue(stream, value.toList()) } - is AmbientLight -> { + is _RenderedQueryGeometry -> { stream.write(187) writeValue(stream, value.toList()) } - is MbxImage -> { + is ProjectedMeters -> { stream.write(188) writeValue(stream, value.toList()) } - is ImageStretches -> { + is MercatorCoordinate -> { stream.write(189) writeValue(stream, value.toList()) } - is ImageContent -> { + is StyleObjectInfo -> { stream.write(190) writeValue(stream, value.toList()) } - is TransitionOptions -> { + is StyleProjection -> { stream.write(191) writeValue(stream, value.toList()) } - is CanonicalTileID -> { + is FlatLight -> { stream.write(192) writeValue(stream, value.toList()) } - is StylePropertyValue -> { + is DirectionalLight -> { stream.write(193) writeValue(stream, value.toList()) } - is TransitionOptions -> { + is AmbientLight -> { + stream.write(194) + writeValue(stream, value.toList()) + } + is MbxImage -> { stream.write(195) writeValue(stream, value.toList()) } - is CanonicalTileID -> { + is ImageStretches -> { stream.write(196) writeValue(stream, value.toList()) } - is StylePropertyValue -> { + is ImageContent -> { stream.write(197) writeValue(stream, value.toList()) } + is TransitionOptions -> { + stream.write(198) + writeValue(stream, value.toList()) + } + is CanonicalTileID -> { + stream.write(199) + writeValue(stream, value.toList()) + } + is StylePropertyValue -> { + stream.write(200) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift index 8c3467aab..0f61e792f 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift @@ -1981,178 +1981,196 @@ private class MapInterfacesPigeonCodecReader: FlutterStandardReader { case 138: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return FillExtrusionBaseAlignment(rawValue: enumResultAsInt) + return Type(rawValue: enumResultAsInt) } return nil case 139: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return FillExtrusionHeightAlignment(rawValue: enumResultAsInt) + return FillExtrusionBaseAlignment(rawValue: enumResultAsInt) } return nil case 140: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return BackgroundPitchAlignment(rawValue: enumResultAsInt) + return FillExtrusionHeightAlignment(rawValue: enumResultAsInt) } return nil case 141: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return StylePackErrorType(rawValue: enumResultAsInt) + return BackgroundPitchAlignment(rawValue: enumResultAsInt) } return nil case 142: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return ResponseErrorReason(rawValue: enumResultAsInt) + return StylePackErrorType(rawValue: enumResultAsInt) } return nil case 143: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return OfflineRegionDownloadState(rawValue: enumResultAsInt) + return ResponseErrorReason(rawValue: enumResultAsInt) } return nil case 144: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return TileStoreUsageMode(rawValue: enumResultAsInt) + return OfflineRegionDownloadState(rawValue: enumResultAsInt) } return nil case 145: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return StylePropertyValueKind(rawValue: enumResultAsInt) + return TileStoreUsageMode(rawValue: enumResultAsInt) } return nil case 146: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return StyleProjectionName(rawValue: enumResultAsInt) + return StylePropertyValueKind(rawValue: enumResultAsInt) } return nil case 147: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return Anchor(rawValue: enumResultAsInt) + return StyleProjectionName(rawValue: enumResultAsInt) } return nil case 148: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return HttpMethod(rawValue: enumResultAsInt) + return Anchor(rawValue: enumResultAsInt) } return nil case 149: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return HttpRequestErrorType(rawValue: enumResultAsInt) + return HttpMethod(rawValue: enumResultAsInt) } return nil case 150: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return DownloadErrorCode(rawValue: enumResultAsInt) + return HttpRequestErrorType(rawValue: enumResultAsInt) } return nil case 151: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return DownloadState(rawValue: enumResultAsInt) + return DownloadErrorCode(rawValue: enumResultAsInt) } return nil case 152: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return TileRegionErrorType(rawValue: enumResultAsInt) + return DownloadState(rawValue: enumResultAsInt) } return nil case 153: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return _MapEvent(rawValue: enumResultAsInt) + return TileRegionErrorType(rawValue: enumResultAsInt) } return nil case 154: - return Point.fromList(self.readValue() as! [Any?]) + let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) + if let enumResultAsInt = enumResultAsInt { + return _MapEvent(rawValue: enumResultAsInt) + } + return nil case 155: - return Feature.fromList(self.readValue() as! [Any?]) + return Point.fromList(self.readValue() as! [Any?]) case 156: - return GlyphsRasterizationOptions.fromList(self.readValue() as! [Any?]) + return Feature.fromList(self.readValue() as! [Any?]) case 157: - return TileCoverOptions.fromList(self.readValue() as! [Any?]) + return GlyphsRasterizationOptions.fromList(self.readValue() as! [Any?]) case 158: - return MbxEdgeInsets.fromList(self.readValue() as! [Any?]) + return TileCoverOptions.fromList(self.readValue() as! [Any?]) case 159: - return CameraOptions.fromList(self.readValue() as! [Any?]) + return MbxEdgeInsets.fromList(self.readValue() as! [Any?]) case 160: - return CameraState.fromList(self.readValue() as! [Any?]) + return CameraOptions.fromList(self.readValue() as! [Any?]) case 161: - return CameraBoundsOptions.fromList(self.readValue() as! [Any?]) + return CameraState.fromList(self.readValue() as! [Any?]) case 162: - return CameraBounds.fromList(self.readValue() as! [Any?]) + return CameraBoundsOptions.fromList(self.readValue() as! [Any?]) case 163: - return MapAnimationOptions.fromList(self.readValue() as! [Any?]) + return CameraBounds.fromList(self.readValue() as! [Any?]) case 164: - return CoordinateBounds.fromList(self.readValue() as! [Any?]) + return MapAnimationOptions.fromList(self.readValue() as! [Any?]) case 165: - return MapDebugOptions.fromList(self.readValue() as! [Any?]) + return CoordinateBounds.fromList(self.readValue() as! [Any?]) case 166: - return TileCacheBudgetInMegabytes.fromList(self.readValue() as! [Any?]) + return MapDebugOptions.fromList(self.readValue() as! [Any?]) case 167: - return TileCacheBudgetInTiles.fromList(self.readValue() as! [Any?]) + return TileCacheBudgetInMegabytes.fromList(self.readValue() as! [Any?]) case 168: - return MapOptions.fromList(self.readValue() as! [Any?]) + return TileCacheBudgetInTiles.fromList(self.readValue() as! [Any?]) case 169: - return ScreenCoordinate.fromList(self.readValue() as! [Any?]) + return MapOptions.fromList(self.readValue() as! [Any?]) case 170: - return ScreenBox.fromList(self.readValue() as! [Any?]) + return ScreenCoordinate.fromList(self.readValue() as! [Any?]) case 171: - return CoordinateBoundsZoom.fromList(self.readValue() as! [Any?]) + return ScreenBox.fromList(self.readValue() as! [Any?]) case 172: - return Size.fromList(self.readValue() as! [Any?]) + return CoordinateBoundsZoom.fromList(self.readValue() as! [Any?]) case 173: - return RenderedQueryOptions.fromList(self.readValue() as! [Any?]) + return Size.fromList(self.readValue() as! [Any?]) case 174: - return SourceQueryOptions.fromList(self.readValue() as! [Any?]) + return RenderedQueryOptions.fromList(self.readValue() as! [Any?]) case 175: - return FeatureExtensionValue.fromList(self.readValue() as! [Any?]) + return SourceQueryOptions.fromList(self.readValue() as! [Any?]) case 176: - return LayerPosition.fromList(self.readValue() as! [Any?]) + return FeatureExtensionValue.fromList(self.readValue() as! [Any?]) case 177: - return QueriedRenderedFeature.fromList(self.readValue() as! [Any?]) + return LayerPosition.fromList(self.readValue() as! [Any?]) case 178: - return QueriedSourceFeature.fromList(self.readValue() as! [Any?]) + return QueriedRenderedFeature.fromList(self.readValue() as! [Any?]) case 179: - return QueriedFeature.fromList(self.readValue() as! [Any?]) + return QueriedSourceFeature.fromList(self.readValue() as! [Any?]) case 180: - return _RenderedQueryGeometry.fromList(self.readValue() as! [Any?]) + return QueriedFeature.fromList(self.readValue() as! [Any?]) case 181: - return ProjectedMeters.fromList(self.readValue() as! [Any?]) + return FeaturesetFeatureId.fromList(self.readValue() as! [Any?]) case 182: - return MercatorCoordinate.fromList(self.readValue() as! [Any?]) + return Interaction.fromList(self.readValue() as! [Any?]) case 183: - return StyleObjectInfo.fromList(self.readValue() as! [Any?]) + return TypedFeaturesetDescriptor.fromList(self.readValue() as! [Any?]) case 184: - return StyleProjection.fromList(self.readValue() as! [Any?]) + return FeaturesetDescriptor.fromList(self.readValue() as! [Any?]) case 185: - return FlatLight.fromList(self.readValue() as! [Any?]) + return FeaturesetFeature.fromList(self.readValue() as! [Any?]) case 186: - return DirectionalLight.fromList(self.readValue() as! [Any?]) + return FeaturesetQueryTarget.fromList(self.readValue() as! [Any?]) case 187: - return AmbientLight.fromList(self.readValue() as! [Any?]) + return _RenderedQueryGeometry.fromList(self.readValue() as! [Any?]) case 188: - return MbxImage.fromList(self.readValue() as! [Any?]) + return ProjectedMeters.fromList(self.readValue() as! [Any?]) case 189: - return ImageStretches.fromList(self.readValue() as! [Any?]) + return MercatorCoordinate.fromList(self.readValue() as! [Any?]) case 190: - return ImageContent.fromList(self.readValue() as! [Any?]) + return StyleObjectInfo.fromList(self.readValue() as! [Any?]) case 191: - return TransitionOptions.fromList(self.readValue() as! [Any?]) + return StyleProjection.fromList(self.readValue() as! [Any?]) case 192: - return CanonicalTileID.fromList(self.readValue() as! [Any?]) + return FlatLight.fromList(self.readValue() as! [Any?]) case 193: + return DirectionalLight.fromList(self.readValue() as! [Any?]) + case 194: + return AmbientLight.fromList(self.readValue() as! [Any?]) + case 195: + return MbxImage.fromList(self.readValue() as! [Any?]) + case 196: + return ImageStretches.fromList(self.readValue() as! [Any?]) + case 197: + return ImageContent.fromList(self.readValue() as! [Any?]) + case 198: + return TransitionOptions.fromList(self.readValue() as! [Any?]) + case 199: + return CanonicalTileID.fromList(self.readValue() as! [Any?]) + case 200: return StylePropertyValue.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) @@ -2189,183 +2207,195 @@ private class MapInterfacesPigeonCodecWriter: FlutterStandardWriter { } else if let value = value as? InteractionType { super.writeByte(137) super.writeValue(value.rawValue) - } else if let value = value as? FillExtrusionBaseAlignment { + } else if let value = value as? Type { super.writeByte(138) super.writeValue(value.rawValue) - } else if let value = value as? FillExtrusionHeightAlignment { + } else if let value = value as? FillExtrusionBaseAlignment { super.writeByte(139) super.writeValue(value.rawValue) - } else if let value = value as? BackgroundPitchAlignment { + } else if let value = value as? FillExtrusionHeightAlignment { super.writeByte(140) super.writeValue(value.rawValue) - } else if let value = value as? StylePackErrorType { + } else if let value = value as? BackgroundPitchAlignment { super.writeByte(141) super.writeValue(value.rawValue) - } else if let value = value as? ResponseErrorReason { + } else if let value = value as? StylePackErrorType { super.writeByte(142) super.writeValue(value.rawValue) - } else if let value = value as? OfflineRegionDownloadState { + } else if let value = value as? ResponseErrorReason { super.writeByte(143) super.writeValue(value.rawValue) - } else if let value = value as? TileStoreUsageMode { + } else if let value = value as? OfflineRegionDownloadState { super.writeByte(144) super.writeValue(value.rawValue) - } else if let value = value as? StylePropertyValueKind { + } else if let value = value as? TileStoreUsageMode { super.writeByte(145) super.writeValue(value.rawValue) - } else if let value = value as? StyleProjectionName { + } else if let value = value as? StylePropertyValueKind { super.writeByte(146) super.writeValue(value.rawValue) - } else if let value = value as? Anchor { + } else if let value = value as? StyleProjectionName { super.writeByte(147) super.writeValue(value.rawValue) - } else if let value = value as? HttpMethod { + } else if let value = value as? Anchor { super.writeByte(148) super.writeValue(value.rawValue) - } else if let value = value as? HttpRequestErrorType { + } else if let value = value as? HttpMethod { super.writeByte(149) super.writeValue(value.rawValue) - } else if let value = value as? DownloadErrorCode { + } else if let value = value as? HttpRequestErrorType { super.writeByte(150) super.writeValue(value.rawValue) - } else if let value = value as? DownloadState { + } else if let value = value as? DownloadErrorCode { super.writeByte(151) super.writeValue(value.rawValue) - } else if let value = value as? TileRegionErrorType { + } else if let value = value as? DownloadState { super.writeByte(152) super.writeValue(value.rawValue) - } else if let value = value as? _MapEvent { + } else if let value = value as? TileRegionErrorType { super.writeByte(153) super.writeValue(value.rawValue) - } else if let value = value as? Point { + } else if let value = value as? _MapEvent { super.writeByte(154) - super.writeValue(value.toList()) - } else if let value = value as? Feature { + super.writeValue(value.rawValue) + } else if let value = value as? Point { super.writeByte(155) super.writeValue(value.toList()) - } else if let value = value as? GlyphsRasterizationOptions { + } else if let value = value as? Feature { super.writeByte(156) super.writeValue(value.toList()) - } else if let value = value as? TileCoverOptions { + } else if let value = value as? GlyphsRasterizationOptions { super.writeByte(157) super.writeValue(value.toList()) - } else if let value = value as? MbxEdgeInsets { + } else if let value = value as? TileCoverOptions { super.writeByte(158) super.writeValue(value.toList()) - } else if let value = value as? CameraOptions { + } else if let value = value as? MbxEdgeInsets { super.writeByte(159) super.writeValue(value.toList()) - } else if let value = value as? CameraState { + } else if let value = value as? CameraOptions { super.writeByte(160) super.writeValue(value.toList()) - } else if let value = value as? CameraBoundsOptions { + } else if let value = value as? CameraState { super.writeByte(161) super.writeValue(value.toList()) - } else if let value = value as? CameraBounds { + } else if let value = value as? CameraBoundsOptions { super.writeByte(162) super.writeValue(value.toList()) - } else if let value = value as? MapAnimationOptions { + } else if let value = value as? CameraBounds { super.writeByte(163) super.writeValue(value.toList()) - } else if let value = value as? CoordinateBounds { + } else if let value = value as? MapAnimationOptions { super.writeByte(164) super.writeValue(value.toList()) - } else if let value = value as? MapDebugOptions { + } else if let value = value as? CoordinateBounds { super.writeByte(165) super.writeValue(value.toList()) - } else if let value = value as? TileCacheBudgetInMegabytes { + } else if let value = value as? MapDebugOptions { super.writeByte(166) super.writeValue(value.toList()) - } else if let value = value as? TileCacheBudgetInTiles { + } else if let value = value as? TileCacheBudgetInMegabytes { super.writeByte(167) super.writeValue(value.toList()) - } else if let value = value as? MapOptions { + } else if let value = value as? TileCacheBudgetInTiles { super.writeByte(168) super.writeValue(value.toList()) - } else if let value = value as? ScreenCoordinate { + } else if let value = value as? MapOptions { super.writeByte(169) super.writeValue(value.toList()) - } else if let value = value as? ScreenBox { + } else if let value = value as? ScreenCoordinate { super.writeByte(170) super.writeValue(value.toList()) - } else if let value = value as? CoordinateBoundsZoom { + } else if let value = value as? ScreenBox { super.writeByte(171) super.writeValue(value.toList()) - } else if let value = value as? Size { + } else if let value = value as? CoordinateBoundsZoom { super.writeByte(172) super.writeValue(value.toList()) - } else if let value = value as? RenderedQueryOptions { + } else if let value = value as? Size { super.writeByte(173) super.writeValue(value.toList()) - } else if let value = value as? SourceQueryOptions { + } else if let value = value as? RenderedQueryOptions { super.writeByte(174) super.writeValue(value.toList()) - } else if let value = value as? FeatureExtensionValue { + } else if let value = value as? SourceQueryOptions { super.writeByte(175) super.writeValue(value.toList()) - } else if let value = value as? LayerPosition { + } else if let value = value as? FeatureExtensionValue { super.writeByte(176) super.writeValue(value.toList()) - } else if let value = value as? QueriedRenderedFeature { + } else if let value = value as? LayerPosition { super.writeByte(177) super.writeValue(value.toList()) - } else if let value = value as? QueriedSourceFeature { + } else if let value = value as? QueriedRenderedFeature { super.writeByte(178) super.writeValue(value.toList()) - } else if let value = value as? QueriedFeature { + } else if let value = value as? QueriedSourceFeature { super.writeByte(179) super.writeValue(value.toList()) - } else if let value = value as? _RenderedQueryGeometry { + } else if let value = value as? QueriedFeature { super.writeByte(180) super.writeValue(value.toList()) - } else if let value = value as? ProjectedMeters { + } else if let value = value as? FeaturesetFeatureId { super.writeByte(181) super.writeValue(value.toList()) - } else if let value = value as? MercatorCoordinate { + } else if let value = value as? Interaction { super.writeByte(182) super.writeValue(value.toList()) - } else if let value = value as? StyleObjectInfo { + } else if let value = value as? TypedFeaturesetDescriptor { super.writeByte(183) super.writeValue(value.toList()) - } else if let value = value as? StyleProjection { + } else if let value = value as? FeaturesetDescriptor { super.writeByte(184) super.writeValue(value.toList()) - } else if let value = value as? FlatLight { + } else if let value = value as? FeaturesetFeature { super.writeByte(185) super.writeValue(value.toList()) - } else if let value = value as? DirectionalLight { + } else if let value = value as? FeaturesetQueryTarget { super.writeByte(186) super.writeValue(value.toList()) - } else if let value = value as? AmbientLight { + } else if let value = value as? _RenderedQueryGeometry { super.writeByte(187) super.writeValue(value.toList()) - } else if let value = value as? MbxImage { + } else if let value = value as? ProjectedMeters { super.writeByte(188) super.writeValue(value.toList()) - } else if let value = value as? ImageStretches { + } else if let value = value as? MercatorCoordinate { super.writeByte(189) super.writeValue(value.toList()) - } else if let value = value as? ImageContent { + } else if let value = value as? StyleObjectInfo { super.writeByte(190) super.writeValue(value.toList()) - } else if let value = value as? TransitionOptions { + } else if let value = value as? StyleProjection { super.writeByte(191) super.writeValue(value.toList()) - } else if let value = value as? CanonicalTileID { + } else if let value = value as? FlatLight { super.writeByte(192) super.writeValue(value.toList()) - } else if let value = value as? StylePropertyValue { + } else if let value = value as? DirectionalLight { super.writeByte(193) super.writeValue(value.toList()) - } else if let value = value as? TransitionOptions { + } else if let value = value as? AmbientLight { + super.writeByte(194) + super.writeValue(value.toList()) + } else if let value = value as? MbxImage { super.writeByte(195) super.writeValue(value.toList()) - } else if let value = value as? CanonicalTileID { + } else if let value = value as? ImageStretches { super.writeByte(196) super.writeValue(value.toList()) - } else if let value = value as? StylePropertyValue { + } else if let value = value as? ImageContent { super.writeByte(197) super.writeValue(value.toList()) + } else if let value = value as? TransitionOptions { + super.writeByte(198) + super.writeValue(value.toList()) + } else if let value = value as? CanonicalTileID { + super.writeByte(199) + super.writeValue(value.toList()) + } else if let value = value as? StylePropertyValue { + super.writeByte(200) + super.writeValue(value.toList()) } else { super.writeValue(value) } diff --git a/lib/src/pigeons/map_interfaces.dart b/lib/src/pigeons/map_interfaces.dart index fc14af720..2d7968cb0 100644 --- a/lib/src/pigeons/map_interfaces.dart +++ b/lib/src/pigeons/map_interfaces.dart @@ -2121,183 +2121,195 @@ class MapInterfaces_PigeonCodec extends StandardMessageCodec { } else if (value is InteractionType) { buffer.putUint8(137); writeValue(buffer, value.index); - } else if (value is FillExtrusionBaseAlignment) { + } else if (value is Type) { buffer.putUint8(138); writeValue(buffer, value.index); - } else if (value is FillExtrusionHeightAlignment) { + } else if (value is FillExtrusionBaseAlignment) { buffer.putUint8(139); writeValue(buffer, value.index); - } else if (value is BackgroundPitchAlignment) { + } else if (value is FillExtrusionHeightAlignment) { buffer.putUint8(140); writeValue(buffer, value.index); - } else if (value is StylePackErrorType) { + } else if (value is BackgroundPitchAlignment) { buffer.putUint8(141); writeValue(buffer, value.index); - } else if (value is ResponseErrorReason) { + } else if (value is StylePackErrorType) { buffer.putUint8(142); writeValue(buffer, value.index); - } else if (value is OfflineRegionDownloadState) { + } else if (value is ResponseErrorReason) { buffer.putUint8(143); writeValue(buffer, value.index); - } else if (value is TileStoreUsageMode) { + } else if (value is OfflineRegionDownloadState) { buffer.putUint8(144); writeValue(buffer, value.index); - } else if (value is StylePropertyValueKind) { + } else if (value is TileStoreUsageMode) { buffer.putUint8(145); writeValue(buffer, value.index); - } else if (value is StyleProjectionName) { + } else if (value is StylePropertyValueKind) { buffer.putUint8(146); writeValue(buffer, value.index); - } else if (value is Anchor) { + } else if (value is StyleProjectionName) { buffer.putUint8(147); writeValue(buffer, value.index); - } else if (value is HttpMethod) { + } else if (value is Anchor) { buffer.putUint8(148); writeValue(buffer, value.index); - } else if (value is HttpRequestErrorType) { + } else if (value is HttpMethod) { buffer.putUint8(149); writeValue(buffer, value.index); - } else if (value is DownloadErrorCode) { + } else if (value is HttpRequestErrorType) { buffer.putUint8(150); writeValue(buffer, value.index); - } else if (value is DownloadState) { + } else if (value is DownloadErrorCode) { buffer.putUint8(151); writeValue(buffer, value.index); - } else if (value is TileRegionErrorType) { + } else if (value is DownloadState) { buffer.putUint8(152); writeValue(buffer, value.index); - } else if (value is _MapEvent) { + } else if (value is TileRegionErrorType) { buffer.putUint8(153); writeValue(buffer, value.index); - } else if (value is Point) { + } else if (value is _MapEvent) { buffer.putUint8(154); - writeValue(buffer, value.encode()); - } else if (value is Feature) { + writeValue(buffer, value.index); + } else if (value is Point) { buffer.putUint8(155); writeValue(buffer, value.encode()); - } else if (value is GlyphsRasterizationOptions) { + } else if (value is Feature) { buffer.putUint8(156); writeValue(buffer, value.encode()); - } else if (value is TileCoverOptions) { + } else if (value is GlyphsRasterizationOptions) { buffer.putUint8(157); writeValue(buffer, value.encode()); - } else if (value is MbxEdgeInsets) { + } else if (value is TileCoverOptions) { buffer.putUint8(158); writeValue(buffer, value.encode()); - } else if (value is CameraOptions) { + } else if (value is MbxEdgeInsets) { buffer.putUint8(159); writeValue(buffer, value.encode()); - } else if (value is CameraState) { + } else if (value is CameraOptions) { buffer.putUint8(160); writeValue(buffer, value.encode()); - } else if (value is CameraBoundsOptions) { + } else if (value is CameraState) { buffer.putUint8(161); writeValue(buffer, value.encode()); - } else if (value is CameraBounds) { + } else if (value is CameraBoundsOptions) { buffer.putUint8(162); writeValue(buffer, value.encode()); - } else if (value is MapAnimationOptions) { + } else if (value is CameraBounds) { buffer.putUint8(163); writeValue(buffer, value.encode()); - } else if (value is CoordinateBounds) { + } else if (value is MapAnimationOptions) { buffer.putUint8(164); writeValue(buffer, value.encode()); - } else if (value is MapDebugOptions) { + } else if (value is CoordinateBounds) { buffer.putUint8(165); writeValue(buffer, value.encode()); - } else if (value is TileCacheBudgetInMegabytes) { + } else if (value is MapDebugOptions) { buffer.putUint8(166); writeValue(buffer, value.encode()); - } else if (value is TileCacheBudgetInTiles) { + } else if (value is TileCacheBudgetInMegabytes) { buffer.putUint8(167); writeValue(buffer, value.encode()); - } else if (value is MapOptions) { + } else if (value is TileCacheBudgetInTiles) { buffer.putUint8(168); writeValue(buffer, value.encode()); - } else if (value is ScreenCoordinate) { + } else if (value is MapOptions) { buffer.putUint8(169); writeValue(buffer, value.encode()); - } else if (value is ScreenBox) { + } else if (value is ScreenCoordinate) { buffer.putUint8(170); writeValue(buffer, value.encode()); - } else if (value is CoordinateBoundsZoom) { + } else if (value is ScreenBox) { buffer.putUint8(171); writeValue(buffer, value.encode()); - } else if (value is Size) { + } else if (value is CoordinateBoundsZoom) { buffer.putUint8(172); writeValue(buffer, value.encode()); - } else if (value is RenderedQueryOptions) { + } else if (value is Size) { buffer.putUint8(173); writeValue(buffer, value.encode()); - } else if (value is SourceQueryOptions) { + } else if (value is RenderedQueryOptions) { buffer.putUint8(174); writeValue(buffer, value.encode()); - } else if (value is FeatureExtensionValue) { + } else if (value is SourceQueryOptions) { buffer.putUint8(175); writeValue(buffer, value.encode()); - } else if (value is LayerPosition) { + } else if (value is FeatureExtensionValue) { buffer.putUint8(176); writeValue(buffer, value.encode()); - } else if (value is QueriedRenderedFeature) { + } else if (value is LayerPosition) { buffer.putUint8(177); writeValue(buffer, value.encode()); - } else if (value is QueriedSourceFeature) { + } else if (value is QueriedRenderedFeature) { buffer.putUint8(178); writeValue(buffer, value.encode()); - } else if (value is QueriedFeature) { + } else if (value is QueriedSourceFeature) { buffer.putUint8(179); writeValue(buffer, value.encode()); - } else if (value is _RenderedQueryGeometry) { + } else if (value is QueriedFeature) { buffer.putUint8(180); writeValue(buffer, value.encode()); - } else if (value is ProjectedMeters) { + } else if (value is FeaturesetFeatureId) { buffer.putUint8(181); writeValue(buffer, value.encode()); - } else if (value is MercatorCoordinate) { + } else if (value is Interaction) { buffer.putUint8(182); writeValue(buffer, value.encode()); - } else if (value is StyleObjectInfo) { + } else if (value is TypedFeaturesetDescriptor) { buffer.putUint8(183); writeValue(buffer, value.encode()); - } else if (value is StyleProjection) { + } else if (value is FeaturesetDescriptor) { buffer.putUint8(184); writeValue(buffer, value.encode()); - } else if (value is FlatLight) { + } else if (value is FeaturesetFeature) { buffer.putUint8(185); writeValue(buffer, value.encode()); - } else if (value is DirectionalLight) { + } else if (value is FeaturesetQueryTarget) { buffer.putUint8(186); writeValue(buffer, value.encode()); - } else if (value is AmbientLight) { + } else if (value is _RenderedQueryGeometry) { buffer.putUint8(187); writeValue(buffer, value.encode()); - } else if (value is MbxImage) { + } else if (value is ProjectedMeters) { buffer.putUint8(188); writeValue(buffer, value.encode()); - } else if (value is ImageStretches) { + } else if (value is MercatorCoordinate) { buffer.putUint8(189); writeValue(buffer, value.encode()); - } else if (value is ImageContent) { + } else if (value is StyleObjectInfo) { buffer.putUint8(190); writeValue(buffer, value.encode()); - } else if (value is TransitionOptions) { + } else if (value is StyleProjection) { buffer.putUint8(191); writeValue(buffer, value.encode()); - } else if (value is CanonicalTileID) { + } else if (value is FlatLight) { buffer.putUint8(192); writeValue(buffer, value.encode()); - } else if (value is StylePropertyValue) { + } else if (value is DirectionalLight) { buffer.putUint8(193); writeValue(buffer, value.encode()); - } else if (value is TransitionOptions) { + } else if (value is AmbientLight) { + buffer.putUint8(194); + writeValue(buffer, value.encode()); + } else if (value is MbxImage) { buffer.putUint8(195); writeValue(buffer, value.encode()); - } else if (value is CanonicalTileID) { + } else if (value is ImageStretches) { buffer.putUint8(196); writeValue(buffer, value.encode()); - } else if (value is StylePropertyValue) { + } else if (value is ImageContent) { buffer.putUint8(197); writeValue(buffer, value.encode()); + } else if (value is TransitionOptions) { + buffer.putUint8(198); + writeValue(buffer, value.encode()); + } else if (value is CanonicalTileID) { + buffer.putUint8(199); + writeValue(buffer, value.encode()); + } else if (value is StylePropertyValue) { + buffer.putUint8(200); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -2335,133 +2347,148 @@ class MapInterfaces_PigeonCodec extends StandardMessageCodec { return value == null ? null : InteractionType.values[value]; case 138: final int? value = readValue(buffer) as int?; - return value == null ? null : FillExtrusionBaseAlignment.values[value]; + return value == null ? null : Type.values[value]; case 139: + final int? value = readValue(buffer) as int?; + return value == null ? null : FillExtrusionBaseAlignment.values[value]; + case 140: final int? value = readValue(buffer) as int?; return value == null ? null : FillExtrusionHeightAlignment.values[value]; - case 140: + case 141: final int? value = readValue(buffer) as int?; return value == null ? null : BackgroundPitchAlignment.values[value]; - case 141: + case 142: final int? value = readValue(buffer) as int?; return value == null ? null : StylePackErrorType.values[value]; - case 142: + case 143: final int? value = readValue(buffer) as int?; return value == null ? null : ResponseErrorReason.values[value]; - case 143: + case 144: final int? value = readValue(buffer) as int?; return value == null ? null : OfflineRegionDownloadState.values[value]; - case 144: + case 145: final int? value = readValue(buffer) as int?; return value == null ? null : TileStoreUsageMode.values[value]; - case 145: + case 146: final int? value = readValue(buffer) as int?; return value == null ? null : StylePropertyValueKind.values[value]; - case 146: + case 147: final int? value = readValue(buffer) as int?; return value == null ? null : StyleProjectionName.values[value]; - case 147: + case 148: final int? value = readValue(buffer) as int?; return value == null ? null : Anchor.values[value]; - case 148: + case 149: final int? value = readValue(buffer) as int?; return value == null ? null : HttpMethod.values[value]; - case 149: + case 150: final int? value = readValue(buffer) as int?; return value == null ? null : HttpRequestErrorType.values[value]; - case 150: + case 151: final int? value = readValue(buffer) as int?; return value == null ? null : DownloadErrorCode.values[value]; - case 151: + case 152: final int? value = readValue(buffer) as int?; return value == null ? null : DownloadState.values[value]; - case 152: + case 153: final int? value = readValue(buffer) as int?; return value == null ? null : TileRegionErrorType.values[value]; - case 153: + case 154: final int? value = readValue(buffer) as int?; return value == null ? null : _MapEvent.values[value]; - case 154: - return Point.decode(readValue(buffer)!); case 155: - return Feature.decode(readValue(buffer)!); + return Point.decode(readValue(buffer)!); case 156: - return GlyphsRasterizationOptions.decode(readValue(buffer)!); + return Feature.decode(readValue(buffer)!); case 157: - return TileCoverOptions.decode(readValue(buffer)!); + return GlyphsRasterizationOptions.decode(readValue(buffer)!); case 158: - return MbxEdgeInsets.decode(readValue(buffer)!); + return TileCoverOptions.decode(readValue(buffer)!); case 159: - return CameraOptions.decode(readValue(buffer)!); + return MbxEdgeInsets.decode(readValue(buffer)!); case 160: - return CameraState.decode(readValue(buffer)!); + return CameraOptions.decode(readValue(buffer)!); case 161: - return CameraBoundsOptions.decode(readValue(buffer)!); + return CameraState.decode(readValue(buffer)!); case 162: - return CameraBounds.decode(readValue(buffer)!); + return CameraBoundsOptions.decode(readValue(buffer)!); case 163: - return MapAnimationOptions.decode(readValue(buffer)!); + return CameraBounds.decode(readValue(buffer)!); case 164: - return CoordinateBounds.decode(readValue(buffer)!); + return MapAnimationOptions.decode(readValue(buffer)!); case 165: - return MapDebugOptions.decode(readValue(buffer)!); + return CoordinateBounds.decode(readValue(buffer)!); case 166: - return TileCacheBudgetInMegabytes.decode(readValue(buffer)!); + return MapDebugOptions.decode(readValue(buffer)!); case 167: - return TileCacheBudgetInTiles.decode(readValue(buffer)!); + return TileCacheBudgetInMegabytes.decode(readValue(buffer)!); case 168: - return MapOptions.decode(readValue(buffer)!); + return TileCacheBudgetInTiles.decode(readValue(buffer)!); case 169: - return ScreenCoordinate.decode(readValue(buffer)!); + return MapOptions.decode(readValue(buffer)!); case 170: - return ScreenBox.decode(readValue(buffer)!); + return ScreenCoordinate.decode(readValue(buffer)!); case 171: - return CoordinateBoundsZoom.decode(readValue(buffer)!); + return ScreenBox.decode(readValue(buffer)!); case 172: - return Size.decode(readValue(buffer)!); + return CoordinateBoundsZoom.decode(readValue(buffer)!); case 173: - return RenderedQueryOptions.decode(readValue(buffer)!); + return Size.decode(readValue(buffer)!); case 174: - return SourceQueryOptions.decode(readValue(buffer)!); + return RenderedQueryOptions.decode(readValue(buffer)!); case 175: - return FeatureExtensionValue.decode(readValue(buffer)!); + return SourceQueryOptions.decode(readValue(buffer)!); case 176: - return LayerPosition.decode(readValue(buffer)!); + return FeatureExtensionValue.decode(readValue(buffer)!); case 177: - return QueriedRenderedFeature.decode(readValue(buffer)!); + return LayerPosition.decode(readValue(buffer)!); case 178: - return QueriedSourceFeature.decode(readValue(buffer)!); + return QueriedRenderedFeature.decode(readValue(buffer)!); case 179: - return QueriedFeature.decode(readValue(buffer)!); + return QueriedSourceFeature.decode(readValue(buffer)!); case 180: - return _RenderedQueryGeometry.decode(readValue(buffer)!); + return QueriedFeature.decode(readValue(buffer)!); case 181: - return ProjectedMeters.decode(readValue(buffer)!); + return FeaturesetFeatureId.decode(readValue(buffer)!); case 182: - return MercatorCoordinate.decode(readValue(buffer)!); + return Interaction.decode(readValue(buffer)!); case 183: - return StyleObjectInfo.decode(readValue(buffer)!); + return TypedFeaturesetDescriptor.decode(readValue(buffer)!); case 184: - return StyleProjection.decode(readValue(buffer)!); + return FeaturesetDescriptor.decode(readValue(buffer)!); case 185: - return FlatLight.decode(readValue(buffer)!); + return FeaturesetFeature.decode(readValue(buffer)!); case 186: - return DirectionalLight.decode(readValue(buffer)!); + return FeaturesetQueryTarget.decode(readValue(buffer)!); case 187: - return AmbientLight.decode(readValue(buffer)!); + return _RenderedQueryGeometry.decode(readValue(buffer)!); case 188: - return MbxImage.decode(readValue(buffer)!); + return ProjectedMeters.decode(readValue(buffer)!); case 189: - return ImageStretches.decode(readValue(buffer)!); + return MercatorCoordinate.decode(readValue(buffer)!); case 190: - return ImageContent.decode(readValue(buffer)!); + return StyleObjectInfo.decode(readValue(buffer)!); case 191: - return TransitionOptions.decode(readValue(buffer)!); + return StyleProjection.decode(readValue(buffer)!); case 192: - return CanonicalTileID.decode(readValue(buffer)!); + return FlatLight.decode(readValue(buffer)!); case 193: + return DirectionalLight.decode(readValue(buffer)!); + case 194: + return AmbientLight.decode(readValue(buffer)!); + case 195: + return MbxImage.decode(readValue(buffer)!); + case 196: + return ImageStretches.decode(readValue(buffer)!); + case 197: + return ImageContent.decode(readValue(buffer)!); + case 198: + return TransitionOptions.decode(readValue(buffer)!); + case 199: + return CanonicalTileID.decode(readValue(buffer)!); + case 200: return StylePropertyValue.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); From 8bfcbe9132d87ffb27d8affcc3be9170ba02379a Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Wed, 8 Jan 2025 18:16:38 -0500 Subject: [PATCH 15/28] Add map.addInteraction, add Standard featuresets --- CHANGELOG.md | 18 + .../com/mapbox/maps/mapbox_maps/Extentions.kt | 45 +- .../mapbox_maps/MapInterfaceController.kt | 53 +- .../maps/mapbox_maps/MapboxMapController.kt | 55 ++ .../maps/mapbox_maps/pigeons/MapInterfaces.kt | 511 +++++++--------- .../interactive_features_test.dart | 83 +-- example/ios/Podfile.lock | 5 + example/lib/interactive_features_example.dart | 107 ++-- .../Classes/Extensions.swift | 36 +- .../Classes/Generated/MapInterfaces.swift | 529 ++++++++--------- .../Classes/MapInterfaceController.swift | 32 +- .../Classes/MapboxMapController.swift | 32 + lib/mapbox_maps_flutter.dart | 4 + lib/src/callbacks.dart | 4 + lib/src/mapbox_map.dart | 76 ++- lib/src/mapbox_maps_platform.dart | 17 + lib/src/pigeons/map_interfaces.dart | 547 ++++++++---------- .../interactive_features.dart | 44 ++ .../standard_buildings.dart | 52 ++ .../standard_place_labels.dart | 64 ++ .../interactive_features/standard_poi.dart | 85 +++ 21 files changed, 1211 insertions(+), 1188 deletions(-) create mode 100644 lib/src/style/interactive_features/interactive_features.dart create mode 100644 lib/src/style/interactive_features/standard_buildings.dart create mode 100644 lib/src/style/interactive_features/standard_place_labels.dart create mode 100644 lib/src/style/interactive_features/standard_poi.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index d54142a3f..1a07373aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,24 @@ # main * Align tap propagation behavior on Android and iOS. +* 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.6.0-beta.1 diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt index 9bb93df88..ec31b99f7 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt @@ -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 @@ -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.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)")) } } } @@ -321,7 +318,7 @@ fun Map.toFeatureState(): com.mapbox.maps.interactions.FeatureStat @OptIn(MapboxExperimental::class) @SuppressLint("RestrictedApi") fun FeaturesetFeature.toFeaturesetFeature(): com.mapbox.maps.interactions.FeaturesetFeature { - 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(), @@ -642,6 +639,16 @@ fun com.mapbox.maps.interactions.FeaturesetFeature.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()) } diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt index f889203c5..086082ae1 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt @@ -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 @@ -180,32 +179,6 @@ class MapInterfaceController( } } - @OptIn(MapboxExperimental::class, MapboxDelicateApi::class) - override fun queryRenderedFeaturesForTargets( - geometry: _RenderedQueryGeometry, - targets: List, - callback: (Result>) -> 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, @@ -214,8 +187,8 @@ class MapInterfaceController( callback: (Result>) -> Unit ) { mapboxMap.queryRenderedFeatures( - geometry.toRenderedQueryGeometry(context), featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor<*, com.mapbox.maps.interactions.FeaturesetFeature>, + geometry.toRenderedQueryGeometry(context), filter?.let { Expression.fromRaw(filter) } ) { callback( @@ -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>, + geometry, filter?.let { Expression.fromRaw(filter) } ) { callback( @@ -264,28 +237,6 @@ class MapInterfaceController( } } - @OptIn(MapboxExperimental::class) - override fun querySourceFeaturesForTargets( - target: FeaturesetQueryTarget, - callback: (Result>) -> 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, diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt index 06002819b..755b0e23b 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt @@ -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 @@ -235,6 +242,54 @@ class MapboxMapController( gestureController.removeListeners() result.success(null) } + "interactions#add_interaction" -> { + val listener = InteractionsListener(messenger, channelSuffix) + val arguments: HashMap = call.arguments as? HashMap ?: return + val featuresetDescriptorList = arguments["featuresetDescriptor"] as? List ?: 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) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt index c788fbc3e..e85888e3a 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt @@ -35,6 +35,10 @@ private fun wrapError(exception: Throwable): List { } } +private fun createConnectionError(channelName: String): FlutterError { + return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "") +} + /** * Error class for passing custom error details to Flutter via a thrown PlatformException. * @property code The error code. @@ -230,8 +234,8 @@ enum class ViewAnnotationAnchor(val raw: Int) { } enum class InteractionType(val raw: Int) { - CLICK(0), - LONG_CLICK(1); + TAP(0), + LONG_TAP(1); companion object { fun ofRaw(raw: Int): InteractionType? { @@ -1398,44 +1402,55 @@ data class FeaturesetFeatureId( } /** Generated class from Pigeon that represents data sent in messages. */ -data class Interaction( - val typedFeaturesetDescriptor: TypedFeaturesetDescriptor, - val interactionType: InteractionType, - val filter: String? = null +data class FeatureState( + val map: Map ) { companion object { - fun fromList(pigeonVar_list: List): Interaction { - val typedFeaturesetDescriptor = pigeonVar_list[0] as TypedFeaturesetDescriptor - val interactionType = pigeonVar_list[1] as InteractionType - val filter = pigeonVar_list[2] as String? - return Interaction(typedFeaturesetDescriptor, interactionType, filter) + fun fromList(pigeonVar_list: List): FeatureState { + val map = pigeonVar_list[0] as Map + return FeatureState(map) } } fun toList(): List { return listOf( - typedFeaturesetDescriptor, - interactionType, - filter, + map, ) } } -/** Generated class from Pigeon that represents data sent in messages. */ -data class TypedFeaturesetDescriptor( +/** + * An interaction that can be added to the map. + * + * To create an interaction use ``TapInteraction`` and ``LongClickInteraction`` implementations. + * + * See also: ``MapboxMap/addInteraction``. + * + * Generated class from Pigeon that represents data sent in messages. + */ +data class Interaction( val featuresetDescriptor: FeaturesetDescriptor, - val featuresetType: String + val interactionType: InteractionType, + val stopPropagation: Boolean, + val filter: String? = null, + val radius: Double? = null ) { companion object { - fun fromList(pigeonVar_list: List): TypedFeaturesetDescriptor { + fun fromList(pigeonVar_list: List): Interaction { val featuresetDescriptor = pigeonVar_list[0] as FeaturesetDescriptor - val featuresetType = pigeonVar_list[1] as String - return TypedFeaturesetDescriptor(featuresetDescriptor, featuresetType) + val interactionType = pigeonVar_list[1] as InteractionType + val stopPropagation = pigeonVar_list[2] as Boolean + val filter = pigeonVar_list[3] as String? + val radius = pigeonVar_list[4] as Double? + return Interaction(featuresetDescriptor, interactionType, stopPropagation, filter, radius) } } fun toList(): List { return listOf( featuresetDescriptor, - featuresetType, + interactionType, + stopPropagation, + filter, + radius, ) } } @@ -1443,34 +1458,12 @@ data class TypedFeaturesetDescriptor( /** * A featureset descriptor. * - * The descriptor instance acts as a universal target for interactions or querying rendered features (see - * ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)``). - * * Generated class from Pigeon that represents data sent in messages. */ data class FeaturesetDescriptor( - /** - * An optional unique identifier for the featureset within the style. - * This id is used to reference a specific featureset. - * - * * Note: If `featuresetId` is provided and valid, it takes precedence over `layerId`, - * * meaning `layerId` will not be considered even if it has a valid value. - */ val featuresetId: String? = null, - /** - * An optional import id that is required if the featureset is defined within an imported style. - * If the featureset belongs to the current style, this field should be set to a null string. - * - * Note: `importId` is only applicable when used in conjunction with `featuresetId` - * and has no effect when used with `layerId`. - */ + /** */ val importId: String? = null, - /** - * An optional unique identifier for the layer within the current style. - * - * Note: If `featuresetId` is valid, `layerId` will be ignored even if it has a valid value. - * Additionally, `importId` does not apply when using `layerId`. - */ val layerId: String? = null ) { companion object { @@ -1490,33 +1483,12 @@ data class FeaturesetDescriptor( } } -/** - * A basic feature of a featureset. - * - * The featureset feature is different to the `Turf.Feature`. The latter represents any GeoJSON feature, while the former is a high level representation of features. - * - * Generated class from Pigeon that represents data sent in messages. - */ +/** Generated class from Pigeon that represents data sent in messages. */ data class FeaturesetFeature( - /** - * An identifier of the feature. - * - * The identifier can be `nil` if the underlying source doesn't have identifiers for features. - * In this case it's impossible to set a feature state for an individual feature. - */ val id: FeaturesetFeatureId? = null, - /** A featureset descriptor denoting the featureset this feature belongs to. */ val featureset: FeaturesetDescriptor, - /** A feature geometry. */ val geometry: Map, - /** Feature JSON properties. */ val properties: Map, - /** - * A feature state. - * - * This is a **snapshot** of the state that the feature had when it was interacted with. - * To update and read the original state, use ``MapboxMap/setFeatureState()`` and ``MapboxMap/getFeatureState()``. - */ val state: Map ) { companion object { @@ -2084,315 +2056,325 @@ private open class MapInterfacesPigeonCodec : StandardMessageCodec() { } 138.toByte() -> { return (readValue(buffer) as Long?)?.let { - Type.ofRaw(it.toInt()) + GestureState.ofRaw(it.toInt()) } } 139.toByte() -> { return (readValue(buffer) as Long?)?.let { - FillExtrusionBaseAlignment.ofRaw(it.toInt()) + Type.ofRaw(it.toInt()) } } 140.toByte() -> { return (readValue(buffer) as Long?)?.let { - FillExtrusionHeightAlignment.ofRaw(it.toInt()) + FillExtrusionBaseAlignment.ofRaw(it.toInt()) } } 141.toByte() -> { return (readValue(buffer) as Long?)?.let { - BackgroundPitchAlignment.ofRaw(it.toInt()) + FillExtrusionHeightAlignment.ofRaw(it.toInt()) } } 142.toByte() -> { return (readValue(buffer) as Long?)?.let { - StylePackErrorType.ofRaw(it.toInt()) + BackgroundPitchAlignment.ofRaw(it.toInt()) } } 143.toByte() -> { return (readValue(buffer) as Long?)?.let { - ResponseErrorReason.ofRaw(it.toInt()) + StylePackErrorType.ofRaw(it.toInt()) } } 144.toByte() -> { return (readValue(buffer) as Long?)?.let { - OfflineRegionDownloadState.ofRaw(it.toInt()) + ResponseErrorReason.ofRaw(it.toInt()) } } 145.toByte() -> { return (readValue(buffer) as Long?)?.let { - TileStoreUsageMode.ofRaw(it.toInt()) + OfflineRegionDownloadState.ofRaw(it.toInt()) } } 146.toByte() -> { return (readValue(buffer) as Long?)?.let { - StylePropertyValueKind.ofRaw(it.toInt()) + TileStoreUsageMode.ofRaw(it.toInt()) } } 147.toByte() -> { return (readValue(buffer) as Long?)?.let { - StyleProjectionName.ofRaw(it.toInt()) + StylePropertyValueKind.ofRaw(it.toInt()) } } 148.toByte() -> { return (readValue(buffer) as Long?)?.let { - Anchor.ofRaw(it.toInt()) + StyleProjectionName.ofRaw(it.toInt()) } } 149.toByte() -> { return (readValue(buffer) as Long?)?.let { - HttpMethod.ofRaw(it.toInt()) + Anchor.ofRaw(it.toInt()) } } 150.toByte() -> { return (readValue(buffer) as Long?)?.let { - HttpRequestErrorType.ofRaw(it.toInt()) + HttpMethod.ofRaw(it.toInt()) } } 151.toByte() -> { return (readValue(buffer) as Long?)?.let { - DownloadErrorCode.ofRaw(it.toInt()) + HttpRequestErrorType.ofRaw(it.toInt()) } } 152.toByte() -> { return (readValue(buffer) as Long?)?.let { - DownloadState.ofRaw(it.toInt()) + DownloadErrorCode.ofRaw(it.toInt()) } } 153.toByte() -> { return (readValue(buffer) as Long?)?.let { - TileRegionErrorType.ofRaw(it.toInt()) + DownloadState.ofRaw(it.toInt()) } } 154.toByte() -> { return (readValue(buffer) as Long?)?.let { - _MapEvent.ofRaw(it.toInt()) + TileRegionErrorType.ofRaw(it.toInt()) } } 155.toByte() -> { - return (readValue(buffer) as? List)?.let { - PointDecoder.fromList(it) + return (readValue(buffer) as Long?)?.let { + _MapEvent.ofRaw(it.toInt()) } } 156.toByte() -> { return (readValue(buffer) as? List)?.let { - FeatureDecoder.fromList(it) + PointDecoder.fromList(it) } } 157.toByte() -> { return (readValue(buffer) as? List)?.let { - GlyphsRasterizationOptions.fromList(it) + FeatureDecoder.fromList(it) } } 158.toByte() -> { return (readValue(buffer) as? List)?.let { - TileCoverOptions.fromList(it) + GlyphsRasterizationOptions.fromList(it) } } 159.toByte() -> { return (readValue(buffer) as? List)?.let { - MbxEdgeInsets.fromList(it) + TileCoverOptions.fromList(it) } } 160.toByte() -> { return (readValue(buffer) as? List)?.let { - CameraOptions.fromList(it) + MbxEdgeInsets.fromList(it) } } 161.toByte() -> { return (readValue(buffer) as? List)?.let { - CameraState.fromList(it) + CameraOptions.fromList(it) } } 162.toByte() -> { return (readValue(buffer) as? List)?.let { - CameraBoundsOptions.fromList(it) + CameraState.fromList(it) } } 163.toByte() -> { return (readValue(buffer) as? List)?.let { - CameraBounds.fromList(it) + CameraBoundsOptions.fromList(it) } } 164.toByte() -> { return (readValue(buffer) as? List)?.let { - MapAnimationOptions.fromList(it) + CameraBounds.fromList(it) } } 165.toByte() -> { return (readValue(buffer) as? List)?.let { - CoordinateBounds.fromList(it) + MapAnimationOptions.fromList(it) } } 166.toByte() -> { return (readValue(buffer) as? List)?.let { - MapDebugOptions.fromList(it) + CoordinateBounds.fromList(it) } } 167.toByte() -> { return (readValue(buffer) as? List)?.let { - TileCacheBudgetInMegabytes.fromList(it) + MapDebugOptions.fromList(it) } } 168.toByte() -> { return (readValue(buffer) as? List)?.let { - TileCacheBudgetInTiles.fromList(it) + TileCacheBudgetInMegabytes.fromList(it) } } 169.toByte() -> { return (readValue(buffer) as? List)?.let { - MapOptions.fromList(it) + TileCacheBudgetInTiles.fromList(it) } } 170.toByte() -> { return (readValue(buffer) as? List)?.let { - ScreenCoordinate.fromList(it) + MapOptions.fromList(it) } } 171.toByte() -> { return (readValue(buffer) as? List)?.let { - ScreenBox.fromList(it) + ScreenCoordinate.fromList(it) } } 172.toByte() -> { return (readValue(buffer) as? List)?.let { - CoordinateBoundsZoom.fromList(it) + ScreenBox.fromList(it) } } 173.toByte() -> { return (readValue(buffer) as? List)?.let { - Size.fromList(it) + CoordinateBoundsZoom.fromList(it) } } 174.toByte() -> { return (readValue(buffer) as? List)?.let { - RenderedQueryOptions.fromList(it) + Size.fromList(it) } } 175.toByte() -> { return (readValue(buffer) as? List)?.let { - SourceQueryOptions.fromList(it) + RenderedQueryOptions.fromList(it) } } 176.toByte() -> { return (readValue(buffer) as? List)?.let { - FeatureExtensionValue.fromList(it) + SourceQueryOptions.fromList(it) } } 177.toByte() -> { return (readValue(buffer) as? List)?.let { - LayerPosition.fromList(it) + FeatureExtensionValue.fromList(it) } } 178.toByte() -> { return (readValue(buffer) as? List)?.let { - QueriedRenderedFeature.fromList(it) + LayerPosition.fromList(it) } } 179.toByte() -> { return (readValue(buffer) as? List)?.let { - QueriedSourceFeature.fromList(it) + QueriedRenderedFeature.fromList(it) } } 180.toByte() -> { return (readValue(buffer) as? List)?.let { - QueriedFeature.fromList(it) + QueriedSourceFeature.fromList(it) } } 181.toByte() -> { return (readValue(buffer) as? List)?.let { - FeaturesetFeatureId.fromList(it) + QueriedFeature.fromList(it) } } 182.toByte() -> { return (readValue(buffer) as? List)?.let { - Interaction.fromList(it) + FeaturesetFeatureId.fromList(it) } } 183.toByte() -> { return (readValue(buffer) as? List)?.let { - TypedFeaturesetDescriptor.fromList(it) + FeatureState.fromList(it) } } 184.toByte() -> { return (readValue(buffer) as? List)?.let { - FeaturesetDescriptor.fromList(it) + Interaction.fromList(it) } } 185.toByte() -> { return (readValue(buffer) as? List)?.let { - FeaturesetFeature.fromList(it) + FeaturesetDescriptor.fromList(it) } } 186.toByte() -> { return (readValue(buffer) as? List)?.let { - FeaturesetQueryTarget.fromList(it) + FeaturesetFeature.fromList(it) } } 187.toByte() -> { return (readValue(buffer) as? List)?.let { - _RenderedQueryGeometry.fromList(it) + FeaturesetQueryTarget.fromList(it) } } 188.toByte() -> { return (readValue(buffer) as? List)?.let { - ProjectedMeters.fromList(it) + MapContentGestureContext.fromList(it) } } 189.toByte() -> { return (readValue(buffer) as? List)?.let { - MercatorCoordinate.fromList(it) + _RenderedQueryGeometry.fromList(it) } } 190.toByte() -> { return (readValue(buffer) as? List)?.let { - StyleObjectInfo.fromList(it) + ProjectedMeters.fromList(it) } } 191.toByte() -> { return (readValue(buffer) as? List)?.let { - StyleProjection.fromList(it) + MercatorCoordinate.fromList(it) } } 192.toByte() -> { return (readValue(buffer) as? List)?.let { - FlatLight.fromList(it) + StyleObjectInfo.fromList(it) } } 193.toByte() -> { return (readValue(buffer) as? List)?.let { - DirectionalLight.fromList(it) + StyleProjection.fromList(it) } } 194.toByte() -> { return (readValue(buffer) as? List)?.let { - AmbientLight.fromList(it) + FlatLight.fromList(it) } } 195.toByte() -> { return (readValue(buffer) as? List)?.let { - MbxImage.fromList(it) + DirectionalLight.fromList(it) } } 196.toByte() -> { return (readValue(buffer) as? List)?.let { - ImageStretches.fromList(it) + AmbientLight.fromList(it) } } 197.toByte() -> { return (readValue(buffer) as? List)?.let { - ImageContent.fromList(it) + MbxImage.fromList(it) } } 198.toByte() -> { return (readValue(buffer) as? List)?.let { - TransitionOptions.fromList(it) + ImageStretches.fromList(it) } } 199.toByte() -> { return (readValue(buffer) as? List)?.let { - CanonicalTileID.fromList(it) + ImageContent.fromList(it) } } 200.toByte() -> { + return (readValue(buffer) as? List)?.let { + TransitionOptions.fromList(it) + } + } + 201.toByte() -> { + return (readValue(buffer) as? List)?.let { + CanonicalTileID.fromList(it) + } + } + 202.toByte() -> { return (readValue(buffer) as? List)?.let { StylePropertyValue.fromList(it) } @@ -2438,258 +2420,266 @@ private open class MapInterfacesPigeonCodec : StandardMessageCodec() { stream.write(137) writeValue(stream, value.raw) } - is Type -> { + is GestureState -> { stream.write(138) writeValue(stream, value.raw) } - is FillExtrusionBaseAlignment -> { + is Type -> { stream.write(139) writeValue(stream, value.raw) } - is FillExtrusionHeightAlignment -> { + is FillExtrusionBaseAlignment -> { stream.write(140) writeValue(stream, value.raw) } - is BackgroundPitchAlignment -> { + is FillExtrusionHeightAlignment -> { stream.write(141) writeValue(stream, value.raw) } - is StylePackErrorType -> { + is BackgroundPitchAlignment -> { stream.write(142) writeValue(stream, value.raw) } - is ResponseErrorReason -> { + is StylePackErrorType -> { stream.write(143) writeValue(stream, value.raw) } - is OfflineRegionDownloadState -> { + is ResponseErrorReason -> { stream.write(144) writeValue(stream, value.raw) } - is TileStoreUsageMode -> { + is OfflineRegionDownloadState -> { stream.write(145) writeValue(stream, value.raw) } - is StylePropertyValueKind -> { + is TileStoreUsageMode -> { stream.write(146) writeValue(stream, value.raw) } - is StyleProjectionName -> { + is StylePropertyValueKind -> { stream.write(147) writeValue(stream, value.raw) } - is Anchor -> { + is StyleProjectionName -> { stream.write(148) writeValue(stream, value.raw) } - is HttpMethod -> { + is Anchor -> { stream.write(149) writeValue(stream, value.raw) } - is HttpRequestErrorType -> { + is HttpMethod -> { stream.write(150) writeValue(stream, value.raw) } - is DownloadErrorCode -> { + is HttpRequestErrorType -> { stream.write(151) writeValue(stream, value.raw) } - is DownloadState -> { + is DownloadErrorCode -> { stream.write(152) writeValue(stream, value.raw) } - is TileRegionErrorType -> { + is DownloadState -> { stream.write(153) writeValue(stream, value.raw) } - is _MapEvent -> { + is TileRegionErrorType -> { stream.write(154) writeValue(stream, value.raw) } - is Point -> { + is _MapEvent -> { stream.write(155) - writeValue(stream, value.toList()) + writeValue(stream, value.raw) } - is Feature -> { + is Point -> { stream.write(156) writeValue(stream, value.toList()) } - is GlyphsRasterizationOptions -> { + is Feature -> { stream.write(157) writeValue(stream, value.toList()) } - is TileCoverOptions -> { + is GlyphsRasterizationOptions -> { stream.write(158) writeValue(stream, value.toList()) } - is MbxEdgeInsets -> { + is TileCoverOptions -> { stream.write(159) writeValue(stream, value.toList()) } - is CameraOptions -> { + is MbxEdgeInsets -> { stream.write(160) writeValue(stream, value.toList()) } - is CameraState -> { + is CameraOptions -> { stream.write(161) writeValue(stream, value.toList()) } - is CameraBoundsOptions -> { + is CameraState -> { stream.write(162) writeValue(stream, value.toList()) } - is CameraBounds -> { + is CameraBoundsOptions -> { stream.write(163) writeValue(stream, value.toList()) } - is MapAnimationOptions -> { + is CameraBounds -> { stream.write(164) writeValue(stream, value.toList()) } - is CoordinateBounds -> { + is MapAnimationOptions -> { stream.write(165) writeValue(stream, value.toList()) } - is MapDebugOptions -> { + is CoordinateBounds -> { stream.write(166) writeValue(stream, value.toList()) } - is TileCacheBudgetInMegabytes -> { + is MapDebugOptions -> { stream.write(167) writeValue(stream, value.toList()) } - is TileCacheBudgetInTiles -> { + is TileCacheBudgetInMegabytes -> { stream.write(168) writeValue(stream, value.toList()) } - is MapOptions -> { + is TileCacheBudgetInTiles -> { stream.write(169) writeValue(stream, value.toList()) } - is ScreenCoordinate -> { + is MapOptions -> { stream.write(170) writeValue(stream, value.toList()) } - is ScreenBox -> { + is ScreenCoordinate -> { stream.write(171) writeValue(stream, value.toList()) } - is CoordinateBoundsZoom -> { + is ScreenBox -> { stream.write(172) writeValue(stream, value.toList()) } - is Size -> { + is CoordinateBoundsZoom -> { stream.write(173) writeValue(stream, value.toList()) } - is RenderedQueryOptions -> { + is Size -> { stream.write(174) writeValue(stream, value.toList()) } - is SourceQueryOptions -> { + is RenderedQueryOptions -> { stream.write(175) writeValue(stream, value.toList()) } - is FeatureExtensionValue -> { + is SourceQueryOptions -> { stream.write(176) writeValue(stream, value.toList()) } - is LayerPosition -> { + is FeatureExtensionValue -> { stream.write(177) writeValue(stream, value.toList()) } - is QueriedRenderedFeature -> { + is LayerPosition -> { stream.write(178) writeValue(stream, value.toList()) } - is QueriedSourceFeature -> { + is QueriedRenderedFeature -> { stream.write(179) writeValue(stream, value.toList()) } - is QueriedFeature -> { + is QueriedSourceFeature -> { stream.write(180) writeValue(stream, value.toList()) } - is FeaturesetFeatureId -> { + is QueriedFeature -> { stream.write(181) writeValue(stream, value.toList()) } - is Interaction -> { + is FeaturesetFeatureId -> { stream.write(182) writeValue(stream, value.toList()) } - is TypedFeaturesetDescriptor -> { + is FeatureState -> { stream.write(183) writeValue(stream, value.toList()) } - is FeaturesetDescriptor -> { + is Interaction -> { stream.write(184) writeValue(stream, value.toList()) } - is FeaturesetFeature -> { + is FeaturesetDescriptor -> { stream.write(185) writeValue(stream, value.toList()) } - is FeaturesetQueryTarget -> { + is FeaturesetFeature -> { stream.write(186) writeValue(stream, value.toList()) } - is _RenderedQueryGeometry -> { + is FeaturesetQueryTarget -> { stream.write(187) writeValue(stream, value.toList()) } - is ProjectedMeters -> { + is MapContentGestureContext -> { stream.write(188) writeValue(stream, value.toList()) } - is MercatorCoordinate -> { + is _RenderedQueryGeometry -> { stream.write(189) writeValue(stream, value.toList()) } - is StyleObjectInfo -> { + is ProjectedMeters -> { stream.write(190) writeValue(stream, value.toList()) } - is StyleProjection -> { + is MercatorCoordinate -> { stream.write(191) writeValue(stream, value.toList()) } - is FlatLight -> { + is StyleObjectInfo -> { stream.write(192) writeValue(stream, value.toList()) } - is DirectionalLight -> { + is StyleProjection -> { stream.write(193) writeValue(stream, value.toList()) } - is AmbientLight -> { + is FlatLight -> { stream.write(194) writeValue(stream, value.toList()) } - is MbxImage -> { + is DirectionalLight -> { stream.write(195) writeValue(stream, value.toList()) } - is ImageStretches -> { + is AmbientLight -> { stream.write(196) writeValue(stream, value.toList()) } - is ImageContent -> { + is MbxImage -> { stream.write(197) writeValue(stream, value.toList()) } - is TransitionOptions -> { + is ImageStretches -> { stream.write(198) writeValue(stream, value.toList()) } - is CanonicalTileID -> { + is ImageContent -> { stream.write(199) writeValue(stream, value.toList()) } - is StylePropertyValue -> { + is TransitionOptions -> { stream.write(200) writeValue(stream, value.toList()) } + is CanonicalTileID -> { + stream.write(201) + writeValue(stream, value.toList()) + } + is StylePropertyValue -> { + stream.write(202) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } @@ -3364,6 +3354,31 @@ interface _CameraManager { } } } +/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */ +class InteractionsListener(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") { + companion object { + /** The codec used by InteractionsListener. */ + val codec: MessageCodec by lazy { + MapInterfacesPigeonCodec() + } + } + fun onInteraction(contextArg: MapContentGestureContext, featureArg: FeaturesetFeature, interactionIDArg: Long, callback: (Result) -> Unit) { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.mapbox_maps_flutter.InteractionsListener.onInteraction$separatedMessageChannelSuffix" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(contextArg, featureArg, interactionIDArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) + } + } + } +} /** * Map class provides map rendering functionality. * @@ -3462,23 +3477,14 @@ interface _MapInterface { * @return A `cancelable` object that could be used to cancel the pending query. */ fun queryRenderedFeatures(geometry: _RenderedQueryGeometry, options: RenderedQueryOptions, callback: (Result>) -> Unit) - /** - * Queries the map for rendered features using featureset descriptors. - * - * This method allows to query both featureset from imported styles and user layers in the root style. - * The results can be additionally filtered per-featureset. - * - * - Important: This is a low-level method. If you need to handle basic gestures on map content, please prefer ``MapboxMap/ queryRenderedFeaturesForFeatureset()``. - * - * @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. - * @param targets An array of targets to query with. - */ - fun queryRenderedFeaturesForTargets(geometry: _RenderedQueryGeometry, targets: List, callback: (Result>) -> Unit) /** * Queries the map for rendered features with one typed featureset. * * The results array will contain features of the type specified by this featureset. * + * - Important: If you need to handle basic gestures on map content, + * please prefer to use Interactions API, see `MapboxMap/addInteraction`. + * * @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. * @param featureset A typed featureset to query with. * @param filter An additional filter for features. @@ -3489,6 +3495,9 @@ interface _MapInterface { * * This is same as `MapboxMap/ queryRenderedFeaturesForFeatureset()`` called with geometry matching the current viewport. * + * - Important: If you need to handle basic gestures on map content, + * please prefer to use Interactions API, see `MapboxMap/addInteraction`. + * * @param featureset A typed featureset to query with. * @param filter An additional filter for features. */ @@ -3501,12 +3510,6 @@ interface _MapInterface { * @param completion The `query features completion` called when the query completes. */ fun querySourceFeatures(sourceId: String, options: SourceQueryOptions, callback: (Result>) -> Unit) - /** - * Queries the source features for a given featureset. - * - * @param target A featureset query target. - */ - fun querySourceFeaturesForTargets(target: FeaturesetQueryTarget, callback: (Result>) -> Unit) /** * Returns all the leaves (original points) of a cluster (given its cluster_id) from a GeoJsonSource, with pagination support: limit is the number of leaves * to return (set to Infinity for all points), and offset is the amount of points to skip (for pagination). @@ -3569,10 +3572,8 @@ interface _MapInterface { * @param featureset The featureset to look the feature in. * @param featureId Identifier of the feature whose state should be updated. * @param state Map of entries to update with their respective new values - * - * @return A `Cancelable` object that could be used to cancel the pending operation. */ - fun setFeatureStateForFeaturesetDescriptor(featureset: TypedFeaturesetDescriptor, featureId: FeaturesetFeatureId, state: Map, callback: (Result) -> Unit) + fun setFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: Map, callback: (Result) -> Unit) /** * Update the state map of an individual feature. * @@ -3581,8 +3582,6 @@ interface _MapInterface { * * @param feature The feature to update. * @param state Map of entries to update with their respective new values - * - * @return A `Cancelable` object that could be used to cancel the pending operation. */ fun setFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, state: Map, callback: (Result) -> Unit) /** @@ -3595,7 +3594,7 @@ interface _MapInterface { * @param sourceLayerId The style source layer identifier (for multi-layer sources such as vector sources). * @param featureId The feature identifier of the feature whose state should be queried. * - * @return A `Cancelable` object that could be used to cancel the pending operation. + * @return A String representing the Feature's state map. */ fun getFeatureState(sourceId: String, sourceLayerId: String?, featureId: String, callback: (Result) -> Unit) /** @@ -3604,15 +3603,15 @@ interface _MapInterface { * @param featureset A featureset the feature belongs to. * @param featureId Identifier of the feature whose state should be queried. * - * @return A `Cancelable` object that could be used to cancel the pending query. + * @return The Feature's state map or an empty map if the feature could not be found. */ fun getFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, callback: (Result>) -> Unit) /** * Get the state map of a feature within a style source. * - * @param feature An interactive feature to query the state from. + * @param feature An interactive feature to query the state of. * - * @return A `Cancelable` object that could be used to cancel the pending query. + * @return The Feature's state map or an empty map if the feature could not be found. */ fun getFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, callback: (Result>) -> Unit) /** @@ -3622,7 +3621,7 @@ interface _MapInterface { * `stateKey`. * * Note that updates to feature state are asynchronous, so changes made by this method might not be - * immediately visible using `getStateFeature`. + * immediately visible using `getFeatureState`. * * @param sourceId The style source identifier. * @param sourceLayerId The style source layer identifier (for multi-layer sources such as vector sources). @@ -3637,8 +3636,6 @@ interface _MapInterface { * @param featureset A featureset the feature belongs to. * @param featureId Identifier of the feature whose state should be removed. * @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. - * - * @return A `Cancelable` object that could be used to cancel the pending operation. */ fun removeFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String?, callback: (Result) -> Unit) /** @@ -3647,8 +3644,6 @@ interface _MapInterface { * * @param feature An interactive feature to update. * @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. - * - * @return A `Cancelable` object that could be used to cancel the pending operation. */ fun removeFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, stateKey: String?, callback: (Result) -> Unit) /** @@ -3658,11 +3653,8 @@ interface _MapInterface { * immediately visible using ``MapboxMap/getFeatureState()``. * * @param featureset A featureset descriptor - * - * @return A `Cancelable` object that could be used to cancel the pending operation. */ fun resetFeatureStatesForFeatureset(featureset: FeaturesetDescriptor, callback: (Result) -> Unit) - fun addInteraction(interaction: Interaction, callback: (Result) -> Unit) /** Reduces memory use. Useful to call when the application gets paused or sent to background. */ fun reduceMemoryUse() /** @@ -4070,27 +4062,6 @@ interface _MapInterface { channel.setMessageHandler(null) } } - run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesForTargets$separatedMessageChannelSuffix", codec) - if (api != null) { - channel.setMessageHandler { message, reply -> - val args = message as List - val geometryArg = args[0] as _RenderedQueryGeometry - val targetsArg = args[1] as List - api.queryRenderedFeaturesForTargets(geometryArg, targetsArg) { result: Result> -> - val error = result.exceptionOrNull() - if (error != null) { - reply.reply(wrapError(error)) - } else { - val data = result.getOrNull() - reply.reply(wrapResult(data)) - } - } - } - } else { - channel.setMessageHandler(null) - } - } run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesForFeatureset$separatedMessageChannelSuffix", codec) if (api != null) { @@ -4155,26 +4126,6 @@ interface _MapInterface { channel.setMessageHandler(null) } } - run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.querySourceFeaturesForTargets$separatedMessageChannelSuffix", codec) - if (api != null) { - channel.setMessageHandler { message, reply -> - val args = message as List - val targetArg = args[0] as FeaturesetQueryTarget - api.querySourceFeaturesForTargets(targetArg) { result: Result> -> - val error = result.exceptionOrNull() - if (error != null) { - reply.reply(wrapError(error)) - } else { - val data = result.getOrNull() - reply.reply(wrapResult(data)) - } - } - } - } else { - channel.setMessageHandler(null) - } - } run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.getGeoJsonClusterLeaves$separatedMessageChannelSuffix", codec) if (api != null) { @@ -4267,7 +4218,7 @@ interface _MapInterface { if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val featuresetArg = args[0] as TypedFeaturesetDescriptor + val featuresetArg = args[0] as FeaturesetDescriptor val featureIdArg = args[1] as FeaturesetFeatureId val stateArg = args[2] as Map api.setFeatureStateForFeaturesetDescriptor(featuresetArg, featureIdArg, stateArg) { result: Result -> @@ -4448,26 +4399,6 @@ interface _MapInterface { channel.setMessageHandler(null) } } - run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.addInteraction$separatedMessageChannelSuffix", codec) - if (api != null) { - channel.setMessageHandler { message, reply -> - val args = message as List - val interactionArg = args[0] as Interaction - api.addInteraction(interactionArg) { result: Result -> - val error = result.exceptionOrNull() - if (error != null) { - reply.reply(wrapError(error)) - } else { - val data = result.getOrNull() - reply.reply(wrapResult(data)) - } - } - } - } else { - channel.setMessageHandler(null) - } - } run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.reduceMemoryUse$separatedMessageChannelSuffix", codec) if (api != null) { diff --git a/example/integration_test/interactive_features_test.dart b/example/integration_test/interactive_features_test.dart index cd98eeec3..34f5f802b 100644 --- a/example/integration_test/interactive_features_test.dart +++ b/example/integration_test/interactive_features_test.dart @@ -83,15 +83,15 @@ void main() { geometry: Point(coordinates: Position(0.01, 0.01)).toJson(), properties: {}, state: {}); - Map state = { + var state = FeatureState(map: { "highlight": true, - }; + }); // test set and get featurestate await mapboxMap.setFeatureStateForFeaturesetFeature(feature, state); var returnedFeatureState = await mapboxMap.getFeatureStateForFeaturesetFeature(feature); - expect(returnedFeatureState, state); + expect(returnedFeatureState, state.map); // test remove featurestate await mapboxMap.removeFeatureStateForFeaturesetFeature( @@ -105,7 +105,7 @@ void main() { await mapboxMap.setFeatureStateForFeaturesetFeature(feature, state); var returnedFeatureState3 = await mapboxMap.getFeatureStateForFeaturesetFeature(feature); - expect(returnedFeatureState3, state); + expect(returnedFeatureState3, state.map); await mapboxMap.resetFeatureStatesForFeatureset( FeaturesetDescriptor(featuresetId: "poi", importId: "nested")); @@ -133,9 +133,9 @@ void main() { var featuresetDescriptor = FeaturesetDescriptor(featuresetId: "poi", importId: "nested"); var featuresetID = FeaturesetFeatureId(id: "11", namespace: "A"); - Map state = { + var state = FeatureState(map: { "highlight": true, - }; + }); await Future.delayed(Duration(seconds: 1)); @@ -145,7 +145,7 @@ void main() { var returnedFeatureState = await mapboxMap.getFeatureStateForFeaturesetDescriptor( featuresetDescriptor, featuresetID); - expect(returnedFeatureState, state); + expect(returnedFeatureState, state.map); // test remove featurestate await mapboxMap.removeFeatureStateForFeaturesetDescriptor( @@ -176,9 +176,9 @@ void main() { var featuresetID = FeaturesetFeatureId(id: "11", namespace: "A"); var featuresetDescriptor = FeaturesetDescriptor(featuresetId: "poi", importId: "nested"); - Map state = { + var state = FeatureState(map: { "hide": true, - }; + }); var filter = '["==",["get", "type"], "A"]'; Map expectedProperties = { "name": "nest1", @@ -196,7 +196,7 @@ void main() { expect(queryResult.length, 1); expect(poi.id?.id, featuresetID.id); expect(poi.id?.namespace, featuresetID.namespace); - expect(poi.state, state); + expect(poi.state, state.map); expect(point.coordinates.lat, closeTo(0.01, 0.05)); expect(point.coordinates.lng, closeTo(0.01, 0.05)); expect(poi.properties, expectedProperties); @@ -222,67 +222,4 @@ void main() { expect(returnedFeaturesets.length, 1); expect(returnedFeaturesets.first.importId, "nested"); }); - - testWidgets('test_query_featureset_target', (WidgetTester tester) async { - // load style and position camera - final mapFuture = app.main( - width: 200, - height: 200, - camera: - CameraOptions(center: Point(coordinates: Position(0, 0)), zoom: 10), - alignment: Alignment(100, 100)); - await tester.pumpAndSettle(); - final mapboxMap = await mapFuture; - var styleJson = await rootBundle.loadString('assets/featuresetsStyle.json'); - mapboxMap.style.setStyleJSON(styleJson); - - await app.events.onMapLoaded.future; - await Future.delayed(Duration(seconds: 1)); - - var featuresetFilter = '["==", ["get", "type"], "B"]'; - var layerFilter = '["==", ["get", "filter"], true]'; - var featuresetPOI = - FeaturesetDescriptor(featuresetId: "poi", importId: "nested"); - var featuresetLayer = FeaturesetDescriptor(layerId: "circle-2"); - var coord = await mapboxMap - .pixelForCoordinate(Point(coordinates: Position(0.01, 0.01))); - var targets = [ - FeaturesetQueryTarget( - featureset: featuresetPOI, filter: featuresetFilter, id: 1), - FeaturesetQueryTarget( - featureset: featuresetLayer, filter: layerFilter, id: 2) - ]; - Map expectedProperties = { - "name": "qux", - "filter": true, - "bar": 2 - }; - Map expectedProperties2 = { - "type": "B", - "class": "poi", - "name": "nest2" - }; - - var returnedQuery = await mapboxMap.queryRenderedFeaturesForTargets( - RenderedQueryGeometry.fromScreenCoordinate(coord), targets); - var firstFeature = - Feature.fromFeature(returnedQuery[0]!.queriedFeature.feature); - var secondFeature = - Feature.fromFeature(returnedQuery[1]!.queriedFeature.feature); - - expect(returnedQuery.length, 2); - expect(returnedQuery[0]?.queryTargets?.length, 1); - expect(returnedQuery[0]?.queryTargets?.last.id, 2); - expect(returnedQuery[0]?.queryTargets?.last.featureset.layerId, "circle-2"); - expect(returnedQuery[0]?.queryTargets?.last.filter, null); - expect(firstFeature.id.toString(), "2"); - expect(firstFeature.properties, expectedProperties); - expect(returnedQuery[1]?.queryTargets?.length, 1); - expect(returnedQuery[1]?.queryTargets?.last.id, 1); - expect(returnedQuery[1]?.queryTargets?.last.featureset.featuresetId, "poi"); - expect(returnedQuery[1]?.queryTargets?.last.featureset.importId, "nested"); - expect(returnedQuery[1]?.queryTargets?.last.filter, null); - expect(secondFeature.id.toString(), "12"); - expect(secondFeature.properties, expectedProperties2); - }); } diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index cb20f101c..412f044f7 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,6 +1,11 @@ PODS: - Flutter (1.0.0) - integration_test (0.0.1): +<<<<<<< HEAD +======= + - Flutter + - mapbox_maps_flutter (2.5.0): +>>>>>>> 085bf46 (Add map.addInteraction, add Standard featuresets) - Flutter - mapbox_maps_flutter (2.6.0-beta.1): - Flutter diff --git a/example/lib/interactive_features_example.dart b/example/lib/interactive_features_example.dart index 46f51b426..23a77bfe6 100644 --- a/example/lib/interactive_features_example.dart +++ b/example/lib/interactive_features_example.dart @@ -8,7 +8,7 @@ class InteractiveFeaturesExample extends StatefulWidget implements Example { @override final String title = 'Interactive Features'; @override - final String? subtitle = 'Click to select buildings, long-click to unselect'; + final String? subtitle = 'Tap a Buildings to highlight it or a POI to hide it'; @override State createState() => InteractiveFeaturesState(); @@ -22,69 +22,41 @@ class InteractiveFeaturesState extends State { this.mapboxMap = mapboxMap; mapboxMap.style; - var featureset = - FeaturesetDescriptor(featuresetId: "buildings", importId: "basemap"); - - var typedFeaturesetDescriptor = TypedFeaturesetDescriptor( - featuresetDescriptor: featureset, featuresetType: "featureset"); - - var interactionType = InteractionType.CLICK; - - var interaction = Interaction( - typedFeaturesetDescriptor: typedFeaturesetDescriptor, - interactionType: interactionType); - mapboxMap.addInteraction(interaction); - } - - _onTap(context) async { - // Define the geometry to query, in this case the point where the user clicked. - var clicked = await mapboxMap?.pixelForCoordinate(context.point); - var renderedQueryGeometry = - RenderedQueryGeometry.fromScreenCoordinate(clicked!); - - // Define the featureset to query. In this case "buildings", which is defined in the - // Standard Experimental style. - var featureset = - FeaturesetDescriptor(featuresetId: "buildings", importId: "basemap"); - - // Query the featureset for the geometry - var queriedFeatures = await mapboxMap?.queryRenderedFeaturesForFeatureset( - geometry: renderedQueryGeometry, featureset: featureset); - var featuresetFeature = queriedFeatures?.first; - - var typedFeaturesetDescriptor = TypedFeaturesetDescriptor( - featuresetDescriptor: featureset, featuresetType: "featureset"); - - if (featuresetFeature != null) { - // Define the state to set for the feature, in this case highlighting - // Set that featurestate on that featuresetFeature - Map state = { - "highlight": true, - }; - mapboxMap?.setFeatureStateForFeaturesetDescriptor( - typedFeaturesetDescriptor, featuresetFeature.id!, state); - //mapboxMap?.setFeatureStateForFeaturesetFeature(featuresetFeature, state); - } - } - - _onLongTap(context) async { - // Define the geometry to query, in this case the point where the user clicked. - var clicked = await mapboxMap?.pixelForCoordinate(context.point); - var renderedQueryGeometry = - RenderedQueryGeometry.fromScreenCoordinate(clicked!); - - // Query the featureset for the geometry - var featureset = - FeaturesetDescriptor(featuresetId: "buildings", importId: "basemap"); - var queriedFeatures = await mapboxMap?.queryRenderedFeaturesForFeatureset( - geometry: renderedQueryGeometry, featureset: featureset); - var featuresetFeatureId = queriedFeatures?.first.id; - - // Remove that feature state - if (featuresetFeatureId != null) { - mapboxMap?.removeFeatureStateForFeaturesetDescriptor( - featureset: featureset, featureId: featuresetFeatureId); - } + /// Define interactions for 3D Buildings + + // Define a tap interaction targeting the Buildings featureset in the Standard style + var tapInteraction = TapInteraction(Featureset.standardBuildings()); + + // Define a state to highlight the building when it is interacted with + StandardBuildingState featureState = StandardBuildingState(highlight: true); + + // Add the tap interaction to the map, set the action to occur when a building is tapped (highlight it) + mapboxMap.addInteraction(tapInteraction, (_, FeaturesetFeature feature) { + mapboxMap.setFeatureStateForFeaturesetFeature(feature, featureState); + var buildingFeature = StandardBuildingsFeature( + feature.geometry, feature.properties, feature.state, + id: feature.id); + print("Building feature id: ${buildingFeature.id}"); + }); + + // On long tap, remove the highlight state + mapboxMap.addInteraction(LongTapInteraction(Featureset.standardBuildings()), + (_, FeaturesetFeature feature) { + mapboxMap.removeFeatureStateForFeaturesetFeature(feature: feature); + }); + + /// Define interactions for Points of Interest + + // Define a tap interaction targeting the POI featureset in the Standard style, including a click radius + // Do not stop propagation of the click event to lower layers + var tapInteractionPOI = TapInteraction(Featureset.standardPoi(), + radius: 10, stopPropagation: false); + + // Define a state to hide the POI when it is interacted with + mapboxMap.addInteraction(tapInteractionPOI, (_, FeaturesetFeature feature) { + mapboxMap.setFeatureStateForFeaturesetFeature( + feature, StandardPoiState(hide: true)); + }); } @override @@ -97,14 +69,9 @@ class InteractiveFeaturesState extends State { bearing: 49.92, zoom: 16.35, pitch: 40), - - /// DON'T USE Standard Experimental style in production, it will break over time. - /// Currently this feature is in preview. - styleUri: MapboxStyles.STANDARD_EXPERIMENTAL, + styleUri: MapboxStyles.STANDARD, textureView: true, onMapCreated: _onMapCreated, - onTapListener: _onTap, - onLongTapListener: _onLongTap, )); } } diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift index 1ab74db94..cc05d4b59 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift @@ -221,13 +221,6 @@ extension FeaturesetFeatureId { } } -extension FeaturesetQueryTarget { - func toMapFeaturesetQueryTarget() -> MapboxMaps.FeaturesetQueryTarget { - let filterExpression = try? filter.flatMap { try $0.toExp() } - return MapboxMaps.FeaturesetQueryTarget(featureset: featureset.toMapFeaturesetDescriptor(), filter: filterExpression, id: id.map { UInt64($0) }) - } -} - extension FeaturesetDescriptor { func toMapFeaturesetDescriptor() -> MapboxMaps.FeaturesetDescriptor { if let featuresetId { @@ -250,27 +243,6 @@ extension FeaturesetFeature { } } -extension Interaction { - func toMapInteraction(completion: @escaping (FeaturesetFeature) -> Void) -> MapboxMaps.Interaction { - let filterExpression = try? filter.flatMap { try $0.toExp() } - - switch interactionType { - case .cLICK: - return TapInteraction.init(typedFeaturesetDescriptor.featuresetDescriptor.toMapFeaturesetDescriptor(), filter: filterExpression) { _, _ in - // TODO: figure out how to pass typed completion with pigeon - print("tapped") - return true - } - case .lONGCLICK: - return LongPressInteraction.init(typedFeaturesetDescriptor.featuresetDescriptor.toMapFeaturesetDescriptor(), filter: filterExpression) { featuresetFeature, _ in - - completion(featuresetFeature.toFLTFeaturesetFeature()) - return true - } - } - } -} - extension MercatorCoordinate { func toMercatorCoordinate() -> MapboxMaps.MercatorCoordinate { return MapboxMaps.MercatorCoordinate(x: x, y: y) @@ -491,17 +463,11 @@ extension MapboxMaps.QueriedSourceFeature { return QueriedSourceFeature(queriedFeature: queriedFeature.toFLTQueriedFeature()) } } -extension MapboxMaps.FeaturesetQueryTarget { - func toFLTFeaturesetQueryTarget() -> FeaturesetQueryTarget { - return FeaturesetQueryTarget(featureset: featureset.toFLTFeaturesetDescriptor(), filter: filter?.description, id: id.map { Int64($0) }) - } -} extension MapboxMaps.QueriedRenderedFeature { func toFLTQueriedRenderedFeature() -> QueriedRenderedFeature { return QueriedRenderedFeature( queriedFeature: queriedFeature.toFLTQueriedFeature(), - layers: layers, - queryTargets: queryTargets.map({$0.toFLTFeaturesetQueryTarget()})) + layers: layers) } } extension MapboxMaps.QueriedFeature { diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift index 0f61e792f..36fcbba28 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift @@ -57,6 +57,10 @@ private func wrapError(_ error: Any) -> [Any?] { ] } +private func createConnectionError(withChannelName channelName: String) -> MapInterfacesError { + return MapInterfacesError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "") +} + private func isNullish(_ value: Any?) -> Bool { return value is NSNull || value == nil } @@ -187,8 +191,8 @@ enum ViewAnnotationAnchor: Int { } enum InteractionType: Int { - case cLICK = 0 - case lONGCLICK = 1 + case tAP = 0 + case lONGTAP = 1 } /// Type information of the variant's content @@ -1244,78 +1248,72 @@ struct FeaturesetFeatureId { } /// Generated class from Pigeon that represents data sent in messages. -struct Interaction { - var typedFeaturesetDescriptor: TypedFeaturesetDescriptor - var interactionType: InteractionType - var filter: String? +struct FeatureState { + var map: [String: Any?] // swift-format-ignore: AlwaysUseLowerCamelCase - static func fromList(_ pigeonVar_list: [Any?]) -> Interaction? { - let typedFeaturesetDescriptor = pigeonVar_list[0] as! TypedFeaturesetDescriptor - let interactionType = pigeonVar_list[1] as! InteractionType - let filter: String? = nilOrValue(pigeonVar_list[2]) + static func fromList(_ pigeonVar_list: [Any?]) -> FeatureState? { + let map = pigeonVar_list[0] as! [String: Any?] - return Interaction( - typedFeaturesetDescriptor: typedFeaturesetDescriptor, - interactionType: interactionType, - filter: filter + return FeatureState( + map: map ) } func toList() -> [Any?] { return [ - typedFeaturesetDescriptor, - interactionType, - filter, + map ] } } +/// An interaction that can be added to the map. +/// +/// To create an interaction use ``TapInteraction`` and ``LongClickInteraction`` implementations. +/// +/// See also: ``MapboxMap/addInteraction``. +/// /// Generated class from Pigeon that represents data sent in messages. -struct TypedFeaturesetDescriptor { +struct Interaction { var featuresetDescriptor: FeaturesetDescriptor - var featuresetType: String + var interactionType: InteractionType + var stopPropagation: Bool + var filter: String? + var radius: Double? // swift-format-ignore: AlwaysUseLowerCamelCase - static func fromList(_ pigeonVar_list: [Any?]) -> TypedFeaturesetDescriptor? { + static func fromList(_ pigeonVar_list: [Any?]) -> Interaction? { let featuresetDescriptor = pigeonVar_list[0] as! FeaturesetDescriptor - let featuresetType = pigeonVar_list[1] as! String + let interactionType = pigeonVar_list[1] as! InteractionType + let stopPropagation = pigeonVar_list[2] as! Bool + let filter: String? = nilOrValue(pigeonVar_list[3]) + let radius: Double? = nilOrValue(pigeonVar_list[4]) - return TypedFeaturesetDescriptor( + return Interaction( featuresetDescriptor: featuresetDescriptor, - featuresetType: featuresetType + interactionType: interactionType, + stopPropagation: stopPropagation, + filter: filter, + radius: radius ) } func toList() -> [Any?] { return [ featuresetDescriptor, - featuresetType, + interactionType, + stopPropagation, + filter, + radius, ] } } /// A featureset descriptor. /// -/// The descriptor instance acts as a universal target for interactions or querying rendered features (see -/// ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)``). -/// /// Generated class from Pigeon that represents data sent in messages. struct FeaturesetDescriptor { - /// An optional unique identifier for the featureset within the style. - /// This id is used to reference a specific featureset. - /// - /// * Note: If `featuresetId` is provided and valid, it takes precedence over `layerId`, - /// * meaning `layerId` will not be considered even if it has a valid value. var featuresetId: String? - /// An optional import id that is required if the featureset is defined within an imported style. - /// If the featureset belongs to the current style, this field should be set to a null string. - /// - /// Note: `importId` is only applicable when used in conjunction with `featuresetId` - /// and has no effect when used with `layerId`. + /// var importId: String? - /// An optional unique identifier for the layer within the current style. - /// - /// Note: If `featuresetId` is valid, `layerId` will be ignored even if it has a valid value. - /// Additionally, `importId` does not apply when using `layerId`. var layerId: String? // swift-format-ignore: AlwaysUseLowerCamelCase @@ -1339,27 +1337,12 @@ struct FeaturesetDescriptor { } } -/// A basic feature of a featureset. -/// -/// The featureset feature is different to the `Turf.Feature`. The latter represents any GeoJSON feature, while the former is a high level representation of features. -/// /// Generated class from Pigeon that represents data sent in messages. struct FeaturesetFeature { - /// An identifier of the feature. - /// - /// The identifier can be `nil` if the underlying source doesn't have identifiers for features. - /// In this case it's impossible to set a feature state for an individual feature. var id: FeaturesetFeatureId? - /// A featureset descriptor denoting the featureset this feature belongs to. var featureset: FeaturesetDescriptor - /// A feature geometry. var geometry: [String?: Any?] - /// Feature JSON properties. var properties: [String: Any?] - /// A feature state. - /// - /// This is a **snapshot** of the state that the feature had when it was interacted with. - /// To update and read the original state, use ``MapboxMap/setFeatureState()`` and ``MapboxMap/getFeatureState()``. var state: [String: Any?] // swift-format-ignore: AlwaysUseLowerCamelCase @@ -1981,196 +1964,204 @@ private class MapInterfacesPigeonCodecReader: FlutterStandardReader { case 138: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return Type(rawValue: enumResultAsInt) + return GestureState(rawValue: enumResultAsInt) } return nil case 139: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return FillExtrusionBaseAlignment(rawValue: enumResultAsInt) + return Type(rawValue: enumResultAsInt) } return nil case 140: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return FillExtrusionHeightAlignment(rawValue: enumResultAsInt) + return FillExtrusionBaseAlignment(rawValue: enumResultAsInt) } return nil case 141: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return BackgroundPitchAlignment(rawValue: enumResultAsInt) + return FillExtrusionHeightAlignment(rawValue: enumResultAsInt) } return nil case 142: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return StylePackErrorType(rawValue: enumResultAsInt) + return BackgroundPitchAlignment(rawValue: enumResultAsInt) } return nil case 143: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return ResponseErrorReason(rawValue: enumResultAsInt) + return StylePackErrorType(rawValue: enumResultAsInt) } return nil case 144: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return OfflineRegionDownloadState(rawValue: enumResultAsInt) + return ResponseErrorReason(rawValue: enumResultAsInt) } return nil case 145: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return TileStoreUsageMode(rawValue: enumResultAsInt) + return OfflineRegionDownloadState(rawValue: enumResultAsInt) } return nil case 146: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return StylePropertyValueKind(rawValue: enumResultAsInt) + return TileStoreUsageMode(rawValue: enumResultAsInt) } return nil case 147: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return StyleProjectionName(rawValue: enumResultAsInt) + return StylePropertyValueKind(rawValue: enumResultAsInt) } return nil case 148: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return Anchor(rawValue: enumResultAsInt) + return StyleProjectionName(rawValue: enumResultAsInt) } return nil case 149: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return HttpMethod(rawValue: enumResultAsInt) + return Anchor(rawValue: enumResultAsInt) } return nil case 150: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return HttpRequestErrorType(rawValue: enumResultAsInt) + return HttpMethod(rawValue: enumResultAsInt) } return nil case 151: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return DownloadErrorCode(rawValue: enumResultAsInt) + return HttpRequestErrorType(rawValue: enumResultAsInt) } return nil case 152: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return DownloadState(rawValue: enumResultAsInt) + return DownloadErrorCode(rawValue: enumResultAsInt) } return nil case 153: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return TileRegionErrorType(rawValue: enumResultAsInt) + return DownloadState(rawValue: enumResultAsInt) } return nil case 154: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return _MapEvent(rawValue: enumResultAsInt) + return TileRegionErrorType(rawValue: enumResultAsInt) } return nil case 155: - return Point.fromList(self.readValue() as! [Any?]) + let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) + if let enumResultAsInt = enumResultAsInt { + return _MapEvent(rawValue: enumResultAsInt) + } + return nil case 156: - return Feature.fromList(self.readValue() as! [Any?]) + return Point.fromList(self.readValue() as! [Any?]) case 157: - return GlyphsRasterizationOptions.fromList(self.readValue() as! [Any?]) + return Feature.fromList(self.readValue() as! [Any?]) case 158: - return TileCoverOptions.fromList(self.readValue() as! [Any?]) + return GlyphsRasterizationOptions.fromList(self.readValue() as! [Any?]) case 159: - return MbxEdgeInsets.fromList(self.readValue() as! [Any?]) + return TileCoverOptions.fromList(self.readValue() as! [Any?]) case 160: - return CameraOptions.fromList(self.readValue() as! [Any?]) + return MbxEdgeInsets.fromList(self.readValue() as! [Any?]) case 161: - return CameraState.fromList(self.readValue() as! [Any?]) + return CameraOptions.fromList(self.readValue() as! [Any?]) case 162: - return CameraBoundsOptions.fromList(self.readValue() as! [Any?]) + return CameraState.fromList(self.readValue() as! [Any?]) case 163: - return CameraBounds.fromList(self.readValue() as! [Any?]) + return CameraBoundsOptions.fromList(self.readValue() as! [Any?]) case 164: - return MapAnimationOptions.fromList(self.readValue() as! [Any?]) + return CameraBounds.fromList(self.readValue() as! [Any?]) case 165: - return CoordinateBounds.fromList(self.readValue() as! [Any?]) + return MapAnimationOptions.fromList(self.readValue() as! [Any?]) case 166: - return MapDebugOptions.fromList(self.readValue() as! [Any?]) + return CoordinateBounds.fromList(self.readValue() as! [Any?]) case 167: - return TileCacheBudgetInMegabytes.fromList(self.readValue() as! [Any?]) + return MapDebugOptions.fromList(self.readValue() as! [Any?]) case 168: - return TileCacheBudgetInTiles.fromList(self.readValue() as! [Any?]) + return TileCacheBudgetInMegabytes.fromList(self.readValue() as! [Any?]) case 169: - return MapOptions.fromList(self.readValue() as! [Any?]) + return TileCacheBudgetInTiles.fromList(self.readValue() as! [Any?]) case 170: - return ScreenCoordinate.fromList(self.readValue() as! [Any?]) + return MapOptions.fromList(self.readValue() as! [Any?]) case 171: - return ScreenBox.fromList(self.readValue() as! [Any?]) + return ScreenCoordinate.fromList(self.readValue() as! [Any?]) case 172: - return CoordinateBoundsZoom.fromList(self.readValue() as! [Any?]) + return ScreenBox.fromList(self.readValue() as! [Any?]) case 173: - return Size.fromList(self.readValue() as! [Any?]) + return CoordinateBoundsZoom.fromList(self.readValue() as! [Any?]) case 174: - return RenderedQueryOptions.fromList(self.readValue() as! [Any?]) + return Size.fromList(self.readValue() as! [Any?]) case 175: - return SourceQueryOptions.fromList(self.readValue() as! [Any?]) + return RenderedQueryOptions.fromList(self.readValue() as! [Any?]) case 176: - return FeatureExtensionValue.fromList(self.readValue() as! [Any?]) + return SourceQueryOptions.fromList(self.readValue() as! [Any?]) case 177: - return LayerPosition.fromList(self.readValue() as! [Any?]) + return FeatureExtensionValue.fromList(self.readValue() as! [Any?]) case 178: - return QueriedRenderedFeature.fromList(self.readValue() as! [Any?]) + return LayerPosition.fromList(self.readValue() as! [Any?]) case 179: - return QueriedSourceFeature.fromList(self.readValue() as! [Any?]) + return QueriedRenderedFeature.fromList(self.readValue() as! [Any?]) case 180: - return QueriedFeature.fromList(self.readValue() as! [Any?]) + return QueriedSourceFeature.fromList(self.readValue() as! [Any?]) case 181: - return FeaturesetFeatureId.fromList(self.readValue() as! [Any?]) + return QueriedFeature.fromList(self.readValue() as! [Any?]) case 182: - return Interaction.fromList(self.readValue() as! [Any?]) + return FeaturesetFeatureId.fromList(self.readValue() as! [Any?]) case 183: - return TypedFeaturesetDescriptor.fromList(self.readValue() as! [Any?]) + return FeatureState.fromList(self.readValue() as! [Any?]) case 184: - return FeaturesetDescriptor.fromList(self.readValue() as! [Any?]) + return Interaction.fromList(self.readValue() as! [Any?]) case 185: - return FeaturesetFeature.fromList(self.readValue() as! [Any?]) + return FeaturesetDescriptor.fromList(self.readValue() as! [Any?]) case 186: - return FeaturesetQueryTarget.fromList(self.readValue() as! [Any?]) + return FeaturesetFeature.fromList(self.readValue() as! [Any?]) case 187: - return _RenderedQueryGeometry.fromList(self.readValue() as! [Any?]) + return FeaturesetQueryTarget.fromList(self.readValue() as! [Any?]) case 188: - return ProjectedMeters.fromList(self.readValue() as! [Any?]) + return MapContentGestureContext.fromList(self.readValue() as! [Any?]) case 189: - return MercatorCoordinate.fromList(self.readValue() as! [Any?]) + return _RenderedQueryGeometry.fromList(self.readValue() as! [Any?]) case 190: - return StyleObjectInfo.fromList(self.readValue() as! [Any?]) + return ProjectedMeters.fromList(self.readValue() as! [Any?]) case 191: - return StyleProjection.fromList(self.readValue() as! [Any?]) + return MercatorCoordinate.fromList(self.readValue() as! [Any?]) case 192: - return FlatLight.fromList(self.readValue() as! [Any?]) + return StyleObjectInfo.fromList(self.readValue() as! [Any?]) case 193: - return DirectionalLight.fromList(self.readValue() as! [Any?]) + return StyleProjection.fromList(self.readValue() as! [Any?]) case 194: - return AmbientLight.fromList(self.readValue() as! [Any?]) + return FlatLight.fromList(self.readValue() as! [Any?]) case 195: - return MbxImage.fromList(self.readValue() as! [Any?]) + return DirectionalLight.fromList(self.readValue() as! [Any?]) case 196: - return ImageStretches.fromList(self.readValue() as! [Any?]) + return AmbientLight.fromList(self.readValue() as! [Any?]) case 197: - return ImageContent.fromList(self.readValue() as! [Any?]) + return MbxImage.fromList(self.readValue() as! [Any?]) case 198: - return TransitionOptions.fromList(self.readValue() as! [Any?]) + return ImageStretches.fromList(self.readValue() as! [Any?]) case 199: - return CanonicalTileID.fromList(self.readValue() as! [Any?]) + return ImageContent.fromList(self.readValue() as! [Any?]) case 200: + return TransitionOptions.fromList(self.readValue() as! [Any?]) + case 201: + return CanonicalTileID.fromList(self.readValue() as! [Any?]) + case 202: return StylePropertyValue.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) @@ -2207,195 +2198,201 @@ private class MapInterfacesPigeonCodecWriter: FlutterStandardWriter { } else if let value = value as? InteractionType { super.writeByte(137) super.writeValue(value.rawValue) - } else if let value = value as? Type { + } else if let value = value as? GestureState { super.writeByte(138) super.writeValue(value.rawValue) - } else if let value = value as? FillExtrusionBaseAlignment { + } else if let value = value as? Type { super.writeByte(139) super.writeValue(value.rawValue) - } else if let value = value as? FillExtrusionHeightAlignment { + } else if let value = value as? FillExtrusionBaseAlignment { super.writeByte(140) super.writeValue(value.rawValue) - } else if let value = value as? BackgroundPitchAlignment { + } else if let value = value as? FillExtrusionHeightAlignment { super.writeByte(141) super.writeValue(value.rawValue) - } else if let value = value as? StylePackErrorType { + } else if let value = value as? BackgroundPitchAlignment { super.writeByte(142) super.writeValue(value.rawValue) - } else if let value = value as? ResponseErrorReason { + } else if let value = value as? StylePackErrorType { super.writeByte(143) super.writeValue(value.rawValue) - } else if let value = value as? OfflineRegionDownloadState { + } else if let value = value as? ResponseErrorReason { super.writeByte(144) super.writeValue(value.rawValue) - } else if let value = value as? TileStoreUsageMode { + } else if let value = value as? OfflineRegionDownloadState { super.writeByte(145) super.writeValue(value.rawValue) - } else if let value = value as? StylePropertyValueKind { + } else if let value = value as? TileStoreUsageMode { super.writeByte(146) super.writeValue(value.rawValue) - } else if let value = value as? StyleProjectionName { + } else if let value = value as? StylePropertyValueKind { super.writeByte(147) super.writeValue(value.rawValue) - } else if let value = value as? Anchor { + } else if let value = value as? StyleProjectionName { super.writeByte(148) super.writeValue(value.rawValue) - } else if let value = value as? HttpMethod { + } else if let value = value as? Anchor { super.writeByte(149) super.writeValue(value.rawValue) - } else if let value = value as? HttpRequestErrorType { + } else if let value = value as? HttpMethod { super.writeByte(150) super.writeValue(value.rawValue) - } else if let value = value as? DownloadErrorCode { + } else if let value = value as? HttpRequestErrorType { super.writeByte(151) super.writeValue(value.rawValue) - } else if let value = value as? DownloadState { + } else if let value = value as? DownloadErrorCode { super.writeByte(152) super.writeValue(value.rawValue) - } else if let value = value as? TileRegionErrorType { + } else if let value = value as? DownloadState { super.writeByte(153) super.writeValue(value.rawValue) - } else if let value = value as? _MapEvent { + } else if let value = value as? TileRegionErrorType { super.writeByte(154) super.writeValue(value.rawValue) - } else if let value = value as? Point { + } else if let value = value as? _MapEvent { super.writeByte(155) - super.writeValue(value.toList()) - } else if let value = value as? Feature { + super.writeValue(value.rawValue) + } else if let value = value as? Point { super.writeByte(156) super.writeValue(value.toList()) - } else if let value = value as? GlyphsRasterizationOptions { + } else if let value = value as? Feature { super.writeByte(157) super.writeValue(value.toList()) - } else if let value = value as? TileCoverOptions { + } else if let value = value as? GlyphsRasterizationOptions { super.writeByte(158) super.writeValue(value.toList()) - } else if let value = value as? MbxEdgeInsets { + } else if let value = value as? TileCoverOptions { super.writeByte(159) super.writeValue(value.toList()) - } else if let value = value as? CameraOptions { + } else if let value = value as? MbxEdgeInsets { super.writeByte(160) super.writeValue(value.toList()) - } else if let value = value as? CameraState { + } else if let value = value as? CameraOptions { super.writeByte(161) super.writeValue(value.toList()) - } else if let value = value as? CameraBoundsOptions { + } else if let value = value as? CameraState { super.writeByte(162) super.writeValue(value.toList()) - } else if let value = value as? CameraBounds { + } else if let value = value as? CameraBoundsOptions { super.writeByte(163) super.writeValue(value.toList()) - } else if let value = value as? MapAnimationOptions { + } else if let value = value as? CameraBounds { super.writeByte(164) super.writeValue(value.toList()) - } else if let value = value as? CoordinateBounds { + } else if let value = value as? MapAnimationOptions { super.writeByte(165) super.writeValue(value.toList()) - } else if let value = value as? MapDebugOptions { + } else if let value = value as? CoordinateBounds { super.writeByte(166) super.writeValue(value.toList()) - } else if let value = value as? TileCacheBudgetInMegabytes { + } else if let value = value as? MapDebugOptions { super.writeByte(167) super.writeValue(value.toList()) - } else if let value = value as? TileCacheBudgetInTiles { + } else if let value = value as? TileCacheBudgetInMegabytes { super.writeByte(168) super.writeValue(value.toList()) - } else if let value = value as? MapOptions { + } else if let value = value as? TileCacheBudgetInTiles { super.writeByte(169) super.writeValue(value.toList()) - } else if let value = value as? ScreenCoordinate { + } else if let value = value as? MapOptions { super.writeByte(170) super.writeValue(value.toList()) - } else if let value = value as? ScreenBox { + } else if let value = value as? ScreenCoordinate { super.writeByte(171) super.writeValue(value.toList()) - } else if let value = value as? CoordinateBoundsZoom { + } else if let value = value as? ScreenBox { super.writeByte(172) super.writeValue(value.toList()) - } else if let value = value as? Size { + } else if let value = value as? CoordinateBoundsZoom { super.writeByte(173) super.writeValue(value.toList()) - } else if let value = value as? RenderedQueryOptions { + } else if let value = value as? Size { super.writeByte(174) super.writeValue(value.toList()) - } else if let value = value as? SourceQueryOptions { + } else if let value = value as? RenderedQueryOptions { super.writeByte(175) super.writeValue(value.toList()) - } else if let value = value as? FeatureExtensionValue { + } else if let value = value as? SourceQueryOptions { super.writeByte(176) super.writeValue(value.toList()) - } else if let value = value as? LayerPosition { + } else if let value = value as? FeatureExtensionValue { super.writeByte(177) super.writeValue(value.toList()) - } else if let value = value as? QueriedRenderedFeature { + } else if let value = value as? LayerPosition { super.writeByte(178) super.writeValue(value.toList()) - } else if let value = value as? QueriedSourceFeature { + } else if let value = value as? QueriedRenderedFeature { super.writeByte(179) super.writeValue(value.toList()) - } else if let value = value as? QueriedFeature { + } else if let value = value as? QueriedSourceFeature { super.writeByte(180) super.writeValue(value.toList()) - } else if let value = value as? FeaturesetFeatureId { + } else if let value = value as? QueriedFeature { super.writeByte(181) super.writeValue(value.toList()) - } else if let value = value as? Interaction { + } else if let value = value as? FeaturesetFeatureId { super.writeByte(182) super.writeValue(value.toList()) - } else if let value = value as? TypedFeaturesetDescriptor { + } else if let value = value as? FeatureState { super.writeByte(183) super.writeValue(value.toList()) - } else if let value = value as? FeaturesetDescriptor { + } else if let value = value as? Interaction { super.writeByte(184) super.writeValue(value.toList()) - } else if let value = value as? FeaturesetFeature { + } else if let value = value as? FeaturesetDescriptor { super.writeByte(185) super.writeValue(value.toList()) - } else if let value = value as? FeaturesetQueryTarget { + } else if let value = value as? FeaturesetFeature { super.writeByte(186) super.writeValue(value.toList()) - } else if let value = value as? _RenderedQueryGeometry { + } else if let value = value as? FeaturesetQueryTarget { super.writeByte(187) super.writeValue(value.toList()) - } else if let value = value as? ProjectedMeters { + } else if let value = value as? MapContentGestureContext { super.writeByte(188) super.writeValue(value.toList()) - } else if let value = value as? MercatorCoordinate { + } else if let value = value as? _RenderedQueryGeometry { super.writeByte(189) super.writeValue(value.toList()) - } else if let value = value as? StyleObjectInfo { + } else if let value = value as? ProjectedMeters { super.writeByte(190) super.writeValue(value.toList()) - } else if let value = value as? StyleProjection { + } else if let value = value as? MercatorCoordinate { super.writeByte(191) super.writeValue(value.toList()) - } else if let value = value as? FlatLight { + } else if let value = value as? StyleObjectInfo { super.writeByte(192) super.writeValue(value.toList()) - } else if let value = value as? DirectionalLight { + } else if let value = value as? StyleProjection { super.writeByte(193) super.writeValue(value.toList()) - } else if let value = value as? AmbientLight { + } else if let value = value as? FlatLight { super.writeByte(194) super.writeValue(value.toList()) - } else if let value = value as? MbxImage { + } else if let value = value as? DirectionalLight { super.writeByte(195) super.writeValue(value.toList()) - } else if let value = value as? ImageStretches { + } else if let value = value as? AmbientLight { super.writeByte(196) super.writeValue(value.toList()) - } else if let value = value as? ImageContent { + } else if let value = value as? MbxImage { super.writeByte(197) super.writeValue(value.toList()) - } else if let value = value as? TransitionOptions { + } else if let value = value as? ImageStretches { super.writeByte(198) super.writeValue(value.toList()) - } else if let value = value as? CanonicalTileID { + } else if let value = value as? ImageContent { super.writeByte(199) super.writeValue(value.toList()) - } else if let value = value as? StylePropertyValue { + } else if let value = value as? TransitionOptions { super.writeByte(200) super.writeValue(value.toList()) + } else if let value = value as? CanonicalTileID { + super.writeByte(201) + super.writeValue(value.toList()) + } else if let value = value as? StylePropertyValue { + super.writeByte(202) + super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -3121,6 +3118,39 @@ class _CameraManagerSetup { } } } +/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. +protocol InteractionsListenerProtocol { + func onInteraction(context contextArg: MapContentGestureContext, feature featureArg: FeaturesetFeature, interactionID interactionIDArg: Int64, completion: @escaping (Result) -> Void) +} +class InteractionsListener: InteractionsListenerProtocol { + private let binaryMessenger: FlutterBinaryMessenger + private let messageChannelSuffix: String + init(binaryMessenger: FlutterBinaryMessenger, messageChannelSuffix: String = "") { + self.binaryMessenger = binaryMessenger + self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + } + var codec: MapInterfacesPigeonCodec { + return MapInterfacesPigeonCodec.shared + } + func onInteraction(context contextArg: MapContentGestureContext, feature featureArg: FeaturesetFeature, interactionID interactionIDArg: Int64, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.mapbox_maps_flutter.InteractionsListener.onInteraction\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([contextArg, featureArg, interactionIDArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(MapInterfacesError(code: code, message: message, details: details))) + } else { + completion(.success(Void())) + } + } + } +} /// Map class provides map rendering functionality. /// /// @@ -3195,19 +3225,12 @@ protocol _MapInterface { /// @param completion The `query features completion` called when the query completes. /// @return A `cancelable` object that could be used to cancel the pending query. func queryRenderedFeatures(geometry: _RenderedQueryGeometry, options: RenderedQueryOptions, completion: @escaping (Result<[QueriedRenderedFeature?], Error>) -> Void) - /// Queries the map for rendered features using featureset descriptors. - /// - /// This method allows to query both featureset from imported styles and user layers in the root style. - /// The results can be additionally filtered per-featureset. - /// - /// - Important: This is a low-level method. If you need to handle basic gestures on map content, please prefer ``MapboxMap/ queryRenderedFeaturesForFeatureset()``. - /// - /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. - /// @param targets An array of targets to query with. - func queryRenderedFeaturesForTargets(geometry: _RenderedQueryGeometry, targets: [FeaturesetQueryTarget], completion: @escaping (Result<[QueriedRenderedFeature], Error>) -> Void) /// Queries the map for rendered features with one typed featureset. /// /// The results array will contain features of the type specified by this featureset. + /// + /// - Important: If you need to handle basic gestures on map content, + /// please prefer to use Interactions API, see `MapboxMap/addInteraction`. /// /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param featureset A typed featureset to query with. @@ -3216,6 +3239,9 @@ protocol _MapInterface { /// Queries all rendered features in current viewport, using one typed featureset. /// /// This is same as `MapboxMap/ queryRenderedFeaturesForFeatureset()`` called with geometry matching the current viewport. + /// + /// - Important: If you need to handle basic gestures on map content, + /// please prefer to use Interactions API, see `MapboxMap/addInteraction`. /// /// @param featureset A typed featureset to query with. /// @param filter An additional filter for features. @@ -3226,10 +3252,6 @@ protocol _MapInterface { /// @param options The `source query options` for querying source features. /// @param completion The `query features completion` called when the query completes. func querySourceFeatures(sourceId: String, options: SourceQueryOptions, completion: @escaping (Result<[QueriedSourceFeature?], Error>) -> Void) - /// Queries the source features for a given featureset. - /// - /// @param target A featureset query target. - func querySourceFeaturesForTargets(target: FeaturesetQueryTarget, completion: @escaping (Result<[QueriedSourceFeature], Error>) -> Void) /// Returns all the leaves (original points) of a cluster (given its cluster_id) from a GeoJsonSource, with pagination support: limit is the number of leaves /// to return (set to Infinity for all points), and offset is the amount of points to skip (for pagination). /// @@ -3283,9 +3305,7 @@ protocol _MapInterface { /// @param featureset The featureset to look the feature in. /// @param featureId Identifier of the feature whose state should be updated. /// @param state Map of entries to update with their respective new values - /// - /// @return A `Cancelable` object that could be used to cancel the pending operation. - func setFeatureStateForFeaturesetDescriptor(featureset: TypedFeaturesetDescriptor, featureId: FeaturesetFeatureId, state: [String: Any?], completion: @escaping (Result) -> Void) + func setFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: [String: Any?], completion: @escaping (Result) -> Void) /// Update the state map of an individual feature. /// /// The feature should have a non-nil ``FeaturesetFeatureType/id``. Otherwise, @@ -3293,8 +3313,6 @@ protocol _MapInterface { /// /// @param feature The feature to update. /// @param state Map of entries to update with their respective new values - /// - /// @return A `Cancelable` object that could be used to cancel the pending operation. func setFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, state: [String: Any?], completion: @escaping (Result) -> Void) /// Gets the state map of a feature within a style source. /// @@ -3305,20 +3323,20 @@ protocol _MapInterface { /// @param sourceLayerId The style source layer identifier (for multi-layer sources such as vector sources). /// @param featureId The feature identifier of the feature whose state should be queried. /// - /// @return A `Cancelable` object that could be used to cancel the pending operation. + /// @return A String representing the Feature's state map. func getFeatureState(sourceId: String, sourceLayerId: String?, featureId: String, completion: @escaping (Result) -> Void) /// Get the state map of a feature within a style source. /// /// @param featureset A featureset the feature belongs to. /// @param featureId Identifier of the feature whose state should be queried. /// - /// @return A `Cancelable` object that could be used to cancel the pending query. + /// @return The Feature's state map or an empty map if the feature could not be found. func getFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, completion: @escaping (Result<[String: Any?], Error>) -> Void) /// Get the state map of a feature within a style source. /// - /// @param feature An interactive feature to query the state from. + /// @param feature An interactive feature to query the state of. /// - /// @return A `Cancelable` object that could be used to cancel the pending query. + /// @return The Feature's state map or an empty map if the feature could not be found. func getFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, completion: @escaping (Result<[String: Any?], Error>) -> Void) /// Removes entries from a feature state object. /// @@ -3326,7 +3344,7 @@ protocol _MapInterface { /// `stateKey`. /// /// Note that updates to feature state are asynchronous, so changes made by this method might not be - /// immediately visible using `getStateFeature`. + /// immediately visible using `getFeatureState`. /// /// @param sourceId The style source identifier. /// @param sourceLayerId The style source layer identifier (for multi-layer sources such as vector sources). @@ -3339,16 +3357,12 @@ protocol _MapInterface { /// @param featureset A featureset the feature belongs to. /// @param featureId Identifier of the feature whose state should be removed. /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. - /// - /// @return A `Cancelable` object that could be used to cancel the pending operation. func removeFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String?, completion: @escaping (Result) -> Void) /// Removes entries from a specified Feature. /// Remove a specified property or all property from a feature's state object, depending on the value of `stateKey`. /// /// @param feature An interactive feature to update. /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. - /// - /// @return A `Cancelable` object that could be used to cancel the pending operation. func removeFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, stateKey: String?, completion: @escaping (Result) -> Void) /// Reset all the feature states within a featureset. /// @@ -3356,10 +3370,7 @@ protocol _MapInterface { /// immediately visible using ``MapboxMap/getFeatureState()``. /// /// @param featureset A featureset descriptor - /// - /// @return A `Cancelable` object that could be used to cancel the pending operation. func resetFeatureStatesForFeatureset(featureset: FeaturesetDescriptor, completion: @escaping (Result) -> Void) - func addInteraction(interaction: Interaction, completion: @escaping (Result) -> Void) /// Reduces memory use. Useful to call when the application gets paused or sent to background. func reduceMemoryUse() throws /// Gets elevation for the given coordinate. @@ -3752,36 +3763,12 @@ class _MapInterfaceSetup { } else { queryRenderedFeaturesChannel.setMessageHandler(nil) } - /// Queries the map for rendered features using featureset descriptors. - /// - /// This method allows to query both featureset from imported styles and user layers in the root style. - /// The results can be additionally filtered per-featureset. - /// - /// - Important: This is a low-level method. If you need to handle basic gestures on map content, please prefer ``MapboxMap/ queryRenderedFeaturesForFeatureset()``. - /// - /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. - /// @param targets An array of targets to query with. - let queryRenderedFeaturesForTargetsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesForTargets\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - queryRenderedFeaturesForTargetsChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - let geometryArg = args[0] as! _RenderedQueryGeometry - let targetsArg = args[1] as! [FeaturesetQueryTarget] - api.queryRenderedFeaturesForTargets(geometry: geometryArg, targets: targetsArg) { result in - switch result { - case .success(let res): - reply(wrapResult(res)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - queryRenderedFeaturesForTargetsChannel.setMessageHandler(nil) - } /// Queries the map for rendered features with one typed featureset. /// /// The results array will contain features of the type specified by this featureset. + /// + /// - Important: If you need to handle basic gestures on map content, + /// please prefer to use Interactions API, see `MapboxMap/addInteraction`. /// /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param featureset A typed featureset to query with. @@ -3808,6 +3795,9 @@ class _MapInterfaceSetup { /// Queries all rendered features in current viewport, using one typed featureset. /// /// This is same as `MapboxMap/ queryRenderedFeaturesForFeatureset()`` called with geometry matching the current viewport. + /// + /// - Important: If you need to handle basic gestures on map content, + /// please prefer to use Interactions API, see `MapboxMap/addInteraction`. /// /// @param featureset A typed featureset to query with. /// @param filter An additional filter for features. @@ -3852,26 +3842,6 @@ class _MapInterfaceSetup { } else { querySourceFeaturesChannel.setMessageHandler(nil) } - /// Queries the source features for a given featureset. - /// - /// @param target A featureset query target. - let querySourceFeaturesForTargetsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.querySourceFeaturesForTargets\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - querySourceFeaturesForTargetsChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - let targetArg = args[0] as! FeaturesetQueryTarget - api.querySourceFeaturesForTargets(target: targetArg) { result in - switch result { - case .success(let res): - reply(wrapResult(res)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - querySourceFeaturesForTargetsChannel.setMessageHandler(nil) - } /// Returns all the leaves (original points) of a cluster (given its cluster_id) from a GeoJsonSource, with pagination support: limit is the number of leaves /// to return (set to Infinity for all points), and offset is the amount of points to skip (for pagination). /// @@ -3997,13 +3967,11 @@ class _MapInterfaceSetup { /// @param featureset The featureset to look the feature in. /// @param featureId Identifier of the feature whose state should be updated. /// @param state Map of entries to update with their respective new values - /// - /// @return A `Cancelable` object that could be used to cancel the pending operation. let setFeatureStateForFeaturesetDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.setFeatureStateForFeaturesetDescriptor\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { setFeatureStateForFeaturesetDescriptorChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let featuresetArg = args[0] as! TypedFeaturesetDescriptor + let featuresetArg = args[0] as! FeaturesetDescriptor let featureIdArg = args[1] as! FeaturesetFeatureId let stateArg = args[2] as! [String: Any?] api.setFeatureStateForFeaturesetDescriptor(featureset: featuresetArg, featureId: featureIdArg, state: stateArg) { result in @@ -4025,8 +3993,6 @@ class _MapInterfaceSetup { /// /// @param feature The feature to update. /// @param state Map of entries to update with their respective new values - /// - /// @return A `Cancelable` object that could be used to cancel the pending operation. let setFeatureStateForFeaturesetFeatureChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.setFeatureStateForFeaturesetFeature\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { setFeatureStateForFeaturesetFeatureChannel.setMessageHandler { message, reply in @@ -4054,7 +4020,7 @@ class _MapInterfaceSetup { /// @param sourceLayerId The style source layer identifier (for multi-layer sources such as vector sources). /// @param featureId The feature identifier of the feature whose state should be queried. /// - /// @return A `Cancelable` object that could be used to cancel the pending operation. + /// @return A String representing the Feature's state map. let getFeatureStateChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.getFeatureState\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { getFeatureStateChannel.setMessageHandler { message, reply in @@ -4079,7 +4045,7 @@ class _MapInterfaceSetup { /// @param featureset A featureset the feature belongs to. /// @param featureId Identifier of the feature whose state should be queried. /// - /// @return A `Cancelable` object that could be used to cancel the pending query. + /// @return The Feature's state map or an empty map if the feature could not be found. let getFeatureStateForFeaturesetDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.getFeatureStateForFeaturesetDescriptor\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { getFeatureStateForFeaturesetDescriptorChannel.setMessageHandler { message, reply in @@ -4100,9 +4066,9 @@ class _MapInterfaceSetup { } /// Get the state map of a feature within a style source. /// - /// @param feature An interactive feature to query the state from. + /// @param feature An interactive feature to query the state of. /// - /// @return A `Cancelable` object that could be used to cancel the pending query. + /// @return The Feature's state map or an empty map if the feature could not be found. let getFeatureStateForFeaturesetFeatureChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.getFeatureStateForFeaturesetFeature\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { getFeatureStateForFeaturesetFeatureChannel.setMessageHandler { message, reply in @@ -4126,7 +4092,7 @@ class _MapInterfaceSetup { /// `stateKey`. /// /// Note that updates to feature state are asynchronous, so changes made by this method might not be - /// immediately visible using `getStateFeature`. + /// immediately visible using `getFeatureState`. /// /// @param sourceId The style source identifier. /// @param sourceLayerId The style source layer identifier (for multi-layer sources such as vector sources). @@ -4158,8 +4124,6 @@ class _MapInterfaceSetup { /// @param featureset A featureset the feature belongs to. /// @param featureId Identifier of the feature whose state should be removed. /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. - /// - /// @return A `Cancelable` object that could be used to cancel the pending operation. let removeFeatureStateForFeaturesetDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureStateForFeaturesetDescriptor\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { removeFeatureStateForFeaturesetDescriptorChannel.setMessageHandler { message, reply in @@ -4184,8 +4148,6 @@ class _MapInterfaceSetup { /// /// @param feature An interactive feature to update. /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. - /// - /// @return A `Cancelable` object that could be used to cancel the pending operation. let removeFeatureStateForFeaturesetFeatureChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.removeFeatureStateForFeaturesetFeature\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { removeFeatureStateForFeaturesetFeatureChannel.setMessageHandler { message, reply in @@ -4210,8 +4172,6 @@ class _MapInterfaceSetup { /// immediately visible using ``MapboxMap/getFeatureState()``. /// /// @param featureset A featureset descriptor - /// - /// @return A `Cancelable` object that could be used to cancel the pending operation. let resetFeatureStatesForFeaturesetChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.resetFeatureStatesForFeatureset\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { resetFeatureStatesForFeaturesetChannel.setMessageHandler { message, reply in @@ -4229,23 +4189,6 @@ class _MapInterfaceSetup { } else { resetFeatureStatesForFeaturesetChannel.setMessageHandler(nil) } - let addInteractionChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.addInteraction\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - addInteractionChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - let interactionArg = args[0] as! Interaction - api.addInteraction(interaction: interactionArg) { result in - switch result { - case .success(let res): - reply(wrapResult(res)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - addInteractionChannel.setMessageHandler(nil) - } /// Reduces memory use. Useful to call when the application gets paused or sent to background. let reduceMemoryUseChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.reduceMemoryUse\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift index c13980e7c..51a522397 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift @@ -198,17 +198,6 @@ final class MapInterfaceController: _MapInterface { } } - func queryRenderedFeaturesForTargets(geometry: _RenderedQueryGeometry, targets: [FeaturesetQueryTarget], completion: @escaping (Result<[QueriedRenderedFeature], any Error>) -> Void) { - self.mapboxMap.queryRenderedFeatures(with: geometry, targets: targets.map({$0.toMapFeaturesetQueryTarget()})) { result in - switch result { - case .success(let features): - completion(.success(features.map({$0.toFLTQueriedRenderedFeature()}))) - case .failure(let error): - completion(.failure(FlutterError(code: MapInterfaceController.errorCode, message: "\(error)", details: nil))) - } - } - } - func queryRenderedFeaturesForFeatureset(geometry: _RenderedQueryGeometry, featureset: FeaturesetDescriptor, filter: String?, completion: @escaping (Result<[FeaturesetFeature], any Error>) -> Void) { let filterExpression = try? filter.flatMap { try $0.toExp() } self.mapboxMap.queryRenderedFeatures(with: geometry, featureset: featureset.toMapFeaturesetDescriptor(), filter: filterExpression) { result in @@ -248,17 +237,6 @@ final class MapInterfaceController: _MapInterface { } } - func querySourceFeaturesForTargets(target: FeaturesetQueryTarget, completion: @escaping (Result<[QueriedSourceFeature], any Error>) -> Void) { - self.mapboxMap.querySourceFeatures(for: target.toMapFeaturesetQueryTarget()) { result in - switch result { - case .success(let features): - completion(.success(features.map({$0.toFLTQueriedSourceFeature()}))) - case .failure(let error): - completion(.failure(FlutterError(code: MapInterfaceController.errorCode, message: "\(error)", details: nil))) - } - } - } - func getGeoJsonClusterLeaves(sourceIdentifier: String, cluster: [String?: Any?], limit: Int64?, offset: Int64?, completion: @escaping (Result) -> Void) { guard let feature = convertDictionaryToFeature(dict: cluster) else { completion(.failure(FlutterError(code: MapInterfaceController.errorCode, message: "Feature format error", details: convertDictionaryToString(dict: cluster)))) @@ -315,11 +293,11 @@ final class MapInterfaceController: _MapInterface { } } - func setFeatureStateForFeaturesetDescriptor(featureset: TypedFeaturesetDescriptor, featureId: FeaturesetFeatureId, state: [String: Any?], completion: @escaping (Result) -> Void) { + func setFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: [String: Any?], completion: @escaping (Result) -> Void) { guard let state = JSONObject.init(turfRawValue: state) else { return } - self.mapboxMap.setFeatureState(featureset: featureset.featuresetDescriptor.toMapFeaturesetDescriptor(), featureId: featureId.toMapFeaturesetFeatureId(), state: state) { error in + self.mapboxMap.setFeatureState(featureset: featureset.toMapFeaturesetDescriptor(), featureId: featureId.toMapFeaturesetFeatureId(), state: state) { error in if let error { completion(.failure(FlutterError( code: "setFeatureStateError", @@ -435,12 +413,6 @@ final class MapInterfaceController: _MapInterface { } } - func addInteraction(interaction: Interaction, completion: @escaping (Result) -> Void) { - mapboxMap.addInteraction(interaction.toMapInteraction(completion: { _ in - print("no op") - })) - } - func reduceMemoryUse() throws { mapboxMap.reduceMemoryUse() } diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapboxMapController.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapboxMapController.swift index 2c276ce6b..f83d7c87e 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapboxMapController.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapboxMapController.swift @@ -106,6 +106,38 @@ final class MapboxMapController: NSObject, FlutterPlatformView { case "gesture#remove_listeners": gesturesController!.removeListeners() result(nil) + case "interactions#add_interaction": + let listener = InteractionsListener(binaryMessenger: binaryMessenger.messenger, messageChannelSuffix: binaryMessenger.suffix) + guard let arguments = methodCall.arguments as? [String: Any], + let featuresetDescriptorList = arguments["featuresetDescriptor"] as? [String?], + let featuresetDescriptor = FeaturesetDescriptor.fromList(featuresetDescriptorList), + let interactionTypeRaw = arguments["interactionType"] as? Int, + let interactionType = InteractionType(rawValue: interactionTypeRaw), + let stopPropagation = arguments["stopPropagation"] as? Bool, + let id = arguments["id"] as? Int else { + return + } + let filter = arguments["filter"] as? String + let filterExpression = try? filter.flatMap { try $0.toExp() } + let radius = arguments["radius"] as? CGFloat + + switch interactionType { + case .tAP: + mapboxMap.addInteraction( + TapInteraction(featuresetDescriptor.toMapFeaturesetDescriptor(), filter: filterExpression, radius: radius, action: { featuresetFeature, context in + listener.onInteraction(context: context.toFLTMapContentGestureContext(), feature: featuresetFeature.toFLTFeaturesetFeature(), interactionID: Int64(id)) { _ in } + return stopPropagation + }) + ) + case .lONGTAP: + mapboxMap.addInteraction( + LongPressInteraction(featuresetDescriptor.toMapFeaturesetDescriptor(), filter: filterExpression, radius: radius, action: { featuresetFeature, context in + listener.onInteraction(context: context.toFLTMapContentGestureContext(), feature: featuresetFeature.toFLTFeaturesetFeature(), interactionID: Int64(id)) { _ in } + return stopPropagation + }) + ) + } + result(nil) case "platform#releaseMethodChannels": releaseMethodChannels() result(nil) diff --git a/lib/mapbox_maps_flutter.dart b/lib/mapbox_maps_flutter.dart index 0078a9c6f..ad3147662 100644 --- a/lib/mapbox_maps_flutter.dart +++ b/lib/mapbox_maps_flutter.dart @@ -57,6 +57,10 @@ part 'src/style/source/rasterdem_source.dart'; part 'src/style/source/rasterarray_source.dart'; part 'src/style/source/vector_source.dart'; part 'src/style/style.dart'; +part 'src/style/interactive_features/interactive_features.dart'; +part 'src/style/interactive_features/standard_buildings.dart'; +part 'src/style/interactive_features/standard_place_labels.dart'; +part 'src/style/interactive_features/standard_poi.dart'; part 'src/location_settings.dart'; part 'src/snapshotter/snapshotter.dart'; part 'src/log_configuration.dart'; diff --git a/lib/src/callbacks.dart b/lib/src/callbacks.dart index c01a9b337..735eed688 100644 --- a/lib/src/callbacks.dart +++ b/lib/src/callbacks.dart @@ -72,3 +72,7 @@ typedef void OnTileRegionLoadProgressListener(TileRegionLoadProgress progress); // TileRegionEstimate load progress callback. typedef void OnTileRegionEstimateProgressListenter( TileRegionEstimateProgress progress); + +// Interaction callback called when a featureset or layer is interacted with. +typedef void OnInteraction( + MapContentGestureContext context, FeaturesetFeature feature); diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index 0bf513820..d4450177d 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -443,15 +443,6 @@ class MapboxMap extends ChangeNotifier { _RenderedQueryGeometry(value: geometry.value, type: geometry.type), options); - /// Queries the map for rendered features using featureset descriptors. - @experimental - Future> queryRenderedFeaturesForTargets( - RenderedQueryGeometry geometry, - List targets) async => - _mapInterface.queryRenderedFeaturesForTargets( - _RenderedQueryGeometry(value: geometry.value, type: geometry.type), - targets); - /// Queries the map for rendered features with one typed featureset. @experimental Future> queryRenderedFeaturesForFeatureset( @@ -474,12 +465,6 @@ class MapboxMap extends ChangeNotifier { String sourceId, SourceQueryOptions options) => _mapInterface.querySourceFeatures(sourceId, options); - /// Queries the source features for a given featureset. - @experimental - Future> querySourceFeaturesForTargets( - FeaturesetQueryTarget target) async => - _mapInterface.querySourceFeaturesForTargets(target); - /// Returns all the leaves (original points) of a cluster (given its cluster_id) from a GeoJsonSource, with pagination support: limit is the number of leaves /// to return (set to Infinity for all points), and offset is the amount of points to skip (for pagination). /// @@ -522,11 +507,11 @@ class MapboxMap extends ChangeNotifier { /// will be updated. An entry in the feature state map that is not listed in `state` will retain its previous value. @experimental Future setFeatureStateForFeaturesetDescriptor( - TypedFeaturesetDescriptor featureset, + FeaturesetDescriptor featureset, FeaturesetFeatureId featureId, - Map state) => + FeatureState state) => _mapInterface.setFeatureStateForFeaturesetDescriptor( - featureset, featureId, state); + featureset, featureId, state.map); /// Update the state map of an individual feature. /// @@ -534,8 +519,8 @@ class MapboxMap extends ChangeNotifier { /// the operation will be no-op and callback will receive an error. @experimental Future setFeatureStateForFeaturesetFeature( - FeaturesetFeature feature, Map state) => - _mapInterface.setFeatureStateForFeaturesetFeature(feature, state); + FeaturesetFeature feature, FeatureState state) => + _mapInterface.setFeatureStateForFeaturesetFeature(feature, state.map); /// Gets the state map of a feature within a style source. /// @@ -596,8 +581,22 @@ class MapboxMap extends ChangeNotifier { FeaturesetDescriptor featureset) => _mapInterface.resetFeatureStatesForFeatureset(featureset); - Future addInteraction(Interaction interaction) => - _mapInterface.addInteraction(interaction); + // References for all interactions added to the map. + _InteractionsList interactionsList = _InteractionsList(interactions: {}); + + /// Add an interaction + @experimental + void addInteraction(Interaction interaction, OnInteraction action) { + interactionsList.interactions[action.hashCode] = _InteractionListener( + onInteractionListener: action, + interactionID: action.hashCode, + ); + + InteractionsListener.setUp(interactionsList, + binaryMessenger: _mapboxMapsPlatform.binaryMessenger, + messageChannelSuffix: _mapboxMapsPlatform.channelSuffix.toString()); + _mapboxMapsPlatform.addInteractionsListeners(interaction, action.hashCode); + } /// Reduces memory use. Useful to call when the application gets paused or sent to background. Future reduceMemoryUse() => _mapInterface.reduceMemoryUse(); @@ -769,3 +768,36 @@ class _GestureListener extends GestureListener { onMapScrollListener?.call(context); } } + +// Listen for a single interaction added to the map, identified by its id +class _InteractionListener extends InteractionsListener { + _InteractionListener({ + required this.interactionID, + required this.onInteractionListener, + }); + + int interactionID; + + final OnInteraction onInteractionListener; + + @override + void onInteraction(MapContentGestureContext context, + FeaturesetFeature feature, int interactionID) { + onInteractionListener.call(context, feature); + } +} + +// Listen to all interactions on the map, determine which interaction to call +class _InteractionsList extends InteractionsListener { + _InteractionsList({ + required this.interactions, + }); + + Map interactions; + + @override + void onInteraction(MapContentGestureContext context, + FeaturesetFeature feature, int interactionID) { + interactions[interactionID]?.onInteraction(context, feature, interactionID); + } +} diff --git a/lib/src/mapbox_maps_platform.dart b/lib/src/mapbox_maps_platform.dart index 8b092c325..6108fa2e6 100644 --- a/lib/src/mapbox_maps_platform.dart +++ b/lib/src/mapbox_maps_platform.dart @@ -161,6 +161,23 @@ class _MapboxMapsPlatform { } } + Future addInteractionsListeners( + Interaction interaction, int interactionID) async { + try { + return _channel + .invokeMethod('interactions#add_interaction', { + 'featuresetDescriptor': interaction.featuresetDescriptor.encode(), + 'interactionType': interaction.interactionType.index, + 'filter': interaction.filter, + 'radius': interaction.radius, + 'stopPropagation': interaction.stopPropagation, + 'id': interactionID, + }); + } on PlatformException catch (e) { + return new Future.error(e); + } + } + Future removeGestureListeners() async { try { return _channel.invokeMethod('gesture#remove_listeners'); diff --git a/lib/src/pigeons/map_interfaces.dart b/lib/src/pigeons/map_interfaces.dart index 2d7968cb0..aa420e897 100644 --- a/lib/src/pigeons/map_interfaces.dart +++ b/lib/src/pigeons/map_interfaces.dart @@ -169,8 +169,8 @@ enum ViewAnnotationAnchor { } enum InteractionType { - CLICK, - LONG_CLICK, + TAP, + LONG_TAP, } /// Type information of the variant's content @@ -1340,67 +1340,74 @@ class FeaturesetFeatureId { } } -class Interaction { - Interaction({ - required this.typedFeaturesetDescriptor, - required this.interactionType, - this.filter, +class FeatureState { + FeatureState({ + required this.map, }); - TypedFeaturesetDescriptor typedFeaturesetDescriptor; - - InteractionType interactionType; - - String? filter; + Map map; Object encode() { return [ - typedFeaturesetDescriptor, - interactionType, - filter, + map, ]; } - static Interaction decode(Object result) { + static FeatureState decode(Object result) { result as List; - return Interaction( - typedFeaturesetDescriptor: result[0]! as TypedFeaturesetDescriptor, - interactionType: result[1]! as InteractionType, - filter: result[2] as String?, + return FeatureState( + map: (result[0] as Map?)!.cast(), ); } } -class TypedFeaturesetDescriptor { - TypedFeaturesetDescriptor({ +/// An interaction that can be added to the map. +/// +/// To create an interaction use ``TapInteraction`` and ``LongClickInteraction`` implementations. +/// +/// See also: ``MapboxMap/addInteraction``. +class Interaction { + Interaction({ required this.featuresetDescriptor, - required this.featuresetType, + required this.interactionType, + required this.stopPropagation, + this.filter, + this.radius, }); FeaturesetDescriptor featuresetDescriptor; - String featuresetType; + InteractionType interactionType; + + bool stopPropagation; + + String? filter; + + double? radius; Object encode() { return [ featuresetDescriptor, - featuresetType, + interactionType, + stopPropagation, + filter, + radius, ]; } - static TypedFeaturesetDescriptor decode(Object result) { + static Interaction decode(Object result) { result as List; - return TypedFeaturesetDescriptor( + return Interaction( featuresetDescriptor: result[0]! as FeaturesetDescriptor, - featuresetType: result[1]! as String, + interactionType: result[1]! as InteractionType, + stopPropagation: result[2]! as bool, + filter: result[3] as String?, + radius: result[4] as double?, ); } } -/// A featureset descriptor. -/// -/// The descriptor instance acts as a universal target for interactions or querying rendered features (see -/// ``MapboxMap/queryRenderedFeatures(with:featureset:filter:completion:)``). +///A featureset descriptor. class FeaturesetDescriptor { FeaturesetDescriptor({ this.featuresetId, @@ -1408,24 +1415,11 @@ class FeaturesetDescriptor { this.layerId, }); - /// An optional unique identifier for the featureset within the style. - /// This id is used to reference a specific featureset. - /// - /// * Note: If `featuresetId` is provided and valid, it takes precedence over `layerId`, - /// * meaning `layerId` will not be considered even if it has a valid value. String? featuresetId; - /// An optional import id that is required if the featureset is defined within an imported style. - /// If the featureset belongs to the current style, this field should be set to a null string. /// - /// Note: `importId` is only applicable when used in conjunction with `featuresetId` - /// and has no effect when used with `layerId`. String? importId; - /// An optional unique identifier for the layer within the current style. - /// - /// Note: If `featuresetId` is valid, `layerId` will be ignored even if it has a valid value. - /// Additionally, `importId` does not apply when using `layerId`. String? layerId; Object encode() { @@ -1446,9 +1440,6 @@ class FeaturesetDescriptor { } } -/// A basic feature of a featureset. -/// -/// The featureset feature is different to the `Turf.Feature`. The latter represents any GeoJSON feature, while the former is a high level representation of features. class FeaturesetFeature { FeaturesetFeature({ this.id, @@ -1458,25 +1449,14 @@ class FeaturesetFeature { required this.state, }); - /// An identifier of the feature. - /// - /// The identifier can be `nil` if the underlying source doesn't have identifiers for features. - /// In this case it's impossible to set a feature state for an individual feature. FeaturesetFeatureId? id; - /// A featureset descriptor denoting the featureset this feature belongs to. FeaturesetDescriptor featureset; - /// A feature geometry. Map geometry; - /// Feature JSON properties. Map properties; - /// A feature state. - /// - /// This is a **snapshot** of the state that the feature had when it was interacted with. - /// To update and read the original state, use ``MapboxMap/setFeatureState()`` and ``MapboxMap/getFeatureState()``. Map state; Object encode() { @@ -2121,195 +2101,201 @@ class MapInterfaces_PigeonCodec extends StandardMessageCodec { } else if (value is InteractionType) { buffer.putUint8(137); writeValue(buffer, value.index); - } else if (value is Type) { + } else if (value is GestureState) { buffer.putUint8(138); writeValue(buffer, value.index); - } else if (value is FillExtrusionBaseAlignment) { + } else if (value is Type) { buffer.putUint8(139); writeValue(buffer, value.index); - } else if (value is FillExtrusionHeightAlignment) { + } else if (value is FillExtrusionBaseAlignment) { buffer.putUint8(140); writeValue(buffer, value.index); - } else if (value is BackgroundPitchAlignment) { + } else if (value is FillExtrusionHeightAlignment) { buffer.putUint8(141); writeValue(buffer, value.index); - } else if (value is StylePackErrorType) { + } else if (value is BackgroundPitchAlignment) { buffer.putUint8(142); writeValue(buffer, value.index); - } else if (value is ResponseErrorReason) { + } else if (value is StylePackErrorType) { buffer.putUint8(143); writeValue(buffer, value.index); - } else if (value is OfflineRegionDownloadState) { + } else if (value is ResponseErrorReason) { buffer.putUint8(144); writeValue(buffer, value.index); - } else if (value is TileStoreUsageMode) { + } else if (value is OfflineRegionDownloadState) { buffer.putUint8(145); writeValue(buffer, value.index); - } else if (value is StylePropertyValueKind) { + } else if (value is TileStoreUsageMode) { buffer.putUint8(146); writeValue(buffer, value.index); - } else if (value is StyleProjectionName) { + } else if (value is StylePropertyValueKind) { buffer.putUint8(147); writeValue(buffer, value.index); - } else if (value is Anchor) { + } else if (value is StyleProjectionName) { buffer.putUint8(148); writeValue(buffer, value.index); - } else if (value is HttpMethod) { + } else if (value is Anchor) { buffer.putUint8(149); writeValue(buffer, value.index); - } else if (value is HttpRequestErrorType) { + } else if (value is HttpMethod) { buffer.putUint8(150); writeValue(buffer, value.index); - } else if (value is DownloadErrorCode) { + } else if (value is HttpRequestErrorType) { buffer.putUint8(151); writeValue(buffer, value.index); - } else if (value is DownloadState) { + } else if (value is DownloadErrorCode) { buffer.putUint8(152); writeValue(buffer, value.index); - } else if (value is TileRegionErrorType) { + } else if (value is DownloadState) { buffer.putUint8(153); writeValue(buffer, value.index); - } else if (value is _MapEvent) { + } else if (value is TileRegionErrorType) { buffer.putUint8(154); writeValue(buffer, value.index); - } else if (value is Point) { + } else if (value is _MapEvent) { buffer.putUint8(155); - writeValue(buffer, value.encode()); - } else if (value is Feature) { + writeValue(buffer, value.index); + } else if (value is Point) { buffer.putUint8(156); writeValue(buffer, value.encode()); - } else if (value is GlyphsRasterizationOptions) { + } else if (value is Feature) { buffer.putUint8(157); writeValue(buffer, value.encode()); - } else if (value is TileCoverOptions) { + } else if (value is GlyphsRasterizationOptions) { buffer.putUint8(158); writeValue(buffer, value.encode()); - } else if (value is MbxEdgeInsets) { + } else if (value is TileCoverOptions) { buffer.putUint8(159); writeValue(buffer, value.encode()); - } else if (value is CameraOptions) { + } else if (value is MbxEdgeInsets) { buffer.putUint8(160); writeValue(buffer, value.encode()); - } else if (value is CameraState) { + } else if (value is CameraOptions) { buffer.putUint8(161); writeValue(buffer, value.encode()); - } else if (value is CameraBoundsOptions) { + } else if (value is CameraState) { buffer.putUint8(162); writeValue(buffer, value.encode()); - } else if (value is CameraBounds) { + } else if (value is CameraBoundsOptions) { buffer.putUint8(163); writeValue(buffer, value.encode()); - } else if (value is MapAnimationOptions) { + } else if (value is CameraBounds) { buffer.putUint8(164); writeValue(buffer, value.encode()); - } else if (value is CoordinateBounds) { + } else if (value is MapAnimationOptions) { buffer.putUint8(165); writeValue(buffer, value.encode()); - } else if (value is MapDebugOptions) { + } else if (value is CoordinateBounds) { buffer.putUint8(166); writeValue(buffer, value.encode()); - } else if (value is TileCacheBudgetInMegabytes) { + } else if (value is MapDebugOptions) { buffer.putUint8(167); writeValue(buffer, value.encode()); - } else if (value is TileCacheBudgetInTiles) { + } else if (value is TileCacheBudgetInMegabytes) { buffer.putUint8(168); writeValue(buffer, value.encode()); - } else if (value is MapOptions) { + } else if (value is TileCacheBudgetInTiles) { buffer.putUint8(169); writeValue(buffer, value.encode()); - } else if (value is ScreenCoordinate) { + } else if (value is MapOptions) { buffer.putUint8(170); writeValue(buffer, value.encode()); - } else if (value is ScreenBox) { + } else if (value is ScreenCoordinate) { buffer.putUint8(171); writeValue(buffer, value.encode()); - } else if (value is CoordinateBoundsZoom) { + } else if (value is ScreenBox) { buffer.putUint8(172); writeValue(buffer, value.encode()); - } else if (value is Size) { + } else if (value is CoordinateBoundsZoom) { buffer.putUint8(173); writeValue(buffer, value.encode()); - } else if (value is RenderedQueryOptions) { + } else if (value is Size) { buffer.putUint8(174); writeValue(buffer, value.encode()); - } else if (value is SourceQueryOptions) { + } else if (value is RenderedQueryOptions) { buffer.putUint8(175); writeValue(buffer, value.encode()); - } else if (value is FeatureExtensionValue) { + } else if (value is SourceQueryOptions) { buffer.putUint8(176); writeValue(buffer, value.encode()); - } else if (value is LayerPosition) { + } else if (value is FeatureExtensionValue) { buffer.putUint8(177); writeValue(buffer, value.encode()); - } else if (value is QueriedRenderedFeature) { + } else if (value is LayerPosition) { buffer.putUint8(178); writeValue(buffer, value.encode()); - } else if (value is QueriedSourceFeature) { + } else if (value is QueriedRenderedFeature) { buffer.putUint8(179); writeValue(buffer, value.encode()); - } else if (value is QueriedFeature) { + } else if (value is QueriedSourceFeature) { buffer.putUint8(180); writeValue(buffer, value.encode()); - } else if (value is FeaturesetFeatureId) { + } else if (value is QueriedFeature) { buffer.putUint8(181); writeValue(buffer, value.encode()); - } else if (value is Interaction) { + } else if (value is FeaturesetFeatureId) { buffer.putUint8(182); writeValue(buffer, value.encode()); - } else if (value is TypedFeaturesetDescriptor) { + } else if (value is FeatureState) { buffer.putUint8(183); writeValue(buffer, value.encode()); - } else if (value is FeaturesetDescriptor) { + } else if (value is Interaction) { buffer.putUint8(184); writeValue(buffer, value.encode()); - } else if (value is FeaturesetFeature) { + } else if (value is FeaturesetDescriptor) { buffer.putUint8(185); writeValue(buffer, value.encode()); - } else if (value is FeaturesetQueryTarget) { + } else if (value is FeaturesetFeature) { buffer.putUint8(186); writeValue(buffer, value.encode()); - } else if (value is _RenderedQueryGeometry) { + } else if (value is FeaturesetQueryTarget) { buffer.putUint8(187); writeValue(buffer, value.encode()); - } else if (value is ProjectedMeters) { + } else if (value is MapContentGestureContext) { buffer.putUint8(188); writeValue(buffer, value.encode()); - } else if (value is MercatorCoordinate) { + } else if (value is _RenderedQueryGeometry) { buffer.putUint8(189); writeValue(buffer, value.encode()); - } else if (value is StyleObjectInfo) { + } else if (value is ProjectedMeters) { buffer.putUint8(190); writeValue(buffer, value.encode()); - } else if (value is StyleProjection) { + } else if (value is MercatorCoordinate) { buffer.putUint8(191); writeValue(buffer, value.encode()); - } else if (value is FlatLight) { + } else if (value is StyleObjectInfo) { buffer.putUint8(192); writeValue(buffer, value.encode()); - } else if (value is DirectionalLight) { + } else if (value is StyleProjection) { buffer.putUint8(193); writeValue(buffer, value.encode()); - } else if (value is AmbientLight) { + } else if (value is FlatLight) { buffer.putUint8(194); writeValue(buffer, value.encode()); - } else if (value is MbxImage) { + } else if (value is DirectionalLight) { buffer.putUint8(195); writeValue(buffer, value.encode()); - } else if (value is ImageStretches) { + } else if (value is AmbientLight) { buffer.putUint8(196); writeValue(buffer, value.encode()); - } else if (value is ImageContent) { + } else if (value is MbxImage) { buffer.putUint8(197); writeValue(buffer, value.encode()); - } else if (value is TransitionOptions) { + } else if (value is ImageStretches) { buffer.putUint8(198); writeValue(buffer, value.encode()); - } else if (value is CanonicalTileID) { + } else if (value is ImageContent) { buffer.putUint8(199); writeValue(buffer, value.encode()); - } else if (value is StylePropertyValue) { + } else if (value is TransitionOptions) { buffer.putUint8(200); writeValue(buffer, value.encode()); + } else if (value is CanonicalTileID) { + buffer.putUint8(201); + writeValue(buffer, value.encode()); + } else if (value is StylePropertyValue) { + buffer.putUint8(202); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -2347,148 +2333,153 @@ class MapInterfaces_PigeonCodec extends StandardMessageCodec { return value == null ? null : InteractionType.values[value]; case 138: final int? value = readValue(buffer) as int?; - return value == null ? null : Type.values[value]; + return value == null ? null : GestureState.values[value]; case 139: final int? value = readValue(buffer) as int?; - return value == null ? null : FillExtrusionBaseAlignment.values[value]; + return value == null ? null : Type.values[value]; case 140: + final int? value = readValue(buffer) as int?; + return value == null ? null : FillExtrusionBaseAlignment.values[value]; + case 141: final int? value = readValue(buffer) as int?; return value == null ? null : FillExtrusionHeightAlignment.values[value]; - case 141: + case 142: final int? value = readValue(buffer) as int?; return value == null ? null : BackgroundPitchAlignment.values[value]; - case 142: + case 143: final int? value = readValue(buffer) as int?; return value == null ? null : StylePackErrorType.values[value]; - case 143: + case 144: final int? value = readValue(buffer) as int?; return value == null ? null : ResponseErrorReason.values[value]; - case 144: + case 145: final int? value = readValue(buffer) as int?; return value == null ? null : OfflineRegionDownloadState.values[value]; - case 145: + case 146: final int? value = readValue(buffer) as int?; return value == null ? null : TileStoreUsageMode.values[value]; - case 146: + case 147: final int? value = readValue(buffer) as int?; return value == null ? null : StylePropertyValueKind.values[value]; - case 147: + case 148: final int? value = readValue(buffer) as int?; return value == null ? null : StyleProjectionName.values[value]; - case 148: + case 149: final int? value = readValue(buffer) as int?; return value == null ? null : Anchor.values[value]; - case 149: + case 150: final int? value = readValue(buffer) as int?; return value == null ? null : HttpMethod.values[value]; - case 150: + case 151: final int? value = readValue(buffer) as int?; return value == null ? null : HttpRequestErrorType.values[value]; - case 151: + case 152: final int? value = readValue(buffer) as int?; return value == null ? null : DownloadErrorCode.values[value]; - case 152: + case 153: final int? value = readValue(buffer) as int?; return value == null ? null : DownloadState.values[value]; - case 153: + case 154: final int? value = readValue(buffer) as int?; return value == null ? null : TileRegionErrorType.values[value]; - case 154: + case 155: final int? value = readValue(buffer) as int?; return value == null ? null : _MapEvent.values[value]; - case 155: - return Point.decode(readValue(buffer)!); case 156: - return Feature.decode(readValue(buffer)!); + return Point.decode(readValue(buffer)!); case 157: - return GlyphsRasterizationOptions.decode(readValue(buffer)!); + return Feature.decode(readValue(buffer)!); case 158: - return TileCoverOptions.decode(readValue(buffer)!); + return GlyphsRasterizationOptions.decode(readValue(buffer)!); case 159: - return MbxEdgeInsets.decode(readValue(buffer)!); + return TileCoverOptions.decode(readValue(buffer)!); case 160: - return CameraOptions.decode(readValue(buffer)!); + return MbxEdgeInsets.decode(readValue(buffer)!); case 161: - return CameraState.decode(readValue(buffer)!); + return CameraOptions.decode(readValue(buffer)!); case 162: - return CameraBoundsOptions.decode(readValue(buffer)!); + return CameraState.decode(readValue(buffer)!); case 163: - return CameraBounds.decode(readValue(buffer)!); + return CameraBoundsOptions.decode(readValue(buffer)!); case 164: - return MapAnimationOptions.decode(readValue(buffer)!); + return CameraBounds.decode(readValue(buffer)!); case 165: - return CoordinateBounds.decode(readValue(buffer)!); + return MapAnimationOptions.decode(readValue(buffer)!); case 166: - return MapDebugOptions.decode(readValue(buffer)!); + return CoordinateBounds.decode(readValue(buffer)!); case 167: - return TileCacheBudgetInMegabytes.decode(readValue(buffer)!); + return MapDebugOptions.decode(readValue(buffer)!); case 168: - return TileCacheBudgetInTiles.decode(readValue(buffer)!); + return TileCacheBudgetInMegabytes.decode(readValue(buffer)!); case 169: - return MapOptions.decode(readValue(buffer)!); + return TileCacheBudgetInTiles.decode(readValue(buffer)!); case 170: - return ScreenCoordinate.decode(readValue(buffer)!); + return MapOptions.decode(readValue(buffer)!); case 171: - return ScreenBox.decode(readValue(buffer)!); + return ScreenCoordinate.decode(readValue(buffer)!); case 172: - return CoordinateBoundsZoom.decode(readValue(buffer)!); + return ScreenBox.decode(readValue(buffer)!); case 173: - return Size.decode(readValue(buffer)!); + return CoordinateBoundsZoom.decode(readValue(buffer)!); case 174: - return RenderedQueryOptions.decode(readValue(buffer)!); + return Size.decode(readValue(buffer)!); case 175: - return SourceQueryOptions.decode(readValue(buffer)!); + return RenderedQueryOptions.decode(readValue(buffer)!); case 176: - return FeatureExtensionValue.decode(readValue(buffer)!); + return SourceQueryOptions.decode(readValue(buffer)!); case 177: - return LayerPosition.decode(readValue(buffer)!); + return FeatureExtensionValue.decode(readValue(buffer)!); case 178: - return QueriedRenderedFeature.decode(readValue(buffer)!); + return LayerPosition.decode(readValue(buffer)!); case 179: - return QueriedSourceFeature.decode(readValue(buffer)!); + return QueriedRenderedFeature.decode(readValue(buffer)!); case 180: - return QueriedFeature.decode(readValue(buffer)!); + return QueriedSourceFeature.decode(readValue(buffer)!); case 181: - return FeaturesetFeatureId.decode(readValue(buffer)!); + return QueriedFeature.decode(readValue(buffer)!); case 182: - return Interaction.decode(readValue(buffer)!); + return FeaturesetFeatureId.decode(readValue(buffer)!); case 183: - return TypedFeaturesetDescriptor.decode(readValue(buffer)!); + return FeatureState.decode(readValue(buffer)!); case 184: - return FeaturesetDescriptor.decode(readValue(buffer)!); + return Interaction.decode(readValue(buffer)!); case 185: - return FeaturesetFeature.decode(readValue(buffer)!); + return FeaturesetDescriptor.decode(readValue(buffer)!); case 186: - return FeaturesetQueryTarget.decode(readValue(buffer)!); + return FeaturesetFeature.decode(readValue(buffer)!); case 187: - return _RenderedQueryGeometry.decode(readValue(buffer)!); + return FeaturesetQueryTarget.decode(readValue(buffer)!); case 188: - return ProjectedMeters.decode(readValue(buffer)!); + return MapContentGestureContext.decode(readValue(buffer)!); case 189: - return MercatorCoordinate.decode(readValue(buffer)!); + return _RenderedQueryGeometry.decode(readValue(buffer)!); case 190: - return StyleObjectInfo.decode(readValue(buffer)!); + return ProjectedMeters.decode(readValue(buffer)!); case 191: - return StyleProjection.decode(readValue(buffer)!); + return MercatorCoordinate.decode(readValue(buffer)!); case 192: - return FlatLight.decode(readValue(buffer)!); + return StyleObjectInfo.decode(readValue(buffer)!); case 193: - return DirectionalLight.decode(readValue(buffer)!); + return StyleProjection.decode(readValue(buffer)!); case 194: - return AmbientLight.decode(readValue(buffer)!); + return FlatLight.decode(readValue(buffer)!); case 195: - return MbxImage.decode(readValue(buffer)!); + return DirectionalLight.decode(readValue(buffer)!); case 196: - return ImageStretches.decode(readValue(buffer)!); + return AmbientLight.decode(readValue(buffer)!); case 197: - return ImageContent.decode(readValue(buffer)!); + return MbxImage.decode(readValue(buffer)!); case 198: - return TransitionOptions.decode(readValue(buffer)!); + return ImageStretches.decode(readValue(buffer)!); case 199: - return CanonicalTileID.decode(readValue(buffer)!); + return ImageContent.decode(readValue(buffer)!); case 200: + return TransitionOptions.decode(readValue(buffer)!); + case 201: + return CanonicalTileID.decode(readValue(buffer)!); + case 202: return StylePropertyValue.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -3352,6 +3343,60 @@ class _CameraManager { } } +abstract class InteractionsListener { + static const MessageCodec pigeonChannelCodec = + MapInterfaces_PigeonCodec(); + + void onInteraction(MapContentGestureContext context, + FeaturesetFeature feature, int interactionID); + + static void setUp( + InteractionsListener? api, { + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) { + messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.mapbox_maps_flutter.InteractionsListener.onInteraction$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.mapbox_maps_flutter.InteractionsListener.onInteraction was null.'); + final List args = (message as List?)!; + final MapContentGestureContext? arg_context = + (args[0] as MapContentGestureContext?); + assert(arg_context != null, + 'Argument for dev.flutter.pigeon.mapbox_maps_flutter.InteractionsListener.onInteraction was null, expected non-null MapContentGestureContext.'); + final FeaturesetFeature? arg_feature = + (args[1] as FeaturesetFeature?); + assert(arg_feature != null, + 'Argument for dev.flutter.pigeon.mapbox_maps_flutter.InteractionsListener.onInteraction was null, expected non-null FeaturesetFeature.'); + final int? arg_interactionID = (args[2] as int?); + assert(arg_interactionID != null, + 'Argument for dev.flutter.pigeon.mapbox_maps_flutter.InteractionsListener.onInteraction was null, expected non-null int.'); + try { + api.onInteraction(arg_context!, arg_feature!, arg_interactionID!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } +} + /// Map class provides map rendering functionality. /// class _MapInterface { @@ -3972,51 +4017,13 @@ class _MapInterface { } } - /// Queries the map for rendered features using featureset descriptors. - /// - /// This method allows to query both featureset from imported styles and user layers in the root style. - /// The results can be additionally filtered per-featureset. - /// - /// - Important: This is a low-level method. If you need to handle basic gestures on map content, please prefer ``MapboxMap/ queryRenderedFeaturesForFeatureset()``. - /// - /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. - /// @param targets An array of targets to query with. - Future> queryRenderedFeaturesForTargets( - _RenderedQueryGeometry geometry, - List targets) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesForTargets$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([geometry, targets]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as List?)! - .cast(); - } - } - /// Queries the map for rendered features with one typed featureset. /// /// The results array will contain features of the type specified by this featureset. /// + /// - Important: If you need to handle basic gestures on map content, + /// please prefer to use Interactions API, see `MapboxMap/addInteraction`. + /// /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param featureset A typed featureset to query with. /// @param filter An additional filter for features. @@ -4057,6 +4064,9 @@ class _MapInterface { /// /// This is same as `MapboxMap/ queryRenderedFeaturesForFeatureset()`` called with geometry matching the current viewport. /// + /// - Important: If you need to handle basic gestures on map content, + /// please prefer to use Interactions API, see `MapboxMap/addInteraction`. + /// /// @param featureset A typed featureset to query with. /// @param filter An additional filter for features. Future> queryRenderedFeaturesInViewport( @@ -4126,40 +4136,6 @@ class _MapInterface { } } - /// Queries the source features for a given featureset. - /// - /// @param target A featureset query target. - Future> querySourceFeaturesForTargets( - FeaturesetQueryTarget target) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.querySourceFeaturesForTargets$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([target]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as List?)! - .cast(); - } - } - /// Returns all the leaves (original points) of a cluster (given its cluster_id) from a GeoJsonSource, with pagination support: limit is the number of leaves /// to return (set to Infinity for all points), and offset is the amount of points to skip (for pagination). /// @@ -4326,10 +4302,8 @@ class _MapInterface { /// @param featureset The featureset to look the feature in. /// @param featureId Identifier of the feature whose state should be updated. /// @param state Map of entries to update with their respective new values - /// - /// @return A `Cancelable` object that could be used to cancel the pending operation. Future setFeatureStateForFeaturesetDescriptor( - TypedFeaturesetDescriptor featureset, + FeaturesetDescriptor featureset, FeaturesetFeatureId featureId, Map state) async { final String pigeonVar_channelName = @@ -4362,8 +4336,6 @@ class _MapInterface { /// /// @param feature The feature to update. /// @param state Map of entries to update with their respective new values - /// - /// @return A `Cancelable` object that could be used to cancel the pending operation. Future setFeatureStateForFeaturesetFeature( FeaturesetFeature feature, Map state) async { final String pigeonVar_channelName = @@ -4398,7 +4370,7 @@ class _MapInterface { /// @param sourceLayerId The style source layer identifier (for multi-layer sources such as vector sources). /// @param featureId The feature identifier of the feature whose state should be queried. /// - /// @return A `Cancelable` object that could be used to cancel the pending operation. + /// @return A String representing the Feature's state map. Future getFeatureState( String sourceId, String? sourceLayerId, String featureId) async { final String pigeonVar_channelName = @@ -4434,7 +4406,7 @@ class _MapInterface { /// @param featureset A featureset the feature belongs to. /// @param featureId Identifier of the feature whose state should be queried. /// - /// @return A `Cancelable` object that could be used to cancel the pending query. + /// @return The Feature's state map or an empty map if the feature could not be found. Future> getFeatureStateForFeaturesetDescriptor( FeaturesetDescriptor featureset, FeaturesetFeatureId featureId) async { final String pigeonVar_channelName = @@ -4468,9 +4440,9 @@ class _MapInterface { /// Get the state map of a feature within a style source. /// - /// @param feature An interactive feature to query the state from. + /// @param feature An interactive feature to query the state of. /// - /// @return A `Cancelable` object that could be used to cancel the pending query. + /// @return The Feature's state map or an empty map if the feature could not be found. Future> getFeatureStateForFeaturesetFeature( FeaturesetFeature feature) async { final String pigeonVar_channelName = @@ -4508,7 +4480,7 @@ class _MapInterface { /// `stateKey`. /// /// Note that updates to feature state are asynchronous, so changes made by this method might not be - /// immediately visible using `getStateFeature`. + /// immediately visible using `getFeatureState`. /// /// @param sourceId The style source identifier. /// @param sourceLayerId The style source layer identifier (for multi-layer sources such as vector sources). @@ -4546,8 +4518,6 @@ class _MapInterface { /// @param featureset A featureset the feature belongs to. /// @param featureId Identifier of the feature whose state should be removed. /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. - /// - /// @return A `Cancelable` object that could be used to cancel the pending operation. Future removeFeatureStateForFeaturesetDescriptor( FeaturesetDescriptor featureset, FeaturesetFeatureId featureId, @@ -4580,8 +4550,6 @@ class _MapInterface { /// /// @param feature An interactive feature to update. /// @param stateKey The key of the property to remove. If `nil`, all feature's state object properties are removed. Defaults to `nil`. - /// - /// @return A `Cancelable` object that could be used to cancel the pending operation. Future removeFeatureStateForFeaturesetFeature( FeaturesetFeature feature, String? stateKey) async { final String pigeonVar_channelName = @@ -4613,8 +4581,6 @@ class _MapInterface { /// immediately visible using ``MapboxMap/getFeatureState()``. /// /// @param featureset A featureset descriptor - /// - /// @return A `Cancelable` object that could be used to cancel the pending operation. Future resetFeatureStatesForFeatureset( FeaturesetDescriptor featureset) async { final String pigeonVar_channelName = @@ -4640,35 +4606,6 @@ class _MapInterface { } } - Future addInteraction(Interaction interaction) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.addInteraction$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([interaction]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as FeaturesetFeature?)!; - } - } - /// Reduces memory use. Useful to call when the application gets paused or sent to background. Future reduceMemoryUse() async { final String pigeonVar_channelName = diff --git a/lib/src/style/interactive_features/interactive_features.dart b/lib/src/style/interactive_features/interactive_features.dart new file mode 100644 index 000000000..a301c5cb2 --- /dev/null +++ b/lib/src/style/interactive_features/interactive_features.dart @@ -0,0 +1,44 @@ +part of mapbox_maps_flutter; + +// A single tap interaction. +class TapInteraction extends Interaction { + TapInteraction(FeaturesetDescriptor featuresetDescriptor, + {String? filter, double? radius, bool stopPropagation = true}) + : super( + featuresetDescriptor: featuresetDescriptor, + interactionType: InteractionType.TAP, + filter: filter, + radius: radius, + stopPropagation: stopPropagation); +} + +// A long tap interaction +class LongTapInteraction extends Interaction { + LongTapInteraction(FeaturesetDescriptor featuresetDescriptor, + {String? filter, double? radius, bool stopPropagation = true}) + : super( + featuresetDescriptor: featuresetDescriptor, + interactionType: InteractionType.LONG_TAP, + filter: filter, + radius: radius, + stopPropagation: stopPropagation); +} + +extension Featureset on FeaturesetDescriptor { + // A Featureset of buildings in the Standard style + static FeaturesetDescriptor standardBuildings({String importId = "basemap"}) { + return FeaturesetDescriptor(featuresetId: "buildings", importId: importId); + } + + // A Featureset of place labels in the Standard style + static FeaturesetDescriptor standardPlaceLabels( + {String importId = "basemap"}) { + return FeaturesetDescriptor( + featuresetId: "place-labels", importId: importId); + } + + // A Featureset of POIs in the Standard style + static FeaturesetDescriptor standardPoi({String importId = "basemap"}) { + return FeaturesetDescriptor(featuresetId: "poi", importId: importId); + } +} diff --git a/lib/src/style/interactive_features/standard_buildings.dart b/lib/src/style/interactive_features/standard_buildings.dart new file mode 100644 index 000000000..79cbd06b8 --- /dev/null +++ b/lib/src/style/interactive_features/standard_buildings.dart @@ -0,0 +1,52 @@ +part of mapbox_maps_flutter; + +// A Feature that represents a building in the Standard style. +class StandardBuildingsFeature extends FeaturesetFeature { + // A feature state. + // + // This is a **snapshot** of the state that the feature had when it was interacted with. + // To update and read the original state, use ``MapboxMap/setFeatureState()`` and ``MapboxMap/getFeatureState()``. + StandardBuildingState get stateSnapshot { + return StandardBuildingState() + ..highlight = state["highlight"] as bool? + ..select = state["select"] as bool?; + } + + // A high-level building group like building-2d, building-3d, etc. + String? get group { + return properties["group"] as String?; + } + + StandardBuildingsFeature(Map geometry, + Map properties, Map state, + {FeaturesetFeatureId? id}) + : super( + id: id, + featureset: Featureset.standardBuildings(), + geometry: geometry, + properties: properties, + state: state); +} + +// Represents available states for Buildings in the Standard style. +class StandardBuildingState extends FeatureState { + // When `true`, the feature is highlighted. Use this state to create a temporary effect (e.g. hover). + bool? highlight; + + // When `true`, the feature is selected. Use this state to create a permanent effect. Note: the `select` state has a higher priority than `highlight`. + bool? select; + + @override + Map get map { + return { + "highlight": highlight, + "select": select, + }; + } + + StandardBuildingState({this.highlight, this.select}) + : super(map: { + "highlight": highlight, + "select": select, + }); +} diff --git a/lib/src/style/interactive_features/standard_place_labels.dart b/lib/src/style/interactive_features/standard_place_labels.dart new file mode 100644 index 000000000..7f0ddcdf8 --- /dev/null +++ b/lib/src/style/interactive_features/standard_place_labels.dart @@ -0,0 +1,64 @@ +part of mapbox_maps_flutter; + +// A feature that labels places including countries, states, cities, +// towns, and neighborhoods in the Standard style. +class StandardPlaceLabelsFeature extends FeaturesetFeature { + // A feature state. + // + // This is a **snapshot** of the state that the feature had when it was interacted with. + // To update and read the original state, use ``MapboxMap/setFeatureState()`` and ``MapboxMap/getFeatureState()``. + StandardPlaceLabelsState get stateSnapshot { + return StandardPlaceLabelsState() + ..hide = state["hide"] as bool? + ..highlight = state["highlight"] as bool? + ..select = state["select"] as bool?; + } + + // Name of the place label. + String? get name { + return properties["name"] as String?; + } + + // Provides a broad distinction between place types. + String? get category { + return properties["class"] as String?; + } + + StandardPlaceLabelsFeature(Map geometry, + Map properties, Map state, + {FeaturesetFeatureId? id}) + : super( + id: id, + featureset: Featureset.standardPlaceLabels(), + geometry: geometry, + properties: properties, + state: state); +} + +// Represents available states for Place Labels in the Standard style. +class StandardPlaceLabelsState extends FeatureState { + // When `true`, hides the label. Use this state when displaying a custom annotation on top. + bool? hide; + + // When `true`, the feature is highlighted. Use this state to create a temporary effect (e.g. hover). + bool? highlight; + + // When `true`, the feature is selected. Use this state to create a permanent effect. Note: the `select` state has a higher priority than `highlight`. + bool? select; + + @override + Map get map { + return { + "hide": hide, + "highlight": highlight, + "select": select, + }; + } + + StandardPlaceLabelsState({this.hide, this.highlight, this.select}) + : super(map: { + "hide": hide, + "highlight": highlight, + "select": select, + }); +} diff --git a/lib/src/style/interactive_features/standard_poi.dart b/lib/src/style/interactive_features/standard_poi.dart new file mode 100644 index 000000000..1d0faa334 --- /dev/null +++ b/lib/src/style/interactive_features/standard_poi.dart @@ -0,0 +1,85 @@ +part of mapbox_maps_flutter; + +// A feature that is a point of interest in the Standard style. +class StandardPoiFeature extends FeaturesetFeature { + // A feature state. + // + // This is a **snapshot** of the state that the feature had when it was interacted with. + // To update and read the original state, use ``MapboxMap/setFeatureState()`` and ``MapboxMap/getFeatureState()``. + StandardPoiState get stateSnapshot { + return StandardPoiState()..hide = state["hide"] as bool?; + } + + // Name of the point of interest. + String? get name { + return properties["name"] as String?; + } + + // A high-level point of interest category like airport, transit, etc. + String? get group { + return properties["group"] as String?; + } + + // A broad category of point of interest. + String? get category { + return properties["class"] as String?; + } + + // An icon identifier, designed to assign icons using the Maki icon project or other icons that follow the same naming scheme. + String? get maki { + return properties["maki"] as String?; + } + + // Mode of transport served by a stop/station. Expected to be null for non-transit points of interest. + String? get transitMode { + return properties["transit_mode"] as String?; + } + + // A type of transit stop. Expected to be null for non-transit points of interest. + String? get transitStopType { + return properties["transit_stop_type"] as String?; + } + + // A rail station network identifier that is part of specific local or regional transit systems. Expected to be null for non-transit points of interest. + String? get transitNetwork { + return properties["transit_network"] as String?; + } + + // A short identifier code of the airport. Expected to be null for non-airport points of interest + String? get airportRef { + return properties["airport_ref"] as String?; + } + + // POI coordinate. + Point? get coordinate { + return this.geometry["coordinates"] as Point?; + } + + StandardPoiFeature(Map geometry, + Map properties, Map state, + {FeaturesetFeatureId? id}) + : super( + id: id, + featureset: Featureset.standardPoi(), + geometry: geometry, + properties: properties, + state: state); +} + +// Represents available states for POIs in the Standard style. +class StandardPoiState extends FeatureState { + // When `true`, hides the icon and text. + bool? hide; + + @override + Map get map { + return { + "hide": hide + }; + } + + StandardPoiState({this.hide}) + : super(map: { + "hide": hide, + }); +} From c42b192414916e0fe97bdc53915f9b3a24380deb Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Wed, 8 Jan 2025 19:19:45 -0500 Subject: [PATCH 16/28] Update for sanity check --- lib/src/mapbox_map.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index d4450177d..7ada4bc1f 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -582,7 +582,8 @@ class MapboxMap extends ChangeNotifier { _mapInterface.resetFeatureStatesForFeatureset(featureset); // References for all interactions added to the map. - _InteractionsList interactionsList = _InteractionsList(interactions: {}); + @experimental + var interactionsList = _InteractionsList(interactions: {}); /// Add an interaction @experimental From d291775c7730eddc8aaa35512f87b3e65af8e712 Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Thu, 9 Jan 2025 15:29:53 -0500 Subject: [PATCH 17/28] Remove viewport query, general cleanup --- .../com/mapbox/maps/mapbox_maps/Extentions.kt | 44 ++------- .../mapbox_maps/MapInterfaceController.kt | 70 ++++++------- .../maps/mapbox_maps/pigeons/MapInterfaces.kt | 99 ++++++++++++------- .../interactive_features_test.dart | 6 +- .../Classes/Generated/MapInterfaces.swift | 37 ------- .../Classes/MapInterfaceController.swift | 12 --- lib/src/mapbox_map.dart | 12 +-- lib/src/pigeons/map_interfaces.dart | 85 ++++++++-------- .../interactive_features/standard_poi.dart | 8 +- 9 files changed, 149 insertions(+), 224 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt index ec31b99f7..34287c5af 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt @@ -4,8 +4,6 @@ import android.annotation.SuppressLint import android.content.Context import android.graphics.Bitmap import com.google.gson.Gson -import com.google.gson.JsonObject -import com.google.gson.JsonParser import com.mapbox.bindgen.Expected import com.mapbox.bindgen.None import com.mapbox.bindgen.Value @@ -273,20 +271,18 @@ fun FeaturesetFeatureId.toFeaturesetFeatureId(): com.mapbox.maps.FeaturesetFeatu return com.mapbox.maps.FeaturesetFeatureId(id, namespace) } -fun FeaturesetDescriptor.toFeatureSetDescriptor(): com.mapbox.maps.FeaturesetDescriptor { - return com.mapbox.maps.FeaturesetDescriptor(featuresetId, importId, layerId) -} - @OptIn(MapboxExperimental::class) -fun FeaturesetDescriptor.toTypedFeaturesetDescriptor(): TypedFeaturesetDescriptor<*, *> { +fun FeaturesetDescriptor.toTypedFeaturesetDescriptor(): TypedFeaturesetDescriptor<*, *>? { featuresetId?.let { return TypedFeaturesetDescriptor.Featureset( featuresetId, importId ) + } ?: layerId?.let { + return TypedFeaturesetDescriptor.Layer( + layerId + ) } - return TypedFeaturesetDescriptor.Layer( - layerId!! // If there is not a featuresetId there will be a layerId - ) + return null } @OptIn(MapboxExperimental::class) @@ -315,26 +311,6 @@ fun Map.toFeatureState(): com.mapbox.maps.interactions.FeatureStat } } -@OptIn(MapboxExperimental::class) -@SuppressLint("RestrictedApi") -fun FeaturesetFeature.toFeaturesetFeature(): com.mapbox.maps.interactions.FeaturesetFeature { - val jsonObject: JsonObject = JsonParser.parseString(Gson().toJson(properties)).getAsJsonObject() - featureset.featuresetId?.let { - return com.mapbox.maps.interactions.FeaturesetFeature( - id?.toFeaturesetFeatureId(), - featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor.Featureset, - state.toFeatureState(), - Feature.fromGeometry(geometry.toGeometry(), jsonObject) - ) - } - return com.mapbox.maps.interactions.FeaturesetFeature( - id?.toFeaturesetFeatureId(), - featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor.Layer, - state.toFeatureState(), - Feature.fromGeometry(geometry.toGeometry(), jsonObject) - ) -} - fun MapDebugOptions.toMapDebugOptions(): com.mapbox.maps.MapDebugOptions { return com.mapbox.maps.MapDebugOptions.values()[data.ordinal] } @@ -610,13 +586,8 @@ fun com.mapbox.maps.QueriedFeature.toFLTQueriedFeature(): QueriedFeature { return QueriedFeature(JSONObject(this.feature.toJson()).toMap(), source, sourceLayer, state.toJson()) } -fun com.mapbox.maps.FeaturesetQueryTarget.toFLTFeaturesetQueryTarget(): FeaturesetQueryTarget { - return FeaturesetQueryTarget(featureset.toFLTFeaturesetDescriptor(), filter?.toString(), id) -} - fun com.mapbox.maps.QueriedRenderedFeature.toFLTQueriedRenderedFeature(): QueriedRenderedFeature { - val queryTargets = targets.map { it.toFLTFeaturesetQueryTarget() } - return QueriedRenderedFeature(queriedFeature.toFLTQueriedFeature(), layers, queryTargets) + return QueriedRenderedFeature(queriedFeature.toFLTQueriedFeature(), layers) } fun com.mapbox.maps.FeaturesetFeatureId.toFLTFeaturesetFeatureId(): FeaturesetFeatureId { @@ -640,7 +611,6 @@ fun com.mapbox.maps.interactions.FeaturesetFeature.toFLTFeatureset } @SuppressLint("RestrictedApi") -@OptIn(MapboxExperimental::class) fun com.mapbox.maps.InteractionContext.toFLTMapContentGestureContext(): MapContentGestureContext { return MapContentGestureContext( ScreenCoordinate(screenCoordinate.x, screenCoordinate.y), diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt index 086082ae1..986619f6d 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt @@ -8,9 +8,6 @@ import com.mapbox.maps.MapView import com.mapbox.maps.MapboxDelicateApi import com.mapbox.maps.MapboxExperimental import com.mapbox.maps.MapboxMap -import com.mapbox.maps.RenderedQueryGeometry -import com.mapbox.maps.ScreenBox -import com.mapbox.maps.ScreenCoordinate import com.mapbox.maps.TileCacheBudget import com.mapbox.maps.extension.observable.eventdata.MapLoadingErrorEventData import com.mapbox.maps.extension.style.expressions.generated.Expression @@ -181,34 +178,15 @@ class MapInterfaceController( @OptIn(MapboxExperimental::class) override fun queryRenderedFeaturesForFeatureset( - geometry: _RenderedQueryGeometry, - featureset: FeaturesetDescriptor, - filter: String?, - callback: (Result>) -> Unit - ) { - mapboxMap.queryRenderedFeatures( - featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor<*, com.mapbox.maps.interactions.FeaturesetFeature>, - geometry.toRenderedQueryGeometry(context), - filter?.let { Expression.fromRaw(filter) } - ) { - callback( - Result.success( - it.map { feature -> feature.toFLTFeaturesetFeature() }.toMutableList() - ) - ) - } - } - - @OptIn(MapboxExperimental::class) - override fun queryRenderedFeaturesInViewport( + geometry: _RenderedQueryGeometry?, featureset: FeaturesetDescriptor, filter: String?, callback: (Result>) -> Unit ) { - val geometry = RenderedQueryGeometry(ScreenBox(ScreenCoordinate(0.0, 0.0), ScreenCoordinate(mapView.width.toDouble(), mapView.height.toDouble()))) + val typedFeaturesetDescriptor = featureset.toTypedFeaturesetDescriptor() as? TypedFeaturesetDescriptor<*, com.mapbox.maps.interactions.FeaturesetFeature> ?: return mapboxMap.queryRenderedFeatures( - featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor<*, com.mapbox.maps.interactions.FeaturesetFeature>, - geometry, + typedFeaturesetDescriptor, + geometry?.toRenderedQueryGeometry(context), filter?.let { Expression.fromRaw(filter) } ) { callback( @@ -320,16 +298,17 @@ class MapInterfaceController( ) { callback(Result.success(Unit)) } } - @OptIn(MapboxExperimental::class) + @OptIn(MapboxExperimental::class, MapboxDelicateApi::class) override fun setFeatureStateForFeaturesetFeature( feature: FeaturesetFeature, state: Map, callback: (Result) -> Unit ) { - val mapsFeature = feature.toFeaturesetFeature() + val id = feature.id?.toFeaturesetFeatureId() ?: return mapboxMap.setFeatureState( - mapsFeature, - state.toFeatureState() + feature.featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor>, + id = id, + state = state.toFeatureState() ) { callback(Result.success(Unit)) } } @@ -356,25 +335,30 @@ class MapInterfaceController( featureId: FeaturesetFeatureId, callback: (Result>) -> Unit ) { - mapboxMap.getFeatureState( - featureset.toTypedFeaturesetDescriptor(), - featureId.toFeaturesetFeatureId() - ) { - callback( - Result.success( - JSONObject(it.asJsonString()).toFilteredMap() + featureset.toTypedFeaturesetDescriptor()?.let { featuresetDescriptor -> + mapboxMap.getFeatureState( + featuresetDescriptor, + featureId.toFeaturesetFeatureId() + ) { + callback( + Result.success( + JSONObject(it.asJsonString()).toFilteredMap() + ) ) - ) + } } } - @OptIn(MapboxExperimental::class) + @OptIn(MapboxExperimental::class, MapboxDelicateApi::class) override fun getFeatureStateForFeaturesetFeature( feature: FeaturesetFeature, callback: (Result>) -> Unit ) { + val id = feature.id?.toFeaturesetFeatureId() ?: return + val featuresetDescriptor = feature.featureset.toTypedFeaturesetDescriptor() ?: return mapboxMap.getFeatureState( - feature.toFeaturesetFeature() + featuresetDescriptor, + id ) { callback( Result.success( @@ -420,14 +404,16 @@ class MapInterfaceController( } } - @OptIn(MapboxExperimental::class) + @OptIn(MapboxExperimental::class, MapboxDelicateApi::class) override fun removeFeatureStateForFeaturesetFeature( feature: FeaturesetFeature, stateKey: String?, callback: (Result) -> Unit ) { + val id = feature.id?.toFeaturesetFeatureId() ?: return mapboxMap.removeFeatureState( - feature.toFeaturesetFeature(), + feature.featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor>, + id, stateKey?.let { FeatureStateKey.create(it) } ) { if (it.isError) { diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt index e85888e3a..4b0836b78 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt @@ -233,8 +233,11 @@ enum class ViewAnnotationAnchor(val raw: Int) { } } +/** The type of interaction, either tap/click or longTap/longClick */ enum class InteractionType(val raw: Int) { + /** A short tap or click */ TAP(0), + /** A long tap or long click */ LONG_TAP(1); companion object { @@ -1401,7 +1404,11 @@ data class FeaturesetFeatureId( } } -/** Generated class from Pigeon that represents data sent in messages. */ +/** + * Wraps a FeatureState map + * + * Generated class from Pigeon that represents data sent in messages. + */ data class FeatureState( val map: Map ) { @@ -1428,10 +1435,15 @@ data class FeatureState( * Generated class from Pigeon that represents data sent in messages. */ data class Interaction( + /** The featureset descriptor that specifies the featureset to be included in the interaction. */ val featuresetDescriptor: FeaturesetDescriptor, + /** The type of interaction, either tap or longTap */ val interactionType: InteractionType, + /** Whether to stop the propagation of the interaction to the map. Defaults to true. */ val stopPropagation: Boolean, + /** An optional filter of features that should trigger the interaction. */ val filter: String? = null, + /** Radius of a tappable area */ val radius: Double? = null ) { companion object { @@ -1458,12 +1470,33 @@ data class Interaction( /** * A featureset descriptor. * + * The descriptor instance acts as a universal target for interactions or querying rendered features (see 'TapInteraction', 'LongTapInteraction') + * * Generated class from Pigeon that represents data sent in messages. */ data class FeaturesetDescriptor( + /** + * An optional unique identifier for the featureset within the style. + * This id is used to reference a specific featureset. + * + * * Note: If `featuresetId` is provided and valid, it takes precedence over `layerId`, + * * meaning `layerId` will not be considered even if it has a valid value. + */ val featuresetId: String? = null, - /** */ + /** + * An optional import id that is required if the featureset is defined within an imported style. + * If the featureset belongs to the current style, this field should be set to a null string. + * + * Note: `importId` is only applicable when used in conjunction with `featuresetId` + * and has no effect when used with `layerId`. + */ val importId: String? = null, + /** + * An optional unique identifier for the layer within the current style. + * + * Note: If `featuresetId` is valid, `layerId` will be ignored even if it has a valid value. + * Additionally, `importId` does not apply when using `layerId`. + */ val layerId: String? = null ) { companion object { @@ -1483,12 +1516,35 @@ data class FeaturesetDescriptor( } } -/** Generated class from Pigeon that represents data sent in messages. */ +/** + * A basic feature of a featureset. + * + * If you use Standard Style, you can use typed alternatives like `StandardPoiFeature`, `StandardPlaceLabelsFeature`, `StandardBuildingsFeature`. + * + * The featureset feature is different to the `Turf.Feature`. The latter represents any GeoJSON feature, while the former is a high level representation of features. + * + * Generated class from Pigeon that represents data sent in messages. + */ data class FeaturesetFeature( + /** + * An identifier of the feature. + * + * The identifier can be `nil` if the underlying source doesn't have identifiers for features. + * In this case it's impossible to set a feature state for an individual feature. + */ val id: FeaturesetFeatureId? = null, + /** A featureset descriptor denoting the featureset this feature belongs to. */ val featureset: FeaturesetDescriptor, + /** A feature geometry. */ val geometry: Map, + /** Feature JSON properties. */ val properties: Map, + /** + * A feature state. + * + * This is a **snapshot** of the state that the feature had when it was interacted with. + * To update and read the original state, use ``MapboxMap/setFeatureState()`` and ``MapboxMap/getFeatureState()``. + */ val state: Map ) { companion object { @@ -3489,19 +3545,7 @@ interface _MapInterface { * @param featureset A typed featureset to query with. * @param filter An additional filter for features. */ - fun queryRenderedFeaturesForFeatureset(geometry: _RenderedQueryGeometry, featureset: FeaturesetDescriptor, filter: String?, callback: (Result>) -> Unit) - /** - * Queries all rendered features in current viewport, using one typed featureset. - * - * This is same as `MapboxMap/ queryRenderedFeaturesForFeatureset()`` called with geometry matching the current viewport. - * - * - Important: If you need to handle basic gestures on map content, - * please prefer to use Interactions API, see `MapboxMap/addInteraction`. - * - * @param featureset A typed featureset to query with. - * @param filter An additional filter for features. - */ - fun queryRenderedFeaturesInViewport(featureset: FeaturesetDescriptor, filter: String?, callback: (Result>) -> Unit) + fun queryRenderedFeaturesForFeatureset(geometry: _RenderedQueryGeometry?, featureset: FeaturesetDescriptor, filter: String?, callback: (Result>) -> Unit) /** * Queries the map for source features. * @@ -4067,7 +4111,7 @@ interface _MapInterface { if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val geometryArg = args[0] as _RenderedQueryGeometry + val geometryArg = args[0] as _RenderedQueryGeometry? val featuresetArg = args[1] as FeaturesetDescriptor val filterArg = args[2] as String? api.queryRenderedFeaturesForFeatureset(geometryArg, featuresetArg, filterArg) { result: Result> -> @@ -4084,27 +4128,6 @@ interface _MapInterface { channel.setMessageHandler(null) } } - run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesInViewport$separatedMessageChannelSuffix", codec) - if (api != null) { - channel.setMessageHandler { message, reply -> - val args = message as List - val featuresetArg = args[0] as FeaturesetDescriptor - val filterArg = args[1] as String? - api.queryRenderedFeaturesInViewport(featuresetArg, filterArg) { result: Result> -> - val error = result.exceptionOrNull() - if (error != null) { - reply.reply(wrapError(error)) - } else { - val data = result.getOrNull() - reply.reply(wrapResult(data)) - } - } - } - } else { - channel.setMessageHandler(null) - } - } run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.querySourceFeatures$separatedMessageChannelSuffix", codec) if (api != null) { diff --git a/example/integration_test/interactive_features_test.dart b/example/integration_test/interactive_features_test.dart index 34f5f802b..82c5c1a9f 100644 --- a/example/integration_test/interactive_features_test.dart +++ b/example/integration_test/interactive_features_test.dart @@ -49,8 +49,8 @@ void main() { expect(featuresetFilterQuery[0].properties["name"], "nest1"); expect(featuresetFilterQuery[0].properties["class"], "poi"); - //test queryRenderedFeaturesInViewport - var viewportQuery = await mapboxMap.queryRenderedFeaturesInViewport( + //test queryRenderedFeatures for full viewport + var viewportQuery = await mapboxMap.queryRenderedFeaturesForFeatureset( featureset: FeaturesetDescriptor(featuresetId: "poi", importId: "nested")); @@ -188,7 +188,7 @@ void main() { await mapboxMap.setFeatureStateForFeaturesetDescriptor( featuresetDescriptor, featuresetID, state); - var queryResult = await mapboxMap.queryRenderedFeaturesInViewport( + var queryResult = await mapboxMap.queryRenderedFeaturesForFeatureset( featureset: featuresetDescriptor, filter: filter); var poi = queryResult.first; var point = Point.decode(poi.geometry); diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift index 36fcbba28..257d4d683 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift @@ -3236,16 +3236,6 @@ protocol _MapInterface { /// @param featureset A typed featureset to query with. /// @param filter An additional filter for features. func queryRenderedFeaturesForFeatureset(geometry: _RenderedQueryGeometry, featureset: FeaturesetDescriptor, filter: String?, completion: @escaping (Result<[FeaturesetFeature], Error>) -> Void) - /// Queries all rendered features in current viewport, using one typed featureset. - /// - /// This is same as `MapboxMap/ queryRenderedFeaturesForFeatureset()`` called with geometry matching the current viewport. - /// - /// - Important: If you need to handle basic gestures on map content, - /// please prefer to use Interactions API, see `MapboxMap/addInteraction`. - /// - /// @param featureset A typed featureset to query with. - /// @param filter An additional filter for features. - func queryRenderedFeaturesInViewport(featureset: FeaturesetDescriptor, filter: String?, completion: @escaping (Result<[FeaturesetFeature], Error>) -> Void) /// Queries the map for source features. /// /// @param sourceId The style source identifier used to query for source features. @@ -3792,33 +3782,6 @@ class _MapInterfaceSetup { } else { queryRenderedFeaturesForFeaturesetChannel.setMessageHandler(nil) } - /// Queries all rendered features in current viewport, using one typed featureset. - /// - /// This is same as `MapboxMap/ queryRenderedFeaturesForFeatureset()`` called with geometry matching the current viewport. - /// - /// - Important: If you need to handle basic gestures on map content, - /// please prefer to use Interactions API, see `MapboxMap/addInteraction`. - /// - /// @param featureset A typed featureset to query with. - /// @param filter An additional filter for features. - let queryRenderedFeaturesInViewportChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesInViewport\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - queryRenderedFeaturesInViewportChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - let featuresetArg = args[0] as! FeaturesetDescriptor - let filterArg: String? = nilOrValue(args[1]) - api.queryRenderedFeaturesInViewport(featureset: featuresetArg, filter: filterArg) { result in - switch result { - case .success(let res): - reply(wrapResult(res)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - queryRenderedFeaturesInViewportChannel.setMessageHandler(nil) - } /// Queries the map for source features. /// /// @param sourceId The style source identifier used to query for source features. diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift index 51a522397..45cae64f6 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift @@ -210,18 +210,6 @@ final class MapInterfaceController: _MapInterface { } } - func queryRenderedFeaturesInViewport(featureset: FeaturesetDescriptor, filter: String?, completion: @escaping (Result<[FeaturesetFeature], any Error>) -> Void) { - let filterExpression = try? filter.flatMap { try $0.toExp() } - self.mapboxMap.queryRenderedFeatures(featureset: featureset.toMapFeaturesetDescriptor(), filter: filterExpression) { result in - switch result { - case .success(let features): - completion(.success(features.map({$0.toFLTFeaturesetFeature()}))) - case .failure(let error): - completion(.failure(FlutterError(code: MapInterfaceController.errorCode, message: "\(error)", details: nil))) - } - } - } - func querySourceFeatures(sourceId: String, options: SourceQueryOptions, completion: @escaping (Result<[QueriedSourceFeature?], Error>) -> Void) { do { try self.mapboxMap.querySourceFeatures(for: sourceId, options: options.toSourceQueryOptions()) { result in diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index 7ada4bc1f..4bb190718 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -446,20 +446,16 @@ class MapboxMap extends ChangeNotifier { /// Queries the map for rendered features with one typed featureset. @experimental Future> queryRenderedFeaturesForFeatureset( - {required RenderedQueryGeometry geometry, + {RenderedQueryGeometry? geometry, required FeaturesetDescriptor featureset, String? filter}) async => _mapInterface.queryRenderedFeaturesForFeatureset( - _RenderedQueryGeometry(value: geometry.value, type: geometry.type), + geometry != null + ? _RenderedQueryGeometry(value: geometry.value, type: geometry.type) + : null, featureset, filter); - /// Queries all rendered features in current viewport, using one typed featureset. - @experimental - Future> queryRenderedFeaturesInViewport( - {required FeaturesetDescriptor featureset, String? filter}) async => - _mapInterface.queryRenderedFeaturesInViewport(featureset, filter); - /// Queries the map for source features. Future> querySourceFeatures( String sourceId, SourceQueryOptions options) => diff --git a/lib/src/pigeons/map_interfaces.dart b/lib/src/pigeons/map_interfaces.dart index aa420e897..9f3ed7c7b 100644 --- a/lib/src/pigeons/map_interfaces.dart +++ b/lib/src/pigeons/map_interfaces.dart @@ -168,8 +168,12 @@ enum ViewAnnotationAnchor { CENTER, } +/// The type of interaction, either tap/click or longTap/longClick enum InteractionType { + /// A short tap or click TAP, + + /// A long tap or long click LONG_TAP, } @@ -1340,6 +1344,7 @@ class FeaturesetFeatureId { } } +/// Wraps a FeatureState map class FeatureState { FeatureState({ required this.map, @@ -1375,14 +1380,19 @@ class Interaction { this.radius, }); + /// The featureset descriptor that specifies the featureset to be included in the interaction. FeaturesetDescriptor featuresetDescriptor; + /// The type of interaction, either tap or longTap InteractionType interactionType; + /// Whether to stop the propagation of the interaction to the map. Defaults to true. bool stopPropagation; + /// An optional filter of features that should trigger the interaction. String? filter; + /// Radius of a tappable area double? radius; Object encode() { @@ -1407,7 +1417,9 @@ class Interaction { } } -///A featureset descriptor. +/// A featureset descriptor. +/// +/// The descriptor instance acts as a universal target for interactions or querying rendered features (see 'TapInteraction', 'LongTapInteraction') class FeaturesetDescriptor { FeaturesetDescriptor({ this.featuresetId, @@ -1415,11 +1427,24 @@ class FeaturesetDescriptor { this.layerId, }); + /// An optional unique identifier for the featureset within the style. + /// This id is used to reference a specific featureset. + /// + /// * Note: If `featuresetId` is provided and valid, it takes precedence over `layerId`, + /// * meaning `layerId` will not be considered even if it has a valid value. String? featuresetId; + /// An optional import id that is required if the featureset is defined within an imported style. + /// If the featureset belongs to the current style, this field should be set to a null string. /// + /// Note: `importId` is only applicable when used in conjunction with `featuresetId` + /// and has no effect when used with `layerId`. String? importId; + /// An optional unique identifier for the layer within the current style. + /// + /// Note: If `featuresetId` is valid, `layerId` will be ignored even if it has a valid value. + /// Additionally, `importId` does not apply when using `layerId`. String? layerId; Object encode() { @@ -1440,6 +1465,11 @@ class FeaturesetDescriptor { } } +/// A basic feature of a featureset. +/// +/// If you use Standard Style, you can use typed alternatives like `StandardPoiFeature`, `StandardPlaceLabelsFeature`, `StandardBuildingsFeature`. +/// +/// The featureset feature is different to the `Turf.Feature`. The latter represents any GeoJSON feature, while the former is a high level representation of features. class FeaturesetFeature { FeaturesetFeature({ this.id, @@ -1449,14 +1479,25 @@ class FeaturesetFeature { required this.state, }); + /// An identifier of the feature. + /// + /// The identifier can be `nil` if the underlying source doesn't have identifiers for features. + /// In this case it's impossible to set a feature state for an individual feature. FeaturesetFeatureId? id; + /// A featureset descriptor denoting the featureset this feature belongs to. FeaturesetDescriptor featureset; + /// A feature geometry. Map geometry; + /// Feature JSON properties. Map properties; + /// A feature state. + /// + /// This is a **snapshot** of the state that the feature had when it was interacted with. + /// To update and read the original state, use ``MapboxMap/setFeatureState()`` and ``MapboxMap/getFeatureState()``. Map state; Object encode() { @@ -4028,7 +4069,7 @@ class _MapInterface { /// @param featureset A typed featureset to query with. /// @param filter An additional filter for features. Future> queryRenderedFeaturesForFeatureset( - _RenderedQueryGeometry geometry, + _RenderedQueryGeometry? geometry, FeaturesetDescriptor featureset, String? filter) async { final String pigeonVar_channelName = @@ -4060,46 +4101,6 @@ class _MapInterface { } } - /// Queries all rendered features in current viewport, using one typed featureset. - /// - /// This is same as `MapboxMap/ queryRenderedFeaturesForFeatureset()`` called with geometry matching the current viewport. - /// - /// - Important: If you need to handle basic gestures on map content, - /// please prefer to use Interactions API, see `MapboxMap/addInteraction`. - /// - /// @param featureset A typed featureset to query with. - /// @param filter An additional filter for features. - Future> queryRenderedFeaturesInViewport( - FeaturesetDescriptor featureset, String? filter) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesInViewport$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final List? pigeonVar_replyList = await pigeonVar_channel - .send([featureset, filter]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as List?)! - .cast(); - } - } - /// Queries the map for source features. /// /// @param sourceId The style source identifier used to query for source features. diff --git a/lib/src/style/interactive_features/standard_poi.dart b/lib/src/style/interactive_features/standard_poi.dart index 1d0faa334..f6aaab6bc 100644 --- a/lib/src/style/interactive_features/standard_poi.dart +++ b/lib/src/style/interactive_features/standard_poi.dart @@ -68,14 +68,12 @@ class StandardPoiFeature extends FeaturesetFeature { // Represents available states for POIs in the Standard style. class StandardPoiState extends FeatureState { - // When `true`, hides the icon and text. + // When `true`, hides the icon and text. bool? hide; - + @override Map get map { - return { - "hide": hide - }; + return {"hide": hide}; } StandardPoiState({this.hide}) From 9d6a8d19ed624bc54e640cb55fab94d050cd168d Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Thu, 9 Jan 2025 19:06:02 -0500 Subject: [PATCH 18/28] Update viewport QRF implementation --- .../mapbox_maps/MapInterfaceController.kt | 4 ++-- .../maps/mapbox_maps/MapboxMapController.kt | 21 +++++++++---------- .../maps/mapbox_maps/pigeons/MapInterfaces.kt | 11 +++++----- .../interactive_features_test.dart | 6 +++--- .../Classes/Generated/MapInterfaces.swift | 8 +++---- .../Classes/MapInterfaceController.swift | 9 ++++++-- lib/src/mapbox_map.dart | 15 ++++++------- lib/src/pigeons/map_interfaces.dart | 7 ++++--- 8 files changed, 44 insertions(+), 37 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt index 986619f6d..25d05fe14 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt @@ -178,8 +178,8 @@ class MapInterfaceController( @OptIn(MapboxExperimental::class) override fun queryRenderedFeaturesForFeatureset( - geometry: _RenderedQueryGeometry?, featureset: FeaturesetDescriptor, + geometry: _RenderedQueryGeometry?, filter: String?, callback: (Result>) -> Unit ) { @@ -354,8 +354,8 @@ class MapInterfaceController( feature: FeaturesetFeature, callback: (Result>) -> Unit ) { - val id = feature.id?.toFeaturesetFeatureId() ?: return val featuresetDescriptor = feature.featureset.toTypedFeaturesetDescriptor() ?: return + val id = feature.id?.toFeaturesetFeatureId() ?: return mapboxMap.getFeatureState( featuresetDescriptor, id diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt index 755b0e23b..0cc0e259f 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt @@ -250,21 +250,21 @@ class MapboxMapController( 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 id = (arguments["id"] as? Int)?.toLong() ?: return + val filter = (arguments["filter"] as? String).toValue() 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()) { _ -> } + ClickInteraction.featureset(id = it, importId = featuresetDescriptor.importId, filter = filter, radius = radius) { featuresetFeature, context -> + listener.onInteraction(context.toFLTMapContentGestureContext(), featuresetFeature.toFLTFeaturesetFeature(), id) { _ -> } 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()) { _ -> } + LongClickInteraction.featureset(id = it, importId = featuresetDescriptor.importId, filter = filter, radius = radius) { featuresetFeature, context -> + listener.onInteraction(context.toFLTMapContentGestureContext(), featuresetFeature.toFLTFeaturesetFeature(), id) { _ -> } return@featureset stopPropagation } ) @@ -273,21 +273,20 @@ class MapboxMapController( } ?: 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()) { _ -> } + ClickInteraction.layer(id = it, filter = filter, radius = radius) { featuresetFeature, context -> + listener.onInteraction(context.toFLTMapContentGestureContext(), featuresetFeature.toFLTFeaturesetFeature(), id) { _ -> } 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()) { _ -> } + LongClickInteraction.layer(id = it, filter = filter, radius = radius) { featuresetFeature, context -> + listener.onInteraction(context.toFLTMapContentGestureContext(), featuresetFeature.toFLTFeaturesetFeature(), id) { _ -> } return@layer stopPropagation } ) null -> return } } - result.success(null) } "platform#releaseMethodChannels" -> { diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt index 4b0836b78..df6823d66 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt @@ -3541,11 +3541,12 @@ interface _MapInterface { * - Important: If you need to handle basic gestures on map content, * please prefer to use Interactions API, see `MapboxMap/addInteraction`. * - * @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. * @param featureset A typed featureset to query with. + * @param geometry An optional screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. + * If omitted, the full viewport is queried. * @param filter An additional filter for features. */ - fun queryRenderedFeaturesForFeatureset(geometry: _RenderedQueryGeometry?, featureset: FeaturesetDescriptor, filter: String?, callback: (Result>) -> Unit) + fun queryRenderedFeaturesForFeatureset(featureset: FeaturesetDescriptor, geometry: _RenderedQueryGeometry?, filter: String?, callback: (Result>) -> Unit) /** * Queries the map for source features. * @@ -4111,10 +4112,10 @@ interface _MapInterface { if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val geometryArg = args[0] as _RenderedQueryGeometry? - val featuresetArg = args[1] as FeaturesetDescriptor + val featuresetArg = args[0] as FeaturesetDescriptor + val geometryArg = args[1] as _RenderedQueryGeometry? val filterArg = args[2] as String? - api.queryRenderedFeaturesForFeatureset(geometryArg, featuresetArg, filterArg) { result: Result> -> + api.queryRenderedFeaturesForFeatureset(featuresetArg, geometryArg, filterArg) { result: Result> -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) diff --git a/example/integration_test/interactive_features_test.dart b/example/integration_test/interactive_features_test.dart index 82c5c1a9f..241d5b931 100644 --- a/example/integration_test/interactive_features_test.dart +++ b/example/integration_test/interactive_features_test.dart @@ -27,9 +27,9 @@ void main() { var coord = await mapboxMap .pixelForCoordinate(Point(coordinates: Position(0.01, 0.01))); var featuresetQuery = await mapboxMap.queryRenderedFeaturesForFeatureset( - geometry: RenderedQueryGeometry.fromScreenCoordinate(coord), featureset: - FeaturesetDescriptor(featuresetId: "poi", importId: "nested")); + FeaturesetDescriptor(featuresetId: "poi", importId: "nested"), + geometry: RenderedQueryGeometry.fromScreenCoordinate(coord)); expect(featuresetQuery.length, 2); expect(featuresetQuery.first.properties["name"], "nest2"); @@ -40,9 +40,9 @@ void main() { var filter = '["==",["get", "type"], "A"]'; var featuresetFilterQuery = await mapboxMap.queryRenderedFeaturesForFeatureset( - geometry: RenderedQueryGeometry.fromScreenCoordinate(coord), featureset: FeaturesetDescriptor(featuresetId: "poi", importId: "nested"), + geometry: RenderedQueryGeometry.fromScreenCoordinate(coord), filter: filter); expect(featuresetFilterQuery.length, 1); diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift index 257d4d683..f1a5b6562 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift @@ -3235,7 +3235,7 @@ protocol _MapInterface { /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param featureset A typed featureset to query with. /// @param filter An additional filter for features. - func queryRenderedFeaturesForFeatureset(geometry: _RenderedQueryGeometry, featureset: FeaturesetDescriptor, filter: String?, completion: @escaping (Result<[FeaturesetFeature], Error>) -> Void) + func queryRenderedFeaturesForFeatureset(featureset: FeaturesetDescriptor, geometry: _RenderedQueryGeometry?, filter: String?, completion: @escaping (Result<[FeaturesetFeature], Error>) -> Void) /// Queries the map for source features. /// /// @param sourceId The style source identifier used to query for source features. @@ -3767,10 +3767,10 @@ class _MapInterfaceSetup { if let api = api { queryRenderedFeaturesForFeaturesetChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let geometryArg = args[0] as! _RenderedQueryGeometry - let featuresetArg = args[1] as! FeaturesetDescriptor + let featuresetArg = args[0] as! FeaturesetDescriptor + let geometryArg: _RenderedQueryGeometry? = nilOrValue(args[1]) let filterArg: String? = nilOrValue(args[2]) - api.queryRenderedFeaturesForFeatureset(geometry: geometryArg, featureset: featuresetArg, filter: filterArg) { result in + api.queryRenderedFeaturesForFeatureset(featureset: featuresetArg, geometry: geometryArg, filter: filterArg) { result in switch result { case .success(let res): reply(wrapResult(res)) diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift index 45cae64f6..68ce25870 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift @@ -198,9 +198,9 @@ final class MapInterfaceController: _MapInterface { } } - func queryRenderedFeaturesForFeatureset(geometry: _RenderedQueryGeometry, featureset: FeaturesetDescriptor, filter: String?, completion: @escaping (Result<[FeaturesetFeature], any Error>) -> Void) { + func queryRenderedFeaturesForFeatureset(featureset: FeaturesetDescriptor, geometry: _RenderedQueryGeometry?, filter: String?, completion: @escaping (Result<[FeaturesetFeature], any Error>) -> Void) { let filterExpression = try? filter.flatMap { try $0.toExp() } - self.mapboxMap.queryRenderedFeatures(with: geometry, featureset: featureset.toMapFeaturesetDescriptor(), filter: filterExpression) { result in + let fltCompletion: (Result<[MapboxMaps.FeaturesetFeature], Error>) -> Void = { result in switch result { case .success(let features): completion(.success(features.map({$0.toFLTFeaturesetFeature()}))) @@ -208,6 +208,11 @@ final class MapInterfaceController: _MapInterface { completion(.failure(FlutterError(code: MapInterfaceController.errorCode, message: "\(error)", details: nil))) } } + if let geometry { + self.mapboxMap.queryRenderedFeatures(with: geometry, featureset: featureset.toMapFeaturesetDescriptor(), filter: filterExpression, completion: fltCompletion) + } else { + self.mapboxMap.queryRenderedFeatures(featureset: featureset.toMapFeaturesetDescriptor(), filter: filterExpression, completion: fltCompletion) + } } func querySourceFeatures(sourceId: String, options: SourceQueryOptions, completion: @escaping (Result<[QueriedSourceFeature?], Error>) -> Void) { diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index 4bb190718..e0802ba30 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -446,15 +446,16 @@ class MapboxMap extends ChangeNotifier { /// Queries the map for rendered features with one typed featureset. @experimental Future> queryRenderedFeaturesForFeatureset( - {RenderedQueryGeometry? geometry, - required FeaturesetDescriptor featureset, - String? filter}) async => - _mapInterface.queryRenderedFeaturesForFeatureset( - geometry != null + {required FeaturesetDescriptor featureset, + RenderedQueryGeometry? geometry, + String? filter}) async { + return _mapInterface.queryRenderedFeaturesForFeatureset( + featureset, + (geometry != null) ? _RenderedQueryGeometry(value: geometry.value, type: geometry.type) : null, - featureset, - filter); + filter); + } /// Queries the map for source features. Future> querySourceFeatures( diff --git a/lib/src/pigeons/map_interfaces.dart b/lib/src/pigeons/map_interfaces.dart index 9f3ed7c7b..f783bdc9e 100644 --- a/lib/src/pigeons/map_interfaces.dart +++ b/lib/src/pigeons/map_interfaces.dart @@ -4065,12 +4065,13 @@ class _MapInterface { /// - Important: If you need to handle basic gestures on map content, /// please prefer to use Interactions API, see `MapboxMap/addInteraction`. /// - /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param featureset A typed featureset to query with. + /// @param geometry An optional screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. + /// If omitted, the full viewport is queried. /// @param filter An additional filter for features. Future> queryRenderedFeaturesForFeatureset( - _RenderedQueryGeometry? geometry, FeaturesetDescriptor featureset, + _RenderedQueryGeometry? geometry, String? filter) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesForFeatureset$pigeonVar_messageChannelSuffix'; @@ -4081,7 +4082,7 @@ class _MapInterface { binaryMessenger: pigeonVar_binaryMessenger, ); final List? pigeonVar_replyList = await pigeonVar_channel - .send([geometry, featureset, filter]) as List?; + .send([featureset, geometry, filter]) as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { From fd1913bc27e94e8ca074ea09a5d64fa875096078 Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Wed, 15 Jan 2025 11:33:28 -0500 Subject: [PATCH 19/28] Remove FeaturesetQueryTarget, minor updates from review --- .../mapbox_maps/MapInterfaceController.kt | 88 +++++++------- .../maps/mapbox_maps/pigeons/MapInterfaces.kt | 109 +++++------------- example/lib/interactive_features_example.dart | 23 ++-- .../Classes/Extensions.swift | 3 +- .../Classes/Generated/MapInterfaces.swift | 104 +++++------------ .../Classes/MapInterfaceController.swift | 54 ++++++--- lib/src/pigeons/map_interfaces.dart | 107 +++++------------ .../interactive_features.dart | 8 +- .../standard_buildings.dart | 2 +- .../standard_place_labels.dart | 2 +- .../interactive_features/standard_poi.dart | 2 +- 11 files changed, 199 insertions(+), 303 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt index 25d05fe14..13ab712f9 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt @@ -183,17 +183,18 @@ class MapInterfaceController( filter: String?, callback: (Result>) -> Unit ) { - val typedFeaturesetDescriptor = featureset.toTypedFeaturesetDescriptor() as? TypedFeaturesetDescriptor<*, com.mapbox.maps.interactions.FeaturesetFeature> ?: return - mapboxMap.queryRenderedFeatures( - typedFeaturesetDescriptor, - geometry?.toRenderedQueryGeometry(context), - filter?.let { Expression.fromRaw(filter) } - ) { - callback( - Result.success( - it.map { feature -> feature.toFLTFeaturesetFeature() }.toMutableList() + (featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor<*, com.mapbox.maps.interactions.FeaturesetFeature>).let { typedFeaturesetDescriptor -> + mapboxMap.queryRenderedFeatures( + typedFeaturesetDescriptor, + geometry?.toRenderedQueryGeometry(context), + filter?.let { Expression.fromRaw(filter) } + ) { + callback( + Result.success( + it.map { feature -> feature.toFLTFeaturesetFeature() }.toMutableList() + ) ) - ) + } } } @@ -304,12 +305,15 @@ class MapInterfaceController( state: Map, callback: (Result) -> Unit ) { - val id = feature.id?.toFeaturesetFeatureId() ?: return - mapboxMap.setFeatureState( - feature.featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor>, - id = id, - state = state.toFeatureState() - ) { callback(Result.success(Unit)) } + if (feature.id?.toFeaturesetFeatureId() != null) { + mapboxMap.setFeatureState( + feature.featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor>, + id = feature.id.toFeaturesetFeatureId(), + state = state.toFeatureState() + ) { callback(Result.success(Unit)) } + } else { + callback(Result.failure(Throwable("Invalid feature for the requested feature: ${feature.id}"))) + } } override fun getFeatureState( @@ -347,6 +351,7 @@ class MapInterfaceController( ) } } + callback(Result.failure(Throwable("Cannot get feature state for the requested featureset: {featureId: ${featureset.featuresetId}, importId: ${featureset.importId}, layerId: ${featureset.layerId} and featureID: $featureId."))) } @OptIn(MapboxExperimental::class, MapboxDelicateApi::class) @@ -354,17 +359,19 @@ class MapInterfaceController( feature: FeaturesetFeature, callback: (Result>) -> Unit ) { - val featuresetDescriptor = feature.featureset.toTypedFeaturesetDescriptor() ?: return - val id = feature.id?.toFeaturesetFeatureId() ?: return - mapboxMap.getFeatureState( - featuresetDescriptor, - id - ) { - callback( - Result.success( - JSONObject(it.asJsonString()).toFilteredMap() + if (feature.id?.toFeaturesetFeatureId() != null) { + mapboxMap.getFeatureState( + feature.featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor>, + feature.id.toFeaturesetFeatureId() + ) { + callback( + Result.success( + JSONObject(it.asJsonString()).toFilteredMap() + ) ) - ) + } + } else { + callback(Result.failure(Throwable("Invalid feature id for the requested feature: ${feature.id}"))) } } @@ -377,7 +384,7 @@ class MapInterfaceController( ) { mapboxMap.removeFeatureState(sourceId, sourceLayerId, featureId, stateKey) { if (it.isError) { - callback(Result.failure(Throwable(it.error))) + callback(Result.failure(Throwable("Cannot remove feature state for the requested feature: $featureId"))) } else { callback(Result.success(Unit)) } @@ -397,7 +404,7 @@ class MapInterfaceController( stateKey?.let { FeatureStateKey.create(it) } ) { if (it.isError) { - callback(Result.failure(Throwable(it.error))) + callback(Result.failure(Throwable("Cannot remove feature state for the requested featureset: {featureId: ${featureset.featuresetId}, importId: ${featureset.importId}, layerId: ${featureset.layerId} and featureID: $featureId."))) } else { callback(Result.success(Unit)) } @@ -410,17 +417,20 @@ class MapInterfaceController( stateKey: String?, callback: (Result) -> Unit ) { - val id = feature.id?.toFeaturesetFeatureId() ?: return - mapboxMap.removeFeatureState( - feature.featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor>, - id, - stateKey?.let { FeatureStateKey.create(it) } - ) { - if (it.isError) { - callback(Result.failure(Throwable(it.error))) - } else { - callback(Result.success(Unit)) + if (feature.id?.toFeaturesetFeatureId() != null) { + mapboxMap.removeFeatureState( + feature.featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor>, + feature.id.toFeaturesetFeatureId(), + stateKey?.let { FeatureStateKey.create(it) } + ) { + if (it.isError) { + callback(Result.failure(Throwable("Cannot remove feature state for the requested feature: ${feature.id}"))) + } else { + callback(Result.success(Unit)) + } } + } else { + callback(Result.failure(Throwable("Invalid feature id for the requested feature: ${feature.id}"))) } } @@ -433,7 +443,7 @@ class MapInterfaceController( featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor> ) { if (it.isError) { - callback(Result.failure(Throwable(it.error))) + callback(Result.failure(Throwable("Cannot reset feature state for the requested featureset: {featureId: ${featureset.featuresetId}, importId: ${featureset.importId}, layerId: ${featureset.layerId}."))) } else { callback(Result.success(Unit)) } diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt index df6823d66..a9fbb6cfa 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt @@ -1281,27 +1281,19 @@ data class QueriedRenderedFeature( * If the feature has been rendered in multiple layers, multiple Ids will be provided. * If the feature is only rendered in one layer, a single Id will be provided. */ - val layers: List, - /** - * An array of feature query targets that correspond to this queried feature. - * - * - Note: Returned query targets will omit the original `filter` data. - */ - val queryTargets: List? = null + val layers: List ) { companion object { fun fromList(pigeonVar_list: List): QueriedRenderedFeature { val queriedFeature = pigeonVar_list[0] as QueriedFeature val layers = pigeonVar_list[1] as List - val queryTargets = pigeonVar_list[2] as List? - return QueriedRenderedFeature(queriedFeature, layers, queryTargets) + return QueriedRenderedFeature(queriedFeature, layers) } } fun toList(): List { return listOf( queriedFeature, layers, - queryTargets, ) } } @@ -1568,36 +1560,6 @@ data class FeaturesetFeature( } } -/** - * Defines the parameters for querying features from a Featureset with an optional filter and id. - * - * Generated class from Pigeon that represents data sent in messages. - */ -data class FeaturesetQueryTarget( - /** A `FeaturesetDescriptor` that specifies the featureset to be included in the query. */ - val featureset: FeaturesetDescriptor, - /** An optional filter expression used to refine the query results based on conditions related to the specified featureset. */ - val filter: String? = null, - /** An optional unique identifier associated with the target. */ - val id: Long? = null -) { - companion object { - fun fromList(pigeonVar_list: List): FeaturesetQueryTarget { - val featureset = pigeonVar_list[0] as FeaturesetDescriptor - val filter = pigeonVar_list[1] as String? - val id = pigeonVar_list[2] as Long? - return FeaturesetQueryTarget(featureset, filter, id) - } - } - fun toList(): List { - return listOf( - featureset, - filter, - id, - ) - } -} - /** * Geometry for querying rendered features. * @@ -2356,81 +2318,76 @@ private open class MapInterfacesPigeonCodec : StandardMessageCodec() { } } 187.toByte() -> { - return (readValue(buffer) as? List)?.let { - FeaturesetQueryTarget.fromList(it) - } - } - 188.toByte() -> { return (readValue(buffer) as? List)?.let { MapContentGestureContext.fromList(it) } } - 189.toByte() -> { + 188.toByte() -> { return (readValue(buffer) as? List)?.let { _RenderedQueryGeometry.fromList(it) } } - 190.toByte() -> { + 189.toByte() -> { return (readValue(buffer) as? List)?.let { ProjectedMeters.fromList(it) } } - 191.toByte() -> { + 190.toByte() -> { return (readValue(buffer) as? List)?.let { MercatorCoordinate.fromList(it) } } - 192.toByte() -> { + 191.toByte() -> { return (readValue(buffer) as? List)?.let { StyleObjectInfo.fromList(it) } } - 193.toByte() -> { + 192.toByte() -> { return (readValue(buffer) as? List)?.let { StyleProjection.fromList(it) } } - 194.toByte() -> { + 193.toByte() -> { return (readValue(buffer) as? List)?.let { FlatLight.fromList(it) } } - 195.toByte() -> { + 194.toByte() -> { return (readValue(buffer) as? List)?.let { DirectionalLight.fromList(it) } } - 196.toByte() -> { + 195.toByte() -> { return (readValue(buffer) as? List)?.let { AmbientLight.fromList(it) } } - 197.toByte() -> { + 196.toByte() -> { return (readValue(buffer) as? List)?.let { MbxImage.fromList(it) } } - 198.toByte() -> { + 197.toByte() -> { return (readValue(buffer) as? List)?.let { ImageStretches.fromList(it) } } - 199.toByte() -> { + 198.toByte() -> { return (readValue(buffer) as? List)?.let { ImageContent.fromList(it) } } - 200.toByte() -> { + 199.toByte() -> { return (readValue(buffer) as? List)?.let { TransitionOptions.fromList(it) } } - 201.toByte() -> { + 200.toByte() -> { return (readValue(buffer) as? List)?.let { CanonicalTileID.fromList(it) } } - 202.toByte() -> { + 201.toByte() -> { return (readValue(buffer) as? List)?.let { StylePropertyValue.fromList(it) } @@ -2672,68 +2629,64 @@ private open class MapInterfacesPigeonCodec : StandardMessageCodec() { stream.write(186) writeValue(stream, value.toList()) } - is FeaturesetQueryTarget -> { - stream.write(187) - writeValue(stream, value.toList()) - } is MapContentGestureContext -> { - stream.write(188) + stream.write(187) writeValue(stream, value.toList()) } is _RenderedQueryGeometry -> { - stream.write(189) + stream.write(188) writeValue(stream, value.toList()) } is ProjectedMeters -> { - stream.write(190) + stream.write(189) writeValue(stream, value.toList()) } is MercatorCoordinate -> { - stream.write(191) + stream.write(190) writeValue(stream, value.toList()) } is StyleObjectInfo -> { - stream.write(192) + stream.write(191) writeValue(stream, value.toList()) } is StyleProjection -> { - stream.write(193) + stream.write(192) writeValue(stream, value.toList()) } is FlatLight -> { - stream.write(194) + stream.write(193) writeValue(stream, value.toList()) } is DirectionalLight -> { - stream.write(195) + stream.write(194) writeValue(stream, value.toList()) } is AmbientLight -> { - stream.write(196) + stream.write(195) writeValue(stream, value.toList()) } is MbxImage -> { - stream.write(197) + stream.write(196) writeValue(stream, value.toList()) } is ImageStretches -> { - stream.write(198) + stream.write(197) writeValue(stream, value.toList()) } is ImageContent -> { - stream.write(199) + stream.write(198) writeValue(stream, value.toList()) } is TransitionOptions -> { - stream.write(200) + stream.write(199) writeValue(stream, value.toList()) } is CanonicalTileID -> { - stream.write(201) + stream.write(200) writeValue(stream, value.toList()) } is StylePropertyValue -> { - stream.write(202) + stream.write(201) writeValue(stream, value.toList()) } else -> super.writeValue(stream, value) diff --git a/example/lib/interactive_features_example.dart b/example/lib/interactive_features_example.dart index 23a77bfe6..8c7f9bc2c 100644 --- a/example/lib/interactive_features_example.dart +++ b/example/lib/interactive_features_example.dart @@ -1,14 +1,17 @@ +import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart'; import 'example.dart'; class InteractiveFeaturesExample extends StatefulWidget implements Example { @override - final Widget leading = const Icon(Icons.map); + final Widget leading = const Icon(Icons.touch_app); @override final String title = 'Interactive Features'; @override - final String? subtitle = 'Tap a Buildings to highlight it or a POI to hide it'; + final String? subtitle = 'Tap a building to highlight it or a POI to hide it'; + + const InteractiveFeaturesExample({super.key}); @override State createState() => InteractiveFeaturesState(); @@ -25,7 +28,7 @@ class InteractiveFeaturesState extends State { /// Define interactions for 3D Buildings // Define a tap interaction targeting the Buildings featureset in the Standard style - var tapInteraction = TapInteraction(Featureset.standardBuildings()); + var tapInteraction = TapInteraction(Standard.buildings()); // Define a state to highlight the building when it is interacted with StandardBuildingState featureState = StandardBuildingState(highlight: true); @@ -36,20 +39,20 @@ class InteractiveFeaturesState extends State { var buildingFeature = StandardBuildingsFeature( feature.geometry, feature.properties, feature.state, id: feature.id); - print("Building feature id: ${buildingFeature.id}"); + log("Building feature id: ${buildingFeature.id?.id}"); }); - + // On long tap, remove the highlight state - mapboxMap.addInteraction(LongTapInteraction(Featureset.standardBuildings()), - (_, FeaturesetFeature feature) { + mapboxMap.addInteraction(LongTapInteraction(Standard.buildings()), + (_, FeaturesetFeature feature) { mapboxMap.removeFeatureStateForFeaturesetFeature(feature: feature); }); - /// Define interactions for Points of Interest - + /// Define interactions for Points of Interest + // Define a tap interaction targeting the POI featureset in the Standard style, including a click radius // Do not stop propagation of the click event to lower layers - var tapInteractionPOI = TapInteraction(Featureset.standardPoi(), + var tapInteractionPOI = TapInteraction(Standard.pois(), radius: 10, stopPropagation: false); // Define a state to hide the POI when it is interacted with diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift index cc05d4b59..284ff2caa 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift @@ -644,7 +644,8 @@ extension MapboxMaps.Geometry { return multiLineString.toMap() case .multiPolygon(let multiPolygon): return multiPolygon.toMap() - case .geometryCollection: + // includes GeometryCollection as it is not supported + default: return [:] } } diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift index f1a5b6562..691f73e9f 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift @@ -1123,28 +1123,21 @@ struct QueriedRenderedFeature { /// If the feature has been rendered in multiple layers, multiple Ids will be provided. /// If the feature is only rendered in one layer, a single Id will be provided. var layers: [String?] - /// An array of feature query targets that correspond to this queried feature. - /// - /// - Note: Returned query targets will omit the original `filter` data. - var queryTargets: [FeaturesetQueryTarget]? // swift-format-ignore: AlwaysUseLowerCamelCase static func fromList(_ pigeonVar_list: [Any?]) -> QueriedRenderedFeature? { let queriedFeature = pigeonVar_list[0] as! QueriedFeature let layers = pigeonVar_list[1] as! [String?] - let queryTargets: [FeaturesetQueryTarget]? = nilOrValue(pigeonVar_list[2]) return QueriedRenderedFeature( queriedFeature: queriedFeature, - layers: layers, - queryTargets: queryTargets + layers: layers ) } func toList() -> [Any?] { return [ queriedFeature, layers, - queryTargets, ] } } @@ -1372,38 +1365,6 @@ struct FeaturesetFeature { } } -/// Defines the parameters for querying features from a Featureset with an optional filter and id. -/// -/// Generated class from Pigeon that represents data sent in messages. -struct FeaturesetQueryTarget { - /// A `FeaturesetDescriptor` that specifies the featureset to be included in the query. - var featureset: FeaturesetDescriptor - /// An optional filter expression used to refine the query results based on conditions related to the specified featureset. - var filter: String? - /// An optional unique identifier associated with the target. - var id: Int64? - - // swift-format-ignore: AlwaysUseLowerCamelCase - static func fromList(_ pigeonVar_list: [Any?]) -> FeaturesetQueryTarget? { - let featureset = pigeonVar_list[0] as! FeaturesetDescriptor - let filter: String? = nilOrValue(pigeonVar_list[1]) - let id: Int64? = nilOrValue(pigeonVar_list[2]) - - return FeaturesetQueryTarget( - featureset: featureset, - filter: filter, - id: id - ) - } - func toList() -> [Any?] { - return [ - featureset, - filter, - id, - ] - } -} - /// Geometry for querying rendered features. /// /// Generated class from Pigeon that represents data sent in messages. @@ -2132,36 +2093,34 @@ private class MapInterfacesPigeonCodecReader: FlutterStandardReader { case 186: return FeaturesetFeature.fromList(self.readValue() as! [Any?]) case 187: - return FeaturesetQueryTarget.fromList(self.readValue() as! [Any?]) - case 188: return MapContentGestureContext.fromList(self.readValue() as! [Any?]) - case 189: + case 188: return _RenderedQueryGeometry.fromList(self.readValue() as! [Any?]) - case 190: + case 189: return ProjectedMeters.fromList(self.readValue() as! [Any?]) - case 191: + case 190: return MercatorCoordinate.fromList(self.readValue() as! [Any?]) - case 192: + case 191: return StyleObjectInfo.fromList(self.readValue() as! [Any?]) - case 193: + case 192: return StyleProjection.fromList(self.readValue() as! [Any?]) - case 194: + case 193: return FlatLight.fromList(self.readValue() as! [Any?]) - case 195: + case 194: return DirectionalLight.fromList(self.readValue() as! [Any?]) - case 196: + case 195: return AmbientLight.fromList(self.readValue() as! [Any?]) - case 197: + case 196: return MbxImage.fromList(self.readValue() as! [Any?]) - case 198: + case 197: return ImageStretches.fromList(self.readValue() as! [Any?]) - case 199: + case 198: return ImageContent.fromList(self.readValue() as! [Any?]) - case 200: + case 199: return TransitionOptions.fromList(self.readValue() as! [Any?]) - case 201: + case 200: return CanonicalTileID.fromList(self.readValue() as! [Any?]) - case 202: + case 201: return StylePropertyValue.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) @@ -2345,53 +2304,50 @@ private class MapInterfacesPigeonCodecWriter: FlutterStandardWriter { } else if let value = value as? FeaturesetFeature { super.writeByte(186) super.writeValue(value.toList()) - } else if let value = value as? FeaturesetQueryTarget { - super.writeByte(187) - super.writeValue(value.toList()) } else if let value = value as? MapContentGestureContext { - super.writeByte(188) + super.writeByte(187) super.writeValue(value.toList()) } else if let value = value as? _RenderedQueryGeometry { - super.writeByte(189) + super.writeByte(188) super.writeValue(value.toList()) } else if let value = value as? ProjectedMeters { - super.writeByte(190) + super.writeByte(189) super.writeValue(value.toList()) } else if let value = value as? MercatorCoordinate { - super.writeByte(191) + super.writeByte(190) super.writeValue(value.toList()) } else if let value = value as? StyleObjectInfo { - super.writeByte(192) + super.writeByte(191) super.writeValue(value.toList()) } else if let value = value as? StyleProjection { - super.writeByte(193) + super.writeByte(192) super.writeValue(value.toList()) } else if let value = value as? FlatLight { - super.writeByte(194) + super.writeByte(193) super.writeValue(value.toList()) } else if let value = value as? DirectionalLight { - super.writeByte(195) + super.writeByte(194) super.writeValue(value.toList()) } else if let value = value as? AmbientLight { - super.writeByte(196) + super.writeByte(195) super.writeValue(value.toList()) } else if let value = value as? MbxImage { - super.writeByte(197) + super.writeByte(196) super.writeValue(value.toList()) } else if let value = value as? ImageStretches { - super.writeByte(198) + super.writeByte(197) super.writeValue(value.toList()) } else if let value = value as? ImageContent { - super.writeByte(199) + super.writeByte(198) super.writeValue(value.toList()) } else if let value = value as? TransitionOptions { - super.writeByte(200) + super.writeByte(199) super.writeValue(value.toList()) } else if let value = value as? CanonicalTileID { - super.writeByte(201) + super.writeByte(200) super.writeValue(value.toList()) } else if let value = value as? StylePropertyValue { - super.writeByte(202) + super.writeByte(201) super.writeValue(value.toList()) } else { super.writeValue(value) diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift index 68ce25870..37e998abc 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift @@ -205,7 +205,11 @@ final class MapInterfaceController: _MapInterface { case .success(let features): completion(.success(features.map({$0.toFLTFeaturesetFeature()}))) case .failure(let error): - completion(.failure(FlutterError(code: MapInterfaceController.errorCode, message: "\(error)", details: nil))) + completion(.failure(FlutterError( + code: MapInterfaceController.errorCode, + message: "\(error)", + details: "Error querying rendered features for featureset: {featureId: \(String(describing: featureset.featuresetId)), importId: \(String(describing: featureset.importId)), layerId: \(String(describing: featureset.layerId))}." + ))) } } if let geometry { @@ -288,14 +292,19 @@ final class MapInterfaceController: _MapInterface { func setFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, state: [String: Any?], completion: @escaping (Result) -> Void) { guard let state = JSONObject.init(turfRawValue: state) else { + completion(.failure(FlutterError( + code: "setFeatureStateError", + message: "Error converting feature state.", + details: nil + ))) return } - self.mapboxMap.setFeatureState(featureset: featureset.toMapFeaturesetDescriptor(), featureId: featureId.toMapFeaturesetFeatureId(), state: state) { error in + _ = self.mapboxMap.setFeatureState(featureset: featureset.toMapFeaturesetDescriptor(), featureId: featureId.toMapFeaturesetFeatureId(), state: state) { error in if let error { completion(.failure(FlutterError( code: "setFeatureStateError", message: error.localizedDescription, - details: nil + details: "Error setting feature state for featureset: {featureId: \(String(describing: featureset.featuresetId)), importId: \(String(describing: featureset.importId)), layerId: \(String(describing: featureset.layerId))} and featureId: \(String(describing: featureId))." ))) } else { completion(.success(())) @@ -305,14 +314,19 @@ final class MapInterfaceController: _MapInterface { func setFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, state: [String: Any?], completion: @escaping (Result) -> Void) { guard let state = JSONObject.init(turfRawValue: state) else { + completion(.failure(FlutterError( + code: "setFeatureStateError", + message: "Error converting feature state.", + details: nil + ))) return } - self.mapboxMap.setFeatureState(feature.toMapFeaturesetFeature(), state: state) { error in + _ = self.mapboxMap.setFeatureState(feature.toMapFeaturesetFeature(), state: state) { error in if let error { completion(.failure(FlutterError( code: "setFeatureStateError", message: error.localizedDescription, - details: nil + details: "Error setting feature state for feature: \(String(describing: feature.id))." ))) } else { completion(.success(())) @@ -337,7 +351,11 @@ final class MapInterfaceController: _MapInterface { case .success(let state): completion(.success(state.mapValues { $0?.rawValue })) case .failure(let error): - completion(.failure(FlutterError(code: MapInterfaceController.errorCode, message: "\(error)", details: nil))) + completion(.failure(FlutterError( + code: "getFeatureStateError", + message: "\(error)", + details: "Error getting feature state for featureset: {featureId: \(String(describing: featureset.featuresetId)), importId: \(String(describing: featureset.importId)), layerId: \(String(describing: featureset.layerId))}." + ))) } } } @@ -348,7 +366,11 @@ final class MapInterfaceController: _MapInterface { case .success(let state): completion(.success(state.mapValues { $0?.rawValue })) case .failure(let error): - completion(.failure(FlutterError(code: MapInterfaceController.errorCode, message: "\(error)", details: nil))) + completion(.failure(FlutterError( + code: "getFeatureStateError", + message: "\(error)", + details: "Error getting feature state for feature: \(String(describing: feature.id))." + ))) } } } @@ -366,11 +388,11 @@ final class MapInterfaceController: _MapInterface { func removeFeatureStateForFeaturesetDescriptor(featureset: FeaturesetDescriptor, featureId: FeaturesetFeatureId, stateKey: String?, completion: @escaping (Result) -> Void) { self.mapboxMap.removeFeatureState(featureset: featureset.toMapFeaturesetDescriptor(), featureId: featureId.toMapFeaturesetFeatureId(), stateKey: stateKey) { error in - if let error { + if error != nil { completion(.failure(FlutterError( code: "removeFeatureStateError", - message: error.localizedDescription, - details: nil + message: "Cannot remove feature state.", + details: "The requested featureset: \(featureset) is not valid." ))) } else { completion(.success(())) @@ -380,11 +402,11 @@ final class MapInterfaceController: _MapInterface { func removeFeatureStateForFeaturesetFeature(feature: FeaturesetFeature, stateKey: String?, completion: @escaping (Result) -> Void) { self.mapboxMap.removeFeatureState(feature.toMapFeaturesetFeature(), stateKey: stateKey) { error in - if let error { + if error != nil { completion(.failure(FlutterError( code: "removeFeatureStateError", - message: error.localizedDescription, - details: nil + message: "Cannot remove feature state.", + details: "The requested feature: \(String(describing: feature.id)) is not valid" ))) } else { completion(.success(())) @@ -394,11 +416,11 @@ final class MapInterfaceController: _MapInterface { func resetFeatureStatesForFeatureset(featureset: FeaturesetDescriptor, completion: @escaping (Result) -> Void) { self.mapboxMap.resetFeatureStates(featureset: featureset.toMapFeaturesetDescriptor()) { error in - if let error { + if error != nil { completion(.failure(FlutterError( code: "resetFeatureStateError", - message: error.localizedDescription, - details: nil + message: "Cannot remove feature state.", + details: "The requested featureset: {featureId: \(String(describing: featureset.featuresetId)), importId: \(String(describing: featureset.importId)), layerId: \(String(describing: featureset.layerId))} is not valid." ))) } else { completion(.success(())) diff --git a/lib/src/pigeons/map_interfaces.dart b/lib/src/pigeons/map_interfaces.dart index f783bdc9e..db471b751 100644 --- a/lib/src/pigeons/map_interfaces.dart +++ b/lib/src/pigeons/map_interfaces.dart @@ -1204,7 +1204,6 @@ class QueriedRenderedFeature { QueriedRenderedFeature({ required this.queriedFeature, required this.layers, - this.queryTargets, }); /// Feature returned by the query. @@ -1215,16 +1214,10 @@ class QueriedRenderedFeature { /// If the feature is only rendered in one layer, a single Id will be provided. List layers; - /// An array of feature query targets that correspond to this queried feature. - /// - /// - Note: Returned query targets will omit the original `filter` data. - List? queryTargets; - Object encode() { return [ queriedFeature, layers, - queryTargets, ]; } @@ -1233,8 +1226,6 @@ class QueriedRenderedFeature { return QueriedRenderedFeature( queriedFeature: result[0]! as QueriedFeature, layers: (result[1] as List?)!.cast(), - queryTargets: - (result[2] as List?)?.cast(), ); } } @@ -1523,41 +1514,6 @@ class FeaturesetFeature { } } -/// Defines the parameters for querying features from a Featureset with an optional filter and id. -class FeaturesetQueryTarget { - FeaturesetQueryTarget({ - required this.featureset, - this.filter, - this.id, - }); - - /// A `FeaturesetDescriptor` that specifies the featureset to be included in the query. - FeaturesetDescriptor featureset; - - /// An optional filter expression used to refine the query results based on conditions related to the specified featureset. - String? filter; - - /// An optional unique identifier associated with the target. - int? id; - - Object encode() { - return [ - featureset, - filter, - id, - ]; - } - - static FeaturesetQueryTarget decode(Object result) { - result as List; - return FeaturesetQueryTarget( - featureset: result[0]! as FeaturesetDescriptor, - filter: result[1] as String?, - id: result[2] as int?, - ); - } -} - /// Geometry for querying rendered features. class _RenderedQueryGeometry { _RenderedQueryGeometry({ @@ -2289,53 +2245,50 @@ class MapInterfaces_PigeonCodec extends StandardMessageCodec { } else if (value is FeaturesetFeature) { buffer.putUint8(186); writeValue(buffer, value.encode()); - } else if (value is FeaturesetQueryTarget) { - buffer.putUint8(187); - writeValue(buffer, value.encode()); } else if (value is MapContentGestureContext) { - buffer.putUint8(188); + buffer.putUint8(187); writeValue(buffer, value.encode()); } else if (value is _RenderedQueryGeometry) { - buffer.putUint8(189); + buffer.putUint8(188); writeValue(buffer, value.encode()); } else if (value is ProjectedMeters) { - buffer.putUint8(190); + buffer.putUint8(189); writeValue(buffer, value.encode()); } else if (value is MercatorCoordinate) { - buffer.putUint8(191); + buffer.putUint8(190); writeValue(buffer, value.encode()); } else if (value is StyleObjectInfo) { - buffer.putUint8(192); + buffer.putUint8(191); writeValue(buffer, value.encode()); } else if (value is StyleProjection) { - buffer.putUint8(193); + buffer.putUint8(192); writeValue(buffer, value.encode()); } else if (value is FlatLight) { - buffer.putUint8(194); + buffer.putUint8(193); writeValue(buffer, value.encode()); } else if (value is DirectionalLight) { - buffer.putUint8(195); + buffer.putUint8(194); writeValue(buffer, value.encode()); } else if (value is AmbientLight) { - buffer.putUint8(196); + buffer.putUint8(195); writeValue(buffer, value.encode()); } else if (value is MbxImage) { - buffer.putUint8(197); + buffer.putUint8(196); writeValue(buffer, value.encode()); } else if (value is ImageStretches) { - buffer.putUint8(198); + buffer.putUint8(197); writeValue(buffer, value.encode()); } else if (value is ImageContent) { - buffer.putUint8(199); + buffer.putUint8(198); writeValue(buffer, value.encode()); } else if (value is TransitionOptions) { - buffer.putUint8(200); + buffer.putUint8(199); writeValue(buffer, value.encode()); } else if (value is CanonicalTileID) { - buffer.putUint8(201); + buffer.putUint8(200); writeValue(buffer, value.encode()); } else if (value is StylePropertyValue) { - buffer.putUint8(202); + buffer.putUint8(201); writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); @@ -2491,36 +2444,34 @@ class MapInterfaces_PigeonCodec extends StandardMessageCodec { case 186: return FeaturesetFeature.decode(readValue(buffer)!); case 187: - return FeaturesetQueryTarget.decode(readValue(buffer)!); - case 188: return MapContentGestureContext.decode(readValue(buffer)!); - case 189: + case 188: return _RenderedQueryGeometry.decode(readValue(buffer)!); - case 190: + case 189: return ProjectedMeters.decode(readValue(buffer)!); - case 191: + case 190: return MercatorCoordinate.decode(readValue(buffer)!); - case 192: + case 191: return StyleObjectInfo.decode(readValue(buffer)!); - case 193: + case 192: return StyleProjection.decode(readValue(buffer)!); - case 194: + case 193: return FlatLight.decode(readValue(buffer)!); - case 195: + case 194: return DirectionalLight.decode(readValue(buffer)!); - case 196: + case 195: return AmbientLight.decode(readValue(buffer)!); - case 197: + case 196: return MbxImage.decode(readValue(buffer)!); - case 198: + case 197: return ImageStretches.decode(readValue(buffer)!); - case 199: + case 198: return ImageContent.decode(readValue(buffer)!); - case 200: + case 199: return TransitionOptions.decode(readValue(buffer)!); - case 201: + case 200: return CanonicalTileID.decode(readValue(buffer)!); - case 202: + case 201: return StylePropertyValue.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); diff --git a/lib/src/style/interactive_features/interactive_features.dart b/lib/src/style/interactive_features/interactive_features.dart index a301c5cb2..eb521ccaa 100644 --- a/lib/src/style/interactive_features/interactive_features.dart +++ b/lib/src/style/interactive_features/interactive_features.dart @@ -24,21 +24,21 @@ class LongTapInteraction extends Interaction { stopPropagation: stopPropagation); } -extension Featureset on FeaturesetDescriptor { +extension Standard on FeaturesetDescriptor { // A Featureset of buildings in the Standard style - static FeaturesetDescriptor standardBuildings({String importId = "basemap"}) { + static FeaturesetDescriptor buildings({String importId = "basemap"}) { return FeaturesetDescriptor(featuresetId: "buildings", importId: importId); } // A Featureset of place labels in the Standard style - static FeaturesetDescriptor standardPlaceLabels( + static FeaturesetDescriptor placeLabels( {String importId = "basemap"}) { return FeaturesetDescriptor( featuresetId: "place-labels", importId: importId); } // A Featureset of POIs in the Standard style - static FeaturesetDescriptor standardPoi({String importId = "basemap"}) { + static FeaturesetDescriptor pois({String importId = "basemap"}) { return FeaturesetDescriptor(featuresetId: "poi", importId: importId); } } diff --git a/lib/src/style/interactive_features/standard_buildings.dart b/lib/src/style/interactive_features/standard_buildings.dart index 79cbd06b8..4fdcbe6ce 100644 --- a/lib/src/style/interactive_features/standard_buildings.dart +++ b/lib/src/style/interactive_features/standard_buildings.dart @@ -22,7 +22,7 @@ class StandardBuildingsFeature extends FeaturesetFeature { {FeaturesetFeatureId? id}) : super( id: id, - featureset: Featureset.standardBuildings(), + featureset: Standard.buildings(), geometry: geometry, properties: properties, state: state); diff --git a/lib/src/style/interactive_features/standard_place_labels.dart b/lib/src/style/interactive_features/standard_place_labels.dart index 7f0ddcdf8..ab99ca4de 100644 --- a/lib/src/style/interactive_features/standard_place_labels.dart +++ b/lib/src/style/interactive_features/standard_place_labels.dart @@ -29,7 +29,7 @@ class StandardPlaceLabelsFeature extends FeaturesetFeature { {FeaturesetFeatureId? id}) : super( id: id, - featureset: Featureset.standardPlaceLabels(), + featureset: Standard.placeLabels(), geometry: geometry, properties: properties, state: state); diff --git a/lib/src/style/interactive_features/standard_poi.dart b/lib/src/style/interactive_features/standard_poi.dart index f6aaab6bc..1d683e317 100644 --- a/lib/src/style/interactive_features/standard_poi.dart +++ b/lib/src/style/interactive_features/standard_poi.dart @@ -60,7 +60,7 @@ class StandardPoiFeature extends FeaturesetFeature { {FeaturesetFeatureId? id}) : super( id: id, - featureset: Featureset.standardPoi(), + featureset: Standard.pois(), geometry: geometry, properties: properties, state: state); From 51fff7f0f43cd8875aa216354790211f1d540b9d Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Wed, 15 Jan 2025 12:59:35 -0500 Subject: [PATCH 20/28] Update Android interface, some formatting --- .../com/mapbox/maps/mapbox_maps/MapInterfaceController.kt | 5 +++-- example/lib/interactive_features_example.dart | 4 ++-- lib/src/style/interactive_features/interactive_features.dart | 3 +-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt index 13ab712f9..b7e0b7beb 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt @@ -339,7 +339,7 @@ class MapInterfaceController( featureId: FeaturesetFeatureId, callback: (Result>) -> Unit ) { - featureset.toTypedFeaturesetDescriptor()?.let { featuresetDescriptor -> + featureset.toTypedFeaturesetDescriptor()?.also { featuresetDescriptor -> mapboxMap.getFeatureState( featuresetDescriptor, featureId.toFeaturesetFeatureId() @@ -350,8 +350,9 @@ class MapInterfaceController( ) ) } + } ?: run { + callback(Result.failure(Throwable("Cannot get feature state for the requested featureset: {featureId: ${featureset.featuresetId}, importId: ${featureset.importId}, layerId: ${featureset.layerId} and featureID: $featureId."))) } - callback(Result.failure(Throwable("Cannot get feature state for the requested featureset: {featureId: ${featureset.featuresetId}, importId: ${featureset.importId}, layerId: ${featureset.layerId} and featureID: $featureId."))) } @OptIn(MapboxExperimental::class, MapboxDelicateApi::class) diff --git a/example/lib/interactive_features_example.dart b/example/lib/interactive_features_example.dart index 8c7f9bc2c..0057a2246 100644 --- a/example/lib/interactive_features_example.dart +++ b/example/lib/interactive_features_example.dart @@ -52,8 +52,8 @@ class InteractiveFeaturesState extends State { // Define a tap interaction targeting the POI featureset in the Standard style, including a click radius // Do not stop propagation of the click event to lower layers - var tapInteractionPOI = TapInteraction(Standard.pois(), - radius: 10, stopPropagation: false); + var tapInteractionPOI = + TapInteraction(Standard.pois(), radius: 10, stopPropagation: false); // Define a state to hide the POI when it is interacted with mapboxMap.addInteraction(tapInteractionPOI, (_, FeaturesetFeature feature) { diff --git a/lib/src/style/interactive_features/interactive_features.dart b/lib/src/style/interactive_features/interactive_features.dart index eb521ccaa..70a285b7c 100644 --- a/lib/src/style/interactive_features/interactive_features.dart +++ b/lib/src/style/interactive_features/interactive_features.dart @@ -31,8 +31,7 @@ extension Standard on FeaturesetDescriptor { } // A Featureset of place labels in the Standard style - static FeaturesetDescriptor placeLabels( - {String importId = "basemap"}) { + static FeaturesetDescriptor placeLabels({String importId = "basemap"}) { return FeaturesetDescriptor( featuresetId: "place-labels", importId: importId); } From c92d177182e0ddc4f9c36ef3ec23990415336c37 Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Thu, 16 Jan 2025 16:50:42 -0500 Subject: [PATCH 21/28] Make Interactions internal, update implementation --- .../maps/mapbox_maps/MapboxMapController.kt | 4 +- .../maps/mapbox_maps/pigeons/MapInterfaces.kt | 6 +-- .../Classes/Generated/MapInterfaces.swift | 54 +++++++++++++++++-- .../Classes/MapboxMapController.swift | 2 +- lib/src/mapbox_map.dart | 23 ++++---- lib/src/pigeons/map_interfaces.dart | 14 ++--- 6 files changed, 74 insertions(+), 29 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt index 0cc0e259f..0944ae97f 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt @@ -24,13 +24,13 @@ 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 import com.mapbox.maps.mapbox_maps.pigeons.StyleManager import com.mapbox.maps.mapbox_maps.pigeons._AnimationManager import com.mapbox.maps.mapbox_maps.pigeons._CameraManager +import com.mapbox.maps.mapbox_maps.pigeons._InteractionsListener import com.mapbox.maps.mapbox_maps.pigeons._LocationComponentSettingsInterface import com.mapbox.maps.mapbox_maps.pigeons._MapInterface import com.mapbox.maps.mapbox_maps.pigeons._ViewportMessenger @@ -243,7 +243,7 @@ class MapboxMapController( result.success(null) } "interactions#add_interaction" -> { - val listener = InteractionsListener(messenger, channelSuffix) + val listener = _InteractionsListener(messenger, channelSuffix) val arguments: HashMap = call.arguments as? HashMap ?: return val featuresetDescriptorList = arguments["featuresetDescriptor"] as? List ?: return val featuresetDescriptor = com.mapbox.maps.mapbox_maps.pigeons.FeaturesetDescriptor.fromList(featuresetDescriptorList) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt index a9fbb6cfa..c3505abbc 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt @@ -3364,16 +3364,16 @@ interface _CameraManager { } } /** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */ -class InteractionsListener(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") { +class _InteractionsListener(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") { companion object { - /** The codec used by InteractionsListener. */ + /** The codec used by _InteractionsListener. */ val codec: MessageCodec by lazy { MapInterfacesPigeonCodec() } } fun onInteraction(contextArg: MapContentGestureContext, featureArg: FeaturesetFeature, interactionIDArg: Long, callback: (Result) -> Unit) { val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" - val channelName = "dev.flutter.pigeon.mapbox_maps_flutter.InteractionsListener.onInteraction$separatedMessageChannelSuffix" + val channelName = "dev.flutter.pigeon.mapbox_maps_flutter._InteractionsListener.onInteraction$separatedMessageChannelSuffix" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) channel.send(listOf(contextArg, featureArg, interactionIDArg)) { if (it is List<*>) { diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift index 691f73e9f..8c8fdb8dc 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift @@ -190,8 +190,11 @@ enum ViewAnnotationAnchor: Int { case cENTER = 8 } +/// The type of interaction, either tap/click or longTap/longClick enum InteractionType: Int { + /// A short tap or click case tAP = 0 + /// A long tap or long click case lONGTAP = 1 } @@ -1240,6 +1243,8 @@ struct FeaturesetFeatureId { } } +/// Wraps a FeatureState map +/// /// Generated class from Pigeon that represents data sent in messages. struct FeatureState { var map: [String: Any?] @@ -1267,10 +1272,15 @@ struct FeatureState { /// /// Generated class from Pigeon that represents data sent in messages. struct Interaction { + /// The featureset descriptor that specifies the featureset to be included in the interaction. var featuresetDescriptor: FeaturesetDescriptor + /// The type of interaction, either tap or longTap var interactionType: InteractionType + /// Whether to stop the propagation of the interaction to the map. Defaults to true. var stopPropagation: Bool + /// An optional filter of features that should trigger the interaction. var filter: String? + /// Radius of a tappable area var radius: Double? // swift-format-ignore: AlwaysUseLowerCamelCase @@ -1302,11 +1312,26 @@ struct Interaction { /// A featureset descriptor. /// +/// The descriptor instance acts as a universal target for interactions or querying rendered features (see 'TapInteraction', 'LongTapInteraction') +/// /// Generated class from Pigeon that represents data sent in messages. struct FeaturesetDescriptor { + /// An optional unique identifier for the featureset within the style. + /// This id is used to reference a specific featureset. + /// + /// * Note: If `featuresetId` is provided and valid, it takes precedence over `layerId`, + /// * meaning `layerId` will not be considered even if it has a valid value. var featuresetId: String? + /// An optional import id that is required if the featureset is defined within an imported style. + /// If the featureset belongs to the current style, this field should be set to a null string. /// + /// Note: `importId` is only applicable when used in conjunction with `featuresetId` + /// and has no effect when used with `layerId`. var importId: String? + /// An optional unique identifier for the layer within the current style. + /// + /// Note: If `featuresetId` is valid, `layerId` will be ignored even if it has a valid value. + /// Additionally, `importId` does not apply when using `layerId`. var layerId: String? // swift-format-ignore: AlwaysUseLowerCamelCase @@ -1330,12 +1355,29 @@ struct FeaturesetDescriptor { } } +/// A basic feature of a featureset. +/// +/// If you use Standard Style, you can use typed alternatives like `StandardPoiFeature`, `StandardPlaceLabelsFeature`, `StandardBuildingsFeature`. +/// +/// The featureset feature is different to the `Turf.Feature`. The latter represents any GeoJSON feature, while the former is a high level representation of features. +/// /// Generated class from Pigeon that represents data sent in messages. struct FeaturesetFeature { + /// An identifier of the feature. + /// + /// The identifier can be `nil` if the underlying source doesn't have identifiers for features. + /// In this case it's impossible to set a feature state for an individual feature. var id: FeaturesetFeatureId? + /// A featureset descriptor denoting the featureset this feature belongs to. var featureset: FeaturesetDescriptor + /// A feature geometry. var geometry: [String?: Any?] + /// Feature JSON properties. var properties: [String: Any?] + /// A feature state. + /// + /// This is a **snapshot** of the state that the feature had when it was interacted with. + /// To update and read the original state, use ``MapboxMap/setFeatureState()`` and ``MapboxMap/getFeatureState()``. var state: [String: Any?] // swift-format-ignore: AlwaysUseLowerCamelCase @@ -3075,10 +3117,10 @@ class _CameraManagerSetup { } } /// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. -protocol InteractionsListenerProtocol { +protocol _InteractionsListenerProtocol { func onInteraction(context contextArg: MapContentGestureContext, feature featureArg: FeaturesetFeature, interactionID interactionIDArg: Int64, completion: @escaping (Result) -> Void) } -class InteractionsListener: InteractionsListenerProtocol { +class _InteractionsListener: _InteractionsListenerProtocol { private let binaryMessenger: FlutterBinaryMessenger private let messageChannelSuffix: String init(binaryMessenger: FlutterBinaryMessenger, messageChannelSuffix: String = "") { @@ -3089,7 +3131,7 @@ class InteractionsListener: InteractionsListenerProtocol { return MapInterfacesPigeonCodec.shared } func onInteraction(context contextArg: MapContentGestureContext, feature featureArg: FeaturesetFeature, interactionID interactionIDArg: Int64, completion: @escaping (Result) -> Void) { - let channelName: String = "dev.flutter.pigeon.mapbox_maps_flutter.InteractionsListener.onInteraction\(messageChannelSuffix)" + let channelName: String = "dev.flutter.pigeon.mapbox_maps_flutter._InteractionsListener.onInteraction\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) channel.sendMessage([contextArg, featureArg, interactionIDArg] as [Any?]) { response in guard let listResponse = response as? [Any?] else { @@ -3188,8 +3230,9 @@ protocol _MapInterface { /// - Important: If you need to handle basic gestures on map content, /// please prefer to use Interactions API, see `MapboxMap/addInteraction`. /// - /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param featureset A typed featureset to query with. + /// @param geometry An optional screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. + /// If omitted, the full viewport is queried. /// @param filter An additional filter for features. func queryRenderedFeaturesForFeatureset(featureset: FeaturesetDescriptor, geometry: _RenderedQueryGeometry?, filter: String?, completion: @escaping (Result<[FeaturesetFeature], Error>) -> Void) /// Queries the map for source features. @@ -3716,8 +3759,9 @@ class _MapInterfaceSetup { /// - Important: If you need to handle basic gestures on map content, /// please prefer to use Interactions API, see `MapboxMap/addInteraction`. /// - /// @param geometry A screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. /// @param featureset A typed featureset to query with. + /// @param geometry An optional screen geometry to query. Can be a `CGPoint`, `CGRect`, or an array of `CGPoint`. + /// If omitted, the full viewport is queried. /// @param filter An additional filter for features. let queryRenderedFeaturesForFeaturesetChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.mapbox_maps_flutter._MapInterface.queryRenderedFeaturesForFeatureset\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapboxMapController.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapboxMapController.swift index f83d7c87e..306078c05 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapboxMapController.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapboxMapController.swift @@ -107,7 +107,7 @@ final class MapboxMapController: NSObject, FlutterPlatformView { gesturesController!.removeListeners() result(nil) case "interactions#add_interaction": - let listener = InteractionsListener(binaryMessenger: binaryMessenger.messenger, messageChannelSuffix: binaryMessenger.suffix) + let listener = _InteractionsListener(binaryMessenger: binaryMessenger.messenger, messageChannelSuffix: binaryMessenger.suffix) guard let arguments = methodCall.arguments as? [String: Any], let featuresetDescriptorList = arguments["featuresetDescriptor"] as? [String?], let featuresetDescriptor = FeaturesetDescriptor.fromList(featuresetDescriptorList), diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index e0802ba30..788b8090c 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -578,22 +578,23 @@ class MapboxMap extends ChangeNotifier { FeaturesetDescriptor featureset) => _mapInterface.resetFeatureStatesForFeatureset(featureset); - // References for all interactions added to the map. + /// References for all interactions added to the map. @experimental - var interactionsList = _InteractionsList(interactions: {}); + final _InteractionsList _interactionsList = + _InteractionsList(interactions: {}); /// Add an interaction @experimental void addInteraction(Interaction interaction, OnInteraction action) { - interactionsList.interactions[action.hashCode] = _InteractionListener( + var id = _interactionsList.interactions.length; + _interactionsList.interactions[id] = _InteractionListener( onInteractionListener: action, - interactionID: action.hashCode, + interactionID: id, ); - - InteractionsListener.setUp(interactionsList, + _InteractionsListener.setUp(_interactionsList, binaryMessenger: _mapboxMapsPlatform.binaryMessenger, messageChannelSuffix: _mapboxMapsPlatform.channelSuffix.toString()); - _mapboxMapsPlatform.addInteractionsListeners(interaction, action.hashCode); + _mapboxMapsPlatform.addInteractionsListeners(interaction, id); } /// Reduces memory use. Useful to call when the application gets paused or sent to background. @@ -767,8 +768,8 @@ class _GestureListener extends GestureListener { } } -// Listen for a single interaction added to the map, identified by its id -class _InteractionListener extends InteractionsListener { +/// Listen for a single interaction added to the map, identified by its id +class _InteractionListener extends _InteractionsListener { _InteractionListener({ required this.interactionID, required this.onInteractionListener, @@ -785,8 +786,8 @@ class _InteractionListener extends InteractionsListener { } } -// Listen to all interactions on the map, determine which interaction to call -class _InteractionsList extends InteractionsListener { +/// Listen to all interactions on the map, determine which interaction to call +class _InteractionsList extends _InteractionsListener { _InteractionsList({ required this.interactions, }); diff --git a/lib/src/pigeons/map_interfaces.dart b/lib/src/pigeons/map_interfaces.dart index db471b751..a22b4c439 100644 --- a/lib/src/pigeons/map_interfaces.dart +++ b/lib/src/pigeons/map_interfaces.dart @@ -3335,7 +3335,7 @@ class _CameraManager { } } -abstract class InteractionsListener { +abstract class _InteractionsListener { static const MessageCodec pigeonChannelCodec = MapInterfaces_PigeonCodec(); @@ -3343,7 +3343,7 @@ abstract class InteractionsListener { FeaturesetFeature feature, int interactionID); static void setUp( - InteractionsListener? api, { + _InteractionsListener? api, { BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', }) { @@ -3353,7 +3353,7 @@ abstract class InteractionsListener { final BasicMessageChannel< Object?> pigeonVar_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.mapbox_maps_flutter.InteractionsListener.onInteraction$messageChannelSuffix', + 'dev.flutter.pigeon.mapbox_maps_flutter._InteractionsListener.onInteraction$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -3361,19 +3361,19 @@ abstract class InteractionsListener { } else { pigeonVar_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.mapbox_maps_flutter.InteractionsListener.onInteraction was null.'); + 'Argument for dev.flutter.pigeon.mapbox_maps_flutter._InteractionsListener.onInteraction was null.'); final List args = (message as List?)!; final MapContentGestureContext? arg_context = (args[0] as MapContentGestureContext?); assert(arg_context != null, - 'Argument for dev.flutter.pigeon.mapbox_maps_flutter.InteractionsListener.onInteraction was null, expected non-null MapContentGestureContext.'); + 'Argument for dev.flutter.pigeon.mapbox_maps_flutter._InteractionsListener.onInteraction was null, expected non-null MapContentGestureContext.'); final FeaturesetFeature? arg_feature = (args[1] as FeaturesetFeature?); assert(arg_feature != null, - 'Argument for dev.flutter.pigeon.mapbox_maps_flutter.InteractionsListener.onInteraction was null, expected non-null FeaturesetFeature.'); + 'Argument for dev.flutter.pigeon.mapbox_maps_flutter._InteractionsListener.onInteraction was null, expected non-null FeaturesetFeature.'); final int? arg_interactionID = (args[2] as int?); assert(arg_interactionID != null, - 'Argument for dev.flutter.pigeon.mapbox_maps_flutter.InteractionsListener.onInteraction was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.mapbox_maps_flutter._InteractionsListener.onInteraction was null, expected non-null int.'); try { api.onInteraction(arg_context!, arg_feature!, arg_interactionID!); return wrapResponse(empty: true); From f7b47eca6af206758cd0663f1ac6c682675014fa Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Thu, 23 Jan 2025 10:26:50 -0500 Subject: [PATCH 22/28] Initial generics implementation --- example/lib/interactive_features_example.dart | 14 ++++----- lib/src/callbacks.dart | 4 +-- lib/src/mapbox_map.dart | 30 +++++++++++++++---- .../interactive_features.dart | 14 +++------ .../standard_buildings.dart | 13 ++++++-- .../standard_place_labels.dart | 12 ++++++-- .../interactive_features/standard_poi.dart | 12 ++++++-- 7 files changed, 65 insertions(+), 34 deletions(-) diff --git a/example/lib/interactive_features_example.dart b/example/lib/interactive_features_example.dart index 0057a2246..9ab698fd6 100644 --- a/example/lib/interactive_features_example.dart +++ b/example/lib/interactive_features_example.dart @@ -34,12 +34,10 @@ class InteractiveFeaturesState extends State { StandardBuildingState featureState = StandardBuildingState(highlight: true); // Add the tap interaction to the map, set the action to occur when a building is tapped (highlight it) - mapboxMap.addInteraction(tapInteraction, (_, FeaturesetFeature feature) { + mapboxMap.addInteraction(tapInteraction, + (_, feature) { mapboxMap.setFeatureStateForFeaturesetFeature(feature, featureState); - var buildingFeature = StandardBuildingsFeature( - feature.geometry, feature.properties, feature.state, - id: feature.id); - log("Building feature id: ${buildingFeature.id?.id}"); + log("Building group: ${feature.group}"); }); // On long tap, remove the highlight state @@ -56,9 +54,11 @@ class InteractiveFeaturesState extends State { TapInteraction(Standard.pois(), radius: 10, stopPropagation: false); // Define a state to hide the POI when it is interacted with - mapboxMap.addInteraction(tapInteractionPOI, (_, FeaturesetFeature feature) { + mapboxMap.addInteraction(tapInteractionPOI, + (_, StandardPoiFeature feature) { mapboxMap.setFeatureStateForFeaturesetFeature( feature, StandardPoiState(hide: true)); + log("POI feature name: ${feature.name}"); }); } @@ -72,7 +72,7 @@ class InteractiveFeaturesState extends State { bearing: 49.92, zoom: 16.35, pitch: 40), - styleUri: MapboxStyles.STANDARD, + styleUri: MapboxStyles.STANDARD_EXPERIMENTAL, textureView: true, onMapCreated: _onMapCreated, )); diff --git a/lib/src/callbacks.dart b/lib/src/callbacks.dart index 735eed688..32a217b26 100644 --- a/lib/src/callbacks.dart +++ b/lib/src/callbacks.dart @@ -74,5 +74,5 @@ typedef void OnTileRegionEstimateProgressListenter( TileRegionEstimateProgress progress); // Interaction callback called when a featureset or layer is interacted with. -typedef void OnInteraction( - MapContentGestureContext context, FeaturesetFeature feature); +typedef void OnInteraction( + MapContentGestureContext context, T feature); diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index 788b8090c..e712ab31d 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -585,9 +585,10 @@ class MapboxMap extends ChangeNotifier { /// Add an interaction @experimental - void addInteraction(Interaction interaction, OnInteraction action) { + void addInteraction( + Interaction interaction, OnInteraction action) { var id = _interactionsList.interactions.length; - _interactionsList.interactions[id] = _InteractionListener( + _interactionsList.interactions[id] = _InteractionListener( onInteractionListener: action, interactionID: id, ); @@ -769,7 +770,8 @@ class _GestureListener extends GestureListener { } /// Listen for a single interaction added to the map, identified by its id -class _InteractionListener extends _InteractionsListener { +class _InteractionListener + extends _InteractionsListener { _InteractionListener({ required this.interactionID, required this.onInteractionListener, @@ -777,17 +779,33 @@ class _InteractionListener extends _InteractionsListener { int interactionID; - final OnInteraction onInteractionListener; + final OnInteraction onInteractionListener; @override void onInteraction(MapContentGestureContext context, FeaturesetFeature feature, int interactionID) { - onInteractionListener.call(context, feature); + var featuresetID = feature.featureset.featuresetId; + T typedFeature; + + if (featuresetID == "buildings") { + typedFeature = + StandardBuildingsFeature.fromFeaturesetFeature(feature) as T; + } else if (featuresetID == "poi") { + typedFeature = StandardPoiFeature.fromFeaturesetFeature(feature) as T; + } else if (featuresetID == "place-labels") { + typedFeature = + StandardPlaceLabelsFeature.fromFeaturesetFeature(feature) as T; + } else { + typedFeature = feature as T; + } + + onInteractionListener.call(context, typedFeature); } } /// Listen to all interactions on the map, determine which interaction to call -class _InteractionsList extends _InteractionsListener { +class _InteractionsList + extends _InteractionsListener { _InteractionsList({ required this.interactions, }); diff --git a/lib/src/style/interactive_features/interactive_features.dart b/lib/src/style/interactive_features/interactive_features.dart index 70a285b7c..9815117ba 100644 --- a/lib/src/style/interactive_features/interactive_features.dart +++ b/lib/src/style/interactive_features/interactive_features.dart @@ -3,25 +3,19 @@ part of mapbox_maps_flutter; // A single tap interaction. class TapInteraction extends Interaction { TapInteraction(FeaturesetDescriptor featuresetDescriptor, - {String? filter, double? radius, bool stopPropagation = true}) + {super.filter, super.radius, super.stopPropagation = true}) : super( featuresetDescriptor: featuresetDescriptor, - interactionType: InteractionType.TAP, - filter: filter, - radius: radius, - stopPropagation: stopPropagation); + interactionType: InteractionType.TAP); } // A long tap interaction class LongTapInteraction extends Interaction { LongTapInteraction(FeaturesetDescriptor featuresetDescriptor, - {String? filter, double? radius, bool stopPropagation = true}) + {super.filter, super.radius, super.stopPropagation = true}) : super( featuresetDescriptor: featuresetDescriptor, - interactionType: InteractionType.LONG_TAP, - filter: filter, - radius: radius, - stopPropagation: stopPropagation); + interactionType: InteractionType.LONG_TAP); } extension Standard on FeaturesetDescriptor { diff --git a/lib/src/style/interactive_features/standard_buildings.dart b/lib/src/style/interactive_features/standard_buildings.dart index 4fdcbe6ce..14644eb44 100644 --- a/lib/src/style/interactive_features/standard_buildings.dart +++ b/lib/src/style/interactive_features/standard_buildings.dart @@ -18,14 +18,21 @@ class StandardBuildingsFeature extends FeaturesetFeature { } StandardBuildingsFeature(Map geometry, - Map properties, Map state, - {FeaturesetFeatureId? id}) + Map properties, Map state, {super.id}) : super( - id: id, featureset: Standard.buildings(), geometry: geometry, properties: properties, state: state); + + @override + StandardBuildingsFeature.fromFeaturesetFeature(FeaturesetFeature feature) + : super( + id: feature.id, + featureset: feature.featureset, + geometry: feature.geometry, + properties: feature.properties, + state: feature.state); } // Represents available states for Buildings in the Standard style. diff --git a/lib/src/style/interactive_features/standard_place_labels.dart b/lib/src/style/interactive_features/standard_place_labels.dart index ab99ca4de..307fa55be 100644 --- a/lib/src/style/interactive_features/standard_place_labels.dart +++ b/lib/src/style/interactive_features/standard_place_labels.dart @@ -25,14 +25,20 @@ class StandardPlaceLabelsFeature extends FeaturesetFeature { } StandardPlaceLabelsFeature(Map geometry, - Map properties, Map state, - {FeaturesetFeatureId? id}) + Map properties, Map state, {super.id}) : super( - id: id, featureset: Standard.placeLabels(), geometry: geometry, properties: properties, state: state); + + StandardPlaceLabelsFeature.fromFeaturesetFeature(FeaturesetFeature feature) + : super( + id: feature.id, + featureset: feature.featureset, + geometry: feature.geometry, + properties: feature.properties, + state: feature.state); } // Represents available states for Place Labels in the Standard style. diff --git a/lib/src/style/interactive_features/standard_poi.dart b/lib/src/style/interactive_features/standard_poi.dart index 1d683e317..b48eeeb46 100644 --- a/lib/src/style/interactive_features/standard_poi.dart +++ b/lib/src/style/interactive_features/standard_poi.dart @@ -56,14 +56,20 @@ class StandardPoiFeature extends FeaturesetFeature { } StandardPoiFeature(Map geometry, - Map properties, Map state, - {FeaturesetFeatureId? id}) + Map properties, Map state, {super.id}) : super( - id: id, featureset: Standard.pois(), geometry: geometry, properties: properties, state: state); + + StandardPoiFeature.fromFeaturesetFeature(FeaturesetFeature feature) + : super( + id: feature.id, + featureset: feature.featureset, + geometry: feature.geometry, + properties: feature.properties, + state: feature.state); } // Represents available states for POIs in the Standard style. From 67a475b3bad69bdc1fb04fdebcb92ff7f6822dab Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Thu, 23 Jan 2025 12:43:19 -0500 Subject: [PATCH 23/28] Update Podfile --- example/ios/Podfile.lock | 5 ----- 1 file changed, 5 deletions(-) diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 412f044f7..cb20f101c 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,11 +1,6 @@ PODS: - Flutter (1.0.0) - integration_test (0.0.1): -<<<<<<< HEAD -======= - - Flutter - - mapbox_maps_flutter (2.5.0): ->>>>>>> 085bf46 (Add map.addInteraction, add Standard featuresets) - Flutter - mapbox_maps_flutter (2.6.0-beta.1): - Flutter From 714411d4d2c0a10f05b420946c87f25d5c85ec15 Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Thu, 23 Jan 2025 20:44:05 -0500 Subject: [PATCH 24/28] Update to move _Interactions internal --- .../maps/mapbox_maps/MapboxMapController.kt | 12 ++-- .../maps/mapbox_maps/pigeons/MapInterfaces.kt | 22 +++---- example/lib/interactive_features_example.dart | 30 +++++----- .../Classes/Generated/MapInterfaces.swift | 20 +++---- .../Classes/MapboxMapController.swift | 2 +- lib/src/mapbox_map.dart | 6 +- lib/src/mapbox_maps_platform.dart | 2 +- lib/src/pigeons/map_interfaces.dart | 22 +++---- .../interactive_features.dart | 50 ++++++++-------- .../standard_buildings.dart | 28 +++++---- .../standard_place_labels.dart | 34 ++++++----- .../interactive_features/standard_poi.dart | 58 ++++++++++--------- 12 files changed, 153 insertions(+), 133 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt index 0944ae97f..e74a451ba 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt @@ -23,13 +23,13 @@ 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.LogoSettingsInterface import com.mapbox.maps.mapbox_maps.pigeons.Projection import com.mapbox.maps.mapbox_maps.pigeons.ScaleBarSettingsInterface import com.mapbox.maps.mapbox_maps.pigeons.StyleManager import com.mapbox.maps.mapbox_maps.pigeons._AnimationManager import com.mapbox.maps.mapbox_maps.pigeons._CameraManager +import com.mapbox.maps.mapbox_maps.pigeons._InteractionType import com.mapbox.maps.mapbox_maps.pigeons._InteractionsListener import com.mapbox.maps.mapbox_maps.pigeons._LocationComponentSettingsInterface import com.mapbox.maps.mapbox_maps.pigeons._MapInterface @@ -248,7 +248,7 @@ class MapboxMapController( val featuresetDescriptorList = arguments["featuresetDescriptor"] as? List ?: 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 interactionType = _InteractionType.ofRaw(interactionTypeRaw) val stopPropagation = arguments["stopPropagation"] as? Boolean ?: return val id = (arguments["id"] as? Int)?.toLong() ?: return val filter = (arguments["filter"] as? String).toValue() @@ -256,13 +256,13 @@ class MapboxMapController( featuresetDescriptor.featuresetId?.let { when (interactionType) { - InteractionType.TAP -> mapboxMap?.addInteraction( + _InteractionType.TAP -> mapboxMap?.addInteraction( ClickInteraction.featureset(id = it, importId = featuresetDescriptor.importId, filter = filter, radius = radius) { featuresetFeature, context -> listener.onInteraction(context.toFLTMapContentGestureContext(), featuresetFeature.toFLTFeaturesetFeature(), id) { _ -> } return@featureset stopPropagation } ) - InteractionType.LONG_TAP -> mapboxMap?.addInteraction( + _InteractionType.LONG_TAP -> mapboxMap?.addInteraction( LongClickInteraction.featureset(id = it, importId = featuresetDescriptor.importId, filter = filter, radius = radius) { featuresetFeature, context -> listener.onInteraction(context.toFLTMapContentGestureContext(), featuresetFeature.toFLTFeaturesetFeature(), id) { _ -> } return@featureset stopPropagation @@ -272,13 +272,13 @@ class MapboxMapController( } } ?: featuresetDescriptor.layerId?.let { when (interactionType) { - InteractionType.TAP -> mapboxMap?.addInteraction( + _InteractionType.TAP -> mapboxMap?.addInteraction( ClickInteraction.layer(id = it, filter = filter, radius = radius) { featuresetFeature, context -> listener.onInteraction(context.toFLTMapContentGestureContext(), featuresetFeature.toFLTFeaturesetFeature(), id) { _ -> } return@layer stopPropagation } ) - InteractionType.LONG_TAP -> mapboxMap?.addInteraction( + _InteractionType.LONG_TAP -> mapboxMap?.addInteraction( LongClickInteraction.layer(id = it, filter = filter, radius = radius) { featuresetFeature, context -> listener.onInteraction(context.toFLTMapContentGestureContext(), featuresetFeature.toFLTFeaturesetFeature(), id) { _ -> } return@layer stopPropagation diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt index c3505abbc..8d896115d 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt @@ -234,14 +234,14 @@ enum class ViewAnnotationAnchor(val raw: Int) { } /** The type of interaction, either tap/click or longTap/longClick */ -enum class InteractionType(val raw: Int) { +enum class _InteractionType(val raw: Int) { /** A short tap or click */ TAP(0), /** A long tap or long click */ LONG_TAP(1); companion object { - fun ofRaw(raw: Int): InteractionType? { + fun ofRaw(raw: Int): _InteractionType? { return values().firstOrNull { it.raw == raw } } } @@ -1426,11 +1426,11 @@ data class FeatureState( * * Generated class from Pigeon that represents data sent in messages. */ -data class Interaction( +data class _Interaction( /** The featureset descriptor that specifies the featureset to be included in the interaction. */ val featuresetDescriptor: FeaturesetDescriptor, /** The type of interaction, either tap or longTap */ - val interactionType: InteractionType, + val interactionType: _InteractionType, /** Whether to stop the propagation of the interaction to the map. Defaults to true. */ val stopPropagation: Boolean, /** An optional filter of features that should trigger the interaction. */ @@ -1439,13 +1439,13 @@ data class Interaction( val radius: Double? = null ) { companion object { - fun fromList(pigeonVar_list: List): Interaction { + fun fromList(pigeonVar_list: List): _Interaction { val featuresetDescriptor = pigeonVar_list[0] as FeaturesetDescriptor - val interactionType = pigeonVar_list[1] as InteractionType + val interactionType = pigeonVar_list[1] as _InteractionType val stopPropagation = pigeonVar_list[2] as Boolean val filter = pigeonVar_list[3] as String? val radius = pigeonVar_list[4] as Double? - return Interaction(featuresetDescriptor, interactionType, stopPropagation, filter, radius) + return _Interaction(featuresetDescriptor, interactionType, stopPropagation, filter, radius) } } fun toList(): List { @@ -2069,7 +2069,7 @@ private open class MapInterfacesPigeonCodec : StandardMessageCodec() { } 137.toByte() -> { return (readValue(buffer) as Long?)?.let { - InteractionType.ofRaw(it.toInt()) + _InteractionType.ofRaw(it.toInt()) } } 138.toByte() -> { @@ -2304,7 +2304,7 @@ private open class MapInterfacesPigeonCodec : StandardMessageCodec() { } 184.toByte() -> { return (readValue(buffer) as? List)?.let { - Interaction.fromList(it) + _Interaction.fromList(it) } } 185.toByte() -> { @@ -2429,7 +2429,7 @@ private open class MapInterfacesPigeonCodec : StandardMessageCodec() { stream.write(136) writeValue(stream, value.raw) } - is InteractionType -> { + is _InteractionType -> { stream.write(137) writeValue(stream, value.raw) } @@ -2617,7 +2617,7 @@ private open class MapInterfacesPigeonCodec : StandardMessageCodec() { stream.write(183) writeValue(stream, value.toList()) } - is Interaction -> { + is _Interaction -> { stream.write(184) writeValue(stream, value.toList()) } diff --git a/example/lib/interactive_features_example.dart b/example/lib/interactive_features_example.dart index 9ab698fd6..eb9e9b2bd 100644 --- a/example/lib/interactive_features_example.dart +++ b/example/lib/interactive_features_example.dart @@ -27,39 +27,39 @@ class InteractiveFeaturesState extends State { /// Define interactions for 3D Buildings - // Define a tap interaction targeting the Buildings featureset in the Standard style - var tapInteraction = TapInteraction(Standard.buildings()); - // Define a state to highlight the building when it is interacted with StandardBuildingState featureState = StandardBuildingState(highlight: true); - // Add the tap interaction to the map, set the action to occur when a building is tapped (highlight it) - mapboxMap.addInteraction(tapInteraction, + // Define a tap interaction targeting the Buildings featureset in the Standard style + // Set the action to occur when a building is tapped (highlight it) + var tapInteraction = TapInteraction(StandardBuildings(), (_, feature) { mapboxMap.setFeatureStateForFeaturesetFeature(feature, featureState); log("Building group: ${feature.group}"); }); + // Add the tap interaction to the map + mapboxMap.addInteraction(tapInteraction); + // On long tap, remove the highlight state - mapboxMap.addInteraction(LongTapInteraction(Standard.buildings()), - (_, FeaturesetFeature feature) { + mapboxMap.addInteraction( + LongTapInteraction(StandardBuildings(), (_, feature) { mapboxMap.removeFeatureStateForFeaturesetFeature(feature: feature); - }); + })); /// Define interactions for Points of Interest // Define a tap interaction targeting the POI featureset in the Standard style, including a click radius // Do not stop propagation of the click event to lower layers var tapInteractionPOI = - TapInteraction(Standard.pois(), radius: 10, stopPropagation: false); - - // Define a state to hide the POI when it is interacted with - mapboxMap.addInteraction(tapInteractionPOI, - (_, StandardPoiFeature feature) { + TapInteraction(StandardPOIs(), (_, StandardPOIsFeature feature) { mapboxMap.setFeatureStateForFeaturesetFeature( - feature, StandardPoiState(hide: true)); + feature, StandardPOIsState(hide: true)); log("POI feature name: ${feature.name}"); - }); + }, radius: 10, stopPropagation: false); + + // Define a state to hide the POI when it is interacted with + mapboxMap.addInteraction(tapInteractionPOI); } @override diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift index 8c8fdb8dc..a5ca489b1 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift @@ -191,7 +191,7 @@ enum ViewAnnotationAnchor: Int { } /// The type of interaction, either tap/click or longTap/longClick -enum InteractionType: Int { +enum _InteractionType: Int { /// A short tap or click case tAP = 0 /// A long tap or long click @@ -1271,11 +1271,11 @@ struct FeatureState { /// See also: ``MapboxMap/addInteraction``. /// /// Generated class from Pigeon that represents data sent in messages. -struct Interaction { +struct _Interaction { /// The featureset descriptor that specifies the featureset to be included in the interaction. var featuresetDescriptor: FeaturesetDescriptor /// The type of interaction, either tap or longTap - var interactionType: InteractionType + var interactionType: _InteractionType /// Whether to stop the propagation of the interaction to the map. Defaults to true. var stopPropagation: Bool /// An optional filter of features that should trigger the interaction. @@ -1284,14 +1284,14 @@ struct Interaction { var radius: Double? // swift-format-ignore: AlwaysUseLowerCamelCase - static func fromList(_ pigeonVar_list: [Any?]) -> Interaction? { + static func fromList(_ pigeonVar_list: [Any?]) -> _Interaction? { let featuresetDescriptor = pigeonVar_list[0] as! FeaturesetDescriptor - let interactionType = pigeonVar_list[1] as! InteractionType + let interactionType = pigeonVar_list[1] as! _InteractionType let stopPropagation = pigeonVar_list[2] as! Bool let filter: String? = nilOrValue(pigeonVar_list[3]) let radius: Double? = nilOrValue(pigeonVar_list[4]) - return Interaction( + return _Interaction( featuresetDescriptor: featuresetDescriptor, interactionType: interactionType, stopPropagation: stopPropagation, @@ -1961,7 +1961,7 @@ private class MapInterfacesPigeonCodecReader: FlutterStandardReader { case 137: let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) if let enumResultAsInt = enumResultAsInt { - return InteractionType(rawValue: enumResultAsInt) + return _InteractionType(rawValue: enumResultAsInt) } return nil case 138: @@ -2129,7 +2129,7 @@ private class MapInterfacesPigeonCodecReader: FlutterStandardReader { case 183: return FeatureState.fromList(self.readValue() as! [Any?]) case 184: - return Interaction.fromList(self.readValue() as! [Any?]) + return _Interaction.fromList(self.readValue() as! [Any?]) case 185: return FeaturesetDescriptor.fromList(self.readValue() as! [Any?]) case 186: @@ -2196,7 +2196,7 @@ private class MapInterfacesPigeonCodecWriter: FlutterStandardWriter { } else if let value = value as? ViewAnnotationAnchor { super.writeByte(136) super.writeValue(value.rawValue) - } else if let value = value as? InteractionType { + } else if let value = value as? _InteractionType { super.writeByte(137) super.writeValue(value.rawValue) } else if let value = value as? GestureState { @@ -2337,7 +2337,7 @@ private class MapInterfacesPigeonCodecWriter: FlutterStandardWriter { } else if let value = value as? FeatureState { super.writeByte(183) super.writeValue(value.toList()) - } else if let value = value as? Interaction { + } else if let value = value as? _Interaction { super.writeByte(184) super.writeValue(value.toList()) } else if let value = value as? FeaturesetDescriptor { diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapboxMapController.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapboxMapController.swift index 306078c05..a3873de6a 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapboxMapController.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapboxMapController.swift @@ -112,7 +112,7 @@ final class MapboxMapController: NSObject, FlutterPlatformView { let featuresetDescriptorList = arguments["featuresetDescriptor"] as? [String?], let featuresetDescriptor = FeaturesetDescriptor.fromList(featuresetDescriptorList), let interactionTypeRaw = arguments["interactionType"] as? Int, - let interactionType = InteractionType(rawValue: interactionTypeRaw), + let interactionType = _InteractionType(rawValue: interactionTypeRaw), let stopPropagation = arguments["stopPropagation"] as? Bool, let id = arguments["id"] as? Int else { return diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index e712ab31d..8624371af 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -586,10 +586,10 @@ class MapboxMap extends ChangeNotifier { /// Add an interaction @experimental void addInteraction( - Interaction interaction, OnInteraction action) { + TypedInteraction interaction) { var id = _interactionsList.interactions.length; _interactionsList.interactions[id] = _InteractionListener( - onInteractionListener: action, + onInteractionListener: interaction.action, interactionID: id, ); _InteractionsListener.setUp(_interactionsList, @@ -791,7 +791,7 @@ class _InteractionListener typedFeature = StandardBuildingsFeature.fromFeaturesetFeature(feature) as T; } else if (featuresetID == "poi") { - typedFeature = StandardPoiFeature.fromFeaturesetFeature(feature) as T; + typedFeature = StandardPOIsFeature.fromFeaturesetFeature(feature) as T; } else if (featuresetID == "place-labels") { typedFeature = StandardPlaceLabelsFeature.fromFeaturesetFeature(feature) as T; diff --git a/lib/src/mapbox_maps_platform.dart b/lib/src/mapbox_maps_platform.dart index 6108fa2e6..b09359aa2 100644 --- a/lib/src/mapbox_maps_platform.dart +++ b/lib/src/mapbox_maps_platform.dart @@ -162,7 +162,7 @@ class _MapboxMapsPlatform { } Future addInteractionsListeners( - Interaction interaction, int interactionID) async { + _Interaction interaction, int interactionID) async { try { return _channel .invokeMethod('interactions#add_interaction', { diff --git a/lib/src/pigeons/map_interfaces.dart b/lib/src/pigeons/map_interfaces.dart index a22b4c439..468124432 100644 --- a/lib/src/pigeons/map_interfaces.dart +++ b/lib/src/pigeons/map_interfaces.dart @@ -169,7 +169,7 @@ enum ViewAnnotationAnchor { } /// The type of interaction, either tap/click or longTap/longClick -enum InteractionType { +enum _InteractionType { /// A short tap or click TAP, @@ -1362,8 +1362,8 @@ class FeatureState { /// To create an interaction use ``TapInteraction`` and ``LongClickInteraction`` implementations. /// /// See also: ``MapboxMap/addInteraction``. -class Interaction { - Interaction({ +class _Interaction { + _Interaction({ required this.featuresetDescriptor, required this.interactionType, required this.stopPropagation, @@ -1375,7 +1375,7 @@ class Interaction { FeaturesetDescriptor featuresetDescriptor; /// The type of interaction, either tap or longTap - InteractionType interactionType; + _InteractionType interactionType; /// Whether to stop the propagation of the interaction to the map. Defaults to true. bool stopPropagation; @@ -1396,11 +1396,11 @@ class Interaction { ]; } - static Interaction decode(Object result) { + static _Interaction decode(Object result) { result as List; - return Interaction( + return _Interaction( featuresetDescriptor: result[0]! as FeaturesetDescriptor, - interactionType: result[1]! as InteractionType, + interactionType: result[1]! as _InteractionType, stopPropagation: result[2]! as bool, filter: result[3] as String?, radius: result[4] as double?, @@ -2095,7 +2095,7 @@ class MapInterfaces_PigeonCodec extends StandardMessageCodec { } else if (value is ViewAnnotationAnchor) { buffer.putUint8(136); writeValue(buffer, value.index); - } else if (value is InteractionType) { + } else if (value is _InteractionType) { buffer.putUint8(137); writeValue(buffer, value.index); } else if (value is GestureState) { @@ -2236,7 +2236,7 @@ class MapInterfaces_PigeonCodec extends StandardMessageCodec { } else if (value is FeatureState) { buffer.putUint8(183); writeValue(buffer, value.encode()); - } else if (value is Interaction) { + } else if (value is _Interaction) { buffer.putUint8(184); writeValue(buffer, value.encode()); } else if (value is FeaturesetDescriptor) { @@ -2324,7 +2324,7 @@ class MapInterfaces_PigeonCodec extends StandardMessageCodec { return value == null ? null : ViewAnnotationAnchor.values[value]; case 137: final int? value = readValue(buffer) as int?; - return value == null ? null : InteractionType.values[value]; + return value == null ? null : _InteractionType.values[value]; case 138: final int? value = readValue(buffer) as int?; return value == null ? null : GestureState.values[value]; @@ -2438,7 +2438,7 @@ class MapInterfaces_PigeonCodec extends StandardMessageCodec { case 183: return FeatureState.decode(readValue(buffer)!); case 184: - return Interaction.decode(readValue(buffer)!); + return _Interaction.decode(readValue(buffer)!); case 185: return FeaturesetDescriptor.decode(readValue(buffer)!); case 186: diff --git a/lib/src/style/interactive_features/interactive_features.dart b/lib/src/style/interactive_features/interactive_features.dart index 9815117ba..4edd475e7 100644 --- a/lib/src/style/interactive_features/interactive_features.dart +++ b/lib/src/style/interactive_features/interactive_features.dart @@ -1,37 +1,39 @@ -part of mapbox_maps_flutter; +part of '../../../mapbox_maps_flutter.dart'; -// A single tap interaction. -class TapInteraction extends Interaction { - TapInteraction(FeaturesetDescriptor featuresetDescriptor, +/// A single tap interaction. +final class TapInteraction + extends TypedInteraction { + TapInteraction( + FeaturesetDescriptor featuresetDescriptor, OnInteraction action, {super.filter, super.radius, super.stopPropagation = true}) : super( featuresetDescriptor: featuresetDescriptor, - interactionType: InteractionType.TAP); + interactionType: "TAP", + action: action); } -// A long tap interaction -class LongTapInteraction extends Interaction { - LongTapInteraction(FeaturesetDescriptor featuresetDescriptor, +/// A long tap interaction +final class LongTapInteraction + extends TypedInteraction { + LongTapInteraction( + FeaturesetDescriptor featuresetDescriptor, OnInteraction action, {super.filter, super.radius, super.stopPropagation = true}) : super( featuresetDescriptor: featuresetDescriptor, - interactionType: InteractionType.LONG_TAP); + interactionType: "LONG_TAP", + action: action); } -extension Standard on FeaturesetDescriptor { - // A Featureset of buildings in the Standard style - static FeaturesetDescriptor buildings({String importId = "basemap"}) { - return FeaturesetDescriptor(featuresetId: "buildings", importId: importId); - } +/// A typed interaction with an action +final class TypedInteraction extends _Interaction { + TypedInteraction( + {required super.featuresetDescriptor, + super.filter, + super.radius, + super.stopPropagation = true, + required this.action, + required interactionType}) + : super(interactionType: _InteractionType.values.byName(interactionType)); - // A Featureset of place labels in the Standard style - static FeaturesetDescriptor placeLabels({String importId = "basemap"}) { - return FeaturesetDescriptor( - featuresetId: "place-labels", importId: importId); - } - - // A Featureset of POIs in the Standard style - static FeaturesetDescriptor pois({String importId = "basemap"}) { - return FeaturesetDescriptor(featuresetId: "poi", importId: importId); - } + OnInteraction action; } diff --git a/lib/src/style/interactive_features/standard_buildings.dart b/lib/src/style/interactive_features/standard_buildings.dart index 14644eb44..ccffe7f84 100644 --- a/lib/src/style/interactive_features/standard_buildings.dart +++ b/lib/src/style/interactive_features/standard_buildings.dart @@ -1,18 +1,18 @@ -part of mapbox_maps_flutter; +part of '../../../mapbox_maps_flutter.dart'; -// A Feature that represents a building in the Standard style. +/// A Feature that represents a building in the Standard style. class StandardBuildingsFeature extends FeaturesetFeature { - // A feature state. - // - // This is a **snapshot** of the state that the feature had when it was interacted with. - // To update and read the original state, use ``MapboxMap/setFeatureState()`` and ``MapboxMap/getFeatureState()``. + /// A feature state. + /// + /// This is a **snapshot** of the state that the feature had when it was interacted with. + /// To update and read the original state, use ``MapboxMap/setFeatureState()`` and ``MapboxMap/getFeatureState()``. StandardBuildingState get stateSnapshot { return StandardBuildingState() ..highlight = state["highlight"] as bool? ..select = state["select"] as bool?; } - // A high-level building group like building-2d, building-3d, etc. + /// A high-level building group like building-2d, building-3d, etc. String? get group { return properties["group"] as String?; } @@ -20,7 +20,7 @@ class StandardBuildingsFeature extends FeaturesetFeature { StandardBuildingsFeature(Map geometry, Map properties, Map state, {super.id}) : super( - featureset: Standard.buildings(), + featureset: StandardBuildings(), geometry: geometry, properties: properties, state: state); @@ -35,12 +35,18 @@ class StandardBuildingsFeature extends FeaturesetFeature { state: feature.state); } -// Represents available states for Buildings in the Standard style. +/// A Featureset of buildings in the Standard style +class StandardBuildings extends FeaturesetDescriptor { + StandardBuildings({String importId = "basemap"}) + : super(featuresetId: "buildings", importId: importId); +} + +/// Represents available states for Buildings in the Standard style. class StandardBuildingState extends FeatureState { - // When `true`, the feature is highlighted. Use this state to create a temporary effect (e.g. hover). + /// When `true`, the feature is highlighted. Use this state to create a temporary effect (e.g. hover). bool? highlight; - // When `true`, the feature is selected. Use this state to create a permanent effect. Note: the `select` state has a higher priority than `highlight`. + /// When `true`, the feature is selected. Use this state to create a permanent effect. Note: the `select` state has a higher priority than `highlight`. bool? select; @override diff --git a/lib/src/style/interactive_features/standard_place_labels.dart b/lib/src/style/interactive_features/standard_place_labels.dart index 307fa55be..0c8806b41 100644 --- a/lib/src/style/interactive_features/standard_place_labels.dart +++ b/lib/src/style/interactive_features/standard_place_labels.dart @@ -1,12 +1,12 @@ -part of mapbox_maps_flutter; +part of '../../../mapbox_maps_flutter.dart'; -// A feature that labels places including countries, states, cities, -// towns, and neighborhoods in the Standard style. +/// A feature that labels places including countries, states, cities, +/// towns, and neighborhoods in the Standard style. class StandardPlaceLabelsFeature extends FeaturesetFeature { - // A feature state. - // - // This is a **snapshot** of the state that the feature had when it was interacted with. - // To update and read the original state, use ``MapboxMap/setFeatureState()`` and ``MapboxMap/getFeatureState()``. + /// A feature state. + /// + /// This is a **snapshot** of the state that the feature had when it was interacted with. + /// To update and read the original state, use ``MapboxMap/setFeatureState()`` and ``MapboxMap/getFeatureState()``. StandardPlaceLabelsState get stateSnapshot { return StandardPlaceLabelsState() ..hide = state["hide"] as bool? @@ -14,12 +14,12 @@ class StandardPlaceLabelsFeature extends FeaturesetFeature { ..select = state["select"] as bool?; } - // Name of the place label. + /// Name of the place label. String? get name { return properties["name"] as String?; } - // Provides a broad distinction between place types. + /// Provides a broad distinction between place types. String? get category { return properties["class"] as String?; } @@ -27,7 +27,7 @@ class StandardPlaceLabelsFeature extends FeaturesetFeature { StandardPlaceLabelsFeature(Map geometry, Map properties, Map state, {super.id}) : super( - featureset: Standard.placeLabels(), + featureset: StandardPlaceLabels(), geometry: geometry, properties: properties, state: state); @@ -41,15 +41,21 @@ class StandardPlaceLabelsFeature extends FeaturesetFeature { state: feature.state); } -// Represents available states for Place Labels in the Standard style. +/// A Featureset of place labels in the Standard style +class StandardPlaceLabels extends FeaturesetDescriptor { + StandardPlaceLabels({String importId = "basemap"}) + : super(featuresetId: "place-labels", importId: importId); +} + +/// Represents available states for Place Labels in the Standard style. class StandardPlaceLabelsState extends FeatureState { - // When `true`, hides the label. Use this state when displaying a custom annotation on top. + /// When `true`, hides the label. Use this state when displaying a custom annotation on top. bool? hide; - // When `true`, the feature is highlighted. Use this state to create a temporary effect (e.g. hover). + /// When `true`, the feature is highlighted. Use this state to create a temporary effect (e.g. hover). bool? highlight; - // When `true`, the feature is selected. Use this state to create a permanent effect. Note: the `select` state has a higher priority than `highlight`. + /// When `true`, the feature is selected. Use this state to create a permanent effect. Note: the `select` state has a higher priority than `highlight`. bool? select; @override diff --git a/lib/src/style/interactive_features/standard_poi.dart b/lib/src/style/interactive_features/standard_poi.dart index b48eeeb46..39c09f3ba 100644 --- a/lib/src/style/interactive_features/standard_poi.dart +++ b/lib/src/style/interactive_features/standard_poi.dart @@ -1,69 +1,69 @@ -part of mapbox_maps_flutter; - -// A feature that is a point of interest in the Standard style. -class StandardPoiFeature extends FeaturesetFeature { - // A feature state. - // - // This is a **snapshot** of the state that the feature had when it was interacted with. - // To update and read the original state, use ``MapboxMap/setFeatureState()`` and ``MapboxMap/getFeatureState()``. - StandardPoiState get stateSnapshot { - return StandardPoiState()..hide = state["hide"] as bool?; +part of '../../../mapbox_maps_flutter.dart'; + +/// A feature that is a point of interest in the Standard style. +class StandardPOIsFeature extends FeaturesetFeature { + /// A feature state. + /// + /// This is a **snapshot** of the state that the feature had when it was interacted with. + /// To update and read the original state, use ``MapboxMap/setFeatureState()`` and ``MapboxMap/getFeatureState()``. + StandardPOIsState get stateSnapshot { + return StandardPOIsState()..hide = state["hide"] as bool?; } - // Name of the point of interest. + /// Name of the point of interest. String? get name { return properties["name"] as String?; } - // A high-level point of interest category like airport, transit, etc. + /// A high-level point of interest category like airport, transit, etc. String? get group { return properties["group"] as String?; } - // A broad category of point of interest. + /// A broad category of point of interest. String? get category { return properties["class"] as String?; } - // An icon identifier, designed to assign icons using the Maki icon project or other icons that follow the same naming scheme. + /// An icon identifier, designed to assign icons using the Maki icon project or other icons that follow the same naming scheme. String? get maki { return properties["maki"] as String?; } - // Mode of transport served by a stop/station. Expected to be null for non-transit points of interest. + /// Mode of transport served by a stop/station. Expected to be null for non-transit points of interest. String? get transitMode { return properties["transit_mode"] as String?; } - // A type of transit stop. Expected to be null for non-transit points of interest. + /// A type of transit stop. Expected to be null for non-transit points of interest. String? get transitStopType { return properties["transit_stop_type"] as String?; } - // A rail station network identifier that is part of specific local or regional transit systems. Expected to be null for non-transit points of interest. + /// A rail station network identifier that is part of specific local or regional transit systems. Expected to be null for non-transit points of interest. String? get transitNetwork { return properties["transit_network"] as String?; } - // A short identifier code of the airport. Expected to be null for non-airport points of interest + /// A short identifier code of the airport. Expected to be null for non-airport points of interest String? get airportRef { return properties["airport_ref"] as String?; } - // POI coordinate. + /// POI coordinate. Point? get coordinate { return this.geometry["coordinates"] as Point?; } - StandardPoiFeature(Map geometry, + StandardPOIsFeature(Map geometry, Map properties, Map state, {super.id}) : super( - featureset: Standard.pois(), + featureset: StandardPOIs(), geometry: geometry, properties: properties, state: state); - StandardPoiFeature.fromFeaturesetFeature(FeaturesetFeature feature) + StandardPOIsFeature.fromFeaturesetFeature(FeaturesetFeature feature) : super( id: feature.id, featureset: feature.featureset, @@ -72,9 +72,15 @@ class StandardPoiFeature extends FeaturesetFeature { state: feature.state); } -// Represents available states for POIs in the Standard style. -class StandardPoiState extends FeatureState { - // When `true`, hides the icon and text. +/// A Featureset of POIs in the Standard style +class StandardPOIs extends FeaturesetDescriptor { + StandardPOIs({String importId = "basemap"}) + : super(featuresetId: "poi", importId: importId); +} + +/// Represents available states for POIs in the Standard style. +class StandardPOIsState extends FeatureState { + /// When `true`, hides the icon and text. bool? hide; @override @@ -82,7 +88,7 @@ class StandardPoiState extends FeatureState { return {"hide": hide}; } - StandardPoiState({this.hide}) + StandardPOIsState({this.hide}) : super(map: { "hide": hide, }); From 47b3fa829efd98033df40d968493459d85cfcc4a Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Sat, 25 Jan 2025 16:32:32 -0500 Subject: [PATCH 25/28] Update error handling and interfaces --- CHANGELOG.md | 13 +- .../com/mapbox/maps/mapbox_maps/Extentions.kt | 2 +- .../mapbox_maps/MapInterfaceController.kt | 135 +++++++++--------- example/lib/interactive_features_example.dart | 6 +- .../Classes/MapInterfaceController.swift | 16 +-- 5 files changed, 86 insertions(+), 86 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a07373aa..68a40e987 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,12 @@ * 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)); - } -); +var tapInteraction = TapInteraction(StandardBuildings(), + (_, feature) { + mapboxMap.setFeatureStateForFeaturesetFeature(feature, StandardBuildingState(highlight: true)); + log("Building group: ${feature.group}"); +}); +mapboxMap.addInteraction(tapInteraction); ``` Specific changes: diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt index 34287c5af..ddcebbb02 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/Extentions.kt @@ -272,7 +272,7 @@ fun FeaturesetFeatureId.toFeaturesetFeatureId(): com.mapbox.maps.FeaturesetFeatu } @OptIn(MapboxExperimental::class) -fun FeaturesetDescriptor.toTypedFeaturesetDescriptor(): TypedFeaturesetDescriptor<*, *>? { +fun FeaturesetDescriptor.toTypedFeaturesetDescriptor(): TypedFeaturesetDescriptor>? { featuresetId?.let { return TypedFeaturesetDescriptor.Featureset( featuresetId, importId diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt index b7e0b7beb..90c73253e 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt @@ -183,18 +183,18 @@ class MapInterfaceController( filter: String?, callback: (Result>) -> Unit ) { - (featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor<*, com.mapbox.maps.interactions.FeaturesetFeature>).let { typedFeaturesetDescriptor -> + featureset.toTypedFeaturesetDescriptor()?.let { typedFeaturesetDescriptor -> mapboxMap.queryRenderedFeatures( typedFeaturesetDescriptor, geometry?.toRenderedQueryGeometry(context), filter?.let { Expression.fromRaw(filter) } ) { - callback( - Result.success( - it.map { feature -> feature.toFLTFeaturesetFeature() }.toMutableList() - ) - ) + callback(Result.success(it.map { feature -> feature.toFLTFeaturesetFeature() }.toMutableList())) } + } ?: { + callback(Result.failure( + Throwable("Error querying rendered features for featureset: {featureId: ${featureset.featuresetId}, importId: ${featureset.importId}, layerId: ${featureset.layerId}}") + )) } } @@ -292,11 +292,15 @@ class MapInterfaceController( state: Map, callback: (Result) -> Unit ) { - mapboxMap.setFeatureState( - featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor>, - featureId.toFeaturesetFeatureId(), - state.toFeatureState() - ) { callback(Result.success(Unit)) } + featureset.toTypedFeaturesetDescriptor()?.let { typedFeaturesetDescriptor -> + mapboxMap.setFeatureState( + typedFeaturesetDescriptor, + featureId.toFeaturesetFeatureId(), + state.toFeatureState() + ) { callback(Result.success(Unit)) } + } ?: { + callback(Result.failure(Throwable("Error setting feature state for feature $featureId from featureset {featuresetId: ${featureset.featuresetId}, importId: ${featureset.importId}, layerId: ${featureset.layerId}."))) + } } @OptIn(MapboxExperimental::class, MapboxDelicateApi::class) @@ -305,14 +309,15 @@ class MapInterfaceController( state: Map, callback: (Result) -> Unit ) { - if (feature.id?.toFeaturesetFeatureId() != null) { - mapboxMap.setFeatureState( - feature.featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor>, - id = feature.id.toFeaturesetFeatureId(), - state = state.toFeatureState() - ) { callback(Result.success(Unit)) } - } else { - callback(Result.failure(Throwable("Invalid feature for the requested feature: ${feature.id}"))) + feature.id?.let { + setFeatureStateForFeaturesetDescriptor( + feature.featureset, + it, + state, + callback + ) + } ?: { + callback(Result.failure(Throwable("Invalid feature id for the requested feature: $feature"))) } } @@ -344,14 +349,10 @@ class MapInterfaceController( featuresetDescriptor, featureId.toFeaturesetFeatureId() ) { - callback( - Result.success( - JSONObject(it.asJsonString()).toFilteredMap() - ) - ) + callback(Result.success(JSONObject(it.asJsonString()).toFilteredMap())) } - } ?: run { - callback(Result.failure(Throwable("Cannot get feature state for the requested featureset: {featureId: ${featureset.featuresetId}, importId: ${featureset.importId}, layerId: ${featureset.layerId} and featureID: $featureId."))) + } ?: { + callback(Result.failure(Throwable("Error getting feature state for feature $featureId from featureset: {featureId: ${featureset.featuresetId}, importId: ${featureset.importId}, layerId: ${featureset.layerId}}."))) } } @@ -360,19 +361,14 @@ class MapInterfaceController( feature: FeaturesetFeature, callback: (Result>) -> Unit ) { - if (feature.id?.toFeaturesetFeatureId() != null) { - mapboxMap.getFeatureState( - feature.featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor>, - feature.id.toFeaturesetFeatureId() - ) { - callback( - Result.success( - JSONObject(it.asJsonString()).toFilteredMap() - ) - ) - } - } else { - callback(Result.failure(Throwable("Invalid feature id for the requested feature: ${feature.id}"))) + feature.id?.let { + getFeatureStateForFeaturesetDescriptor( + feature.featureset, + it, + callback + ) + } ?: { + callback(Result.failure(Throwable("Invalid feature id for the requested feature: $feature"))) } } @@ -399,15 +395,17 @@ class MapInterfaceController( stateKey: String?, callback: (Result) -> Unit ) { - mapboxMap.removeFeatureState( - featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor>, - featureId.toFeaturesetFeatureId(), - stateKey?.let { FeatureStateKey.create(it) } - ) { - if (it.isError) { - callback(Result.failure(Throwable("Cannot remove feature state for the requested featureset: {featureId: ${featureset.featuresetId}, importId: ${featureset.importId}, layerId: ${featureset.layerId} and featureID: $featureId."))) - } else { - callback(Result.success(Unit)) + featureset.toTypedFeaturesetDescriptor()?.let { typedFeaturesetDescriptor -> + mapboxMap.removeFeatureState( + typedFeaturesetDescriptor, + featureId.toFeaturesetFeatureId(), + stateKey?.let { FeatureStateKey.create(it) } + ) { + if (it.isError) { + callback(Result.failure(Throwable("Cannot remove feature state for the requested featureset: {featureId: ${featureset.featuresetId}, importId: ${featureset.importId}, layerId: ${featureset.layerId} and featureID: $featureId."))) + } else { + callback(Result.success(Unit)) + } } } } @@ -418,20 +416,15 @@ class MapInterfaceController( stateKey: String?, callback: (Result) -> Unit ) { - if (feature.id?.toFeaturesetFeatureId() != null) { - mapboxMap.removeFeatureState( - feature.featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor>, - feature.id.toFeaturesetFeatureId(), - stateKey?.let { FeatureStateKey.create(it) } - ) { - if (it.isError) { - callback(Result.failure(Throwable("Cannot remove feature state for the requested feature: ${feature.id}"))) - } else { - callback(Result.success(Unit)) - } - } - } else { - callback(Result.failure(Throwable("Invalid feature id for the requested feature: ${feature.id}"))) + feature.id?.let { + removeFeatureStateForFeaturesetDescriptor( + feature.featureset, + it, + stateKey, + callback + ) + } ?: { + callback(Result.failure(Throwable("Invalid feature id for the requested feature: $feature"))) } } @@ -440,14 +433,18 @@ class MapInterfaceController( featureset: FeaturesetDescriptor, callback: (Result) -> Unit ) { - mapboxMap.resetFeatureStates( - featureset.toTypedFeaturesetDescriptor() as TypedFeaturesetDescriptor> - ) { - if (it.isError) { - callback(Result.failure(Throwable("Cannot reset feature state for the requested featureset: {featureId: ${featureset.featuresetId}, importId: ${featureset.importId}, layerId: ${featureset.layerId}."))) - } else { - callback(Result.success(Unit)) + featureset.toTypedFeaturesetDescriptor()?.let { typedFeaturesetDescriptor -> + mapboxMap.resetFeatureStates( + typedFeaturesetDescriptor + ) { + if (it.isError) { + callback(Result.failure(Throwable("Error resetting feature states for the requested featureset: {featureId: ${featureset.featuresetId}, importId: ${featureset.importId}, layerId: ${featureset.layerId}}."))) + } else { + callback(Result.success(Unit)) + } } + } ?: { + callback(Result.failure(Throwable("Failed to convert requested featureset: {featureId: ${featureset.featuresetId}, importId: ${featureset.importId}, layerId: ${featureset.layerId}}."))) } } diff --git a/example/lib/interactive_features_example.dart b/example/lib/interactive_features_example.dart index eb9e9b2bd..0cc87f707 100644 --- a/example/lib/interactive_features_example.dart +++ b/example/lib/interactive_features_example.dart @@ -44,7 +44,11 @@ class InteractiveFeaturesState extends State { // On long tap, remove the highlight state mapboxMap.addInteraction( LongTapInteraction(StandardBuildings(), (_, feature) { - mapboxMap.removeFeatureStateForFeaturesetFeature(feature: feature); + mapboxMap.removeFeatureStateForFeaturesetFeature(feature: feature).then( + (value) => log("Feature state removed for: ${feature.id}.") + ).catchError( + (error) => log("Error removing feature state for ${feature.id}, error: $error") + ); })); /// Define interactions for Points of Interest diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift index 37e998abc..cbc6ce00d 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapInterfaceController.swift @@ -304,7 +304,7 @@ final class MapInterfaceController: _MapInterface { completion(.failure(FlutterError( code: "setFeatureStateError", message: error.localizedDescription, - details: "Error setting feature state for featureset: {featureId: \(String(describing: featureset.featuresetId)), importId: \(String(describing: featureset.importId)), layerId: \(String(describing: featureset.layerId))} and featureId: \(String(describing: featureId))." + details: "Error setting feature state for feature \(String(describing: featureId)) from featureset {featuresetId: \(String(describing: featureset.featuresetId)), importId: \(String(describing: featureset.importId)), layerId: \(String(describing: featureset.layerId))}." ))) } else { completion(.success(())) @@ -354,7 +354,7 @@ final class MapInterfaceController: _MapInterface { completion(.failure(FlutterError( code: "getFeatureStateError", message: "\(error)", - details: "Error getting feature state for featureset: {featureId: \(String(describing: featureset.featuresetId)), importId: \(String(describing: featureset.importId)), layerId: \(String(describing: featureset.layerId))}." + details: "Error getting feature state for feature \(String(describing: featureId)) from featureset {featureId: \(String(describing: featureset.featuresetId)), importId: \(String(describing: featureset.importId)), layerId: \(String(describing: featureset.layerId))}." ))) } } @@ -391,8 +391,8 @@ final class MapInterfaceController: _MapInterface { if error != nil { completion(.failure(FlutterError( code: "removeFeatureStateError", - message: "Cannot remove feature state.", - details: "The requested featureset: \(featureset) is not valid." + message: "Cannot remove feature state", + details: "Feature state was not found for the requested feature: \(String(describing: featureId))." ))) } else { completion(.success(())) @@ -405,8 +405,8 @@ final class MapInterfaceController: _MapInterface { if error != nil { completion(.failure(FlutterError( code: "removeFeatureStateError", - message: "Cannot remove feature state.", - details: "The requested feature: \(String(describing: feature.id)) is not valid" + message: "Cannot remove feature state", + details: "Feature state was not found for the requested feature: \(String(describing: feature.id))" ))) } else { completion(.success(())) @@ -419,8 +419,8 @@ final class MapInterfaceController: _MapInterface { if error != nil { completion(.failure(FlutterError( code: "resetFeatureStateError", - message: "Cannot remove feature state.", - details: "The requested featureset: {featureId: \(String(describing: featureset.featuresetId)), importId: \(String(describing: featureset.importId)), layerId: \(String(describing: featureset.layerId))} is not valid." + message: "Cannot reset feature states", + details: "The requested featureset is not valid or was not found: {featureId: \(String(describing: featureset.featuresetId)), importId: \(String(describing: featureset.importId)), layerId: \(String(describing: featureset.layerId))}" ))) } else { completion(.success(())) From 46aaea605ed7bcce2c16e50db2a2a3010bf63d23 Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Sun, 26 Jan 2025 14:30:20 -0500 Subject: [PATCH 26/28] Update for lint --- .../mapbox/maps/mapbox_maps/MapInterfaceController.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt index 90c73253e..844dc8263 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapInterfaceController.kt @@ -11,9 +11,7 @@ import com.mapbox.maps.MapboxMap import com.mapbox.maps.TileCacheBudget import com.mapbox.maps.extension.observable.eventdata.MapLoadingErrorEventData import com.mapbox.maps.extension.style.expressions.generated.Expression -import com.mapbox.maps.interactions.FeatureState import com.mapbox.maps.interactions.FeatureStateKey -import com.mapbox.maps.interactions.TypedFeaturesetDescriptor import com.mapbox.maps.mapbox_maps.pigeons.CanonicalTileID import com.mapbox.maps.mapbox_maps.pigeons.ConstrainMode import com.mapbox.maps.mapbox_maps.pigeons.FeatureExtensionValue @@ -192,9 +190,11 @@ class MapInterfaceController( callback(Result.success(it.map { feature -> feature.toFLTFeaturesetFeature() }.toMutableList())) } } ?: { - callback(Result.failure( - Throwable("Error querying rendered features for featureset: {featureId: ${featureset.featuresetId}, importId: ${featureset.importId}, layerId: ${featureset.layerId}}") - )) + callback( + Result.failure( + Throwable("Error querying rendered features for featureset: {featureId: ${featureset.featuresetId}, importId: ${featureset.importId}, layerId: ${featureset.layerId}}") + ) + ) } } From 0dc725236c7c0731da5d349ae8991a122e7bbceb Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Mon, 27 Jan 2025 19:34:31 -0500 Subject: [PATCH 27/28] Move interaction to pigeon generation --- .../maps/mapbox_maps/MapboxMapController.kt | 19 ++- .../maps/mapbox_maps/pigeons/MapInterfaces.kt | 119 ++++++++++++----- example/lib/interactive_features_example.dart | 18 +-- .../Classes/Extensions.swift | 13 ++ .../Classes/Generated/MapInterfaces.swift | 120 ++++++++++++----- .../Classes/MapboxMapController.swift | 21 ++- lib/src/mapbox_maps_platform.dart | 17 ++- lib/src/pigeons/map_interfaces.dart | 126 +++++++++++++----- 8 files changed, 314 insertions(+), 139 deletions(-) diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt index e74a451ba..25b7e3f42 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/MapboxMapController.kt @@ -13,12 +13,10 @@ 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 @@ -29,6 +27,7 @@ import com.mapbox.maps.mapbox_maps.pigeons.ScaleBarSettingsInterface import com.mapbox.maps.mapbox_maps.pigeons.StyleManager import com.mapbox.maps.mapbox_maps.pigeons._AnimationManager import com.mapbox.maps.mapbox_maps.pigeons._CameraManager +import com.mapbox.maps.mapbox_maps.pigeons._InteractionPigeon import com.mapbox.maps.mapbox_maps.pigeons._InteractionType import com.mapbox.maps.mapbox_maps.pigeons._InteractionsListener import com.mapbox.maps.mapbox_maps.pigeons._LocationComponentSettingsInterface @@ -245,14 +244,14 @@ class MapboxMapController( "interactions#add_interaction" -> { val listener = _InteractionsListener(messenger, channelSuffix) val arguments: HashMap = call.arguments as? HashMap ?: return - val featuresetDescriptorList = arguments["featuresetDescriptor"] as? List ?: 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)?.toLong() ?: return - val filter = (arguments["filter"] as? String).toValue() - val radius = arguments["radius"] as? Double + val interactionList = arguments["interaction"] as? List ?: return + val interaction = _InteractionPigeon.fromList(interactionList) + val featuresetDescriptor = com.mapbox.maps.mapbox_maps.pigeons.FeaturesetDescriptor.fromList(interaction.featuresetDescriptor) + val interactionType = _InteractionType.valueOf(interaction.interactionType) + val stopPropagation = interaction.stopPropagation + val id = interaction.identifier.toLong() + val filter = interaction.filter.toValue() + val radius = interaction.radius featuresetDescriptor.featuresetId?.let { when (interactionType) { diff --git a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt index 8d896115d..e4e43a21e 100644 --- a/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt +++ b/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/pigeons/MapInterfaces.kt @@ -1418,7 +1418,7 @@ data class FeatureState( } /** - * An interaction that can be added to the map. + * Internal: An interaction that can be added to the map. * * To create an interaction use ``TapInteraction`` and ``LongClickInteraction`` implementations. * @@ -1459,6 +1459,48 @@ data class _Interaction( } } +/** + * Internal class to handle pigeon conversions for interactions. + * + * Generated class from Pigeon that represents data sent in messages. + */ +data class _InteractionPigeon( + /** The featureset descriptor that specifies the featureset to be included in the interaction. */ + val featuresetDescriptor: List, + /** Whether to stop the propagation of the interaction to the map */ + val stopPropagation: Boolean, + /** The type of interaction, either tap or longTap as a String */ + val interactionType: String, + /** An identifier for the interaction */ + val identifier: String, + /** An optional filter of features that should trigger the interaction. */ + val filter: String? = null, + /** Radius of a tappable area */ + val radius: Double? = null +) { + companion object { + fun fromList(pigeonVar_list: List): _InteractionPigeon { + val featuresetDescriptor = pigeonVar_list[0] as List + val stopPropagation = pigeonVar_list[1] as Boolean + val interactionType = pigeonVar_list[2] as String + val identifier = pigeonVar_list[3] as String + val filter = pigeonVar_list[4] as String? + val radius = pigeonVar_list[5] as Double? + return _InteractionPigeon(featuresetDescriptor, stopPropagation, interactionType, identifier, filter, radius) + } + } + fun toList(): List { + return listOf( + featuresetDescriptor, + stopPropagation, + interactionType, + identifier, + filter, + radius, + ) + } +} + /** * A featureset descriptor. * @@ -2309,85 +2351,90 @@ private open class MapInterfacesPigeonCodec : StandardMessageCodec() { } 185.toByte() -> { return (readValue(buffer) as? List)?.let { - FeaturesetDescriptor.fromList(it) + _InteractionPigeon.fromList(it) } } 186.toByte() -> { return (readValue(buffer) as? List)?.let { - FeaturesetFeature.fromList(it) + FeaturesetDescriptor.fromList(it) } } 187.toByte() -> { return (readValue(buffer) as? List)?.let { - MapContentGestureContext.fromList(it) + FeaturesetFeature.fromList(it) } } 188.toByte() -> { return (readValue(buffer) as? List)?.let { - _RenderedQueryGeometry.fromList(it) + MapContentGestureContext.fromList(it) } } 189.toByte() -> { return (readValue(buffer) as? List)?.let { - ProjectedMeters.fromList(it) + _RenderedQueryGeometry.fromList(it) } } 190.toByte() -> { return (readValue(buffer) as? List)?.let { - MercatorCoordinate.fromList(it) + ProjectedMeters.fromList(it) } } 191.toByte() -> { return (readValue(buffer) as? List)?.let { - StyleObjectInfo.fromList(it) + MercatorCoordinate.fromList(it) } } 192.toByte() -> { return (readValue(buffer) as? List)?.let { - StyleProjection.fromList(it) + StyleObjectInfo.fromList(it) } } 193.toByte() -> { return (readValue(buffer) as? List)?.let { - FlatLight.fromList(it) + StyleProjection.fromList(it) } } 194.toByte() -> { return (readValue(buffer) as? List)?.let { - DirectionalLight.fromList(it) + FlatLight.fromList(it) } } 195.toByte() -> { return (readValue(buffer) as? List)?.let { - AmbientLight.fromList(it) + DirectionalLight.fromList(it) } } 196.toByte() -> { return (readValue(buffer) as? List)?.let { - MbxImage.fromList(it) + AmbientLight.fromList(it) } } 197.toByte() -> { return (readValue(buffer) as? List)?.let { - ImageStretches.fromList(it) + MbxImage.fromList(it) } } 198.toByte() -> { return (readValue(buffer) as? List)?.let { - ImageContent.fromList(it) + ImageStretches.fromList(it) } } 199.toByte() -> { return (readValue(buffer) as? List)?.let { - TransitionOptions.fromList(it) + ImageContent.fromList(it) } } 200.toByte() -> { return (readValue(buffer) as? List)?.let { - CanonicalTileID.fromList(it) + TransitionOptions.fromList(it) } } 201.toByte() -> { + return (readValue(buffer) as? List)?.let { + CanonicalTileID.fromList(it) + } + } + 202.toByte() -> { return (readValue(buffer) as? List)?.let { StylePropertyValue.fromList(it) } @@ -2621,74 +2668,78 @@ private open class MapInterfacesPigeonCodec : StandardMessageCodec() { stream.write(184) writeValue(stream, value.toList()) } - is FeaturesetDescriptor -> { + is _InteractionPigeon -> { stream.write(185) writeValue(stream, value.toList()) } - is FeaturesetFeature -> { + is FeaturesetDescriptor -> { stream.write(186) writeValue(stream, value.toList()) } - is MapContentGestureContext -> { + is FeaturesetFeature -> { stream.write(187) writeValue(stream, value.toList()) } - is _RenderedQueryGeometry -> { + is MapContentGestureContext -> { stream.write(188) writeValue(stream, value.toList()) } - is ProjectedMeters -> { + is _RenderedQueryGeometry -> { stream.write(189) writeValue(stream, value.toList()) } - is MercatorCoordinate -> { + is ProjectedMeters -> { stream.write(190) writeValue(stream, value.toList()) } - is StyleObjectInfo -> { + is MercatorCoordinate -> { stream.write(191) writeValue(stream, value.toList()) } - is StyleProjection -> { + is StyleObjectInfo -> { stream.write(192) writeValue(stream, value.toList()) } - is FlatLight -> { + is StyleProjection -> { stream.write(193) writeValue(stream, value.toList()) } - is DirectionalLight -> { + is FlatLight -> { stream.write(194) writeValue(stream, value.toList()) } - is AmbientLight -> { + is DirectionalLight -> { stream.write(195) writeValue(stream, value.toList()) } - is MbxImage -> { + is AmbientLight -> { stream.write(196) writeValue(stream, value.toList()) } - is ImageStretches -> { + is MbxImage -> { stream.write(197) writeValue(stream, value.toList()) } - is ImageContent -> { + is ImageStretches -> { stream.write(198) writeValue(stream, value.toList()) } - is TransitionOptions -> { + is ImageContent -> { stream.write(199) writeValue(stream, value.toList()) } - is CanonicalTileID -> { + is TransitionOptions -> { stream.write(200) writeValue(stream, value.toList()) } - is StylePropertyValue -> { + is CanonicalTileID -> { stream.write(201) writeValue(stream, value.toList()) } + is StylePropertyValue -> { + stream.write(202) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } diff --git a/example/lib/interactive_features_example.dart b/example/lib/interactive_features_example.dart index 0cc87f707..7fac8b1eb 100644 --- a/example/lib/interactive_features_example.dart +++ b/example/lib/interactive_features_example.dart @@ -32,8 +32,8 @@ class InteractiveFeaturesState extends State { // Define a tap interaction targeting the Buildings featureset in the Standard style // Set the action to occur when a building is tapped (highlight it) - var tapInteraction = TapInteraction(StandardBuildings(), - (_, feature) { + var tapInteraction = TapInteraction( + StandardBuildings(), (_, feature) { mapboxMap.setFeatureStateForFeaturesetFeature(feature, featureState); log("Building group: ${feature.group}"); }); @@ -42,13 +42,13 @@ class InteractiveFeaturesState extends State { mapboxMap.addInteraction(tapInteraction); // On long tap, remove the highlight state - mapboxMap.addInteraction( - LongTapInteraction(StandardBuildings(), (_, feature) { - mapboxMap.removeFeatureStateForFeaturesetFeature(feature: feature).then( - (value) => log("Feature state removed for: ${feature.id}.") - ).catchError( - (error) => log("Error removing feature state for ${feature.id}, error: $error") - ); + mapboxMap.addInteraction(LongTapInteraction( + StandardBuildings(), (_, feature) { + mapboxMap + .removeFeatureStateForFeaturesetFeature(feature: feature) + .then((value) => log("Feature state removed for: ${feature.id}.")) + .catchError((error) => log( + "Error removing feature state for ${feature.id}, error: $error")); })); /// Define interactions for Points of Interest diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift index 284ff2caa..284dc31d8 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Extensions.swift @@ -1072,6 +1072,19 @@ extension UIGestureRecognizer.State { } } +extension _InteractionType { + static func fromString(_ string: String) -> _InteractionType? { + switch string { + case "TAP": + return .tAP + case "LONG_TAP": + return .lONGTAP + default: + return nil + } + } +} + // MARK: Offline extension MapboxCoreMaps.StylePackLoadOptions { diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift index a5ca489b1..ef9286e2c 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/Generated/MapInterfaces.swift @@ -1264,7 +1264,7 @@ struct FeatureState { } } -/// An interaction that can be added to the map. +/// Internal: An interaction that can be added to the map. /// /// To create an interaction use ``TapInteraction`` and ``LongClickInteraction`` implementations. /// @@ -1310,6 +1310,53 @@ struct _Interaction { } } +/// Internal class to handle pigeon conversions for interactions. +/// +/// Generated class from Pigeon that represents data sent in messages. +struct _InteractionPigeon { + /// The featureset descriptor that specifies the featureset to be included in the interaction. + var featuresetDescriptor: [Any?] + /// Whether to stop the propagation of the interaction to the map + var stopPropagation: Bool + /// The type of interaction, either tap or longTap as a String + var interactionType: String + /// An identifier for the interaction + var identifier: String + /// An optional filter of features that should trigger the interaction. + var filter: String? + /// Radius of a tappable area + var radius: Double? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> _InteractionPigeon? { + let featuresetDescriptor = pigeonVar_list[0] as! [Any?] + let stopPropagation = pigeonVar_list[1] as! Bool + let interactionType = pigeonVar_list[2] as! String + let identifier = pigeonVar_list[3] as! String + let filter: String? = nilOrValue(pigeonVar_list[4]) + let radius: Double? = nilOrValue(pigeonVar_list[5]) + + return _InteractionPigeon( + featuresetDescriptor: featuresetDescriptor, + stopPropagation: stopPropagation, + interactionType: interactionType, + identifier: identifier, + filter: filter, + radius: radius + ) + } + func toList() -> [Any?] { + return [ + featuresetDescriptor, + stopPropagation, + interactionType, + identifier, + filter, + radius, + ] + } +} + /// A featureset descriptor. /// /// The descriptor instance acts as a universal target for interactions or querying rendered features (see 'TapInteraction', 'LongTapInteraction') @@ -2131,38 +2178,40 @@ private class MapInterfacesPigeonCodecReader: FlutterStandardReader { case 184: return _Interaction.fromList(self.readValue() as! [Any?]) case 185: - return FeaturesetDescriptor.fromList(self.readValue() as! [Any?]) + return _InteractionPigeon.fromList(self.readValue() as! [Any?]) case 186: - return FeaturesetFeature.fromList(self.readValue() as! [Any?]) + return FeaturesetDescriptor.fromList(self.readValue() as! [Any?]) case 187: - return MapContentGestureContext.fromList(self.readValue() as! [Any?]) + return FeaturesetFeature.fromList(self.readValue() as! [Any?]) case 188: - return _RenderedQueryGeometry.fromList(self.readValue() as! [Any?]) + return MapContentGestureContext.fromList(self.readValue() as! [Any?]) case 189: - return ProjectedMeters.fromList(self.readValue() as! [Any?]) + return _RenderedQueryGeometry.fromList(self.readValue() as! [Any?]) case 190: - return MercatorCoordinate.fromList(self.readValue() as! [Any?]) + return ProjectedMeters.fromList(self.readValue() as! [Any?]) case 191: - return StyleObjectInfo.fromList(self.readValue() as! [Any?]) + return MercatorCoordinate.fromList(self.readValue() as! [Any?]) case 192: - return StyleProjection.fromList(self.readValue() as! [Any?]) + return StyleObjectInfo.fromList(self.readValue() as! [Any?]) case 193: - return FlatLight.fromList(self.readValue() as! [Any?]) + return StyleProjection.fromList(self.readValue() as! [Any?]) case 194: - return DirectionalLight.fromList(self.readValue() as! [Any?]) + return FlatLight.fromList(self.readValue() as! [Any?]) case 195: - return AmbientLight.fromList(self.readValue() as! [Any?]) + return DirectionalLight.fromList(self.readValue() as! [Any?]) case 196: - return MbxImage.fromList(self.readValue() as! [Any?]) + return AmbientLight.fromList(self.readValue() as! [Any?]) case 197: - return ImageStretches.fromList(self.readValue() as! [Any?]) + return MbxImage.fromList(self.readValue() as! [Any?]) case 198: - return ImageContent.fromList(self.readValue() as! [Any?]) + return ImageStretches.fromList(self.readValue() as! [Any?]) case 199: - return TransitionOptions.fromList(self.readValue() as! [Any?]) + return ImageContent.fromList(self.readValue() as! [Any?]) case 200: - return CanonicalTileID.fromList(self.readValue() as! [Any?]) + return TransitionOptions.fromList(self.readValue() as! [Any?]) case 201: + return CanonicalTileID.fromList(self.readValue() as! [Any?]) + case 202: return StylePropertyValue.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) @@ -2340,57 +2389,60 @@ private class MapInterfacesPigeonCodecWriter: FlutterStandardWriter { } else if let value = value as? _Interaction { super.writeByte(184) super.writeValue(value.toList()) - } else if let value = value as? FeaturesetDescriptor { + } else if let value = value as? _InteractionPigeon { super.writeByte(185) super.writeValue(value.toList()) - } else if let value = value as? FeaturesetFeature { + } else if let value = value as? FeaturesetDescriptor { super.writeByte(186) super.writeValue(value.toList()) - } else if let value = value as? MapContentGestureContext { + } else if let value = value as? FeaturesetFeature { super.writeByte(187) super.writeValue(value.toList()) - } else if let value = value as? _RenderedQueryGeometry { + } else if let value = value as? MapContentGestureContext { super.writeByte(188) super.writeValue(value.toList()) - } else if let value = value as? ProjectedMeters { + } else if let value = value as? _RenderedQueryGeometry { super.writeByte(189) super.writeValue(value.toList()) - } else if let value = value as? MercatorCoordinate { + } else if let value = value as? ProjectedMeters { super.writeByte(190) super.writeValue(value.toList()) - } else if let value = value as? StyleObjectInfo { + } else if let value = value as? MercatorCoordinate { super.writeByte(191) super.writeValue(value.toList()) - } else if let value = value as? StyleProjection { + } else if let value = value as? StyleObjectInfo { super.writeByte(192) super.writeValue(value.toList()) - } else if let value = value as? FlatLight { + } else if let value = value as? StyleProjection { super.writeByte(193) super.writeValue(value.toList()) - } else if let value = value as? DirectionalLight { + } else if let value = value as? FlatLight { super.writeByte(194) super.writeValue(value.toList()) - } else if let value = value as? AmbientLight { + } else if let value = value as? DirectionalLight { super.writeByte(195) super.writeValue(value.toList()) - } else if let value = value as? MbxImage { + } else if let value = value as? AmbientLight { super.writeByte(196) super.writeValue(value.toList()) - } else if let value = value as? ImageStretches { + } else if let value = value as? MbxImage { super.writeByte(197) super.writeValue(value.toList()) - } else if let value = value as? ImageContent { + } else if let value = value as? ImageStretches { super.writeByte(198) super.writeValue(value.toList()) - } else if let value = value as? TransitionOptions { + } else if let value = value as? ImageContent { super.writeByte(199) super.writeValue(value.toList()) - } else if let value = value as? CanonicalTileID { + } else if let value = value as? TransitionOptions { super.writeByte(200) super.writeValue(value.toList()) - } else if let value = value as? StylePropertyValue { + } else if let value = value as? CanonicalTileID { super.writeByte(201) super.writeValue(value.toList()) + } else if let value = value as? StylePropertyValue { + super.writeByte(202) + super.writeValue(value.toList()) } else { super.writeValue(value) } diff --git a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapboxMapController.swift b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapboxMapController.swift index a3873de6a..fb6974a3c 100644 --- a/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapboxMapController.swift +++ b/ios/mapbox_maps_flutter/Sources/mapbox_maps_flutter/Classes/MapboxMapController.swift @@ -109,30 +109,29 @@ final class MapboxMapController: NSObject, FlutterPlatformView { case "interactions#add_interaction": let listener = _InteractionsListener(binaryMessenger: binaryMessenger.messenger, messageChannelSuffix: binaryMessenger.suffix) guard let arguments = methodCall.arguments as? [String: Any], - let featuresetDescriptorList = arguments["featuresetDescriptor"] as? [String?], - let featuresetDescriptor = FeaturesetDescriptor.fromList(featuresetDescriptorList), - let interactionTypeRaw = arguments["interactionType"] as? Int, - let interactionType = _InteractionType(rawValue: interactionTypeRaw), - let stopPropagation = arguments["stopPropagation"] as? Bool, - let id = arguments["id"] as? Int else { + let interactionsList = arguments["interaction"] as? [Any?], + let interaction = _InteractionPigeon.fromList(interactionsList), + let featuresetDescriptor = FeaturesetDescriptor.fromList(interaction.featuresetDescriptor), + let interactionType = _InteractionType.fromString(interaction.interactionType), + let id = Int64(interaction.identifier) else { return } - let filter = arguments["filter"] as? String - let filterExpression = try? filter.flatMap { try $0.toExp() } - let radius = arguments["radius"] as? CGFloat + let stopPropagation = interaction.stopPropagation + let filterExpression = try? interaction.filter.flatMap { try $0.toExp() } + let radius: CGFloat? = interaction.radius.flatMap { CGFloat($0) } switch interactionType { case .tAP: mapboxMap.addInteraction( TapInteraction(featuresetDescriptor.toMapFeaturesetDescriptor(), filter: filterExpression, radius: radius, action: { featuresetFeature, context in - listener.onInteraction(context: context.toFLTMapContentGestureContext(), feature: featuresetFeature.toFLTFeaturesetFeature(), interactionID: Int64(id)) { _ in } + listener.onInteraction(context: context.toFLTMapContentGestureContext(), feature: featuresetFeature.toFLTFeaturesetFeature(), interactionID: id) { _ in } return stopPropagation }) ) case .lONGTAP: mapboxMap.addInteraction( LongPressInteraction(featuresetDescriptor.toMapFeaturesetDescriptor(), filter: filterExpression, radius: radius, action: { featuresetFeature, context in - listener.onInteraction(context: context.toFLTMapContentGestureContext(), feature: featuresetFeature.toFLTFeaturesetFeature(), interactionID: Int64(id)) { _ in } + listener.onInteraction(context: context.toFLTMapContentGestureContext(), feature: featuresetFeature.toFLTFeaturesetFeature(), interactionID: id) { _ in } return stopPropagation }) ) diff --git a/lib/src/mapbox_maps_platform.dart b/lib/src/mapbox_maps_platform.dart index b09359aa2..14fc68ec2 100644 --- a/lib/src/mapbox_maps_platform.dart +++ b/lib/src/mapbox_maps_platform.dart @@ -107,7 +107,7 @@ class _MapboxMapsPlatform { case AndroidPlatformViewHostingMode.HC: return PlatformViewsService.initExpensiveAndroidView; case AndroidPlatformViewHostingMode.VD: - throw "Unexpected hostring mode(VD) when selecting an android view controller"; + throw "Unexpected hosting mode(VD) when selecting an android view controller"; } } @@ -163,15 +163,18 @@ class _MapboxMapsPlatform { Future addInteractionsListeners( _Interaction interaction, int interactionID) async { + var interactionPigeon = _InteractionPigeon( + featuresetDescriptor: + interaction.featuresetDescriptor.encode() as List, + stopPropagation: interaction.stopPropagation, + interactionType: interaction.interactionType.name, + identifier: interactionID.toString(), + radius: interaction.radius, + filter: interaction.filter); try { return _channel .invokeMethod('interactions#add_interaction', { - 'featuresetDescriptor': interaction.featuresetDescriptor.encode(), - 'interactionType': interaction.interactionType.index, - 'filter': interaction.filter, - 'radius': interaction.radius, - 'stopPropagation': interaction.stopPropagation, - 'id': interactionID, + 'interaction': interactionPigeon.encode(), }); } on PlatformException catch (e) { return new Future.error(e); diff --git a/lib/src/pigeons/map_interfaces.dart b/lib/src/pigeons/map_interfaces.dart index 468124432..1539cb573 100644 --- a/lib/src/pigeons/map_interfaces.dart +++ b/lib/src/pigeons/map_interfaces.dart @@ -1357,7 +1357,7 @@ class FeatureState { } } -/// An interaction that can be added to the map. +/// Internal: An interaction that can be added to the map. /// /// To create an interaction use ``TapInteraction`` and ``LongClickInteraction`` implementations. /// @@ -1408,6 +1408,59 @@ class _Interaction { } } +/// Internal class to handle pigeon conversions for interactions. +class _InteractionPigeon { + _InteractionPigeon({ + required this.featuresetDescriptor, + required this.stopPropagation, + required this.interactionType, + required this.identifier, + this.filter, + this.radius, + }); + + /// The featureset descriptor that specifies the featureset to be included in the interaction. + List featuresetDescriptor; + + /// Whether to stop the propagation of the interaction to the map + bool stopPropagation; + + /// The type of interaction, either tap or longTap as a String + String interactionType; + + /// An identifier for the interaction + String identifier; + + /// An optional filter of features that should trigger the interaction. + String? filter; + + /// Radius of a tappable area + double? radius; + + Object encode() { + return [ + featuresetDescriptor, + stopPropagation, + interactionType, + identifier, + filter, + radius, + ]; + } + + static _InteractionPigeon decode(Object result) { + result as List; + return _InteractionPigeon( + featuresetDescriptor: (result[0] as List?)!.cast(), + stopPropagation: result[1]! as bool, + interactionType: result[2]! as String, + identifier: result[3]! as String, + filter: result[4] as String?, + radius: result[5] as double?, + ); + } +} + /// A featureset descriptor. /// /// The descriptor instance acts as a universal target for interactions or querying rendered features (see 'TapInteraction', 'LongTapInteraction') @@ -2239,57 +2292,60 @@ class MapInterfaces_PigeonCodec extends StandardMessageCodec { } else if (value is _Interaction) { buffer.putUint8(184); writeValue(buffer, value.encode()); - } else if (value is FeaturesetDescriptor) { + } else if (value is _InteractionPigeon) { buffer.putUint8(185); writeValue(buffer, value.encode()); - } else if (value is FeaturesetFeature) { + } else if (value is FeaturesetDescriptor) { buffer.putUint8(186); writeValue(buffer, value.encode()); - } else if (value is MapContentGestureContext) { + } else if (value is FeaturesetFeature) { buffer.putUint8(187); writeValue(buffer, value.encode()); - } else if (value is _RenderedQueryGeometry) { + } else if (value is MapContentGestureContext) { buffer.putUint8(188); writeValue(buffer, value.encode()); - } else if (value is ProjectedMeters) { + } else if (value is _RenderedQueryGeometry) { buffer.putUint8(189); writeValue(buffer, value.encode()); - } else if (value is MercatorCoordinate) { + } else if (value is ProjectedMeters) { buffer.putUint8(190); writeValue(buffer, value.encode()); - } else if (value is StyleObjectInfo) { + } else if (value is MercatorCoordinate) { buffer.putUint8(191); writeValue(buffer, value.encode()); - } else if (value is StyleProjection) { + } else if (value is StyleObjectInfo) { buffer.putUint8(192); writeValue(buffer, value.encode()); - } else if (value is FlatLight) { + } else if (value is StyleProjection) { buffer.putUint8(193); writeValue(buffer, value.encode()); - } else if (value is DirectionalLight) { + } else if (value is FlatLight) { buffer.putUint8(194); writeValue(buffer, value.encode()); - } else if (value is AmbientLight) { + } else if (value is DirectionalLight) { buffer.putUint8(195); writeValue(buffer, value.encode()); - } else if (value is MbxImage) { + } else if (value is AmbientLight) { buffer.putUint8(196); writeValue(buffer, value.encode()); - } else if (value is ImageStretches) { + } else if (value is MbxImage) { buffer.putUint8(197); writeValue(buffer, value.encode()); - } else if (value is ImageContent) { + } else if (value is ImageStretches) { buffer.putUint8(198); writeValue(buffer, value.encode()); - } else if (value is TransitionOptions) { + } else if (value is ImageContent) { buffer.putUint8(199); writeValue(buffer, value.encode()); - } else if (value is CanonicalTileID) { + } else if (value is TransitionOptions) { buffer.putUint8(200); writeValue(buffer, value.encode()); - } else if (value is StylePropertyValue) { + } else if (value is CanonicalTileID) { buffer.putUint8(201); writeValue(buffer, value.encode()); + } else if (value is StylePropertyValue) { + buffer.putUint8(202); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -2440,38 +2496,40 @@ class MapInterfaces_PigeonCodec extends StandardMessageCodec { case 184: return _Interaction.decode(readValue(buffer)!); case 185: - return FeaturesetDescriptor.decode(readValue(buffer)!); + return _InteractionPigeon.decode(readValue(buffer)!); case 186: - return FeaturesetFeature.decode(readValue(buffer)!); + return FeaturesetDescriptor.decode(readValue(buffer)!); case 187: - return MapContentGestureContext.decode(readValue(buffer)!); + return FeaturesetFeature.decode(readValue(buffer)!); case 188: - return _RenderedQueryGeometry.decode(readValue(buffer)!); + return MapContentGestureContext.decode(readValue(buffer)!); case 189: - return ProjectedMeters.decode(readValue(buffer)!); + return _RenderedQueryGeometry.decode(readValue(buffer)!); case 190: - return MercatorCoordinate.decode(readValue(buffer)!); + return ProjectedMeters.decode(readValue(buffer)!); case 191: - return StyleObjectInfo.decode(readValue(buffer)!); + return MercatorCoordinate.decode(readValue(buffer)!); case 192: - return StyleProjection.decode(readValue(buffer)!); + return StyleObjectInfo.decode(readValue(buffer)!); case 193: - return FlatLight.decode(readValue(buffer)!); + return StyleProjection.decode(readValue(buffer)!); case 194: - return DirectionalLight.decode(readValue(buffer)!); + return FlatLight.decode(readValue(buffer)!); case 195: - return AmbientLight.decode(readValue(buffer)!); + return DirectionalLight.decode(readValue(buffer)!); case 196: - return MbxImage.decode(readValue(buffer)!); + return AmbientLight.decode(readValue(buffer)!); case 197: - return ImageStretches.decode(readValue(buffer)!); + return MbxImage.decode(readValue(buffer)!); case 198: - return ImageContent.decode(readValue(buffer)!); + return ImageStretches.decode(readValue(buffer)!); case 199: - return TransitionOptions.decode(readValue(buffer)!); + return ImageContent.decode(readValue(buffer)!); case 200: - return CanonicalTileID.decode(readValue(buffer)!); + return TransitionOptions.decode(readValue(buffer)!); case 201: + return CanonicalTileID.decode(readValue(buffer)!); + case 202: return StylePropertyValue.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); From fecfbafbc3de8e55783c919edb1baa9af07408fc Mon Sep 17 00:00:00 2001 From: Release SDK bot for Maps SDK team Date: Tue, 28 Jan 2025 14:23:01 -0500 Subject: [PATCH 28/28] Expand use of generics, rename --- CHANGELOG.md | 2 +- example/lib/interactive_features_example.dart | 14 +++---- lib/src/mapbox_map.dart | 26 +++++++----- .../interactive_features.dart | 42 ++++++++++++++----- .../standard_buildings.dart | 20 +-------- .../standard_place_labels.dart | 19 +-------- .../interactive_features/standard_poi.dart | 18 +------- 7 files changed, 59 insertions(+), 82 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68a40e987..1627f1854 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ * 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(StandardBuildings(), +var tapInteraction = TapInteraction(StandardBuildings(), (_, feature) { mapboxMap.setFeatureStateForFeaturesetFeature(feature, StandardBuildingState(highlight: true)); log("Building group: ${feature.group}"); diff --git a/example/lib/interactive_features_example.dart b/example/lib/interactive_features_example.dart index 7fac8b1eb..a212b5c49 100644 --- a/example/lib/interactive_features_example.dart +++ b/example/lib/interactive_features_example.dart @@ -32,8 +32,7 @@ class InteractiveFeaturesState extends State { // Define a tap interaction targeting the Buildings featureset in the Standard style // Set the action to occur when a building is tapped (highlight it) - var tapInteraction = TapInteraction( - StandardBuildings(), (_, feature) { + var tapInteraction = TapInteraction(StandardBuildings(), (_, feature) { mapboxMap.setFeatureStateForFeaturesetFeature(feature, featureState); log("Building group: ${feature.group}"); }); @@ -42,21 +41,20 @@ class InteractiveFeaturesState extends State { mapboxMap.addInteraction(tapInteraction); // On long tap, remove the highlight state - mapboxMap.addInteraction(LongTapInteraction( - StandardBuildings(), (_, feature) { + mapboxMap + .addInteraction(LongTapInteraction(StandardBuildings(), (_, feature) { mapboxMap .removeFeatureStateForFeaturesetFeature(feature: feature) - .then((value) => log("Feature state removed for: ${feature.id}.")) + .then((value) => log("Feature state removed for: ${feature.id?.id}.")) .catchError((error) => log( - "Error removing feature state for ${feature.id}, error: $error")); + "Error removing feature state for ${feature.id?.id}, error: $error")); })); /// Define interactions for Points of Interest // Define a tap interaction targeting the POI featureset in the Standard style, including a click radius // Do not stop propagation of the click event to lower layers - var tapInteractionPOI = - TapInteraction(StandardPOIs(), (_, StandardPOIsFeature feature) { + var tapInteractionPOI = TapInteraction(StandardPOIs(), (_, feature) { mapboxMap.setFeatureStateForFeaturesetFeature( feature, StandardPOIsState(hide: true)); log("POI feature name: ${feature.name}"); diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index 8624371af..974d9cb14 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -580,19 +580,19 @@ class MapboxMap extends ChangeNotifier { /// References for all interactions added to the map. @experimental - final _InteractionsList _interactionsList = - _InteractionsList(interactions: {}); + final _InteractionsMap _interactionsMap = + _InteractionsMap(interactions: {}); /// Add an interaction @experimental - void addInteraction( + void addInteraction>( TypedInteraction interaction) { - var id = _interactionsList.interactions.length; - _interactionsList.interactions[id] = _InteractionListener( + var id = _interactionsMap.interactions.length; + _interactionsMap.interactions[id] = _InteractionListener( onInteractionListener: interaction.action, interactionID: id, ); - _InteractionsListener.setUp(_interactionsList, + _InteractionsListener.setUp(_interactionsMap, binaryMessenger: _mapboxMapsPlatform.binaryMessenger, messageChannelSuffix: _mapboxMapsPlatform.channelSuffix.toString()); _mapboxMapsPlatform.addInteractionsListeners(interaction, id); @@ -789,12 +789,16 @@ class _InteractionListener if (featuresetID == "buildings") { typedFeature = - StandardBuildingsFeature.fromFeaturesetFeature(feature) as T; + TypedFeaturesetFeature.fromFeaturesetFeature( + feature) as T; } else if (featuresetID == "poi") { - typedFeature = StandardPOIsFeature.fromFeaturesetFeature(feature) as T; + typedFeature = + TypedFeaturesetFeature.fromFeaturesetFeature(feature) + as T; } else if (featuresetID == "place-labels") { typedFeature = - StandardPlaceLabelsFeature.fromFeaturesetFeature(feature) as T; + TypedFeaturesetFeature.fromFeaturesetFeature( + feature) as T; } else { typedFeature = feature as T; } @@ -804,9 +808,9 @@ class _InteractionListener } /// Listen to all interactions on the map, determine which interaction to call -class _InteractionsList +class _InteractionsMap extends _InteractionsListener { - _InteractionsList({ + _InteractionsMap({ required this.interactions, }); diff --git a/lib/src/style/interactive_features/interactive_features.dart b/lib/src/style/interactive_features/interactive_features.dart index 4edd475e7..f2379b8dc 100644 --- a/lib/src/style/interactive_features/interactive_features.dart +++ b/lib/src/style/interactive_features/interactive_features.dart @@ -1,10 +1,10 @@ part of '../../../mapbox_maps_flutter.dart'; -/// A single tap interaction. -final class TapInteraction - extends TypedInteraction { +/// A single tap interaction on a Featureset with a typed `FeaturesetDescriptor`. +final class TapInteraction + extends TypedInteraction> { TapInteraction( - FeaturesetDescriptor featuresetDescriptor, OnInteraction action, + T featuresetDescriptor, OnInteraction> action, {super.filter, super.radius, super.stopPropagation = true}) : super( featuresetDescriptor: featuresetDescriptor, @@ -12,11 +12,11 @@ final class TapInteraction action: action); } -/// A long tap interaction -final class LongTapInteraction - extends TypedInteraction { +/// A long tap interaction on a Featureset with a typed `FeaturesetDescriptor`. +final class LongTapInteraction + extends TypedInteraction> { LongTapInteraction( - FeaturesetDescriptor featuresetDescriptor, OnInteraction action, + T featuresetDescriptor, OnInteraction> action, {super.filter, super.radius, super.stopPropagation = true}) : super( featuresetDescriptor: featuresetDescriptor, @@ -24,8 +24,9 @@ final class LongTapInteraction action: action); } -/// A typed interaction with an action -final class TypedInteraction extends _Interaction { +/// An `_Interaction` with an action that has a typed `FeaturesetFeature` as input. +final class TypedInteraction + extends _Interaction { TypedInteraction( {required super.featuresetDescriptor, super.filter, @@ -37,3 +38,24 @@ final class TypedInteraction extends _Interaction { OnInteraction action; } + +/// A `FeaturesetFeature` with a typed `FeaturesetDescriptor`. This is used to provide typed access to the specific properties and state of a feature with a known type such as StandardPOIs or StandardBuildings. +class TypedFeaturesetFeature + extends FeaturesetFeature { + TypedFeaturesetFeature(T featureset, Map geometry, + Map properties, Map state, {super.id}) + : super( + featureset: featureset, + geometry: geometry, + properties: properties, + state: state); + + /// Creates a `TypedFeaturesetFeature` from a `FeaturesetFeature`. + TypedFeaturesetFeature.fromFeaturesetFeature(FeaturesetFeature feature) + : super( + id: feature.id, + featureset: feature.featureset, + geometry: feature.geometry, + properties: feature.properties, + state: feature.state); +} diff --git a/lib/src/style/interactive_features/standard_buildings.dart b/lib/src/style/interactive_features/standard_buildings.dart index ccffe7f84..e5cc41579 100644 --- a/lib/src/style/interactive_features/standard_buildings.dart +++ b/lib/src/style/interactive_features/standard_buildings.dart @@ -1,7 +1,8 @@ part of '../../../mapbox_maps_flutter.dart'; /// A Feature that represents a building in the Standard style. -class StandardBuildingsFeature extends FeaturesetFeature { +extension StandardBuildingsFeature + on TypedFeaturesetFeature { /// A feature state. /// /// This is a **snapshot** of the state that the feature had when it was interacted with. @@ -16,23 +17,6 @@ class StandardBuildingsFeature extends FeaturesetFeature { String? get group { return properties["group"] as String?; } - - StandardBuildingsFeature(Map geometry, - Map properties, Map state, {super.id}) - : super( - featureset: StandardBuildings(), - geometry: geometry, - properties: properties, - state: state); - - @override - StandardBuildingsFeature.fromFeaturesetFeature(FeaturesetFeature feature) - : super( - id: feature.id, - featureset: feature.featureset, - geometry: feature.geometry, - properties: feature.properties, - state: feature.state); } /// A Featureset of buildings in the Standard style diff --git a/lib/src/style/interactive_features/standard_place_labels.dart b/lib/src/style/interactive_features/standard_place_labels.dart index 0c8806b41..f313b1df5 100644 --- a/lib/src/style/interactive_features/standard_place_labels.dart +++ b/lib/src/style/interactive_features/standard_place_labels.dart @@ -2,7 +2,8 @@ part of '../../../mapbox_maps_flutter.dart'; /// A feature that labels places including countries, states, cities, /// towns, and neighborhoods in the Standard style. -class StandardPlaceLabelsFeature extends FeaturesetFeature { +extension StandardPlaceLabelsFeature + on TypedFeaturesetFeature { /// A feature state. /// /// This is a **snapshot** of the state that the feature had when it was interacted with. @@ -23,22 +24,6 @@ class StandardPlaceLabelsFeature extends FeaturesetFeature { String? get category { return properties["class"] as String?; } - - StandardPlaceLabelsFeature(Map geometry, - Map properties, Map state, {super.id}) - : super( - featureset: StandardPlaceLabels(), - geometry: geometry, - properties: properties, - state: state); - - StandardPlaceLabelsFeature.fromFeaturesetFeature(FeaturesetFeature feature) - : super( - id: feature.id, - featureset: feature.featureset, - geometry: feature.geometry, - properties: feature.properties, - state: feature.state); } /// A Featureset of place labels in the Standard style diff --git a/lib/src/style/interactive_features/standard_poi.dart b/lib/src/style/interactive_features/standard_poi.dart index 39c09f3ba..148dec3da 100644 --- a/lib/src/style/interactive_features/standard_poi.dart +++ b/lib/src/style/interactive_features/standard_poi.dart @@ -1,7 +1,7 @@ part of '../../../mapbox_maps_flutter.dart'; /// A feature that is a point of interest in the Standard style. -class StandardPOIsFeature extends FeaturesetFeature { +extension StandardPOIsFeature on TypedFeaturesetFeature { /// A feature state. /// /// This is a **snapshot** of the state that the feature had when it was interacted with. @@ -54,22 +54,6 @@ class StandardPOIsFeature extends FeaturesetFeature { Point? get coordinate { return this.geometry["coordinates"] as Point?; } - - StandardPOIsFeature(Map geometry, - Map properties, Map state, {super.id}) - : super( - featureset: StandardPOIs(), - geometry: geometry, - properties: properties, - state: state); - - StandardPOIsFeature.fromFeaturesetFeature(FeaturesetFeature feature) - : super( - id: feature.id, - featureset: feature.featureset, - geometry: feature.geometry, - properties: feature.properties, - state: feature.state); } /// A Featureset of POIs in the Standard style