Examples

Multi-layer, map-backed visualizations using real public datasets. Each example downloads live data, builds layers, and renders an interactive deck.gl map.

Global Flight Routes

Real airline routes between the world’s 40 busiest airports, from the OpenFlights database.

Layers: ArcLayer + ScatterplotLayer + TextLayer | Map: Dark Matter

Open visualization ↗

Code
let
    #--------------------------------------------------------------------------------# CSV parser for OpenFlights format (handles quoted fields)
    function parse_of_line(line)
        fields = String[]
        buf = IOBuffer()
        in_q = false
        for c in line
            if c == '"'
                in_q = !in_q
            elseif c == ',' && !in_q
                push!(fields, String(take!(buf)))
            else
                write(buf, c)
            end
        end
        push!(fields, String(take!(buf)))
        return fields
    end

    #--------------------------------------------------------------------------------# Download OpenFlights data
    airports_file = Downloads.download(
        "https://raw.githubusercontent.com/jpatokal/openflights/master/data/airports.dat"
    )
    routes_file = Downloads.download(
        "https://raw.githubusercontent.com/jpatokal/openflights/master/data/routes.dat"
    )

    # Parse airports: IATA → (lat, lng)
    airport_db = Dict{String, @NamedTuple{lat::Float64, lng::Float64}}()
    for line in readlines(airports_file)
        f = parse_of_line(line)
        length(f) >= 8 || continue
        iata = f[5]
        (iata == "" || iata == "\\N" || length(iata) != 3) && continue
        lat = tryparse(Float64, f[7])
        lng = tryparse(Float64, f[8])
        (lat === nothing || lng === nothing) && continue
        airport_db[iata] = (lat = lat, lng = lng)
    end

    # Parse routes: collect direct-flight pairs (deduplicated)
    route_set = Set{Tuple{String,String}}()
    for line in readlines(routes_file)
        f = split(line, ',')
        length(f) >= 8 || continue
        src, dst, stops = f[3], f[5], f[8]
        stops == "0" || continue
        haskey(airport_db, src) && haskey(airport_db, dst) || continue
        push!(route_set, src < dst ? (src, dst) : (dst, src))
    end

    # Rank airports by connectivity, pick top 40
    counts = Dict{String,Int}()
    for (s, d) in route_set
        counts[s] = get(counts, s, 0) + 1
        counts[d] = get(counts, d, 0) + 1
    end
    top = Set(first.(sort(collect(counts), by = last, rev = true)[1:min(40, length(counts))]))

    # Filter to routes between top airports
    sel_routes = [(s, d) for (s, d) in route_set if s in top && d in top]
    sel_routes = sel_routes[1:min(250, length(sel_routes))]

    used = sort(collect(union(Set(first.(sel_routes)), Set(last.(sel_routes)))))

    airports = (
        lng  = [airport_db[a].lng for a in used],
        lat  = [airport_db[a].lat for a in used],
        name = used,
    )
    routes = (
        src_lng = [airport_db[r[1]].lng for r in sel_routes],
        src_lat = [airport_db[r[1]].lat for r in sel_routes],
        tgt_lng = [airport_db[r[2]].lng for r in sel_routes],
        tgt_lat = [airport_db[r[2]].lat for r in sel_routes],
    )

    arcs = ArcLayer(
        data = routes,
        get_source_position = [:src_lng, :src_lat],
        get_target_position = [:tgt_lng, :tgt_lat],
        get_source_color = [0, 180, 235, 160],
        get_target_color = [160, 90, 255, 160],
        get_width = 1,
        great_circle = true,
    )
    dots = ScatterplotLayer(
        data = airports,
        get_position = [:lng, :lat],
        get_fill_color = [0, 210, 235, 220],
        get_radius = 50000,
        radius_min_pixels = 3,
    )
    labels = TextLayer(
        data = airports,
        get_position = [:lng, :lat],
        get_text = :name,
        get_size = 11,
        get_color = [255, 255, 255, 230],
        get_pixel_offset = [0, -14],
        background = true,
        get_background_color = [30, 30, 30, 180],
        background_padding = [4, 2, 4, 2],
        font_family = "Helvetica, Arial, sans-serif",
        font_weight = 700,
    )

    deck = Deck(
        [arcs, dots, labels],
        initial_view_state = ViewState(longitude = 20.0, latitude = 15.0, zoom = 1.5, pitch = 15.0),
        map_style = "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json",
    )
    save_html(deck, "examples/global-flight-routes.html")
