Skip to contents
library(toro)

base_map <- map(
  imageSources = list(
    list(
      id = "map_pin",
      url = "https://upload.wikimedia.org/wikipedia/commons/f/f2/678111-map-marker-512.png"
    )
  )
)

Adding clusters

Note: Clusters currently only work with symbol(icon/pin) layers.

Clusters are disabled by default, but symbols with the same coordinates will always produce a cluster (even if can_cluster is set to false). This is an intentional choice as it is the only way for the user to know that there are multiple pins at the same coordinates. Without this, all pins would be stacked on top of each other and appear as a single pin. If you want to avoid this behaviour, we recommend either adding a small amount of random noise to the coordinates or aggregating the data so that there is only ever one pin per coordinate.

Clicking on a cluster will do one of two things:

  • Zoom into that clusters pins

  • Spiderfy the pins in that cluster so that they all become visible

base_map  |>
  add_symbol_layer(
    id = "earthquakes",
    source = quakes_data,
    layout = toro::get_layout_options(
      "symbol",
      options = list(
        icon_image = "map_pin",
        icon_size = 0.05
      )
    ),
    popup_column = "popup"
  )

Enabling clusters

The can_cluster parameter is used to enable clustering for a symbol layer. When set to TRUE the layer will cluster symbols that are in close proximity to each other.

base_map  |>
  add_symbol_layer(
    id = "earthquakes",
    source = quakes_data,
    layout = toro::get_layout_options(
      "symbol",
      options = list(
        icon_image = "map_pin",
        icon_size = 0.05
      )
    ),
    popup_column = "popup",
    can_cluster = TRUE
  )

Cluster customisation

You can customise the cluster appearance using clusterOptions list passed either the the map(), or any of the add layer functions, there are several items that can be configured. These include:

Cluster circles

clusterOptions.circleOptions changes the appearance of the clusters circle.

Available circle (paint/layout) properties can be found here.

map(
  imageSources = list(
    list(
      id = "map_pin",
      url = "https://upload.wikimedia.org/wikipedia/commons/f/f2/678111-map-marker-512.png"
    )
  ),
  # Setting the appearance globally for all clusters in the map
  clusterOptions = list(
    circleOptions = list(
      paint = list(
        "circle-color" = "#ebc642",
        "circle-stroke-color" = "#fc038c",
        "circle-radius" = 50
      )
    )
  )
) |>
  add_symbol_layer(
    id = "earthquakes",
    source = quakes_data |> head(nrow(quakes_data) / 2),
    layout = toro::get_layout_options(
      "symbol",
      options = list(
        icon_image = "map_pin",
        icon_size = 0.05
      )
    ),
    popup_column = "popup",
    can_cluster = TRUE
  ) |>
  add_symbol_layer(
    id = "earthquakes_two",
    source = quakes_data |> tail(nrow(quakes_data) / 2),
    layout = toro::get_layout_options(
      "symbol",
      options = list(
        icon_image = "map_pin",
        icon_size = 0.05
      )
    ),
    popup_column = "popup",
    can_cluster = TRUE,
    # Override the global cluster appearance for this layer only
    clusterOptions = list(
      circleOptions = list(
        paint = list(
          "circle-color" = "green",
          "circle-stroke-color" = "orange",
          "circle-radius" = 20
        )
      )
    )
  )

Cluster text

clusterOptions.textOptions changes the appearance of the clusters text.

Available text(symbol) (paint/layout) properties can be found here.

base_map  |>
  add_symbol_layer(
    id = "earthquakes",
    source = quakes_data,
    layout = toro::get_layout_options(
      "symbol",
      options = list(
        icon_image = "map_pin",
        icon_size = 0.05
      )
    ),
    popup_column = "popup",
    can_cluster = TRUE,
    clusterOptions = list(
      textOptions = list(
        paint = list(
          "text-color" = "red"
        )
      )
    )
  )

Advanced cluster customisation

An example of some more advanced customising (see this example).

base_map |>
  add_symbol_layer(
    id = "earthquakes",
    source = quakes_data,
    layout = toro::get_layout_options(
      "symbol",
      options = list(
        icon_image = "map_pin",
        icon_size = 0.05
      )
    ),
    popup_column = "popup",
    can_cluster = TRUE,
    clusterOptions = list(
      circleOptions = list(
        paint = list(
          "circle-color" = get_column_steps(
            "point_count",
            c(2, 5, 10),
            c("red", "orange", "yellow", "green")
          ),
          "circle-stroke-color" = get_column_steps(
            "point_count",
            c(2, 5, 10),
            c("red", "orange", "yellow", "green")
          ),
          "circle-radius" = get_column_steps(
            "point_count",
            c(2, 5, 10),
            c(10, 20, 30, 40)
          )
        ),
        layout = list(
          "circle-sort-key" = get_column_steps(
            "point_count",
            c(2, 5, 10),
            c(4, 3, 2, 1)
          )
        )
      )
    )
  )

