From f88e450074f4ca293dfc1aef000157baf6d3ecbb Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Tue, 5 Jul 2022 12:56:48 +0300 Subject: [PATCH 1/9] Initial prototype. --- app/src/main/AndroidManifest.xml | 14 ++ .../CustomLocationProviderActivity.kt | 87 +++++++++ .../main/res/values/example_descriptions.xml | 1 + app/src/main/res/values/example_titles.xml | 1 + .../CustomLocationProvider.kt | 166 ++++++++++++++++++ 5 files changed, 269 insertions(+) create mode 100644 app/src/main/java/com/mapbox/maps/testapp/examples/CustomLocationProviderActivity.kt create mode 100644 plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomLocationProvider.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8dcc41844b..44ff3fe0a8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1195,6 +1195,20 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".ExampleOverviewActivity" /> + + + + + + diff --git a/app/src/main/java/com/mapbox/maps/testapp/examples/CustomLocationProviderActivity.kt b/app/src/main/java/com/mapbox/maps/testapp/examples/CustomLocationProviderActivity.kt new file mode 100644 index 0000000000..55f829a852 --- /dev/null +++ b/app/src/main/java/com/mapbox/maps/testapp/examples/CustomLocationProviderActivity.kt @@ -0,0 +1,87 @@ +package com.mapbox.maps.testapp.examples + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.ViewGroup +import android.widget.Button +import androidx.appcompat.app.AppCompatActivity +import com.mapbox.geojson.Point +import com.mapbox.maps.CameraOptions +import com.mapbox.maps.MapView +import com.mapbox.maps.Style +import com.mapbox.maps.plugin.LocationPuck3D +import com.mapbox.maps.plugin.gestures.OnMapClickListener +import com.mapbox.maps.plugin.gestures.gestures +import com.mapbox.maps.plugin.locationcomponent.CustomLocationProvider +import com.mapbox.maps.plugin.locationcomponent.location2 + +/** + * Example of using custom location provider. + */ +class CustomLocationProviderActivity : AppCompatActivity(), OnMapClickListener { + + private val customLocationProvider = CustomLocationProvider() + private lateinit var mapView: MapView + + @SuppressLint("SetTextI18n") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mapView = MapView(this) + setContentView(mapView) + mapView.addView( + Button(this).apply { + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + text = "Cancel" + setOnClickListener { + customLocationProvider.cancelPlayback() + } + } + ) + mapView.getMapboxMap() + .apply { + setCamera( + CameraOptions.Builder() + .center(HELSINKI) + .pitch(40.0) + .zoom(14.0) + .build() + ) + loadStyleUri(Style.MAPBOX_STREETS) { + initLocationComponent() + initClickListeners() + customLocationProvider.startPlayback() + } + } + } + + private fun initClickListeners() { + mapView.gestures.addOnMapClickListener(this) + } + + private fun initLocationComponent() { + val locationComponentPlugin2 = mapView.location2 + locationComponentPlugin2.setLocationProvider(customLocationProvider) + locationComponentPlugin2.let { + it.locationPuck = LocationPuck3D( + modelUri = "asset://sportcar.glb", + modelScale = listOf(0.1f, 0.1f, 0.1f), + modelTranslation = listOf(0.1f, 0.1f, 0.1f), + modelRotation = listOf(0.0f, 0.0f, 180.0f) + ) + } + locationComponentPlugin2.enabled = true + locationComponentPlugin2.puckBearingEnabled = true + } + + override fun onMapClick(point: Point): Boolean { + customLocationProvider.queueLocationUpdate(point) + return true + } + + companion object { + private val HELSINKI = Point.fromLngLat(24.9384, 60.1699) + } +} \ No newline at end of file diff --git a/app/src/main/res/values/example_descriptions.xml b/app/src/main/res/values/example_descriptions.xml index fd4e7e657c..8f6aea98d0 100644 --- a/app/src/main/res/values/example_descriptions.xml +++ b/app/src/main/res/values/example_descriptions.xml @@ -94,4 +94,5 @@ Viewport camera showcase Advanced viewport with gestures showcase Add a third party vector tile source. + Showcase usage of custom location provider. diff --git a/app/src/main/res/values/example_titles.xml b/app/src/main/res/values/example_titles.xml index 67229d2cba..b6f42e6ce0 100644 --- a/app/src/main/res/values/example_titles.xml +++ b/app/src/main/res/values/example_titles.xml @@ -94,4 +94,5 @@ Viewport camera Advanced Viewport with gestures External Vector Source + Custom location provider diff --git a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomLocationProvider.kt b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomLocationProvider.kt new file mode 100644 index 0000000000..1a3881cbe7 --- /dev/null +++ b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomLocationProvider.kt @@ -0,0 +1,166 @@ +package com.mapbox.maps.plugin.locationcomponent + +import android.os.Handler +import android.os.Looper +import android.view.animation.LinearInterpolator +import com.mapbox.geojson.Point +import com.mapbox.maps.MapboxExperimental +import java.util.concurrent.ConcurrentLinkedQueue +import java.util.concurrent.CopyOnWriteArraySet +import kotlin.math.* + +/** + * A custom location provider implementation that allows to play location updates at constant speed. + */ +@MapboxExperimental +class CustomLocationProvider : LocationProvider { + private var locationConsumers = CopyOnWriteArraySet() + private var bearingAnimateDuration = 100L + private var isPlaying = false + private val handler = Handler(Looper.getMainLooper()) + private val locationList = ConcurrentLinkedQueue>() + private var lastLocation: Point? = null + + /** + * Playback speed in m/s. + */ + var speed = 60.0 + + /** + * Return the remaining locations in the queue. + */ + val remainingLocationsInQueue: List + get() { + with(locationList) { + return this.map { it.first } + } + } + + + /** + * Queue a list of geo locations to be played at constant speed. + */ + fun queueLocationUpdates(locations: List) { + locations.forEach { + queueLocationUpdate(it) + } + } + + /** + * Queue a new location update event to be played at constant speed. + */ + fun queueLocationUpdate( + location: Point + ) { + val bearing = (locationList.lastOrNull()?.first ?: lastLocation)?.let { + bearing(it, location) + } ?: 0.0 + val animationDurationMs = (locationList.lastOrNull()?.first ?: lastLocation)?.let { + (distanceInMeter(it, location) / speed) * 1000.0 + } ?: 1000L + locationList.add(Triple(location, bearing, animationDurationMs.toLong())) + if (locationList.size == 1 && isPlaying) { + drainQueue() + } + } + + /** + * Start the playback, any incoming location updates will be queued and played sequentially. + */ + fun startPlayback() { + isPlaying = true + } + + /** + * Cancel any ongoing playback, new incoming location updates will be queued but not played. + */ + fun pausePlayback() { + isPlaying = false + handler.removeCallbacksAndMessages(null) + } + + /** + * Cancel any ongoing playback and clear remaining location queue. + * New incoming location updates will be queued but not played. + */ + fun cancelPlayback() { + isPlaying = false + locationList.clear() + handler.removeCallbacksAndMessages(null) + } + + private fun drainQueue() { + locationList.peek()?.let { + emitLocationUpdated(it.first, it.second, it.third) { + lastLocation = locationList.poll()?.first + drainQueue() + } + } + } + + private fun emitLocationUpdated( + location: Point, + bearing: Double, + animationDuration: Long, + finished: () -> Unit + ) { + locationConsumers.forEach { + it.onBearingUpdated(bearing) { + duration = bearingAnimateDuration + } + it.onLocationUpdated(location) { + duration = animationDuration + interpolator = LinearInterpolator() + } + } + handler.postDelayed(finished, animationDuration) + } + + override fun registerLocationConsumer(locationConsumer: LocationConsumer) { + this.locationConsumers.add(locationConsumer) + } + + override fun unRegisterLocationConsumer(locationConsumer: LocationConsumer) { + this.locationConsumers.remove(locationConsumer) + } + + private companion object { + /** + * Takes two [Point] and finds the geographic bearing between them. + * + * @param point1 first point used for calculating the bearing + * @param point2 second point used for calculating the bearing + * @return bearing in decimal degrees + */ + fun bearing(point1: Point, point2: Point): Double { + val lon1: Double = degreesToRadians(point1.longitude()) + val lon2: Double = degreesToRadians(point2.longitude()) + val lat1: Double = degreesToRadians(point1.latitude()) + val lat2: Double = degreesToRadians(point2.latitude()) + val value1 = sin(lon2 - lon1) * cos(lat2) + val value2 = cos(lat1) * sin(lat2) - (sin(lat1) * cos(lat2) * cos(lon2 - lon1)) + return radiansToDegrees(atan2(value1, value2)) + } + + fun radiansToDegrees(radians: Double): Double { + val degrees = radians % (2 * Math.PI) + return degrees * 180 / Math.PI + } + + fun degreesToRadians(degrees: Double): Double { + val radians = degrees % 360 + return radians * Math.PI / 180 + } + + fun distanceInMeter(point1: Point, point2: Point): Double { + val radius = 6370000.0 + val lat = degreesToRadians(point2.latitude() - point1.latitude()) + val lon = degreesToRadians(point2.longitude() - point1.longitude()) + val a = sin(lat / 2) * sin(lat / 2) + cos(degreesToRadians(point1.latitude())) * cos( + degreesToRadians(point2.latitude()) + ) * sin(lon / 2) * sin(lon / 2) + val c = 2 * atan2(sqrt(a), sqrt(1 - a)) + return abs(radius * c) + } + } +} \ No newline at end of file From 489d034c36e96d9d264845aa5ce181227c2320aa Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Fri, 8 Jul 2022 18:59:45 +0300 Subject: [PATCH 2/9] Introduce journey abstraction. --- app/src/main/AndroidManifest.xml | 2 +- ... CustomJourneyLocationProviderActivity.kt} | 18 +- .../CustomJourneyLocationProvider.kt | 256 ++++++++++++++++++ .../CustomLocationProvider.kt | 166 ------------ 4 files changed, 267 insertions(+), 175 deletions(-) rename app/src/main/java/com/mapbox/maps/testapp/examples/{CustomLocationProviderActivity.kt => CustomJourneyLocationProviderActivity.kt} (78%) create mode 100644 plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomJourneyLocationProvider.kt delete mode 100644 plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomLocationProvider.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 44ff3fe0a8..c2d309e71f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1197,7 +1197,7 @@ diff --git a/app/src/main/java/com/mapbox/maps/testapp/examples/CustomLocationProviderActivity.kt b/app/src/main/java/com/mapbox/maps/testapp/examples/CustomJourneyLocationProviderActivity.kt similarity index 78% rename from app/src/main/java/com/mapbox/maps/testapp/examples/CustomLocationProviderActivity.kt rename to app/src/main/java/com/mapbox/maps/testapp/examples/CustomJourneyLocationProviderActivity.kt index 55f829a852..dbf1b5b1b5 100644 --- a/app/src/main/java/com/mapbox/maps/testapp/examples/CustomLocationProviderActivity.kt +++ b/app/src/main/java/com/mapbox/maps/testapp/examples/CustomJourneyLocationProviderActivity.kt @@ -12,15 +12,17 @@ import com.mapbox.maps.Style import com.mapbox.maps.plugin.LocationPuck3D import com.mapbox.maps.plugin.gestures.OnMapClickListener import com.mapbox.maps.plugin.gestures.gestures -import com.mapbox.maps.plugin.locationcomponent.CustomLocationProvider +import com.mapbox.maps.plugin.locationcomponent.CustomJourneyLocationProvider +import com.mapbox.maps.plugin.locationcomponent.Journey import com.mapbox.maps.plugin.locationcomponent.location2 /** * Example of using custom location provider. */ -class CustomLocationProviderActivity : AppCompatActivity(), OnMapClickListener { +class CustomJourneyLocationProviderActivity : AppCompatActivity(), OnMapClickListener { - private val customLocationProvider = CustomLocationProvider() + private val journey = Journey() + private val customJourneyLocationProvider = CustomJourneyLocationProvider().apply { loadJourney(journey) } private lateinit var mapView: MapView @SuppressLint("SetTextI18n") @@ -36,7 +38,7 @@ class CustomLocationProviderActivity : AppCompatActivity(), OnMapClickListener { ) text = "Cancel" setOnClickListener { - customLocationProvider.cancelPlayback() + journey.pause() } } ) @@ -52,7 +54,7 @@ class CustomLocationProviderActivity : AppCompatActivity(), OnMapClickListener { loadStyleUri(Style.MAPBOX_STREETS) { initLocationComponent() initClickListeners() - customLocationProvider.startPlayback() + journey.start() } } } @@ -63,11 +65,11 @@ class CustomLocationProviderActivity : AppCompatActivity(), OnMapClickListener { private fun initLocationComponent() { val locationComponentPlugin2 = mapView.location2 - locationComponentPlugin2.setLocationProvider(customLocationProvider) + locationComponentPlugin2.setLocationProvider(customJourneyLocationProvider) locationComponentPlugin2.let { it.locationPuck = LocationPuck3D( modelUri = "asset://sportcar.glb", - modelScale = listOf(0.1f, 0.1f, 0.1f), + modelScale = listOf(0.5f, 0.5f, 0.5f), modelTranslation = listOf(0.1f, 0.1f, 0.1f), modelRotation = listOf(0.0f, 0.0f, 180.0f) ) @@ -77,7 +79,7 @@ class CustomLocationProviderActivity : AppCompatActivity(), OnMapClickListener { } override fun onMapClick(point: Point): Boolean { - customLocationProvider.queueLocationUpdate(point) + journey.queueLocationUpdate(point) return true } diff --git a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomJourneyLocationProvider.kt b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomJourneyLocationProvider.kt new file mode 100644 index 0000000000..31968a6561 --- /dev/null +++ b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomJourneyLocationProvider.kt @@ -0,0 +1,256 @@ +package com.mapbox.maps.plugin.locationcomponent + +import android.os.Handler +import android.os.Looper +import android.view.animation.LinearInterpolator +import com.mapbox.geojson.Point +import com.mapbox.maps.MapboxExperimental +import java.util.concurrent.ConcurrentLinkedQueue +import java.util.concurrent.CopyOnWriteArrayList +import java.util.concurrent.CopyOnWriteArraySet +import kotlin.math.* + +/** + * A custom location provider implementation that allows to play location updates at constant speed. + */ +@MapboxExperimental +class CustomJourneyLocationProvider : LocationProvider { + private var locationConsumers = CopyOnWriteArraySet() + + fun loadJourney(journey: Journey) { + journey.observeJourneyUpdates { point, bearing, locationAnimationDurationMs, bearingAnimationDurationMs -> + emitLocationUpdated(point, bearing, locationAnimationDurationMs, bearingAnimationDurationMs) + true + } + } + + private fun emitLocationUpdated( + location: Point, + bearing: Double, + locationAnimationDuration: Long, + bearingAnimateDuration: Long, + ) { + locationConsumers.forEach { + it.onBearingUpdated(bearing) { + duration = bearingAnimateDuration + } + it.onLocationUpdated(location) { + duration = locationAnimationDuration + interpolator = LinearInterpolator() + } + } + } + + override fun registerLocationConsumer(locationConsumer: LocationConsumer) { + this.locationConsumers.add(locationConsumer) + } + + override fun unRegisterLocationConsumer(locationConsumer: LocationConsumer) { + this.locationConsumers.remove(locationConsumer) + } +} + +@MapboxExperimental +class Journey(val speed: Double = 100.0, val angularSpeed: Double = 100.0) { + private val locationList = CopyOnWriteArrayList() + private val initialTimeStamp: Long = 0 + private val remainingPoints = ConcurrentLinkedQueue() + private var isPlaying = false + private val handler = Handler(Looper.getMainLooper()) + + private val observers = CopyOnWriteArraySet() + + /** + * Return the remaining locations in the queue. + */ + val remainingLocationsInQueue: List + get() { + with(remainingPoints) { + return this.map { it.location } + } + } + + fun observeJourneyUpdates(observer: JourneyDataObserver) { + observers.add(observer) + } + + /** + * Start the playback, any incoming location updates will be queued and played sequentially. + */ + fun start() { + isPlaying = true + drainQueue() + } + + /** + * Cancel any ongoing playback, new incoming location updates will be queued but not played. + */ + fun pause() { + isPlaying = false + handler.removeCallbacksAndMessages(null) + } + + /** + * Resume the remaining journey. + */ + fun resume() { + isPlaying = true + drainQueue() + } + + /** + * Restart the journey. + */ + fun restart() { + remainingPoints.clear() + remainingPoints.addAll(locationList) + isPlaying = true + } + + /** + * Queue a new location update event to be played at constant speed. + */ + fun queueLocationUpdate( + location: Point + ) { + val bearing = locationList.lastOrNull()?.location?.let { + bearing(it, location) + } ?: 0.0 + val animationDurationMs = locationList.lastOrNull()?.location?.let { + (distanceInMeter(it, location) / speed) * 1000.0 + } ?: 1000L + val bearingAnimateDurationMs = + abs(shortestRotation(bearing, locationList.lastOrNull()?.bearing ?: 0.0) / angularSpeed) * 1000.0 + + val nextData = + QueueData(location, bearing, animationDurationMs.toLong(), bearingAnimateDurationMs.toLong()) + locationList.add(nextData) + remainingPoints.add(nextData) + if (remainingPoints.size == 1 && isPlaying) { + drainQueue() + } + } + + /** + * Queue a list of geo locations to be played at constant speed. + */ + fun queueLocationUpdates(locations: List) { + locations.forEach { + queueLocationUpdate(it) + } + } + + private fun drainQueue() { + remainingPoints.peek()?.let { data -> + observers.forEach { + if (it.onNewData( + data.location, + data.bearing, + data.locationAnimationDurationMs, + data.bearingAnimateDurationMs + ) + ) { + if (isPlaying) { + handler.postDelayed( + { + remainingPoints.poll() + drainQueue() + }, + max(data.locationAnimationDurationMs, data.bearingAnimateDurationMs) + ) + } + } else { + observers.remove(it) + } + } + } + } + + private data class QueueData( + val location: Point, + val bearing: Double, + val locationAnimationDurationMs: Long, + val bearingAnimateDurationMs: Long + ) + + private companion object { + /** + * Takes two [Point] and finds the geographic bearing between them. + * + * @param point1 first point used for calculating the bearing + * @param point2 second point used for calculating the bearing + * @return bearing in decimal degrees + */ + fun bearing(point1: Point, point2: Point): Double { + val lon1: Double = degreesToRadians(point1.longitude()) + val lon2: Double = degreesToRadians(point2.longitude()) + val lat1: Double = degreesToRadians(point1.latitude()) + val lat2: Double = degreesToRadians(point2.latitude()) + val value1 = sin(lon2 - lon1) * cos(lat2) + val value2 = cos(lat1) * sin(lat2) - (sin(lat1) * cos(lat2) * cos(lon2 - lon1)) + return radiansToDegrees(atan2(value1, value2)) + } + + fun radiansToDegrees(radians: Double): Double { + val degrees = radians % (2 * Math.PI) + return degrees * 180 / Math.PI + } + + fun degreesToRadians(degrees: Double): Double { + val radians = degrees % 360 + return radians * Math.PI / 180 + } + + fun distanceInMeter(point1: Point, point2: Point): Double { + val radius = 6370000.0 + val lat = degreesToRadians(point2.latitude() - point1.latitude()) + val lon = degreesToRadians(point2.longitude() - point1.longitude()) + val a = sin(lat / 2) * sin(lat / 2) + cos(degreesToRadians(point1.latitude())) * cos( + degreesToRadians(point2.latitude()) + ) * sin(lon / 2) * sin(lon / 2) + val c = 2 * atan2(sqrt(a), sqrt(1 - a)) + return abs(radius * c) + } + + /** + * Util for finding the shortest path from the current rotated degree to the new degree. + * + * @param targetHeading the new position of the rotation + * @param currentHeading the current position of the rotation + * @return the shortest degree of rotation possible + */ + fun shortestRotation(targetHeading: Double, currentHeading: Double): Double { + val diff = currentHeading - targetHeading + return when { + diff > 180.0f -> { + targetHeading + 360.0f + } + diff < -180.0f -> { + targetHeading - 360.0f + } + else -> { + targetHeading + } + } + } + } +} + +fun interface JourneyDataObserver { + /** + * Notifies that new data is available. + * + * @param location the next location update. + * @param bearing the bearing towards the next location update. + * @param locationAnimationDurationMs maximum duration of the animation in ms. + * @param bearingAnimateDurationMs + * + * @return true if new data is needed and stay subscribed. returning false will unsubscribe from further data updates. + */ + fun onNewData( + location: Point, + bearing: Double, + locationAnimationDurationMs: Long, + bearingAnimateDurationMs: Long + ): Boolean +} \ No newline at end of file diff --git a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomLocationProvider.kt b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomLocationProvider.kt deleted file mode 100644 index 1a3881cbe7..0000000000 --- a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomLocationProvider.kt +++ /dev/null @@ -1,166 +0,0 @@ -package com.mapbox.maps.plugin.locationcomponent - -import android.os.Handler -import android.os.Looper -import android.view.animation.LinearInterpolator -import com.mapbox.geojson.Point -import com.mapbox.maps.MapboxExperimental -import java.util.concurrent.ConcurrentLinkedQueue -import java.util.concurrent.CopyOnWriteArraySet -import kotlin.math.* - -/** - * A custom location provider implementation that allows to play location updates at constant speed. - */ -@MapboxExperimental -class CustomLocationProvider : LocationProvider { - private var locationConsumers = CopyOnWriteArraySet() - private var bearingAnimateDuration = 100L - private var isPlaying = false - private val handler = Handler(Looper.getMainLooper()) - private val locationList = ConcurrentLinkedQueue>() - private var lastLocation: Point? = null - - /** - * Playback speed in m/s. - */ - var speed = 60.0 - - /** - * Return the remaining locations in the queue. - */ - val remainingLocationsInQueue: List - get() { - with(locationList) { - return this.map { it.first } - } - } - - - /** - * Queue a list of geo locations to be played at constant speed. - */ - fun queueLocationUpdates(locations: List) { - locations.forEach { - queueLocationUpdate(it) - } - } - - /** - * Queue a new location update event to be played at constant speed. - */ - fun queueLocationUpdate( - location: Point - ) { - val bearing = (locationList.lastOrNull()?.first ?: lastLocation)?.let { - bearing(it, location) - } ?: 0.0 - val animationDurationMs = (locationList.lastOrNull()?.first ?: lastLocation)?.let { - (distanceInMeter(it, location) / speed) * 1000.0 - } ?: 1000L - locationList.add(Triple(location, bearing, animationDurationMs.toLong())) - if (locationList.size == 1 && isPlaying) { - drainQueue() - } - } - - /** - * Start the playback, any incoming location updates will be queued and played sequentially. - */ - fun startPlayback() { - isPlaying = true - } - - /** - * Cancel any ongoing playback, new incoming location updates will be queued but not played. - */ - fun pausePlayback() { - isPlaying = false - handler.removeCallbacksAndMessages(null) - } - - /** - * Cancel any ongoing playback and clear remaining location queue. - * New incoming location updates will be queued but not played. - */ - fun cancelPlayback() { - isPlaying = false - locationList.clear() - handler.removeCallbacksAndMessages(null) - } - - private fun drainQueue() { - locationList.peek()?.let { - emitLocationUpdated(it.first, it.second, it.third) { - lastLocation = locationList.poll()?.first - drainQueue() - } - } - } - - private fun emitLocationUpdated( - location: Point, - bearing: Double, - animationDuration: Long, - finished: () -> Unit - ) { - locationConsumers.forEach { - it.onBearingUpdated(bearing) { - duration = bearingAnimateDuration - } - it.onLocationUpdated(location) { - duration = animationDuration - interpolator = LinearInterpolator() - } - } - handler.postDelayed(finished, animationDuration) - } - - override fun registerLocationConsumer(locationConsumer: LocationConsumer) { - this.locationConsumers.add(locationConsumer) - } - - override fun unRegisterLocationConsumer(locationConsumer: LocationConsumer) { - this.locationConsumers.remove(locationConsumer) - } - - private companion object { - /** - * Takes two [Point] and finds the geographic bearing between them. - * - * @param point1 first point used for calculating the bearing - * @param point2 second point used for calculating the bearing - * @return bearing in decimal degrees - */ - fun bearing(point1: Point, point2: Point): Double { - val lon1: Double = degreesToRadians(point1.longitude()) - val lon2: Double = degreesToRadians(point2.longitude()) - val lat1: Double = degreesToRadians(point1.latitude()) - val lat2: Double = degreesToRadians(point2.latitude()) - val value1 = sin(lon2 - lon1) * cos(lat2) - val value2 = cos(lat1) * sin(lat2) - (sin(lat1) * cos(lat2) * cos(lon2 - lon1)) - return radiansToDegrees(atan2(value1, value2)) - } - - fun radiansToDegrees(radians: Double): Double { - val degrees = radians % (2 * Math.PI) - return degrees * 180 / Math.PI - } - - fun degreesToRadians(degrees: Double): Double { - val radians = degrees % 360 - return radians * Math.PI / 180 - } - - fun distanceInMeter(point1: Point, point2: Point): Double { - val radius = 6370000.0 - val lat = degreesToRadians(point2.latitude() - point1.latitude()) - val lon = degreesToRadians(point2.longitude() - point1.longitude()) - val a = sin(lat / 2) * sin(lat / 2) + cos(degreesToRadians(point1.latitude())) * cos( - degreesToRadians(point2.latitude()) - ) * sin(lon / 2) * sin(lon / 2) - val c = 2 * atan2(sqrt(a), sqrt(1 - a)) - return abs(radius * c) - } - } -} \ No newline at end of file From 5f531c3b63c38ed0a68972945e204a79273dff39 Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Tue, 25 Oct 2022 16:40:10 +0300 Subject: [PATCH 3/9] Allowing initialise multiple instances of location component. --- .../locationcomponent/LayerSourceProvider.kt | 17 +- .../LocationComponentInitOptions.kt | 157 ++++++++++++++++++ .../LocationComponentPluginImpl.kt | 13 +- .../LocationIndicatorLayerRenderer.kt | 25 ++- .../locationcomponent/ModelLayerRenderer.kt | 7 +- 5 files changed, 185 insertions(+), 34 deletions(-) create mode 100644 plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions.kt diff --git a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LayerSourceProvider.kt b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LayerSourceProvider.kt index 9ac4ba077b..757333dbf0 100644 --- a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LayerSourceProvider.kt +++ b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LayerSourceProvider.kt @@ -2,18 +2,15 @@ package com.mapbox.maps.plugin.locationcomponent import com.mapbox.maps.plugin.LocationPuck2D import com.mapbox.maps.plugin.LocationPuck3D -import com.mapbox.maps.plugin.locationcomponent.LocationComponentConstants.LOCATION_INDICATOR_LAYER -import com.mapbox.maps.plugin.locationcomponent.LocationComponentConstants.MODEL_LAYER -import com.mapbox.maps.plugin.locationcomponent.LocationComponentConstants.MODEL_SOURCE -internal class LayerSourceProvider { +internal class LayerSourceProvider(private val locationComponentInitOptions: LocationComponentInitOptions) { fun getModelSource(locationModelLayerOptions: LocationPuck3D): ModelSourceWrapper { if (locationModelLayerOptions.modelUri.isEmpty()) { throw IllegalArgumentException("Model Url must not be empty!") } return ModelSourceWrapper( - MODEL_SOURCE, + locationComponentInitOptions.puck3DSourceId, locationModelLayerOptions.modelUri, locationModelLayerOptions.position.map { it.toDouble() } ) @@ -21,19 +18,19 @@ internal class LayerSourceProvider { fun getModelLayer(locationModelLayerOptions: LocationPuck3D) = ModelLayerWrapper( - MODEL_LAYER, - MODEL_SOURCE, + locationComponentInitOptions.puck3DLayerId, + locationComponentInitOptions.puck3DSourceId, locationModelLayerOptions.modelScale.map { it.toDouble() }, locationModelLayerOptions.modelRotation.map { it.toDouble() }, locationModelLayerOptions.modelTranslation.map { it.toDouble() }, locationModelLayerOptions.modelOpacity.toDouble() ) - fun getLocationIndicatorLayer() = LocationIndicatorLayerWrapper(LOCATION_INDICATOR_LAYER) + fun getLocationIndicatorLayer() = LocationIndicatorLayerWrapper(locationComponentInitOptions.puck2DLayerId) fun getLocationIndicatorLayerRenderer(puckOptions: LocationPuck2D) = - LocationIndicatorLayerRenderer(puckOptions, this) + LocationIndicatorLayerRenderer(locationComponentInitOptions, puckOptions, this) fun getModelLayerRenderer(locationModelLayerOptions: LocationPuck3D) = - ModelLayerRenderer(this, locationModelLayerOptions) + ModelLayerRenderer(locationComponentInitOptions, this, locationModelLayerOptions) } \ No newline at end of file diff --git a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions.kt b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions.kt new file mode 100644 index 0000000000..db4ee0adb1 --- /dev/null +++ b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions.kt @@ -0,0 +1,157 @@ +package com.mapbox.maps.plugin.locationcomponent + +import com.mapbox.maps.plugin.locationcomponent.LocationComponentConstants.BEARING_ICON +import com.mapbox.maps.plugin.locationcomponent.LocationComponentConstants.LOCATION_INDICATOR_LAYER +import com.mapbox.maps.plugin.locationcomponent.LocationComponentConstants.MODEL_LAYER +import com.mapbox.maps.plugin.locationcomponent.LocationComponentConstants.MODEL_SOURCE +import com.mapbox.maps.plugin.locationcomponent.LocationComponentConstants.SHADOW_ICON +import com.mapbox.maps.plugin.locationcomponent.LocationComponentConstants.TOP_ICON +import java.util.Objects +import kotlin.Any +import kotlin.Boolean +import kotlin.Int +import kotlin.String +import kotlin.Unit +import kotlin.jvm.JvmSynthetic + +/** + * Initialisation options for location component to allow multiple instances + * of LocationComponent. + */ +public class LocationComponentInitOptions private constructor( + public val puck2DLayerId: String, + public val puck3DLayerId: String, + public val puck3DSourceId: String, + public val topIconImageId: String, + public val shadowIconImageId: String, + public val bearingIconImageId: String +) { + public override fun toString() = "LocationComponentInitOptions(puck2DLayerId=$puck2DLayerId,puck3DLayerId=$puck3DLayerId, puck3DSourceId=$puck3DSourceId, topIconImageId=$topIconImageId,shadowIconImageId=$shadowIconImageId, bearingIconImageId=$bearingIconImageId)" + + public override fun equals(other: Any?): Boolean = other is LocationComponentInitOptions + && puck2DLayerId == other.puck2DLayerId + && puck3DLayerId == other.puck3DLayerId + && puck3DSourceId == other.puck3DSourceId + && topIconImageId == other.topIconImageId + && shadowIconImageId == other.shadowIconImageId + && bearingIconImageId == other.bearingIconImageId + + public override fun hashCode(): Int = Objects.hash(puck2DLayerId, puck3DLayerId, puck3DSourceId, + topIconImageId, shadowIconImageId, bearingIconImageId) + + /** + * Composes and builds a [LocationComponentInitOptions] object. + * + * This is a concrete implementation of the builder design pattern. + * + * @property + */ + public class Builder { + @set:JvmSynthetic + public var puck2DLayerId: String = LOCATION_INDICATOR_LAYER + + @set:JvmSynthetic + public var puck3DLayerId: String = MODEL_LAYER + + @set:JvmSynthetic + public var puck3DSourceId: String = MODEL_SOURCE + + @set:JvmSynthetic + public var topIconImageId: String = TOP_ICON + + @set:JvmSynthetic + public var shadowIconImageId: String = SHADOW_ICON + + @set:JvmSynthetic + public var bearingIconImageId: String = BEARING_ICON + + /** + * Set puck2DLayerId + * + * @param puck2DLayerId puck2DLayerId + * @return Builder + */ + public fun setPuck2DLayerId(puck2DLayerId: String): Builder { + this.puck2DLayerId = puck2DLayerId + return this + } + + /** + * Set puck3DLayerId + * + * @param puck3DLayerId puck3DLayerId + * @return Builder + */ + public fun setPuck3DLayerId(puck3DLayerId: String): Builder { + this.puck3DLayerId = puck3DLayerId + return this + } + + /** + * Set puck3DSourceId + * + * @param puck3DSourceId puck3DSourceId + * @return Builder + */ + public fun setPuck3DSourceId(puck3DSourceId: String): Builder { + this.puck3DSourceId = puck3DSourceId + return this + } + + /** + * Set topIconImageId + * + * @param topIconImageId topIconImageId + * @return Builder + */ + public fun setTopIconImageId(topIconImageId: String): Builder { + this.topIconImageId = topIconImageId + return this + } + + /** + * Set shadowIconImageId + * + * @param shadowIconImageId shadowIconImageId + * @return Builder + */ + public fun setShadowIconImageId(shadowIconImageId: String): Builder { + this.shadowIconImageId = shadowIconImageId + return this + } + + /** + * Set bearingIconImageId + * + * @param bearingIconImageId bearingIconImageId + * @return Builder + */ + public fun setBearingIconImageId(bearingIconImageId: String): Builder { + this.bearingIconImageId = bearingIconImageId + return this + } + + /** + * Returns a [LocationComponentInitOptions] reference to the object being constructed by the + * builder. + * + * Throws an [IllegalArgumentException] when a non-null property wasn't initialised. + * + * @return LocationComponentInitOptions + */ + public fun build(): LocationComponentInitOptions { + return LocationComponentInitOptions(puck2DLayerId, puck3DLayerId, puck3DSourceId, + topIconImageId, shadowIconImageId, bearingIconImageId) + } + } +} + +/** + * Creates a [LocationComponentInitOptions] through a DSL-style builder. + * + * @param initializer the intialisation block + * @return LocationComponentInitOptions + */ +@JvmSynthetic +public fun LocationComponentInitOptions(initializer: LocationComponentInitOptions.Builder.() -> Unit): + LocationComponentInitOptions = LocationComponentInitOptions.Builder().apply(initializer).build() diff --git a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LocationComponentPluginImpl.kt b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LocationComponentPluginImpl.kt index f7a928b72e..5f061b45fa 100644 --- a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LocationComponentPluginImpl.kt +++ b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LocationComponentPluginImpl.kt @@ -11,8 +11,6 @@ import com.mapbox.maps.RenderedQueryGeometry import com.mapbox.maps.RenderedQueryOptions import com.mapbox.maps.extension.style.StyleInterface import com.mapbox.maps.plugin.delegates.MapDelegateProvider -import com.mapbox.maps.plugin.locationcomponent.LocationComponentConstants.LOCATION_INDICATOR_LAYER -import com.mapbox.maps.plugin.locationcomponent.LocationComponentConstants.MODEL_LAYER import com.mapbox.maps.plugin.locationcomponent.animators.PuckAnimatorManager import com.mapbox.maps.plugin.locationcomponent.generated.* import com.mapbox.maps.plugin.locationcomponent.generated.LocationComponentAttributeParser @@ -23,7 +21,10 @@ import java.util.concurrent.CopyOnWriteArraySet * Default implementation of the LocationComponentPlugin, it renders the configured location puck * to the user's current location. */ -class LocationComponentPluginImpl : LocationComponentPlugin2, LocationConsumer2, +class LocationComponentPluginImpl( + val locationComponentInitOptions: LocationComponentInitOptions = LocationComponentInitOptions.Builder() + .build() +) : LocationComponentPlugin2, LocationConsumer2, LocationComponentSettingsBase2() { private lateinit var delegateProvider: MapDelegateProvider @@ -135,8 +136,8 @@ class LocationComponentPluginImpl : LocationComponentPlugin2, LocationConsumer2, RenderedQueryGeometry(delegateProvider.mapCameraManagerDelegate.pixelForCoordinate(point)), RenderedQueryOptions( listOf( - LOCATION_INDICATOR_LAYER, - MODEL_LAYER + locationComponentInitOptions.puck2DLayerId, + locationComponentInitOptions.puck3DLayerId ), null ) @@ -213,7 +214,7 @@ class LocationComponentPluginImpl : LocationComponentPlugin2, LocationConsumer2, internalSettings.layerAbove, internalSettings.layerBelow ), - layerSourceProvider = LayerSourceProvider(), + layerSourceProvider = LayerSourceProvider(locationComponentInitOptions), animationManager = PuckAnimatorManager( indicatorPositionChangedListener, indicatorBearingChangedListener, diff --git a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LocationIndicatorLayerRenderer.kt b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LocationIndicatorLayerRenderer.kt index 27322a2efc..a83d126be4 100644 --- a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LocationIndicatorLayerRenderer.kt +++ b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LocationIndicatorLayerRenderer.kt @@ -5,16 +5,13 @@ import com.mapbox.bindgen.Value import com.mapbox.geojson.Point import com.mapbox.maps.extension.style.StyleInterface import com.mapbox.maps.plugin.LocationPuck2D -import com.mapbox.maps.plugin.locationcomponent.LocationComponentConstants.BEARING_ICON -import com.mapbox.maps.plugin.locationcomponent.LocationComponentConstants.LOCATION_INDICATOR_LAYER -import com.mapbox.maps.plugin.locationcomponent.LocationComponentConstants.SHADOW_ICON -import com.mapbox.maps.plugin.locationcomponent.LocationComponentConstants.TOP_ICON import com.mapbox.maps.plugin.locationcomponent.utils.BitmapUtils import java.text.DecimalFormat import java.text.NumberFormat import java.util.Locale internal class LocationIndicatorLayerRenderer( + private val locationComponentInitOptions: LocationComponentInitOptions, private val puckOptions: LocationPuck2D, layerSourceProvider: LayerSourceProvider ) : LocationLayerRenderer { @@ -28,7 +25,7 @@ internal class LocationIndicatorLayerRenderer( } override fun isRendererInitialised(): Boolean { - return style?.styleLayerExists(LOCATION_INDICATOR_LAYER) ?: false + return style?.styleLayerExists(locationComponentInitOptions.puck2DLayerId) ?: false } override fun addLayers(positionManager: LocationComponentPositionManager) { @@ -76,22 +73,22 @@ internal class LocationIndicatorLayerRenderer( private fun setupBitmaps() { puckOptions.topImage?.let { BitmapUtils.getBitmapFromDrawable(it) } - ?.let { style?.addImage(TOP_ICON, it) } + ?.let { style?.addImage(locationComponentInitOptions.topIconImageId, it) } puckOptions.bearingImage?.let { BitmapUtils.getBitmapFromDrawable(it) } - ?.let { style?.addImage(BEARING_ICON, it) } + ?.let { style?.addImage(locationComponentInitOptions.bearingIconImageId, it) } puckOptions.shadowImage?.let { BitmapUtils.getBitmapFromDrawable(it) } - ?.let { style?.addImage(SHADOW_ICON, it) } - layer.topImage(TOP_ICON) - layer.bearingImage(BEARING_ICON) - layer.shadowImage(SHADOW_ICON) + ?.let { style?.addImage(locationComponentInitOptions.shadowIconImageId, it) } + layer.topImage(locationComponentInitOptions.topIconImageId) + layer.bearingImage(locationComponentInitOptions.bearingIconImageId) + layer.shadowImage(locationComponentInitOptions.shadowIconImageId) layer.opacity(puckOptions.opacity.toDouble()) } override fun clearBitmaps() { - style?.removeStyleImage(TOP_ICON) - style?.removeStyleImage(BEARING_ICON) - style?.removeStyleImage(SHADOW_ICON) + style?.removeStyleImage(locationComponentInitOptions.topIconImageId) + style?.removeStyleImage(locationComponentInitOptions.bearingIconImageId) + style?.removeStyleImage(locationComponentInitOptions.shadowIconImageId) } override fun updateStyle(style: StyleInterface) { diff --git a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/ModelLayerRenderer.kt b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/ModelLayerRenderer.kt index 5c2618abe7..2cfe3358d9 100644 --- a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/ModelLayerRenderer.kt +++ b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/ModelLayerRenderer.kt @@ -6,10 +6,9 @@ import com.mapbox.bindgen.Value import com.mapbox.geojson.Point import com.mapbox.maps.extension.style.StyleInterface import com.mapbox.maps.plugin.LocationPuck3D -import com.mapbox.maps.plugin.locationcomponent.LocationComponentConstants.MODEL_LAYER -import com.mapbox.maps.plugin.locationcomponent.LocationComponentConstants.MODEL_SOURCE internal class ModelLayerRenderer( + private val locationComponentInitOptions: LocationComponentInitOptions, layerSourceProvider: LayerSourceProvider, private val locationModelLayerOptions: LocationPuck3D ) : LocationLayerRenderer { @@ -32,11 +31,11 @@ internal class ModelLayerRenderer( } private fun isLayerInitialised(): Boolean { - return style?.styleLayerExists(MODEL_LAYER) ?: false + return style?.styleLayerExists(locationComponentInitOptions.puck3DLayerId) ?: false } private fun isSourceInitialised(): Boolean { - return style?.styleSourceExists(MODEL_SOURCE) ?: false + return style?.styleSourceExists(locationComponentInitOptions.puck3DSourceId) ?: false } override fun addLayers(positionManager: LocationComponentPositionManager) { From bcfc0c697bd64ad21a4ef4fb2ba702dbe34c6282 Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Wed, 26 Oct 2022 15:16:38 +0300 Subject: [PATCH 4/9] update api files. --- plugin-locationcomponent/api/metalava.txt | 73 ++++++++++++++++++- .../api/plugin-locationcomponent.api | 70 ++++++++++++++++++ 2 files changed, 142 insertions(+), 1 deletion(-) diff --git a/plugin-locationcomponent/api/metalava.txt b/plugin-locationcomponent/api/metalava.txt index 1e4547e0c8..dbfa4cc7ad 100644 --- a/plugin-locationcomponent/api/metalava.txt +++ b/plugin-locationcomponent/api/metalava.txt @@ -1,6 +1,13 @@ // Signature format: 3.0 package com.mapbox.maps.plugin.locationcomponent { + @com.mapbox.maps.MapboxExperimental public final class CustomJourneyLocationProvider implements com.mapbox.maps.plugin.locationcomponent.LocationProvider { + ctor public CustomJourneyLocationProvider(); + method public void loadJourney(com.mapbox.maps.plugin.locationcomponent.Journey journey); + method public void registerLocationConsumer(com.mapbox.maps.plugin.locationcomponent.LocationConsumer locationConsumer); + method public void unRegisterLocationConsumer(com.mapbox.maps.plugin.locationcomponent.LocationConsumer locationConsumer); + } + public final class DefaultLocationProvider implements com.mapbox.maps.plugin.locationcomponent.LocationProvider { ctor public DefaultLocationProvider(android.content.Context context); method public void addOnCompassCalibrationListener(com.mapbox.maps.plugin.locationcomponent.LocationCompassCalibrationListener listener); @@ -10,6 +17,27 @@ package com.mapbox.maps.plugin.locationcomponent { method public void updatePuckBearingSource(com.mapbox.maps.plugin.PuckBearingSource source); } + @com.mapbox.maps.MapboxExperimental public final class Journey { + ctor public Journey(double speed = 100.0, double angularSpeed = 100.0); + method public double getAngularSpeed(); + method public java.util.List getRemainingLocationsInQueue(); + method public double getSpeed(); + method public void observeJourneyUpdates(com.mapbox.maps.plugin.locationcomponent.JourneyDataObserver observer); + method public void pause(); + method public void queueLocationUpdate(com.mapbox.geojson.Point location); + method public void queueLocationUpdates(java.util.List locations); + method public void restart(); + method public void resume(); + method public void start(); + property public final double angularSpeed; + property public final java.util.List remainingLocationsInQueue; + property public final double speed; + } + + public fun interface JourneyDataObserver { + method public boolean onNewData(com.mapbox.geojson.Point location, double bearing, long locationAnimationDurationMs, long bearingAnimateDurationMs); + } + public fun interface LocationCompassCalibrationListener { method public void onCompassCalibrationNeeded(); } @@ -35,8 +63,49 @@ package com.mapbox.maps.plugin.locationcomponent { field public static final long TRANSITION_ANIMATION_DURATION_MS = 750L; // 0x2eeL } + public final class LocationComponentInitOptions { + method public String! getBearingIconImageId(); + method public String! getPuck2DLayerId(); + method public String! getPuck3DLayerId(); + method public String! getPuck3DSourceId(); + method public String! getShadowIconImageId(); + method public String! getTopIconImageId(); + property public final String! bearingIconImageId; + property public final String! puck2DLayerId; + property public final String! puck3DLayerId; + property public final String! puck3DSourceId; + property public final String! shadowIconImageId; + property public final String! topIconImageId; + } + + public static final class LocationComponentInitOptions.Builder { + ctor public LocationComponentInitOptions.Builder(); + method public com.mapbox.maps.plugin.locationcomponent.LocationComponentInitOptions build(); + method public String getBearingIconImageId(); + method public String getPuck2DLayerId(); + method public String getPuck3DLayerId(); + method public String getPuck3DSourceId(); + method public String getShadowIconImageId(); + method public String getTopIconImageId(); + method public com.mapbox.maps.plugin.locationcomponent.LocationComponentInitOptions.Builder setBearingIconImageId(String bearingIconImageId); + method public com.mapbox.maps.plugin.locationcomponent.LocationComponentInitOptions.Builder setPuck2DLayerId(String puck2DLayerId); + method public com.mapbox.maps.plugin.locationcomponent.LocationComponentInitOptions.Builder setPuck3DLayerId(String puck3DLayerId); + method public com.mapbox.maps.plugin.locationcomponent.LocationComponentInitOptions.Builder setPuck3DSourceId(String puck3DSourceId); + method public com.mapbox.maps.plugin.locationcomponent.LocationComponentInitOptions.Builder setShadowIconImageId(String shadowIconImageId); + method public com.mapbox.maps.plugin.locationcomponent.LocationComponentInitOptions.Builder setTopIconImageId(String topIconImageId); + property public final String bearingIconImageId; + property public final String puck2DLayerId; + property public final String puck3DLayerId; + property public final String puck3DSourceId; + property public final String shadowIconImageId; + property public final String topIconImageId; + } + + public final class LocationComponentInitOptionsKt { + } + public final class LocationComponentPluginImpl extends com.mapbox.maps.plugin.locationcomponent.generated.LocationComponentSettingsBase2 implements com.mapbox.maps.plugin.locationcomponent.LocationComponentPlugin2 com.mapbox.maps.plugin.locationcomponent.LocationConsumer2 { - ctor public LocationComponentPluginImpl(); + ctor public LocationComponentPluginImpl(com.mapbox.maps.plugin.locationcomponent.LocationComponentInitOptions locationComponentInitOptions = LocationComponentInitOptions.().build()); method public void addOnIndicatorAccuracyRadiusChangedListener(com.mapbox.maps.plugin.locationcomponent.OnIndicatorAccuracyRadiusChangedListener listener); method public void addOnIndicatorBearingChangedListener(com.mapbox.maps.plugin.locationcomponent.OnIndicatorBearingChangedListener listener); method public void addOnIndicatorPositionChangedListener(com.mapbox.maps.plugin.locationcomponent.OnIndicatorPositionChangedListener listener); @@ -45,6 +114,7 @@ package com.mapbox.maps.plugin.locationcomponent { method public void bind(android.content.Context context, android.util.AttributeSet? attrs, float pixelRatio); method protected com.mapbox.maps.plugin.locationcomponent.generated.LocationComponentSettings getInternalSettings(); method protected com.mapbox.maps.plugin.locationcomponent.generated.LocationComponentSettings2 getInternalSettings2(); + method public com.mapbox.maps.plugin.locationcomponent.LocationComponentInitOptions getLocationComponentInitOptions(); method public com.mapbox.maps.plugin.locationcomponent.LocationProvider? getLocationProvider(); method public void isLocatedAt(com.mapbox.geojson.Point point, com.mapbox.maps.plugin.locationcomponent.PuckLocatedAtPointListener listener); method public void onAccuracyRadiusUpdated(double[] radius, kotlin.jvm.functions.Function1? options); @@ -65,6 +135,7 @@ package com.mapbox.maps.plugin.locationcomponent { method public void setLocationProvider(com.mapbox.maps.plugin.locationcomponent.LocationProvider locationProvider); property protected com.mapbox.maps.plugin.locationcomponent.generated.LocationComponentSettings internalSettings; property protected com.mapbox.maps.plugin.locationcomponent.generated.LocationComponentSettings2 internalSettings2; + property public final com.mapbox.maps.plugin.locationcomponent.LocationComponentInitOptions locationComponentInitOptions; field public com.mapbox.maps.plugin.locationcomponent.generated.LocationComponentSettings internalSettings; field public com.mapbox.maps.plugin.locationcomponent.generated.LocationComponentSettings2 internalSettings2; } diff --git a/plugin-locationcomponent/api/plugin-locationcomponent.api b/plugin-locationcomponent/api/plugin-locationcomponent.api index ee61fab6a9..f422b8c41a 100644 --- a/plugin-locationcomponent/api/plugin-locationcomponent.api +++ b/plugin-locationcomponent/api/plugin-locationcomponent.api @@ -1,3 +1,10 @@ +public final class com/mapbox/maps/plugin/locationcomponent/CustomJourneyLocationProvider : com/mapbox/maps/plugin/locationcomponent/LocationProvider { + public fun ()V + public final fun loadJourney (Lcom/mapbox/maps/plugin/locationcomponent/Journey;)V + public fun registerLocationConsumer (Lcom/mapbox/maps/plugin/locationcomponent/LocationConsumer;)V + public fun unRegisterLocationConsumer (Lcom/mapbox/maps/plugin/locationcomponent/LocationConsumer;)V +} + public final class com/mapbox/maps/plugin/locationcomponent/DefaultLocationProvider : com/mapbox/maps/plugin/locationcomponent/LocationProvider { public fun (Landroid/content/Context;)V public final fun addOnCompassCalibrationListener (Lcom/mapbox/maps/plugin/locationcomponent/LocationCompassCalibrationListener;)V @@ -7,6 +14,26 @@ public final class com/mapbox/maps/plugin/locationcomponent/DefaultLocationProvi public final fun updatePuckBearingSource (Lcom/mapbox/maps/plugin/PuckBearingSource;)V } +public final class com/mapbox/maps/plugin/locationcomponent/Journey { + public fun ()V + public fun (DD)V + public synthetic fun (DDILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getAngularSpeed ()D + public final fun getRemainingLocationsInQueue ()Ljava/util/List; + public final fun getSpeed ()D + public final fun observeJourneyUpdates (Lcom/mapbox/maps/plugin/locationcomponent/JourneyDataObserver;)V + public final fun pause ()V + public final fun queueLocationUpdate (Lcom/mapbox/geojson/Point;)V + public final fun queueLocationUpdates (Ljava/util/List;)V + public final fun restart ()V + public final fun resume ()V + public final fun start ()V +} + +public abstract interface class com/mapbox/maps/plugin/locationcomponent/JourneyDataObserver { + public abstract fun onNewData (Lcom/mapbox/geojson/Point;DJJ)Z +} + public abstract interface class com/mapbox/maps/plugin/locationcomponent/LocationCompassCalibrationListener { public abstract fun onCompassCalibrationNeeded ()V } @@ -32,13 +59,56 @@ public final class com/mapbox/maps/plugin/locationcomponent/LocationComponentCon public static final field TRANSITION_ANIMATION_DURATION_MS J } +public final class com/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions { + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun equals (Ljava/lang/Object;)Z + public final fun getBearingIconImageId ()Ljava/lang/String; + public final fun getPuck2DLayerId ()Ljava/lang/String; + public final fun getPuck3DLayerId ()Ljava/lang/String; + public final fun getPuck3DSourceId ()Ljava/lang/String; + public final fun getShadowIconImageId ()Ljava/lang/String; + public final fun getTopIconImageId ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions$Builder { + public fun ()V + public final fun build ()Lcom/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions; + public final fun getBearingIconImageId ()Ljava/lang/String; + public final fun getPuck2DLayerId ()Ljava/lang/String; + public final fun getPuck3DLayerId ()Ljava/lang/String; + public final fun getPuck3DSourceId ()Ljava/lang/String; + public final fun getShadowIconImageId ()Ljava/lang/String; + public final fun getTopIconImageId ()Ljava/lang/String; + public final fun setBearingIconImageId (Ljava/lang/String;)Lcom/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions$Builder; + public final synthetic fun setBearingIconImageId (Ljava/lang/String;)V + public final fun setPuck2DLayerId (Ljava/lang/String;)Lcom/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions$Builder; + public final synthetic fun setPuck2DLayerId (Ljava/lang/String;)V + public final fun setPuck3DLayerId (Ljava/lang/String;)Lcom/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions$Builder; + public final synthetic fun setPuck3DLayerId (Ljava/lang/String;)V + public final fun setPuck3DSourceId (Ljava/lang/String;)Lcom/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions$Builder; + public final synthetic fun setPuck3DSourceId (Ljava/lang/String;)V + public final fun setShadowIconImageId (Ljava/lang/String;)Lcom/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions$Builder; + public final synthetic fun setShadowIconImageId (Ljava/lang/String;)V + public final fun setTopIconImageId (Ljava/lang/String;)Lcom/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions$Builder; + public final synthetic fun setTopIconImageId (Ljava/lang/String;)V +} + +public final class com/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptionsKt { + public static final synthetic fun LocationComponentInitOptions (Lkotlin/jvm/functions/Function1;)Lcom/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions; +} + public final class com/mapbox/maps/plugin/locationcomponent/LocationComponentPluginImpl : com/mapbox/maps/plugin/locationcomponent/generated/LocationComponentSettingsBase2, com/mapbox/maps/plugin/locationcomponent/LocationComponentPlugin2, com/mapbox/maps/plugin/locationcomponent/LocationConsumer2 { public fun ()V + public fun (Lcom/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions;)V + public synthetic fun (Lcom/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun addOnIndicatorAccuracyRadiusChangedListener (Lcom/mapbox/maps/plugin/locationcomponent/OnIndicatorAccuracyRadiusChangedListener;)V public fun addOnIndicatorBearingChangedListener (Lcom/mapbox/maps/plugin/locationcomponent/OnIndicatorBearingChangedListener;)V public fun addOnIndicatorPositionChangedListener (Lcom/mapbox/maps/plugin/locationcomponent/OnIndicatorPositionChangedListener;)V public fun bind (Landroid/content/Context;Landroid/util/AttributeSet;F)V public fun cleanup ()V + public final fun getLocationComponentInitOptions ()Lcom/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions; public fun getLocationProvider ()Lcom/mapbox/maps/plugin/locationcomponent/LocationProvider; public fun initialize ()V public fun isLocatedAt (Lcom/mapbox/geojson/Point;Lcom/mapbox/maps/plugin/locationcomponent/PuckLocatedAtPointListener;)V From 00fd47adbc7574039d8db0f3e55f1d101b68d00a Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Fri, 18 Nov 2022 13:15:27 +0200 Subject: [PATCH 5/9] Fix ktlint --- .../LocationComponentInitOptions.kt | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions.kt b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions.kt index db4ee0adb1..162a557324 100644 --- a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions.kt +++ b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/LocationComponentInitOptions.kt @@ -26,25 +26,28 @@ public class LocationComponentInitOptions private constructor( public val shadowIconImageId: String, public val bearingIconImageId: String ) { - public override fun toString() = "LocationComponentInitOptions(puck2DLayerId=$puck2DLayerId,puck3DLayerId=$puck3DLayerId, puck3DSourceId=$puck3DSourceId, topIconImageId=$topIconImageId,shadowIconImageId=$shadowIconImageId, bearingIconImageId=$bearingIconImageId)" - - public override fun equals(other: Any?): Boolean = other is LocationComponentInitOptions - && puck2DLayerId == other.puck2DLayerId - && puck3DLayerId == other.puck3DLayerId - && puck3DSourceId == other.puck3DSourceId - && topIconImageId == other.topIconImageId - && shadowIconImageId == other.shadowIconImageId - && bearingIconImageId == other.bearingIconImageId - - public override fun hashCode(): Int = Objects.hash(puck2DLayerId, puck3DLayerId, puck3DSourceId, - topIconImageId, shadowIconImageId, bearingIconImageId) + public override fun toString() = + "LocationComponentInitOptions(puck2DLayerId=$puck2DLayerId,puck3DLayerId=$puck3DLayerId, puck3DSourceId=$puck3DSourceId, topIconImageId=$topIconImageId,shadowIconImageId=$shadowIconImageId, bearingIconImageId=$bearingIconImageId)" + + public override fun equals(other: Any?): Boolean = other is LocationComponentInitOptions && + puck2DLayerId == other.puck2DLayerId && + puck3DLayerId == other.puck3DLayerId && + puck3DSourceId == other.puck3DSourceId && + topIconImageId == other.topIconImageId && + shadowIconImageId == other.shadowIconImageId && + bearingIconImageId == other.bearingIconImageId + + public override fun hashCode(): Int = Objects.hash( + puck2DLayerId, puck3DLayerId, puck3DSourceId, + topIconImageId, shadowIconImageId, bearingIconImageId + ) /** * Composes and builds a [LocationComponentInitOptions] object. * * This is a concrete implementation of the builder design pattern. * - * @property + * @property */ public class Builder { @set:JvmSynthetic @@ -140,8 +143,10 @@ public class LocationComponentInitOptions private constructor( * @return LocationComponentInitOptions */ public fun build(): LocationComponentInitOptions { - return LocationComponentInitOptions(puck2DLayerId, puck3DLayerId, puck3DSourceId, - topIconImageId, shadowIconImageId, bearingIconImageId) + return LocationComponentInitOptions( + puck2DLayerId, puck3DLayerId, puck3DSourceId, + topIconImageId, shadowIconImageId, bearingIconImageId + ) } } } @@ -154,4 +159,4 @@ public class LocationComponentInitOptions private constructor( */ @JvmSynthetic public fun LocationComponentInitOptions(initializer: LocationComponentInitOptions.Builder.() -> Unit): - LocationComponentInitOptions = LocationComponentInitOptions.Builder().apply(initializer).build() + LocationComponentInitOptions = LocationComponentInitOptions.Builder().apply(initializer).build() \ No newline at end of file From 91912839252feabacce3bcf1549eb449042ab1be Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Fri, 18 Nov 2022 14:19:44 +0200 Subject: [PATCH 6/9] Add multiple location components example. --- app/src/main/AndroidManifest.xml | 15 ++- .../CustomJourneyLocationProviderActivity.kt | 4 +- .../MultipleLocationComponentActivity.kt | 123 ++++++++++++++++++ .../testapp/utils/LocationComponentUtils.kt | 32 +++++ .../main/res/values/example_descriptions.xml | 1 + app/src/main/res/values/example_titles.xml | 1 + .../CustomJourneyLocationProvider.kt | 2 +- 7 files changed, 174 insertions(+), 4 deletions(-) rename app/src/main/java/com/mapbox/maps/testapp/examples/{ => location}/CustomJourneyLocationProviderActivity.kt (96%) create mode 100644 app/src/main/java/com/mapbox/maps/testapp/examples/location/MultipleLocationComponentActivity.kt create mode 100644 app/src/main/java/com/mapbox/maps/testapp/utils/LocationComponentUtils.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c2d309e71f..90f6274174 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1197,7 +1197,7 @@ @@ -1209,6 +1209,19 @@ android:value=".ExampleOverviewActivity" /> + + + + + diff --git a/app/src/main/java/com/mapbox/maps/testapp/examples/CustomJourneyLocationProviderActivity.kt b/app/src/main/java/com/mapbox/maps/testapp/examples/location/CustomJourneyLocationProviderActivity.kt similarity index 96% rename from app/src/main/java/com/mapbox/maps/testapp/examples/CustomJourneyLocationProviderActivity.kt rename to app/src/main/java/com/mapbox/maps/testapp/examples/location/CustomJourneyLocationProviderActivity.kt index dbf1b5b1b5..276d24a188 100644 --- a/app/src/main/java/com/mapbox/maps/testapp/examples/CustomJourneyLocationProviderActivity.kt +++ b/app/src/main/java/com/mapbox/maps/testapp/examples/location/CustomJourneyLocationProviderActivity.kt @@ -1,4 +1,4 @@ -package com.mapbox.maps.testapp.examples +package com.mapbox.maps.testapp.examples.location import android.annotation.SuppressLint import android.os.Bundle @@ -69,7 +69,7 @@ class CustomJourneyLocationProviderActivity : AppCompatActivity(), OnMapClickLis locationComponentPlugin2.let { it.locationPuck = LocationPuck3D( modelUri = "asset://sportcar.glb", - modelScale = listOf(0.5f, 0.5f, 0.5f), + modelScale = listOf(0.2f, 0.2f, 0.2f), modelTranslation = listOf(0.1f, 0.1f, 0.1f), modelRotation = listOf(0.0f, 0.0f, 180.0f) ) diff --git a/app/src/main/java/com/mapbox/maps/testapp/examples/location/MultipleLocationComponentActivity.kt b/app/src/main/java/com/mapbox/maps/testapp/examples/location/MultipleLocationComponentActivity.kt new file mode 100644 index 0000000000..75e207f0b2 --- /dev/null +++ b/app/src/main/java/com/mapbox/maps/testapp/examples/location/MultipleLocationComponentActivity.kt @@ -0,0 +1,123 @@ +package com.mapbox.maps.testapp.examples.location + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.ViewGroup +import android.widget.Button +import androidx.appcompat.app.AppCompatActivity +import com.mapbox.geojson.Point +import com.mapbox.maps.CameraOptions +import com.mapbox.maps.MapView +import com.mapbox.maps.Style +import com.mapbox.maps.extension.style.expressions.dsl.generated.literal +import com.mapbox.maps.plugin.LocationPuck2D +import com.mapbox.maps.plugin.LocationPuck3D +import com.mapbox.maps.plugin.gestures.OnMapClickListener +import com.mapbox.maps.plugin.gestures.gestures +import com.mapbox.maps.plugin.locationcomponent.CustomJourneyLocationProvider +import com.mapbox.maps.plugin.locationcomponent.Journey +import com.mapbox.maps.plugin.locationcomponent.LocationComponentPlugin2 +import com.mapbox.maps.plugin.locationcomponent.location2 +import com.mapbox.maps.testapp.utils.LocationComponentUtils +import com.mapbox.maps.testapp.utils.createLocationComponent +import java.util.concurrent.CopyOnWriteArraySet + +/** + * Example of using multiple location component. + */ +class MultipleLocationComponentActivity : AppCompatActivity(), OnMapClickListener { + + private val journey = Journey() + private val customJourneyLocationProvider = CustomJourneyLocationProvider().apply { loadJourney(journey) } + private lateinit var mapView: MapView + private val locationComponents = CopyOnWriteArraySet() + + @SuppressLint("SetTextI18n") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mapView = MapView(this) + setContentView(mapView) + mapView.addView( + Button(this).apply { + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + text = "Cancel" + setOnClickListener { + journey.pause() + } + } + ) + mapView.getMapboxMap() + .apply { + setCamera( + CameraOptions.Builder() + .center(HELSINKI) + .pitch(40.0) + .zoom(14.0) + .build() + ) + loadStyleUri(Style.MAPBOX_STREETS) { + initLocationComponents() + initClickListeners() + journey.start() + } + } + } + + private fun initClickListeners() { + mapView.gestures.addOnMapClickListener(this) + } + + private fun initLocationComponents() { + locationComponents.add( + mapView.createLocationComponent(LocationComponentUtils.getNextLocationComponentOptions()).apply { + setLocationProvider(customJourneyLocationProvider) + enabled = true + locationPuck = LocationPuck2D( + topImage = null, + bearingImage = null, + ) + puckBearingEnabled = true + pulsingEnabled = true + } + ) + locationComponents.add( + mapView.createLocationComponent(LocationComponentUtils.getNextLocationComponentOptions()).apply { + setLocationProvider(customJourneyLocationProvider) + enabled = true + locationPuck = LocationPuck3D( + modelUri = "asset://sportcar.glb", + modelScale = listOf(0.2f, 0.2f, 0.2f), + modelTranslation = listOf(0.1f, 0.1f, 0.1f), + modelRotation = listOf(0.0f, 0.0f, 180.0f) + ) + puckBearingEnabled = true + } + ) + locationComponents.add( + mapView.createLocationComponent(LocationComponentUtils.getNextLocationComponentOptions()).apply { + setLocationProvider(customJourneyLocationProvider) + enabled = true + locationPuck = LocationPuck3D( + modelUri = "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Duck/glTF-Embedded/Duck.gltf", + modelScale = listOf(0.2f, 0.2f, 0.2f), + modelRotation = listOf(0f, 0f, -90f), + modelTranslation = listOf(0f, 0.0f, 0.0f) + ) + puckBearingEnabled = true + } + ) + + } + + override fun onMapClick(point: Point): Boolean { + journey.queueLocationUpdate(point) + return true + } + + companion object { + private val HELSINKI = Point.fromLngLat(24.9384, 60.1699) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/mapbox/maps/testapp/utils/LocationComponentUtils.kt b/app/src/main/java/com/mapbox/maps/testapp/utils/LocationComponentUtils.kt new file mode 100644 index 0000000000..c626c2d498 --- /dev/null +++ b/app/src/main/java/com/mapbox/maps/testapp/utils/LocationComponentUtils.kt @@ -0,0 +1,32 @@ +package com.mapbox.maps.testapp.utils + +import com.mapbox.maps.MapView +import com.mapbox.maps.plugin.Plugin +import com.mapbox.maps.plugin.locationcomponent.LocationComponentInitOptions +import com.mapbox.maps.plugin.locationcomponent.LocationComponentPlugin2 +import com.mapbox.maps.plugin.locationcomponent.LocationComponentPluginImpl + +public fun MapView.createLocationComponent(locationComponentInitOptions: LocationComponentInitOptions): LocationComponentPlugin2 { + val locationComponent = LocationComponentPluginImpl(locationComponentInitOptions) + val locationComponentPlugin = Plugin.Custom( + locationComponentInitOptions.hashCode().toString(), + locationComponent + ) + this.createPlugin(locationComponentPlugin) + return locationComponent +} + +object LocationComponentUtils { + private var custom2DLayerIdCount = 0 + private var custom3DLayerIdCount = 0 + private var custom3DSourceIdCount = 0 + + fun getNextLocationComponentOptions() = LocationComponentInitOptions { + puck2DLayerId = "custom_location_component_2d_layer_$custom2DLayerIdCount" + puck3DLayerId = "custom_location_component_3d_layer_$custom3DLayerIdCount" + puck3DSourceId = "custom_location_component_3d_source_$custom3DSourceIdCount" + custom2DLayerIdCount++ + custom3DLayerIdCount++ + custom3DSourceIdCount++ + } +} \ No newline at end of file diff --git a/app/src/main/res/values/example_descriptions.xml b/app/src/main/res/values/example_descriptions.xml index 8f6aea98d0..a7af38f6ea 100644 --- a/app/src/main/res/values/example_descriptions.xml +++ b/app/src/main/res/values/example_descriptions.xml @@ -95,4 +95,5 @@ Advanced viewport with gestures showcase Add a third party vector tile source. Showcase usage of custom location provider. + Showcase usage of multiple location component. diff --git a/app/src/main/res/values/example_titles.xml b/app/src/main/res/values/example_titles.xml index b6f42e6ce0..1dc4ae9036 100644 --- a/app/src/main/res/values/example_titles.xml +++ b/app/src/main/res/values/example_titles.xml @@ -95,4 +95,5 @@ Advanced Viewport with gestures External Vector Source Custom location provider + Multiple location components diff --git a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomJourneyLocationProvider.kt b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomJourneyLocationProvider.kt index 31968a6561..fdb2146e29 100644 --- a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomJourneyLocationProvider.kt +++ b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomJourneyLocationProvider.kt @@ -51,7 +51,7 @@ class CustomJourneyLocationProvider : LocationProvider { } @MapboxExperimental -class Journey(val speed: Double = 100.0, val angularSpeed: Double = 100.0) { +class Journey(val speed: Double = 100.0, val angularSpeed: Double = 500.0) { private val locationList = CopyOnWriteArrayList() private val initialTimeStamp: Long = 0 private val remainingPoints = ConcurrentLinkedQueue() From 827b26fb589c7208761925e2162f39a7c47b50fa Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Fri, 18 Nov 2022 17:05:20 +0200 Subject: [PATCH 7/9] Update examples to use separate location providers, fix custom journey location provider. --- .../CustomJourneyLocationProviderActivity.kt | 30 +++- .../MultipleLocationComponentActivity.kt | 148 ++++++++++++------ .../testapp/utils/LocationComponentUtils.kt | 17 +- .../CustomJourneyLocationProvider.kt | 21 ++- 4 files changed, 147 insertions(+), 69 deletions(-) diff --git a/app/src/main/java/com/mapbox/maps/testapp/examples/location/CustomJourneyLocationProviderActivity.kt b/app/src/main/java/com/mapbox/maps/testapp/examples/location/CustomJourneyLocationProviderActivity.kt index 276d24a188..dc6e27892b 100644 --- a/app/src/main/java/com/mapbox/maps/testapp/examples/location/CustomJourneyLocationProviderActivity.kt +++ b/app/src/main/java/com/mapbox/maps/testapp/examples/location/CustomJourneyLocationProviderActivity.kt @@ -10,20 +10,29 @@ import com.mapbox.maps.CameraOptions import com.mapbox.maps.MapView import com.mapbox.maps.Style import com.mapbox.maps.plugin.LocationPuck3D +import com.mapbox.maps.plugin.annotation.annotations +import com.mapbox.maps.plugin.annotation.generated.PointAnnotation +import com.mapbox.maps.plugin.annotation.generated.PointAnnotationManager +import com.mapbox.maps.plugin.annotation.generated.PointAnnotationOptions +import com.mapbox.maps.plugin.annotation.generated.createPointAnnotationManager import com.mapbox.maps.plugin.gestures.OnMapClickListener import com.mapbox.maps.plugin.gestures.gestures import com.mapbox.maps.plugin.locationcomponent.CustomJourneyLocationProvider import com.mapbox.maps.plugin.locationcomponent.Journey import com.mapbox.maps.plugin.locationcomponent.location2 +import com.mapbox.maps.testapp.R +import com.mapbox.maps.testapp.utils.BitmapUtils +import java.util.* /** * Example of using custom location provider. */ class CustomJourneyLocationProviderActivity : AppCompatActivity(), OnMapClickListener { - private val journey = Journey() private val customJourneyLocationProvider = CustomJourneyLocationProvider().apply { loadJourney(journey) } private lateinit var mapView: MapView + private val annotationList = LinkedList() + private lateinit var pointAnnotationManager: PointAnnotationManager @SuppressLint("SetTextI18n") override fun onCreate(savedInstanceState: Bundle?) { @@ -42,6 +51,7 @@ class CustomJourneyLocationProviderActivity : AppCompatActivity(), OnMapClickLis } } ) + pointAnnotationManager = mapView.annotations.createPointAnnotationManager() mapView.getMapboxMap() .apply { setCamera( @@ -57,6 +67,13 @@ class CustomJourneyLocationProviderActivity : AppCompatActivity(), OnMapClickLis journey.start() } } + + journey.observeJourneyUpdates { location, bearing, locationAnimationDurationMs, bearingAnimateDurationMs -> + annotationList.poll()?.let { + pointAnnotationManager.delete(it) + } + true + } } private fun initClickListeners() { @@ -80,6 +97,17 @@ class CustomJourneyLocationProviderActivity : AppCompatActivity(), OnMapClickLis override fun onMapClick(point: Point): Boolean { journey.queueLocationUpdate(point) + BitmapUtils.bitmapFromDrawableRes( + this, + R.drawable.red_marker + )?.let { + pointAnnotationManager.create( + PointAnnotationOptions() + .withPoint(point) + .withIconImage(it) + .withDraggable(true) + ).also { annotation -> annotationList.add(annotation) } + } return true } diff --git a/app/src/main/java/com/mapbox/maps/testapp/examples/location/MultipleLocationComponentActivity.kt b/app/src/main/java/com/mapbox/maps/testapp/examples/location/MultipleLocationComponentActivity.kt index 75e207f0b2..57bdde5f16 100644 --- a/app/src/main/java/com/mapbox/maps/testapp/examples/location/MultipleLocationComponentActivity.kt +++ b/app/src/main/java/com/mapbox/maps/testapp/examples/location/MultipleLocationComponentActivity.kt @@ -3,34 +3,46 @@ package com.mapbox.maps.testapp.examples.location import android.annotation.SuppressLint import android.os.Bundle import android.view.ViewGroup +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.widget.Button import androidx.appcompat.app.AppCompatActivity import com.mapbox.geojson.Point import com.mapbox.maps.CameraOptions import com.mapbox.maps.MapView +import com.mapbox.maps.MapboxExperimental import com.mapbox.maps.Style -import com.mapbox.maps.extension.style.expressions.dsl.generated.literal import com.mapbox.maps.plugin.LocationPuck2D import com.mapbox.maps.plugin.LocationPuck3D +import com.mapbox.maps.plugin.annotation.annotations +import com.mapbox.maps.plugin.annotation.generated.* import com.mapbox.maps.plugin.gestures.OnMapClickListener import com.mapbox.maps.plugin.gestures.gestures import com.mapbox.maps.plugin.locationcomponent.CustomJourneyLocationProvider import com.mapbox.maps.plugin.locationcomponent.Journey import com.mapbox.maps.plugin.locationcomponent.LocationComponentPlugin2 -import com.mapbox.maps.plugin.locationcomponent.location2 +import com.mapbox.maps.testapp.R +import com.mapbox.maps.testapp.utils.BitmapUtils import com.mapbox.maps.testapp.utils.LocationComponentUtils import com.mapbox.maps.testapp.utils.createLocationComponent -import java.util.concurrent.CopyOnWriteArraySet +import java.util.* /** * Example of using multiple location component. */ +@OptIn(MapboxExperimental::class) class MultipleLocationComponentActivity : AppCompatActivity(), OnMapClickListener { - - private val journey = Journey() - private val customJourneyLocationProvider = CustomJourneyLocationProvider().apply { loadJourney(journey) } private lateinit var mapView: MapView - private val locationComponents = CopyOnWriteArraySet() + private val journeys = mutableListOf(Journey(speed = 150.0), Journey(speed = 100.0)) + private val customJourneyLocationProviders = mutableListOf( + CustomJourneyLocationProvider().apply { loadJourney(journeys.first()) }, + CustomJourneyLocationProvider().apply { loadJourney(journeys.last()) } + ) + private val annotationLists = + mutableListOf(LinkedList(), LinkedList()) + private val locationComponents = mutableListOf() + private lateinit var pointAnnotationManager: PointAnnotationManager + + var selectedPuck = 0 @SuppressLint("SetTextI18n") override fun onCreate(savedInstanceState: Bundle?) { @@ -39,16 +51,15 @@ class MultipleLocationComponentActivity : AppCompatActivity(), OnMapClickListene setContentView(mapView) mapView.addView( Button(this).apply { - layoutParams = ViewGroup.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ) - text = "Cancel" + layoutParams = ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT) + text = "Controlling ${if (selectedPuck == 0) "Car" else "Duck"}" setOnClickListener { - journey.pause() + toggleControl() + text = "Controlling ${if (selectedPuck == 0) "Car" else "Duck"}" } } ) + pointAnnotationManager = mapView.annotations.createPointAnnotationManager() mapView.getMapboxMap() .apply { setCamera( @@ -61,9 +72,22 @@ class MultipleLocationComponentActivity : AppCompatActivity(), OnMapClickListene loadStyleUri(Style.MAPBOX_STREETS) { initLocationComponents() initClickListeners() - journey.start() + journeys.forEach { it.start() } + } + } + + journeys.forEachIndexed { index, journey -> + journey.observeJourneyUpdates { _, _, _, _ -> + annotationLists[index].poll()?.let { + pointAnnotationManager.delete(it) } + true } + } + } + + private fun toggleControl() { + selectedPuck = (selectedPuck + 1) % 2 } private fun initClickListeners() { @@ -71,49 +95,77 @@ class MultipleLocationComponentActivity : AppCompatActivity(), OnMapClickListene } private fun initLocationComponents() { + // Puck with pulsing car locationComponents.add( - mapView.createLocationComponent(LocationComponentUtils.getNextLocationComponentOptions()).apply { - setLocationProvider(customJourneyLocationProvider) - enabled = true - locationPuck = LocationPuck2D( - topImage = null, - bearingImage = null, - ) - puckBearingEnabled = true - pulsingEnabled = true - } + mapView.createLocationComponent(LocationComponentUtils.getNextLocationComponentOptions()) + .apply { + setLocationProvider(customJourneyLocationProviders.first()) + enabled = true + locationPuck = LocationPuck2D( + topImage = null, + bearingImage = null, + ) + puckBearingEnabled = true + pulsingEnabled = true + } ) locationComponents.add( - mapView.createLocationComponent(LocationComponentUtils.getNextLocationComponentOptions()).apply { - setLocationProvider(customJourneyLocationProvider) - enabled = true - locationPuck = LocationPuck3D( - modelUri = "asset://sportcar.glb", - modelScale = listOf(0.2f, 0.2f, 0.2f), - modelTranslation = listOf(0.1f, 0.1f, 0.1f), - modelRotation = listOf(0.0f, 0.0f, 180.0f) - ) - puckBearingEnabled = true - } + mapView.createLocationComponent(LocationComponentUtils.getNextLocationComponentOptions()) + .apply { + setLocationProvider(customJourneyLocationProviders.first()) + enabled = true + locationPuck = LocationPuck3D( + modelUri = "asset://sportcar.glb", + modelScale = listOf(0.3f, 0.3f, 0.3f), + modelTranslation = listOf(0.1f, 0.1f, 0.1f), + modelRotation = listOf(0.0f, 0.0f, 180.0f) + ) + puckBearingEnabled = true + } ) + // Puck with pulsing duck locationComponents.add( - mapView.createLocationComponent(LocationComponentUtils.getNextLocationComponentOptions()).apply { - setLocationProvider(customJourneyLocationProvider) - enabled = true - locationPuck = LocationPuck3D( - modelUri = "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Duck/glTF-Embedded/Duck.gltf", - modelScale = listOf(0.2f, 0.2f, 0.2f), - modelRotation = listOf(0f, 0f, -90f), - modelTranslation = listOf(0f, 0.0f, 0.0f) - ) - puckBearingEnabled = true - } + mapView.createLocationComponent(LocationComponentUtils.getNextLocationComponentOptions()) + .apply { + setLocationProvider(customJourneyLocationProviders.last()) + enabled = true + locationPuck = LocationPuck2D( + topImage = null, + bearingImage = null, + ) + puckBearingEnabled = true + pulsingEnabled = true + } + ) + locationComponents.add( + mapView.createLocationComponent(LocationComponentUtils.getNextLocationComponentOptions()) + .apply { + setLocationProvider(customJourneyLocationProviders.last()) + enabled = true + locationPuck = LocationPuck3D( + modelUri = "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Duck/glTF-Embedded/Duck.gltf", + modelScale = listOf(0.5f, 0.5f, 0.5f), + modelRotation = listOf(0f, 0f, -90f), + modelTranslation = listOf(0f, 0.0f, 0.0f) + ) + puckBearingEnabled = true + } ) - } override fun onMapClick(point: Point): Boolean { - journey.queueLocationUpdate(point) + journeys[selectedPuck].queueLocationUpdate(point) + BitmapUtils.bitmapFromDrawableRes( + this, + if (selectedPuck == 0) R.drawable.red_marker else R.drawable.blue_marker_view + )?.let { + pointAnnotationManager.create( + PointAnnotationOptions() + .withPoint(point) + .withIconImage(it) + .withDraggable(true) + ).also { annotation -> annotationLists[selectedPuck].add(annotation) } + } return true } diff --git a/app/src/main/java/com/mapbox/maps/testapp/utils/LocationComponentUtils.kt b/app/src/main/java/com/mapbox/maps/testapp/utils/LocationComponentUtils.kt index c626c2d498..3f54b20a6d 100644 --- a/app/src/main/java/com/mapbox/maps/testapp/utils/LocationComponentUtils.kt +++ b/app/src/main/java/com/mapbox/maps/testapp/utils/LocationComponentUtils.kt @@ -17,16 +17,15 @@ public fun MapView.createLocationComponent(locationComponentInitOptions: Locatio } object LocationComponentUtils { - private var custom2DLayerIdCount = 0 - private var custom3DLayerIdCount = 0 - private var custom3DSourceIdCount = 0 + private var customLocationComponentCount = 0 fun getNextLocationComponentOptions() = LocationComponentInitOptions { - puck2DLayerId = "custom_location_component_2d_layer_$custom2DLayerIdCount" - puck3DLayerId = "custom_location_component_3d_layer_$custom3DLayerIdCount" - puck3DSourceId = "custom_location_component_3d_source_$custom3DSourceIdCount" - custom2DLayerIdCount++ - custom3DLayerIdCount++ - custom3DSourceIdCount++ + puck2DLayerId = "custom_location_component_2d_layer_$customLocationComponentCount" + puck3DLayerId = "custom_location_component_3d_layer_$customLocationComponentCount" + puck3DSourceId = "custom_location_component_3d_source_$customLocationComponentCount" + puck3DSourceId = "custom_location_component_top_icon_image_id_$customLocationComponentCount" + puck3DSourceId = "custom_location_component_shadow_icon_image_id_$customLocationComponentCount" + puck3DSourceId = "custom_location_component_bearing_icon_image_id_$customLocationComponentCount" + customLocationComponentCount++ } } \ No newline at end of file diff --git a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomJourneyLocationProvider.kt b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomJourneyLocationProvider.kt index fdb2146e29..601c09f0a6 100644 --- a/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomJourneyLocationProvider.kt +++ b/plugin-locationcomponent/src/main/java/com/mapbox/maps/plugin/locationcomponent/CustomJourneyLocationProvider.kt @@ -143,26 +143,25 @@ class Journey(val speed: Double = 100.0, val angularSpeed: Double = 500.0) { private fun drainQueue() { remainingPoints.peek()?.let { data -> observers.forEach { - if (it.onNewData( + if (!it.onNewData( data.location, data.bearing, data.locationAnimationDurationMs, data.bearingAnimateDurationMs ) ) { - if (isPlaying) { - handler.postDelayed( - { - remainingPoints.poll() - drainQueue() - }, - max(data.locationAnimationDurationMs, data.bearingAnimateDurationMs) - ) - } - } else { observers.remove(it) } } + if (isPlaying) { + handler.postDelayed( + { + remainingPoints.poll() + drainQueue() + }, + max(data.locationAnimationDurationMs, data.bearingAnimateDurationMs) + ) + } } } From 3dccf51e51496d101eb9757c07fbd7c25b60df32 Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Fri, 25 Nov 2022 18:10:01 +0200 Subject: [PATCH 8/9] Add MultiPuckViewportState. --- .../MultipleLocationComponentActivity.kt | 56 ++-- .../testapp/utils/LocationComponentUtils.kt | 14 - .../activity_multi_locationcomponent.xml | 36 +++ .../LocationComponentInitOptions.kt | 27 +- .../plugin/viewport/ViewportPluginImpl.kt | 18 +- .../state/MultiPuckViewportStateImpl.kt | 285 ++++++++++++++++++ .../maps/plugin/viewport/ViewportPlugin.kt | 12 +- .../data/MultiPuckViewportStateBearing.kt | 57 ++++ .../data/MultiPuckViewportStateOptions.kt | 135 +++++++++ .../viewport/state/MultiPuckViewportState.kt | 22 ++ 10 files changed, 615 insertions(+), 47 deletions(-) create mode 100644 app/src/main/res/layout/activity_multi_locationcomponent.xml create mode 100644 plugin-viewport/src/main/kotlin/com/mapbox/maps/plugin/viewport/state/MultiPuckViewportStateImpl.kt create mode 100644 sdk-base/src/main/java/com/mapbox/maps/plugin/viewport/data/MultiPuckViewportStateBearing.kt create mode 100644 sdk-base/src/main/java/com/mapbox/maps/plugin/viewport/data/MultiPuckViewportStateOptions.kt create mode 100644 sdk-base/src/main/java/com/mapbox/maps/plugin/viewport/state/MultiPuckViewportState.kt diff --git a/app/src/main/java/com/mapbox/maps/testapp/examples/location/MultipleLocationComponentActivity.kt b/app/src/main/java/com/mapbox/maps/testapp/examples/location/MultipleLocationComponentActivity.kt index 57bdde5f16..9f71c643c3 100644 --- a/app/src/main/java/com/mapbox/maps/testapp/examples/location/MultipleLocationComponentActivity.kt +++ b/app/src/main/java/com/mapbox/maps/testapp/examples/location/MultipleLocationComponentActivity.kt @@ -5,12 +5,10 @@ import android.os.Bundle import android.view.ViewGroup import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.widget.Button +import android.widget.LinearLayout import androidx.appcompat.app.AppCompatActivity import com.mapbox.geojson.Point -import com.mapbox.maps.CameraOptions -import com.mapbox.maps.MapView -import com.mapbox.maps.MapboxExperimental -import com.mapbox.maps.Style +import com.mapbox.maps.* import com.mapbox.maps.plugin.LocationPuck2D import com.mapbox.maps.plugin.LocationPuck3D import com.mapbox.maps.plugin.annotation.annotations @@ -19,10 +17,18 @@ import com.mapbox.maps.plugin.gestures.OnMapClickListener import com.mapbox.maps.plugin.gestures.gestures import com.mapbox.maps.plugin.locationcomponent.CustomJourneyLocationProvider import com.mapbox.maps.plugin.locationcomponent.Journey +import com.mapbox.maps.plugin.locationcomponent.LocationComponentInitOptions import com.mapbox.maps.plugin.locationcomponent.LocationComponentPlugin2 +import com.mapbox.maps.plugin.viewport.data.FollowPuckViewportStateBearing +import com.mapbox.maps.plugin.viewport.data.FollowPuckViewportStateOptions +import com.mapbox.maps.plugin.viewport.data.MultiPuckViewportStateBearing +import com.mapbox.maps.plugin.viewport.data.MultiPuckViewportStateOptions +import com.mapbox.maps.plugin.viewport.state.MultiPuckViewportState +import com.mapbox.maps.plugin.viewport.viewport import com.mapbox.maps.testapp.R +import com.mapbox.maps.testapp.databinding.ActivityMultiDisplayBinding +import com.mapbox.maps.testapp.databinding.ActivityMultiLocationcomponentBinding import com.mapbox.maps.testapp.utils.BitmapUtils -import com.mapbox.maps.testapp.utils.LocationComponentUtils import com.mapbox.maps.testapp.utils.createLocationComponent import java.util.* @@ -32,7 +38,7 @@ import java.util.* @OptIn(MapboxExperimental::class) class MultipleLocationComponentActivity : AppCompatActivity(), OnMapClickListener { private lateinit var mapView: MapView - private val journeys = mutableListOf(Journey(speed = 150.0), Journey(speed = 100.0)) + private val journeys = mutableListOf(Journey(speed = 150.0, angularSpeed = 150.0), Journey(speed = 100.0, angularSpeed = 150.0)) private val customJourneyLocationProviders = mutableListOf( CustomJourneyLocationProvider().apply { loadJourney(journeys.first()) }, CustomJourneyLocationProvider().apply { loadJourney(journeys.last()) } @@ -41,24 +47,26 @@ class MultipleLocationComponentActivity : AppCompatActivity(), OnMapClickListene mutableListOf(LinkedList(), LinkedList()) private val locationComponents = mutableListOf() private lateinit var pointAnnotationManager: PointAnnotationManager + private lateinit var multiPuckViewportState: MultiPuckViewportState var selectedPuck = 0 @SuppressLint("SetTextI18n") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - mapView = MapView(this) - setContentView(mapView) - mapView.addView( - Button(this).apply { - layoutParams = ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT) + val binding = ActivityMultiLocationcomponentBinding.inflate(layoutInflater) + setContentView(binding.root) + mapView = binding.mapView + binding.toggleControlBtn.apply { + text = "Controlling ${if (selectedPuck == 0) "Car" else "Duck"}" + setOnClickListener { + toggleControl() text = "Controlling ${if (selectedPuck == 0) "Car" else "Duck"}" - setOnClickListener { - toggleControl() - text = "Controlling ${if (selectedPuck == 0) "Car" else "Duck"}" - } } - ) + } + binding.followPucksButton.setOnClickListener { + mapView.viewport.transitionTo(multiPuckViewportState) + } pointAnnotationManager = mapView.annotations.createPointAnnotationManager() mapView.getMapboxMap() .apply { @@ -97,7 +105,7 @@ class MultipleLocationComponentActivity : AppCompatActivity(), OnMapClickListene private fun initLocationComponents() { // Puck with pulsing car locationComponents.add( - mapView.createLocationComponent(LocationComponentUtils.getNextLocationComponentOptions()) + mapView.createLocationComponent(LocationComponentInitOptions.getNextUniqueLocationComponentOptions()) .apply { setLocationProvider(customJourneyLocationProviders.first()) enabled = true @@ -110,7 +118,7 @@ class MultipleLocationComponentActivity : AppCompatActivity(), OnMapClickListene } ) locationComponents.add( - mapView.createLocationComponent(LocationComponentUtils.getNextLocationComponentOptions()) + mapView.createLocationComponent(LocationComponentInitOptions.getNextUniqueLocationComponentOptions()) .apply { setLocationProvider(customJourneyLocationProviders.first()) enabled = true @@ -125,7 +133,7 @@ class MultipleLocationComponentActivity : AppCompatActivity(), OnMapClickListene ) // Puck with pulsing duck locationComponents.add( - mapView.createLocationComponent(LocationComponentUtils.getNextLocationComponentOptions()) + mapView.createLocationComponent(LocationComponentInitOptions.getNextUniqueLocationComponentOptions()) .apply { setLocationProvider(customJourneyLocationProviders.last()) enabled = true @@ -138,7 +146,7 @@ class MultipleLocationComponentActivity : AppCompatActivity(), OnMapClickListene } ) locationComponents.add( - mapView.createLocationComponent(LocationComponentUtils.getNextLocationComponentOptions()) + mapView.createLocationComponent(LocationComponentInitOptions.getNextUniqueLocationComponentOptions()) .apply { setLocationProvider(customJourneyLocationProviders.last()) enabled = true @@ -151,6 +159,14 @@ class MultipleLocationComponentActivity : AppCompatActivity(), OnMapClickListene puckBearingEnabled = true } ) + multiPuckViewportState = mapView.viewport.makeMultiPuckViewportState( + MultiPuckViewportStateOptions + .Builder() + .bearing(MultiPuckViewportStateBearing.SyncWithLocationPuck(locationComponents.first())) + .padding(EdgeInsets(100.0, 100.0, 100.0, 100.0)) + .build(), + locationComponents = locationComponents + ) } override fun onMapClick(point: Point): Boolean { diff --git a/app/src/main/java/com/mapbox/maps/testapp/utils/LocationComponentUtils.kt b/app/src/main/java/com/mapbox/maps/testapp/utils/LocationComponentUtils.kt index 3f54b20a6d..2a138b682c 100644 --- a/app/src/main/java/com/mapbox/maps/testapp/utils/LocationComponentUtils.kt +++ b/app/src/main/java/com/mapbox/maps/testapp/utils/LocationComponentUtils.kt @@ -14,18 +14,4 @@ public fun MapView.createLocationComponent(locationComponentInitOptions: Locatio ) this.createPlugin(locationComponentPlugin) return locationComponent -} - -object LocationComponentUtils { - private var customLocationComponentCount = 0 - - fun getNextLocationComponentOptions() = LocationComponentInitOptions { - puck2DLayerId = "custom_location_component_2d_layer_$customLocationComponentCount" - puck3DLayerId = "custom_location_component_3d_layer_$customLocationComponentCount" - puck3DSourceId = "custom_location_component_3d_source_$customLocationComponentCount" - puck3DSourceId = "custom_location_component_top_icon_image_id_$customLocationComponentCount" - puck3DSourceId = "custom_location_component_shadow_icon_image_id_$customLocationComponentCount" - puck3DSourceId = "custom_location_component_bearing_icon_image_id_$customLocationComponentCount" - customLocationComponentCount++ - } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_multi_locationcomponent.xml b/app/src/main/res/layout/activity_multi_locationcomponent.xml new file mode 100644 index 0000000000..6cfa8cbf62 --- /dev/null +++ b/app/src/main/res/layout/activity_multi_locationcomponent.xml @@ -0,0 +1,36 @@ + + + + + + + +