end

Tokyo Region Earthquakes

Seismic activity near Tokyo (2020–2024, M1.5+) from the USGS Earthquake Hazards Program.

Layers: HexagonLayer | Map: Dark Matter

Open visualization ↗

Code
let
    url = string(
        "https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson",
        "&starttime=2020-01-01&endtime=2024-12-31",
        "&minlatitude=34.5&maxlatitude=36.5",
        "&minlongitude=138.5&maxlongitude=141.0",
        "&minmagnitude=1.5&limit=5000",
    )
    response = JSON3.read(read(Downloads.download(url), String))
    features = response[:features]

    data = (
        lng = Float64[f[:geometry][:coordinates][1] for f in features],
        lat = Float64[f[:geometry][:coordinates][2] for f in features],
    )

    hexagons = HexagonLayer(
        data = data,
        get_position = [:lng, :lat],
        radius = 3000,
        elevation_scale = 50,
        extruded = true,
        coverage = 0.88,
        color_range = [
            [255, 255, 178],
            [254, 204, 92],
            [253, 141, 60],
            [240, 59, 32],
            [189, 0, 38],
            [128, 0, 38],
        ],
        opacity = 0.85,
    )

    deck = Deck(
        [hexagons],
        initial_view_state = ViewState(longitude = 139.9, latitude = 35.4, zoom = 8.0, pitch = 50.0, bearing = -20.0),
        map_style = "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json",
    )
    save_html(deck, "examples/tokyo-earthquakes.html")
end

London Buildings 3D

Real building footprints with heights from OpenStreetMap in the City of London financial district.

Layers: PolygonLayer | Map: Positron

Open visualization ↗

Code
let
    query = "[out:json];way[\"building\"][\"height\"](51.505,-0.095,51.52,-0.07);out%20geom;"
    url = "https://overpass-api.de/api/interpreter?data=$(query)"
    response = JSON3.read(read(Downloads.download(url), String))
    elements = response[:elements]

    polygons = Vector{Vector{Vector{Float64}}}()
    heights = Float64[]
    colors = Vector{Int}[]

    for elem in elements
        poly = [[Float64(pt[:lon]), Float64(pt[:lat])] for pt in elem[:geometry]]
        push!(polygons, poly)

        h_str = get(elem[:tags], :height, nothing)
        h = h_str !== nothing ? tryparse(Float64, replace(string(h_str), r"[^0-9.]" => "")) : nothing
        h = something(h, 15.0)
        push!(heights, h)

        t = clamp((h - 5) / 175, 0.0, 1.0)
        push!(colors, [round(Int, 40 + 215 * t), round(Int, 140 + 80 * (1 - t)), round(Int, 200 * (1 - t))])
    end

    buildings_data = (polygon = polygons, elevation = heights, color = colors)

    polys = PolygonLayer(
        data = buildings_data,
        get_polygon = :polygon,
        get_fill_color = :color,
        get_elevation = :elevation,
        extruded = true,
        wireframe = true,
        get_line_color = [255, 255, 255, 80],
        elevation_scale = 1,
        opacity = 0.85,
    )

    deck = Deck(
        [polys],
        initial_view_state = ViewState(longitude = -0.083, latitude = 51.513, zoom = 15.0, pitch = 55.0, bearing = 30.0),
        map_style = "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json",
    )
    save_html(deck, "examples/london-buildings-3d.html")
end

Paris Trees

Heatmap of 5,000 trees managed by the City of Paris, weighted by tree height.

Layers: HeatmapLayer + ScatterplotLayer | Map: Voyager

Open visualization ↗

