> For the complete documentation index, see [llms.txt](https://docs.mapbox.com/ios/maps/llms.txt)

# Display a raster particle layer

Your browser doesn't support HTML5 video. Open [link to the video](https://static-assets.mapbox.com/maps/ios/documentation/raster_particle_layer_0.mp4).

This example showcases a SwiftUI view structure implementing a [`RasterParticleLayer`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/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`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/rasterparticlelayer/) is configured with specific parameters like speed factor, maximum speed, count, fade opacity factor, and color gradient defined by [`particlesSpeedGradient`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/rasterparticlelayer/rasterparticlecolor/). This example demonstrates the seamless integration of map layers and interactive elements using the Maps SDK's SwiftUI components and Raster Particle functionality.

> **Note: iOS Examples App Available**
> 
> This example code is part of the **Maps SDK for iOS Examples App**, a working iOS project [available on Github](https://github.com/mapbox/mapbox-maps-ios/tree/11.29.0/Sources/Examples). 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](https://docs.mapbox.com/help/tutorials/maps-sdk-ios-examples-app/) tutorial for step-by-step instructions.

**Swift**

Title: `BasicMapExample.swift`

[View on GitHub](https://github.com/mapbox/mapbox-maps-ios/blob/main/Sources/Examples/SwiftUI%20Examples/RasterParticleExample.swift)

```swift
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 }
}
```