Cluster spiderfying

Spiderfying handles viewing multiple pins in a cluster (pins in close proximity). When the map is zoomed in to the max zoom level and cluster that still appear will spiderfy when clicked on. This is so that the user is able to view all pins even if they appear in the same cluster.

There are two reasons why a cluster would need to spiderfy:

  • At the maps max zoom there are still pins with close enough coordinates that they are in a cluster (layer clustering is enabled).

  • A group of pins share the same coordinates. In this case, regardless of whether or not the layer has clustering enabled a cluster will always appear. This is due to the fact that without this there would be no way for the user to know that there are multiple pins there as they would all be stacked on top of each other and look like a single pin.

Spiderfying on max zoom

By setting a low max zoom we can see that points that there are clusters for points we know are not at the same coordinates (from above examples) but are in close enough proximity at max zoom to still be in a cluster.

map(
  maxZoom = 5,
  imageSources = list(
    list(
      id = "map_pin",
      url = "https://upload.wikimedia.org/wikipedia/commons/f/f2/678111-map-marker-512.png"
    )
  )
) |>
  add_symbol_layer(
    id = "earthquakes",
    source = quakes_data,
    layout = toro::get_layout_options(
      "symbol",
      options = list(
        icon_image = "map_pin",
        icon_size = 0.05
      )
    ),
    popup_column = "popup",
    can_cluster = TRUE
  )

Spiderfy with same coordinates

When pins have the same coordinates they will always appear in a cluster, even if the layer has set can_cluster to false.

base_map |>
  add_symbol_layer(
    id = "duplicated_pins",
    source = duplicated_data,
    layout = toro::get_layout_options(
      "symbol",
      options = list(
        icon_image = "map_pin",
        icon_size = 0.05
      )
    ),
    popup_column = "popup",
    can_cluster = FALSE
  )

Spiderfy customisation

There are 2 aspects of the spiderfying that are customisable:

Customise max pins shown in spiderfy

By default the spiderfying only shows 100 pins. You can see this in the example above where one of the clusters says it has 501 points in it but you can only see 100 pins when spiderfying.

This can be changed by setting clusterOptions.spiderfyOptions.maxSpiderfyPins when creating the map. Or, if you want to override the global setting for a specific layer, you can set clusterOptions.spiderfyOptions.maxSpiderfyPins when adding the layer.

map(
  imageSources = list(
    list(
      id = "map_pin",
      url = "https://upload.wikimedia.org/wikipedia/commons/f/f2/678111-map-marker-512.png"
    )
  ),
  clusterOptions = list(
    spiderfyOptions = list(
      maxSpiderfyPins = 500
    )
  )
) |>
  add_symbol_layer(
    id = "duplicated_pins",
    source = duplicated_data,
    layout = toro::get_layout_options(
      "symbol",
      options = list(
        icon_image = "map_pin",
        icon_size = 0.05
      )
    ),
    popup_column = "popup",
    can_cluster = FALSE,
    clusterOptions = list(
      spiderfyOptions = list(
        # maxSpiderfyPins = 5 # This would override the global setting for this layer only
      )
    )
  )

Customise spiderfy appearance

Like with clusters you can customise the spiderfys appearance by changing clusterOptions.spiderfyOptions.maxSpiderfyPins. Spiderfying appearance can only be set globally, not per layer.

Available line (paint/layout) properties can be found here.

map(
  imageSources = list(
    list(
      id = "map_pin",
      url = "https://upload.wikimedia.org/wikipedia/commons/f/f2/678111-map-marker-512.png"
    )
  ),
  clusterOptions = list(
    spiderfyOptions = list(
      paint = list(
        "line-color" = "purple",
        "line-width" = 2
      )
    )
  )
) |>
  add_symbol_layer(
    id = "duplicated_pins",
    source = duplicated_data,
    layout = toro::get_layout_options(
      "symbol",
      options = list(
        icon_image = "map_pin",
        icon_size = 0.05
      )
    ),
    popup_column = "popup",
    can_cluster = FALSE
  )

Cluster zoom on click

To control the behaviour that happens when a cluster is clicked, use the clusterOptions.zoomOnClick parameter. By default, a click on a cluster will zoom into the pins in that cluster until the maps max zoom is reached. After this point a click on a cluster will result in it spiderfying. To turn off the zooming on cluster click functionality, set the clusterOptions.zoomOnClick to FALSE.

Setting clusterOptions.zoomOnClick to be FALSE in the map() function will determine the default across all layers on the map. Setting clusterOptions.zoomOnClick on individual layers will override this for that specific layer.