Add custom raster source
This example demonstrates how to add a CustomRasterSource to a map using the Mapbox Maps SDK for iOS. A series of images are added to a layer and are store in a CustomRasterSource
to then be added to the MapView
. We identify and then display the required tiles. Lastly the layer is styled with different color properties based on specified expressions.
In the code below, to create a CustomRasterSource
, we start by defining a CustomRasterSourceClient
to detect once the CustomRasterSource
is added. Next a CustomRasterSourceOptions
struct is created and parameters are passed to adjust the zoom levels and tile sizes. Lastly, a RasterLayer
is added to the map with the custom data.
This example uses 4 custom images:
These images are all located in the Examples repository in the RasterSource example assets folder.
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.
import UIKit
import os
@_spi(Experimental) import MapboxMaps
final class ViewController: UIViewController {
private var mapView: MapView!
private var cancelables: Set<AnyCancelable> = []
private var requiredTiles: [CanonicalTileID] = []
private enum ID {
static let customRasterSource = "custom-raster-source"
static let rasterLayer = "customRaster"
}
override func viewDidLoad() {
super.viewDidLoad()
mapView = MapView(frame: view.bounds, mapInitOptions: .init(cameraOptions: CameraOptions(center: .helsinki, zoom: 2)))
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(mapView)
mapView.mapboxMap.onStyleLoaded.observeNext { [weak self] _ in
self?.setupExample()
}
.store(in: &cancelables)
}
private func setupExample() {
let rasterSourceClient = CustomRasterSourceClient.fromCustomRasterSourceTileStatusChangedCallback { [weak self] (tileID, status) in
os_log(.info, "Tile status changed: tileId={%@}, status=%@", tileID.log, status.log)
guard let self else { return }
switch status {
case .required:
if !self.requiredTiles.contains(where: { $0 == tileID }) {
self.requiredTiles.append(tileID)
}
self.refreshTiles()
case .notNeeded, .optional:
if let index = self.requiredTiles.firstIndex(of: tileID) {
self.requiredTiles.remove(at: index)
}
try! self.mapView.mapboxMap.setCustomRasterSourceTileData(
forSourceId: ID.customRasterSource,
tiles: [CustomRasterSourceTileData(tileId: tileID, image: nil)])
default: break
}
}
let rasterSourceOptions = CustomRasterSourceOptions(clientCallback: rasterSourceClient, minZoom: 0, maxZoom: 0, tileSize: 256)
let customRasterSource = CustomRasterSource(id: ID.customRasterSource, options: rasterSourceOptions)
do {
try mapView.mapboxMap.addSource(customRasterSource)
var rasterLayer = RasterLayer(id: ID.rasterLayer, source: ID.customRasterSource)
rasterLayer.rasterColorMix = .constant([1, 0, 0, 0])
rasterLayer.rasterColor = .expression(
Exp(.interpolate) {
Exp(.linear)
Exp(.lineProgress)
0
"rgba(0.0, 0.0, 0.0, 0.0)"
0.3
"rgba(7, 238, 251, 0.4)"
0.5
"rgba(0, 255, 42, 0.5)"
0.7
"rgba(255, 255, 0, 0.7)"
1
"rgba(255, 30, 0, 0.9)"
}
)
try mapView.mapboxMap.addLayer(rasterLayer)
refreshTiles()
} catch {
print("[Example/ViewController] Error:\(error)")
}
}
private func refreshTiles() {
let rasterImage = nextImage()
let tiles = requiredTiles
.map { CustomRasterSourceTileData(tileId: $0, image: rasterImage) }
try! mapView.mapboxMap.setCustomRasterSourceTileData(forSourceId: ID.customRasterSource, tiles: tiles)
}
// MARK: Raster Images
private var currentImageIndex = 0
private let rasterImages: [UIImage] = [
UIImage(named: "RasterSource/wind_0")!,
UIImage(named: "RasterSource/wind_1")!,
UIImage(named: "RasterSource/wind_2")!,
UIImage(named: "RasterSource/wind_3")!,
]
private func nextImage() -> UIImage {
var currentImageIndex = self.currentImageIndex + 1
if currentImageIndex >= self.rasterImages.endIndex {
currentImageIndex = 0
}
return rasterImages[currentImageIndex]
}
}