Tracking / Follow me
Contents
The Mobile Maps SDK examples require that you first complete the initial project set-up.
Track a vehicle or other assets on the map.
activity_sample_follow_me.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent">
<com.trimblemaps.mapsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:visibility="invisible"
android:onClick="onButtonRecenterClick"
android:id="@+id/btn_recenter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|center"
android:text="Recenter" />
</com.trimblemaps.mapsdk.maps.MapView>
</FrameLayout>
SampleFollowMeActivity.java
Before running the Java or Kotlin code, the theme needs to be set in the Theme.xml
file as shown below.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.AsyncAcctInit" parent="Theme.AppCompat" />
</resources>
Also, make sure permissions are set.
import android.annotation.SuppressLint;
import android.location.Location;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import com.trimblemaps.account.LicensedFeature;
import com.trimblemaps.account.TrimbleMapsAccountManager;
import com.trimblemaps.account.models.TrimbleMapsAccount;
import com.trimblemaps.android.core.location.LocationEngine;
import com.trimblemaps.android.core.location.LocationEngineCallback;
import com.trimblemaps.android.core.location.LocationEngineProvider;
import com.trimblemaps.android.core.location.LocationEngineRequest;
import com.trimblemaps.android.core.location.LocationEngineResult;
import com.trimblemaps.android.core.permissions.PermissionsListener;
import com.trimblemaps.android.core.permissions.PermissionsManager;
import com.trimblemaps.android.gestures.MoveGestureDetector;
import com.trimblemaps.mapsdk.TrimbleMaps;
import com.trimblemaps.mapsdk.camera.CameraPosition;
import com.trimblemaps.mapsdk.location.LocationComponent;
import com.trimblemaps.mapsdk.location.LocationComponentActivationOptions;
import com.trimblemaps.mapsdk.location.LocationUpdate;
import com.trimblemaps.mapsdk.location.modes.CameraMode;
import com.trimblemaps.mapsdk.location.modes.RenderMode;
import com.trimblemaps.mapsdk.maps.MapView;
import com.trimblemaps.mapsdk.maps.OnMapReadyCallback;
import com.trimblemaps.mapsdk.maps.Style;
import com.trimblemaps.mapsdk.maps.TrimbleMapsMap;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import kotlin.collections.ArraysKt;
import kotlin.jvm.internal.Intrinsics;
public class SampleFollowMeActivity extends AppCompatActivity implements OnMapReadyCallback, LocationEngineCallback<LocationEngineResult>, TrimbleMapsMap.OnMoveListener {
private static final long DEFAULT_INTERVAL_IN_MILLISECONDS = 900L;
private static final long DEFAULT_MAX_WAIT_TIME = DEFAULT_INTERVAL_IN_MILLISECONDS * 5;
private static final int ANIMATION_TIME = 400;
private MapView mapView;
private TrimbleMapsMap map;
private LocationComponent locationComponent;
private LocationEngine locationEngine;
private Button recenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Authorize the API key for the session.
// .apiKey() requires your Trimble Maps API key
TrimbleMapsAccount trimbleMapsAccount = TrimbleMapsAccount.builder()
.apiKey("Your-API-key-here")
.addLicensedFeature(LicensedFeature.MAPS_SDK)
.build();
// Initialize the session
TrimbleMapsAccountManager.initialize(trimbleMapsAccount);
TrimbleMapsAccountManager.awaitInitialization();
// Get an instance of the map, done before the layout is set.
TrimbleMaps.getInstance(this);
setContentView(R.layout.activity_sample_follow_me);
// Find the recenter button
recenter = (Button) findViewById(R.id.btn_recenter);
// Set up the MapView from the layout
mapView = (MapView) findViewById(R.id.mapView);
// Implementing OnMapReadyCallback into the activity
mapView.getMapAsync(this);
}
@Override
public void onMapReady(TrimbleMapsMap trimbleMapsMap) {
// The TrimbleMapsMap object is created, now a style can be applied to render a map.
map = trimbleMapsMap;
map.addOnMoveListener(this);
map.setStyle(Style.MOBILE_DEFAULT, new Style.OnStyleLoaded() {
@Override
public void onStyleLoaded(@NonNull Style style) {
// Not setting the start location here, as that will be tracked by the user's
// location. Instead, just defining the initial zoom.
map.setCameraPosition(new CameraPosition.Builder().zoom(15).build());
setupLocationComponent();
}
});
}
// Before a user's location can be used, location permissions will need to
// be given to the app. This code assumes those permissions have already been granted by this
// point. For more information on how to grant location permissions to your app,
// see: https://developer.android.com/training/location/permissions
@SuppressLint("MissingPermission")
public void setupLocationComponent() {
// Create the location component
locationComponent = map.getLocationComponent();
// Set the style to attach it to, in this case the current style on the map
locationComponent.activateLocationComponent(LocationComponentActivationOptions.builder(this, map.getStyle()).build());
// Turn it on
locationComponent.setLocationComponentEnabled(true);
// Set the tracking and render modes
// Tracking with the compass allows bearing to rotate the map as needed.
locationComponent.setCameraMode(CameraMode.TRACKING_COMPASS);
locationComponent.setRenderMode(RenderMode.COMPASS);
// Set the location engine
locationEngine = LocationEngineProvider.getBestLocationEngine(this);
startTracking();
}
@SuppressLint("MissingPermission")
public void startTracking() {
// Create the location engine request to track any changes to a user's location.
LocationEngineRequest locationEngineRequest = new LocationEngineRequest.Builder(DEFAULT_INTERVAL_IN_MILLISECONDS)
.setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY)
.setMaxWaitTime(DEFAULT_MAX_WAIT_TIME)
.build();
// Start listening for changes to location, then fire the callback with the result
locationEngine.requestLocationUpdates(locationEngineRequest, this, getMainLooper());
// Start with the last current location
locationEngine.getLastLocation(this);
}
@Override
public void onSuccess(LocationEngineResult result) {
// We have a successful callback, check the location isn't null and update as needed.
Location currentLocation = result.getLastLocation();
if(currentLocation != null) {
LocationUpdate locationUpdate = new LocationUpdate.Builder()
.location((result.getLastLocation())) // Set the location from the result
.animationDuration(Long.valueOf(ANIMATION_TIME))
.build();
map.getLocationComponent().forceLocationUpdate(locationUpdate);
}
}
@SuppressLint("MissingPermission")
public void onButtonRecenterClick(View view) {
// When Recenter is tapped, use the tracking mode
locationComponent.setCameraMode(CameraMode.TRACKING_COMPASS);
// Force an update
locationEngine.getLastLocation(this);
// Hide the button
recenter.setVisibility(View.INVISIBLE);
}
@Override
public void onFailure(Exception exception) {
// The request failed
}
@Override
public void onMoveBegin(MoveGestureDetector moveGestureDetector) {
// when the user begins to move the map
// Display the recenter button since panning has happened
recenter.setVisibility(View.VISIBLE);
}
@Override
public void onMove(MoveGestureDetector moveGestureDetector) {
// the user is moving the map
}
@Override
public void onMoveEnd(MoveGestureDetector moveGestureDetector) {
// the user has stopped moving the map
}
/**
* Activity Overrides
*/
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
@Override
public void onStart() {
super.onStart();
mapView.onStart();
}
@Override
public void onResume() {
super.onResume();
mapView.onResume();
}
@Override
public void onStop() {
super.onStop();
mapView.onStop();
}
@Override
public void onPause() {
super.onPause();
mapView.onPause();
}
@Override
public void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
}
import android.annotation.SuppressLint
import android.app.Activity
import android.location.Location
import android.os.Bundle
import android.view.View
import android.widget.Button
import androidx.core.content.ContextCompat
import com.trimblemaps.account.LicensedFeature
import com.trimblemaps.account.TrimbleMapsAccountManager
import com.trimblemaps.account.models.TrimbleMapsAccount
import com.trimblemaps.android.core.location.LocationEngine
import com.trimblemaps.android.core.location.LocationEngineCallback
import com.trimblemaps.android.core.location.LocationEngineProvider
import com.trimblemaps.android.core.location.LocationEngineRequest
import com.trimblemaps.android.core.location.LocationEngineResult
import com.trimblemaps.android.core.permissions.PermissionsListener
import com.trimblemaps.android.core.permissions.PermissionsManager
import com.trimblemaps.android.gestures.MoveGestureDetector
import com.trimblemaps.mapsdk.TrimbleMaps
import com.trimblemaps.mapsdk.camera.CameraPosition
import com.trimblemaps.mapsdk.location.LocationComponent
import com.trimblemaps.mapsdk.location.LocationComponentActivationOptions
import com.trimblemaps.mapsdk.location.LocationUpdate
import com.trimblemaps.mapsdk.location.modes.CameraMode
import com.trimblemaps.mapsdk.location.modes.RenderMode
import com.trimblemaps.mapsdk.maps.MapView
import com.trimblemaps.mapsdk.maps.OnMapReadyCallback
import com.trimblemaps.mapsdk.maps.Style
import com.trimblemaps.mapsdk.maps.TrimbleMapsMap
class SampleFollowMeActivity : Activity(), OnMapReadyCallback,
LocationEngineCallback<LocationEngineResult>, OnMoveListener {
private var mapView: MapView? = null
private var map: TrimbleMapsMap? = null
private var locationComponent: LocationComponent? = null
private var locationEngine: LocationEngine? = null
private var recenter: Button? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Authorize the API key for the session.
// .apiKey() requires your Trimble Maps API key
val trimbleMapsAccount = TrimbleMapsAccount.builder()
.apiKey("Your-API-key-here")
.addLicensedFeature(LicensedFeature.MAPS_SDK)
.build()
// Initialize the session
TrimbleMapsAccountManager.initialize(trimbleMapsAccount)
TrimbleMapsAccountManager.awaitInitialization()
// Get an instance of the map, done before the layout is set.
TrimbleMaps.getInstance(this)
setContentView(R.layout.activity_sample_follow_me)
// Find the recenter button
recenter = findViewById<View>(R.id.btn_recenter) as Button
// Set up the MapView from the layout
mapView = findViewById<View>(R.id.mapView) as MapView
// Implementing OnMapReadyCallback into the activity
mapView!!.getMapAsync(this)
}
override fun onMapReady(trimbleMapsMap: TrimbleMapsMap) {
// The TrimbleMapsMap object is created, now a style can be applied to render a map.
map = trimbleMapsMap
map!!.addOnMoveListener(this)
map!!.setStyle(Style.MOBILE_DEFAULT) { // Not setting the start location here, as that will be tracked by the user's
// location. Instead, just defining the initial zoom.
map!!.cameraPosition = CameraPosition.Builder().zoom(15.0).build()
setupLocationComponent()
}
}
// Before a user's location can be used, location permissions will need to
// be given to the app. This code assumes those permissions have already been granted by this
// point. For more information on how to grant location permissions to your app,
// see: https://developer.android.com/training/location/permissions
@SuppressLint("MissingPermission")
fun setupLocationComponent() {
// Create the location component
locationComponent = map!!.locationComponent
// Set the style to attach it to, in this case the current style on the map
locationComponent!!.activateLocationComponent(
LocationComponentActivationOptions.builder(
this,
map!!.style!!
).build()
)
// Turn it on
locationComponent!!.isLocationComponentEnabled = true
// Set the tracking and render modes
// Tracking with the compass allows bearing to rotate the map as needed.
locationComponent!!.cameraMode = CameraMode.TRACKING_COMPASS
locationComponent!!.renderMode = RenderMode.COMPASS
// Set the location engine
locationEngine = LocationEngineProvider.getBestLocationEngine(this)
startTracking()
}
@SuppressLint("MissingPermission")
fun startTracking() {
// Create the location engine request to track any changes to a user's location.
val locationEngineRequest = LocationEngineRequest.Builder(DEFAULT_INTERVAL_IN_MILLISECONDS)
.setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY)
.setMaxWaitTime(DEFAULT_MAX_WAIT_TIME)
.build()
// Start listening for changes to location, then fire the callback with the result
locationEngine!!.requestLocationUpdates(locationEngineRequest, this, mainLooper)
// Start with the last current location
locationEngine!!.getLastLocation(this)
}
override fun onSuccess(result: LocationEngineResult) {
// We have a successful callback, check the location isn't null and update as needed.
val currentLocation: Location? = result.lastLocation
if (currentLocation != null) {
val locationUpdate = LocationUpdate.Builder()
.location(result.lastLocation) // Set the location from the result
.animationDuration(java.lang.Long.valueOf(ANIMATION_TIME.toLong()))
.build()
map!!.locationComponent.forceLocationUpdate(locationUpdate)
}
}
@SuppressLint("MissingPermission")
fun onButtonRecenterClick(view: View) {
// When Recenter is tapped, use the tracking mode
locationComponent!!.cameraMode = CameraMode.TRACKING_COMPASS
// Force an update
locationEngine!!.getLastLocation(this)
// Hide the button
recenter?.visibility = View.INVISIBLE
}
override fun onFailure(exception: Exception) {
// The request failed
}
override fun onMoveBegin(moveGestureDetector: MoveGestureDetector) {
// when the user begins to move the map
// Display the recenter button since panning has happened
recenter?.visibility = View.VISIBLE
}
override fun onMove(moveGestureDetector: MoveGestureDetector) {
// the user is moving the map
}
override fun onMoveEnd(moveGestureDetector: MoveGestureDetector) {
// the user has stopped moving the map
}
companion object {
private const val DEFAULT_INTERVAL_IN_MILLISECONDS = 900L
private const val DEFAULT_MAX_WAIT_TIME = DEFAULT_INTERVAL_IN_MILLISECONDS * 5
private const val ANIMATION_TIME = 400
}
/**
* Activity Overrides
*/
override fun onStart() {
super.onStart()
mapView?.onStart()
}
override fun onResume() {
super.onResume()
mapView?.onResume()
}
override fun onPause() {
super.onPause()
mapView?.onPause()
}
override fun onStop() {
super.onStop()
mapView?.onStop()
}
override fun onLowMemory() {
super.onLowMemory()
mapView?.onLowMemory()
}
override fun onDestroy() {
super.onDestroy()
mapView?.onDestroy()
}
}