Code
let
    url = "https://opendata.paris.fr/api/explore/v2.1/catalog/datasets/les-arbres/exports/geojson?limit=5000"
    response = JSON3.read(read(Downloads.download(url), String))
    features = response[:features]

    lngs = Float64[]
    lats = Float64[]
    heights = Float64[]
    for f in features
        geo = f[:geometry]
        geo === nothing && continue
        coords = geo[:coordinates]
        push!(lngs, Float64(coords[1]))
        push!(lats, Float64(coords[2]))
        h = get(f[:properties], :hauteurenm, nothing)
        push!(heights, h !== nothing && h isa Number ? Float64(h) : 5.0)
    end

    data = (lng = lngs, lat = lats, height = heights)

    heat = HeatmapLayer(
        data = data,
        get_position = [:lng, :lat],
        get_weight = :height,
        radius_pixels = 30,
        intensity = 1.2,
        threshold = 0.03,
        color_range = [
            [255, 255, 178, 25],
            [254, 217, 118, 85],
            [254, 178, 76, 127],
            [253, 141, 60, 170],
            [240, 59, 32, 210],
            [189, 0, 38, 240],
        ],
        opacity = 0.7,
    )
    scatter = ScatterplotLayer(
        data = data,
        get_position = [:lng, :lat],
        get_fill_color = [34, 139, 34, 80],
        get_radius = 5,
        radius_min_pixels = 1,
        opacity = 0.3,
    )

    deck = Deck(
        [heat, scatter],
        initial_view_state = ViewState(longitude = 2.34, latitude = 48.86, zoom = 12.5),
        map_style = "https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json",
    )
    save_html(deck, "examples/paris-trees.html")
end

UK Road Accidents

~140,000 personal injury accidents aggregated into a 3D grid across Great Britain.

Layers: GridLayer | Map: Dark Matter

Open visualization ↗

Code
let
    url = "https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv"
    lines = readlines(Downloads.download(url))

    lngs = Float64[]
    lats = Float64[]
    for i in 2:length(lines)
        parts = split(lines[i], ',')
        length(parts) >= 2 || continue
        lng = tryparse(Float64, parts[1])
        lat = tryparse(Float64, parts[2])
        (lng === nothing || lat === nothing) && continue
        push!(lngs, lng)
        push!(lats, lat)
    end

    data = (lng = lngs, lat = lats)

    grid = GridLayer(
        data = data,
        get_position = [:lng, :lat],
        cell_size = 200,
        elevation_scale = 8,
        extruded = true,
        coverage = 0.9,
        color_range = [
            [255, 255, 204],
            [199, 233, 180],
            [127, 205, 187],
            [65, 182, 196],
            [44, 127, 184],
            [37, 52, 148],
        ],
        opacity = 0.8,
    )

    deck = Deck(
        [grid],
        initial_view_state = ViewState(longitude = -0.12, latitude = 51.51, zoom = 11.5, pitch = 45.0, bearing = -15.0),
        map_style = "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json",
    )
    save_html(deck, "examples/uk-road-accidents.html")
end

Australia / Indo-Pacific Earthquakes

Seismic activity across the Indo-Pacific region (2024, M4.5+) with per-event color by magnitude.

Layers: ScatterplotLayer + HexagonLayer + ArcLayer | Map: Positron

Open visualization ↗

