Skip to main content

Create a rotating globe

This example demonstrates how to implement a spinning globe using the Mapbox Maps SDK for iOS.

The example uses the setProjection method on the MapboxMap class to switch from Web Mercator to the Globe Projection. Then uses the Atmosphere class to style how the globe is displayed, and the ease method to rotate the globe over time.

iOS Examples App Available

This example code is part of the Maps SDK for iOS Examples App, a working iOS project available on Github. iOS developers are encouraged to run the examples app locally to interact with this example in an emulator and explore other features of the Maps SDK.

See our Run the Maps SDK for iOS Examples App tutorial for step-by-step instructions.

SpinningGlobeExample.swift
import Foundation
import UIKit
import MapboxMaps
import CoreLocation

final class SpinningGlobeExample: UIViewController, GestureManagerDelegate, ExampleProtocol {
private var userInteracting = false
private var mapView: MapView!
private var cancelables = Set<AnyCancelable>()

override func viewDidLoad() {
super.viewDidLoad()

mapView = MapView(frame: view.bounds, mapInitOptions: .init(styleURI: .satellite))
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.mapboxMap.setCamera(to: .init(center: CLLocationCoordinate2D(latitude: 40, longitude: -90), zoom: 1.0))
try! self.mapView.mapboxMap.setProjection(StyleProjection(name: .globe))

mapView.mapboxMap.onStyleLoaded.observeNext { _ in
try! self.mapView.mapboxMap.setAtmosphere(Atmosphere())
self.spinGlobe()
self.finish()
}.store(in: &cancelables)

addStyleLoadingDebugEvents()

mapView.gestures.delegate = self

// Enable the camera debug option to see camera state
let debugOptions: MapViewDebugOptions = [.camera]
mapView.debugOptions = debugOptions

view.addSubview(mapView)
}

func addStyleLoadingDebugEvents() {
func logEvent<T: LogableEvent>(_ signal: Signal<T>) {
signal.observe {
print(Date(), $0.logString)
}.store(in: &cancelables)
}

logEvent(mapView.mapboxMap.onMapLoadingError)
logEvent(mapView.mapboxMap.onStyleDataLoaded)
logEvent(mapView.mapboxMap.onStyleImageMissing)
logEvent(mapView.mapboxMap.onMapIdle)
logEvent(mapView.mapboxMap.onMapLoaded)
logEvent(mapView.mapboxMap.onStyleImageMissing)
logEvent(mapView.mapboxMap.onStyleImageMissing)
}

func spinGlobe() {
// At low zooms, complete a revolution every two minutes.
let secPerRevolution = 120.0
// Above zoom level 5, do not rotate.
let maxSpinZoom = 5.0
// Rotate at intermediate speeds between zoom levels 3 and 5.
let slowSpinZoom = 3.0

let zoom = mapView.mapboxMap.cameraState.zoom
if !userInteracting && zoom < maxSpinZoom {
var distancePerSecond = 360.0 / secPerRevolution
if zoom > slowSpinZoom {
// Slow spinning at higher zooms
let zoomDif = (maxSpinZoom - zoom) / (maxSpinZoom - slowSpinZoom)
distancePerSecond *= zoomDif
}
let center = mapView.mapboxMap.cameraState.center
let targetCenter = CLLocationCoordinate2D(latitude: center.latitude, longitude: center.longitude - distancePerSecond)

// Smoothly animate the map over one second.
// When this animation is complete, call it again
mapView.camera.ease(to: .init(center: targetCenter), duration: 1.0, curve: .linear) { [weak self] rotating in

guard rotating == .end else {
return
}
self?.spinGlobe()
}
}
}

func gestureManager(_ gestureManager: GestureManager, didBegin gestureType: GestureType) {
userInteracting = true
}

func gestureManager(_ gestureManager: GestureManager, didEnd gestureType: GestureType, willAnimate: Bool) {

if !willAnimate {
userInteracting = false
DispatchQueue.main.async {
self.spinGlobe()
}
}
}

func gestureManager(_ gestureManager: GestureManager, didEndAnimatingFor gestureType: GestureType) {
userInteracting = false
DispatchQueue.main.async {
self.spinGlobe()
}
}
}
Was this example helpful?