Display a raster particle layer
This example showcases a SwiftUI view structure implementing a RasterParticleLayer provided by Mapbox Maps SDK of iOS. Users can adjust settings such as particle count, opacity factor, reset rate, and speed factor through sliders displayed at the bottom of the map. The RasterParticleLayer is configured with specific parameters like speed factor, maximum speed, count, fade opacity factor, and color gradient defined by particlesSpeedGradient. This example demonstrates the seamless integration of map layers and interactive elements using the Maps SDK's SwiftUI components and Raster Particle functionality.
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 SwiftUI
@_spi(Experimental) import MapboxMaps
struct RasterParticleExample: View {
@State var mapStyle = MapStyle.standard(theme: .monochrome, lightPreset: .night)
@State var rasterParticleCount: Double = 2048
@State var rasterParticleFadeOpacityFactor = 0.8
@State var resetRateFactor = 0.4
@State var speedFactor = 0.4
@State private var queryRenderedRasterValue: Double?
var body: some View {
MapReader { proxy in
Map(initialViewport: .camera(zoom: 1)) {
RasterArraySource(id: "wind-mrt-source")
.url("mapbox://mapbox.gfs-winds")
RasterParticleLayer(id: "layer_particles", source: "wind-mrt-source")
.sourceLayer("10winds")
.rasterParticleSpeedFactor(speedFactor)
.rasterParticleMaxSpeed(70)
.rasterParticleCount(rasterParticleCount)
.rasterParticleFadeOpacityFactor(rasterParticleFadeOpacityFactor)
.rasterParticleResetRateFactor(resetRateFactor)
.rasterParticleColor(particlesSpeedGradient)
TapInteraction { context in
if let map = proxy.map, context.isOnSurface {
map.queryRenderedRasterValues(
for: context.point,
options: RenderedRasterQueryOptions(layers: ["layer_particles"])
) { result in
switch result {
case .success(let rasterValues):
let values = rasterValues.layers["layer_particles"] ?? []
queryRenderedRasterValue = values.first?.doubleValue
case .failure(let failure):
print("Error querying raster values: \(failure)")
}
}
}
return true
}
}
.mapStyle(mapStyle)
.debugOptions(.camera)
}
.ignoresSafeArea()
.overlay(alignment: .trailing) {
MapStyleSelectorButton(mapStyle: $mapStyle)
}
.overlay(alignment: .bottom) {
VStack(alignment: .center) {
QueryRenderedRasterValuesView(value: queryRenderedRasterValue)
SliderSettingView(title: "Particle Count", value: $rasterParticleCount, range: 1...4096, step: 1)
SliderSettingView(title: "Opacity Factor", value: $rasterParticleFadeOpacityFactor, range: 0...1, step: 0.01)
SliderSettingView(title: "Reset Rate", value: $resetRateFactor, range: 0...1, step: 0.01)
SliderSettingView(title: "Speed Factor", value: $speedFactor, range: 0...1, step: 0.01)
}
.foregroundColor(.white)
.padding(.bottom, 40)
.padding(.horizontal, 16)
}
}
}
private struct SliderSettingView: View {
var title: String
@Binding var value: Double
var range: ClosedRange<Double>
var step: Double
var body: some View {
HStack {
Text("\(title)")
Slider(value: $value, in: range, step: step) {
} minimumValueLabel: {
Text("")
} maximumValueLabel: {
Text("\(String(format: "%.2f", value))")
.font(.system(size: 12))
}
}
}
}
private struct QueryRenderedRasterValuesView: View {
var value: Double?
var body: some View {
HStack {
Text("Raster values")
Spacer()
Group {
if let value {
Text("\(value)")
} else {
Text("n/a")
}
}
.font(.system(size: 12))
}
}
}
private let particlesSpeedGradient = Exp(.interpolate) {
Exp(.linear)
Exp(.rasterParticleSpeed)
1.5
Exp(.rgb) { 134.0; 163.0; 171.0 }
2.5
Exp(.rgb) { 134.0; 163.0; 171.0 }
4.63
Exp(.rgb) { 110.0; 143.0; 208.0 }
6.17
Exp(.rgb) { 15.0; 147.0; 167.0 }
7.72
Exp(.rgb) { 15.0; 147.0; 167.0 }
9.26
Exp(.rgb) { 57.0; 163.0; 57.0 }
10.29
Exp(.rgb) { 57.0; 163.0; 57.0 }
11.83
Exp(.rgb) { 194.0; 134.0; 62.0 }
13.37
Exp(.rgb) { 194.0; 134.0; 63.0 }
14.92
Exp(.rgb) { 200.0; 66.0; 13.0 }
16.46
Exp(.rgb) { 200.0; 66.0; 13.0 }
18.00
Exp(.rgb) { 210.0; 0.0; 50.0 }
20.06
Exp(.rgb) { 215.0; 0.0; 50.0 }
21.60
Exp(.rgb) { 175.0; 80.0; 136.0 }
23.66
Exp(.rgb) { 175.0; 80.0; 136.0 }
25.21
Exp(.rgb) { 117.0; 74.0; 147.0 }
27.78
Exp(.rgb) { 117.0; 74.0; 147.0 }
29.32
Exp(.rgb) { 68.0; 105.0; 141.0 }
31.89
Exp(.rgb) { 68.0; 105.0; 141.0 }
33.44
Exp(.rgb) { 194.0; 251.0; 119.0 }
42.18
Exp(.rgb) { 194.0; 251.0; 119.0 }
43.72
Exp(.rgb) { 241.0; 255.0; 109.0 }
48.87
Exp(.rgb) { 241.0; 255.0; 109.0 }
50.41
Exp(.rgb) { 255.0; 255.0; 255.0 }
57.61
Exp(.rgb) { 255.0; 255.0; 255.0 }
59.16
Exp(.rgb) { 255.0; 255.0; 255.0 }
68.93
Exp(.rgb) { 255.0; 255.0; 255.0 }
69.44
Exp(.rgb) { 255.0; 37.0; 255.0 }
}