Skip to main content

Display User Location

Learn how to show the user's current location on the map with a blue dot indicator that updates as they move.

What You'll Build

An app that displays the user's location on the map with:

  • Blue dot showing current position
  • Accuracy circle indicating GPS precision (automatic)
  • Automatic location updates as the user moves
  • Arrow indicator when user is moving (automatic)

Time to complete: ~5 minutes (iOS), ~20 minutes (Android)

Prerequisites

  • Completed Your First Map tutorial
  • Basic understanding of location permissions on iOS/Android

Choose Your Platform

📱 iOS (Swift)

Step 1: Request Location Permissions

Add permission descriptions to your Info.plist:

<key>NSLocationWhenInUseUsageDescription</key>
<string>We need your location to show you on the map</string>

For iOS 14+, also add:

<key>NSLocationWhenInUseUsageDescription</key>
<string>We need your location to show you on the map</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We need your location for navigation features</string>

Step 2: Request Location Permission and Enable Display

GLMapView handles location tracking internally. You only need to request permission:

import UIKit
import GLMap
import GLMapSwift
import CoreLocation

class ViewController: UIViewController {
var mapView: GLMapView!

override func viewDidLoad() {
super.viewDidLoad()

// Setup map view (from previous tutorial)
mapView = GLMapView(frame: view.bounds)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(mapView)

// Request location permission
requestLocationPermission()

// Enable user location display
mapView.showUserLocation = true
}

func requestLocationPermission() {
let status = CLLocationManager.authorizationStatus()
if status == .notDetermined {
// Create a temporary location manager to request permission
let locationManager = CLLocationManager()
locationManager.requestWhenInUseAuthorization()
}
}
}

That's it! GLMapView automatically:

  • Creates its own internal location manager
  • Starts location updates when showUserLocation = true
  • Displays blue dot with accuracy circle
  • Shows arrow indicator when moving

Complete Code

import UIKit
import GLMap
import GLMapSwift
import CoreLocation

class ViewController: UIViewController {
var mapView: GLMapView!

override func viewDidLoad() {
super.viewDidLoad()

// Setup map view
mapView = GLMapView(frame: view.bounds)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(mapView)

// Request permission
if CLLocationManager.authorizationStatus() == .notDetermined {
CLLocationManager().requestWhenInUseAuthorization()
}

// Enable user location - GLMapView handles everything else
mapView.showUserLocation = true
}
}

Run Your App

Build and run. When prompted, allow location access. The map should show a blue dot at your current location!

🤖 Android (Kotlin)

Note: Android API will be simplified in a future release to match iOS simplicity. Currently, displaying user location with a blue dot and accuracy circle requires manual implementation using GLMapImage and GLMapVectorLayer.

Here's the complete helper class from our demo app:

package com.example.myapp

import android.content.res.AssetManager
import android.location.Location
import globus.glmap.*
import kotlin.math.cos
import kotlin.math.sin

class CurLocationHelper(private val renderer: GLMapViewRenderer) {
private var userMovementImage: GLMapImage
private var userLocationImage: GLMapImage
private var accuracyCircle: GLMapVectorLayer
private var lastLocation: Location? = null

init {
val manager = renderer.attachedView.context.assets
userLocationImage = createImage(manager, "circle_new.svg")
userMovementImage = createImage(manager, "arrow_new.svg")
accuracyCircle = createAccuracyCircle()
}

private fun createImage(manager: AssetManager, filename: String): GLMapImage {
val image = SVGRender.render(
manager,
filename,
SVGRender.transform(renderer.screenScale.toDouble())
) ?: throw IllegalArgumentException("SVGRender can't render image")

return GLMapImage(100).apply {
setBitmap(image)
isHidden = true
setOffset(image.width / 2, image.height / 2)
renderer.add(this)
image.recycle()
}
}

private fun createAccuracyCircle(): GLMapVectorLayer {
val points = Array(100) {
val f = 2 * Math.PI * it / 100
MapPoint(sin(f) * 2048, cos(f) * 2048)
}

val circleStyle = GLMapVectorCascadeStyle.createStyle(
"area{layer:100; width:1pt; fill-color:#3D99FA26; color:#3D99FA26;}"
)!!

val circle = GLMapVectorObject.createPolygon(arrayOf(points), null)

return GLMapVectorLayer(99).apply {
setTransformMode(GLMapDrawable.TransformMode.Custom)
setVectorObject(circle, circleStyle, null)
renderer.add(this)
}
}

fun onLocationChanged(location: Location) {
val position = MapPoint.CreateFromGeoCoordinates(location.latitude, location.longitude)

// Accuracy radius
val r = renderer.convertMetersToInternal(location.accuracy.toDouble()).toFloat()

// Set initial position
if (lastLocation == null) {
lastLocation = location
userMovementImage.position = position
userLocationImage.position = position
if (location.hasBearing()) userLocationImage.angle = -location.bearing
accuracyCircle.position = position
accuracyCircle.scale = r / 2048.0f.toDouble()
}

// Select what image to display
if (location.hasBearing()) {
userMovementImage.isHidden = false
userLocationImage.isHidden = true
} else {
userLocationImage.isHidden = false
userMovementImage.isHidden = true
}

// Animate position updates
renderer.animate {
it.setTransition(GLMapAnimation.Linear)
it.setDuration(1.0)
userMovementImage.position = position
userLocationImage.position = position
accuracyCircle.position = position
accuracyCircle.scale = r / 2048.0f.toDouble()
if (location.hasBearing()) userLocationImage.angle = -location.bearing
}
}
}