Code
let
    url = string(
        "https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson",
        "&starttime=2024-01-01&endtime=2024-12-31",
        "&minlatitude=-50&maxlatitude=10",
        "&minlongitude=95&maxlongitude=180",
        "&minmagnitude=4.5&limit=5000",
    )
    response = JSON3.read(read(Downloads.download(url), String))
    features = response[:features]

    lngs = Float64[f[:geometry][:coordinates][1] for f in features]
    lats = Float64[f[:geometry][:coordinates][2] for f in features]
    mags = Float64[something(f[:properties][:mag], 4.5) for f in features]

    colors = map(mags) do mag
        t = clamp((mag - 4.5) / 3.0, 0.0, 1.0)
        r = t < 0.5 ? round(Int, 255 * 2t) : 255
        g = t < 0.5 ? 220 : round(Int, 220 * (1 - 2(t - 0.5)))
        [r, g, 30, round(Int, 120 + 135 * t)]
    end

    quakes = (lng = lngs, lat = lats, magnitude = mags, color = colors)

    stations = (
        src_lng = [151.21, 151.21, 174.78, 174.78, 106.85],
        src_lat = [-33.87, -33.87, -41.29, -41.29,  -6.21],
        tgt_lng = [147.0,  155.0,  178.0,  167.0,  125.0],
        tgt_lat = [ -6.0,   -7.0,  -18.0,  -15.0,  -10.0],
    )

    scatter = ScatterplotLayer(
        data = quakes,
        get_position = [:lng, :lat],
        get_fill_color = :color,
        get_radius = :magnitude,
        radius_scale = 12000,
        radius_min_pixels = 2,
        radius_max_pixels = 25,
        opacity = 0.75,
    )
    hexbins = HexagonLayer(
        data = quakes,
        get_position = [:lng, :lat],
        radius = 50000,
        extruded = true,
        elevation_scale = 500,
        coverage = 0.85,
        opacity = 0.4,
    )
    arcs = ArcLayer(
        data = stations,
        get_source_position = [:src_lng, :src_lat],
        get_target_position = [:tgt_lng, :tgt_lat],
        get_source_color = [0, 200, 255, 180],
        get_target_color = [255, 100, 50, 180],
        get_width = 1.5,
        great_circle = true,
    )

    deck = Deck(
        [hexbins, scatter, arcs],
        initial_view_state = ViewState(longitude = 145.0, latitude = -20.0, zoom = 3.5, pitch = 30.0, bearing = 10.0),
        map_style = "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json",
    )
    save_html(deck, "examples/australia-earthquakes.html")
end

US Fire Hotspots

Near real-time satellite fire detections from NASA FIRMS (VIIRS NOAA-20, 7-day window).

Layers: HeatmapLayer + ScatterplotLayer + TextLayer | Map: Dark Matter

Open visualization ↗

Code
let
    url = "https://firms.modaps.eosdis.nasa.gov/data/active_fire/noaa-20-viirs-c2/csv/J1_VIIRS_C2_USA_contiguous_and_Hawaii_7d.csv"
    lines = readlines(Downloads.download(url))

    lngs = Float64[]
    lats = Float64[]
    frps = Float64[]
    for i in 2:length(lines)
        parts = split(lines[i], ',')
        length(parts) >= 12 || continue
        lat = parse(Float64, parts[1])
        lon = parse(Float64, parts[2])
        frp = tryparse(Float64, parts[12])
        push!(lats, lat)
        push!(lngs, lon)
        push!(frps, something(frp, 1.0))
    end

    data = (lng = lngs, lat = lats, frp = frps)

    cities = (
        lng  = [-118.24, -122.42, -104.99, -112.07, -97.74,  -84.39, -90.07],
        lat  = [  34.05,   37.77,   39.74,   33.45,  30.27,   33.75,  29.95],
        name = ["Los Angeles", "San Francisco", "Denver", "Phoenix", "Houston", "Atlanta", "New Orleans"],
    )

    heat = HeatmapLayer(
        data = data,
        get_position = [:lng, :lat],
        get_weight = :frp,
        radius_pixels = 20,
        intensity = 0.8,
        threshold = 0.03,
        color_range = [
            [255, 255, 200, 30],
            [255, 220, 80, 100],
            [255, 160, 20, 160],
            [240, 80, 0, 200],
            [200, 20, 0, 230],
            [120, 0, 0, 250],
        ],
        opacity = 0.75,
    )
    fire_scatter = ScatterplotLayer(
        data = data,
        get_position = [:lng, :lat],
        get_fill_color = [255, 80, 0, 80],
        get_radius = 2000,
        radius_min_pixels = 1,
        opacity = 0.4,
    )
    city_labels = TextLayer(
        data = cities,
        get_position = [:lng, :lat],
        get_text = :name,
        get_size = 12,
        get_color = [255, 255, 255, 230],
        background = true,
        get_background_color = [30, 30, 30, 190],
        background_padding = [5, 2, 5, 2],
        font_family = "Helvetica, Arial, sans-serif",
        font_weight = 700,
    )

    deck = Deck(
        [heat, fire_scatter, city_labels],
        initial_view_state = ViewState(longitude = -98.0, latitude = 39.0, zoom = 4.0),
        map_style = "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json",
    )
    save_html(deck, "examples/us-fire-hotspots.html")
end