Creating Responsive Heatmaps with Leaflet Extras: A Step-by-Step Guide

Responsive addWebGLHeatmap with crosstalk and Leaflet in

Introduction

In this article, we will explore how to create a responsive heatmap using the addWebGLHeatmap function from the Leaflet Extras library. We will also cover how to handle two main issues: redrawn heatmaps on zoom level changes and separation of heatmap points from markers.

Background

The original question comes from a user who is trying to create a leaflet map with a responsive heatmap using the addHeatmap function from the Leaflet library. However, they are facing two main issues:

  1. Redrawn heatmaps on zoom level changes.
  2. Separation of heatmap points from markers.

To address these issues, we will use the addWebGLHeatmap function and explore how to handle them using JavaScript code.

Step 1: Setting Up Leaflet Data

First, let’s create a sample data for our leaflet map:

# Create a new data frame with id, lat, lng, and group columns.
n <- 200
data <- data.frame(
    id = seq(1, n * 2),
    lat = rnorm(n, 0, 15),
    long = rnorm(n, 0, 15),
    group = c(rep("Heatmap", n), rep("Markers", n)),
    mag = rep(as.integer(runif(n, 0, 20)), 2)
)

# Convert the data frame into a SharedData object.
sd <- SharedData$new(data)

Step 2: Creating Leaflet Map with addWebGLHeatmap

Next, let’s create our leaflet map using the bscols function and adding the addWebGLHeatmap layer:

# Create a new bscols object.
bscols <- bscols(widths = c(3, 9))

# Add filter slider for magnitude column.
filter_slider <- addFilterSlider(
    sd,
    "Magnitude",
    sd$mag,
    step = 0.1,
    min = sd$mag[1],
    max = sd$mag[n]
)

# Create leaflet map with setView, tiles, and markers.
leaflet(sd) %>%
    addTiles() %>%
    setView(lat = 0, lng = 0, zoom = 4) %>%
    addMarkers(group = ~group) %>%
    # Add webGL heatmap layer
    leaflet::addWebGLHeatmap(
        layerId = "heatmapwebgl",
        size = 1000000,
        units = "m",
        alphaRange = c(0, 1)
    ) %>%
    # Remove webGL heatmap layer when not visible.
    leaflet.extras::removeWebGLHeatmap("heatmapwebgl") %>%
    # Add layers control.
    addLayersControl(
        overlayGroups = c("Heatmap", "Markers"),
        options = layersControlOptions(collapsed = FALSE)
    ) %>%
    # Define function to render heatmap on map.
    htmlwidgets::onRender(function(el, x) {
        var myMap = this;
        var coord_state;

        // Hide heatmap markers
        setTimeout(
            function() {
                myMap.eachLayer(function(layer) {
                    if (layer.options.group == "Heatmap") {
                        layer.setOpacity(0);
                        layer.getElement().style.pointerEvents = 'none';
                    }
                })
            },
            100
        )

        // Function to get heatmap markers
        function get_markers() {
            coord_state = [];
            myMap.eachLayer(function(layer) {
                if (layer.options.group == "Heatmap") {
                    coord_state.push([layer.options.lat, layer.options.lng, 0.5]);
                    layer.getElement().style.pointerEvents = 'none';
                }
            });
            return(coord_state);
        }

        // Function to redraw heatmap
        function redraw_heatmap() {
            heatmap.setData(get_markers());
        }

        var heatmap = L.webGLHeatmap({
            size: 1000000,
            units: "m",
            alphaRange: c(0, 1)
        });
        heatmap.setData(get_markers());
        myMap.addLayer(heatmap);

        // Event listeners for layer add and remove
        myMap.on("layerremove", redraw_heatmap);
        myMap.on("layeradd", redraw_heatmap);
    })

Step 3: Separating Heatmap Points from Markers

To separate heatmap points from markers, we need to adjust the opacity of each marker based on its group. We will use two separate addCircleMarkers layers for heatmaps and markers.

# Create a new bscols object.
bscols <- bscols(widths = c(3, 9))

# Add filter slider for magnitude column.
filter_slider <- addFilterSlider(
    sd,
    "Magnitude",
    sd$mag,
    step = 0.1,
    min = sd$mag[1],
    max = sd$mag[n]
)

# Create leaflet map with setView, tiles, and markers.
leaflet(sd) %>%
    addTiles() %>%
    setView(lat = 0, lng = 0, zoom = 4) %>%
    # Add heatmap layer
    leaflet::addWebGLHeatmap(
        layerId = "heatmapwebgl",
        size = 1000000,
        units = "m",
        alphaRange = c(0, 1)
    ) %>%
    # Remove webGL heatmap layer when not visible.
    leaflet.extras::removeWebGLHeatmap("heatmapwebgl") %>%
    # Add layers control for heatmaps and markers
    addLayersControl(
        overlayGroups = c("Heatmap", "Markers"),
        options = layersControlOptions(collapsed = FALSE)
    ) %>%
    # Define function to render heatmap on map.
    htmlwidgets::onRender(function(el, x) {
        var myMap = this;
        var coord_state;

        // Function to get heatmap markers
        function get_markers() {
            coord_state = [];
            myMap.eachLayer(function(layer) {
                if (layer.options.group == "Heatmap") {
                    coord_state.push([layer.options.lat, layer.options.lng, 0.5]);
                }
            });
            return(coord_state);
        }

        // Function to redraw heatmap
        function redraw_heatmap() {
            heatmap.setData(get_markers());
        }

        var heatmap = L.webGLHeatmap({
            size: 1000000,
            units: "m",
            alphaRange: c(0, 1)
        });
        heatmap.setData(get_markers());
        myMap.addLayer(heatmap);

        // Event listeners for layer add and remove
        myMap.on("layerremove", redraw_heatmap);
        myMap.on("layeradd", redraw_heatmap)

        # Define function to render heatmap markers on map.
        function get_markers() {
            coord_state = [];
            myMap.eachLayer(function(layer) {
                if (layer.options.group == "Markers") {
                    coord_state.push([layer.options.lat, layer.options.lng]);
                }
            });
            return(coord_state);
        }

        // Function to redraw heatmap markers
        function redraw_heatmap() {
            markers.setData(get_markers());
        }

        var markers = L.circleMarker()
            .setLatLng([0, 0])
            .addTo(myMap)

        # Event listeners for layer add and remove
        myMap.on("layerremove", redraw_heatmark);
        myMap.on("layeradd", redraw_heatmark);

    })

Conclusion

In this guide, we explored how to create a leaflet map with addWebGLHeatmap and separate heatmap points from markers using JavaScript code. By following these steps, you can now visualize your data in a more interactive and efficient way.


Last modified on 2024-12-22