Usage in Activity

import android.Manifest
import android.content.pm.PackageManager
import android.location.Location
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.android.gms.location.*
import globus.glmap.GLMapView

class MainActivity : AppCompatActivity() {
private lateinit var mapView: GLMapView
private lateinit var fusedLocationClient: FusedLocationProviderClient
private lateinit var locationHelper: CurLocationHelper
private lateinit var locationCallback: LocationCallback

companion object {
private const val LOCATION_PERMISSION_REQUEST_CODE = 1
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

mapView = findViewById(R.id.map_view)

// Create location helper
locationHelper = CurLocationHelper(mapView.renderer)

// Setup location updates
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
checkLocationPermission()
}

private fun checkLocationPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
LOCATION_PERMISSION_REQUEST_CODE
)
} else {
startLocationUpdates()
}
}

override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startLocationUpdates()
}
}
}

private fun startLocationUpdates() {
val locationRequest = LocationRequest.Builder(
Priority.PRIORITY_HIGH_ACCURACY,
1000
).build()

locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
locationResult.lastLocation?.let { location ->
locationHelper.onLocationChanged(location)
}
}
}

if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
mainLooper
)
}
}

override fun onDestroy() {
super.onDestroy()
fusedLocationClient.removeLocationUpdates(locationCallback)
}
}

Required Assets

You'll need two SVG icons in your assets folder:

  • circle_new.svg - Static location dot
  • arrow_new.svg - Movement arrow

See the demo app assets for examples.

Permissions in AndroidManifest.xml

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

Add Google Play Services

In your app's build.gradle:

dependencies {
implementation 'com.google.android.gms:play-services-location:21.0.1'
}

Understanding the Code

Location Permissions

Both platforms require runtime permissions for location access:

  • iOS: Add descriptions to Info.plist and request via CLLocationManager
  • Android: Add to manifest and request at runtime via ActivityCompat

Built-in User Location Support

iOS: GLMapView creates its own internal CLLocationManager and handles everything automatically:

// iOS - Just enable, GLMapView does the rest
mapView.showUserLocation = true

This automatically:

  • Creates location manager
  • Requests location updates
  • Displays blue dot with accuracy circle
  • Shows arrow indicator when moving

Android: Currently requires manual implementation using GLMapImage and GLMapVectorLayer (shown in the code above). The API will be simplified in a future release to match iOS simplicity.

For location updates, use FusedLocationProviderClient:

  • More battery efficient than direct GPS
  • Combines GPS, Wi-Fi, and cell tower data
  • Request with priority (HIGH_ACCURACY, BALANCED_POWER_ACCURACY, etc.)

When to Create Your Own Location Manager (iOS)

The simple approach above works for most apps. Create your own CLLocationManager only if you need:

  • Custom update frequency or accuracy
  • Background location updates
  • Custom accuracy circles or location markers
  • Access to raw location data for other purposes

See the MapViewWithUserLocation demo for an advanced example with custom accuracy circles.


Advanced: Custom User Location Icon

You can customize the user location marker:

iOS - Custom Icons
// Load custom SVG icons
if let locationImagePath = Bundle.main.path(forResource: "custom_dot", ofType: "svg"),
let locationImage = GLMapVectorImageFactory.shared.image(fromSvg: locationImagePath),
let movementImagePath = Bundle.main.path(forResource: "custom_arrow", ofType: "svg"),
let movementImage = GLMapVectorImageFactory.shared.image(fromSvg: movementImagePath) {

mapView.setUserLocationImage(locationImage, movementImage: movementImage)
}
Android - Custom Icons
// Use GLMapImage for custom user location
val locationImage = GLMapImage(100)
val bitmap = SVGRender.render(assets, "custom_dot.svg",
SVGRender.transform(renderer.screenScale))
locationImage.setBitmap(bitmap)
locationImage.setPosition(position)
mapView.renderer.add(locationImage)

Troubleshooting

No location displayed:

  • Check permissions are granted
  • Ensure device location services are enabled
  • Try running on a physical device (simulator may not have location)

iOS: Permission dialog doesn't appear:

  • Verify Info.plist has location usage description
  • Check if permission was previously denied (reset in Settings → Privacy → Location)

Android: Location updates slow or inaccurate:

  • Check priority level (PRIORITY_HIGH_ACCURACY for best GPS)
  • Increase update interval if battery is a concern
  • Ensure Google Play Services is installed

App crashes when requesting location:

  • iOS: Verify showUserLocation is set after map view is fully initialized
  • Android: Check permissions before calling requestLocationUpdates()

What's Next?

Now that you can display user location, try: