rmappingmapsr-highcharter

Mapline not displaying connections on a Highcharts map


I'm trying to create a world map in R using the Highcharter package. The goal is to plot connections (lines) between São Paulo and the top 10 export destinations, with each connection represented by a line (mapline type) on the map. However, the lines are not displaying.

I've tried formatting the data in several ways, but the lines still don’t show up on the map. Below is the code I'm using, along with a description of the dataset structure and my intended output.

plot with highchart example with the lines, but it's not the package I want

# df
top_destinations <- tibble(
  Cidade = rep("São Paulo - SP", 10),
  Nome_Pais = c("China", "Canadá", "Estados Unidos", "Itália", "Bélgica", 
                "Emirados Árabes Unidos", "Suíça", "Argentina", "Índia", "Argélia"),
  Lat_Municipio = rep(-23.6, 10),
  Long_Municipio = rep(-46.6, 10),
  Lat_PAIS = c(33.4, 59.1, 39.6, 42.7, 50.6, 23.5, 46.9, -35.4, 21.0, 28.2),
  Long_PAIS = c(107.0, -109.0, -98.6, 12.8, 4.66, 54.2, 7.91, -65.2, 78.5, 2.63),
  US_FOB = c(1e9, 3.46e8, 3.08e8, 2.26e8, 2.04e8, 1.63e8, 1.14e8, 1.08e8, 9.3e7, 8.12e7)
)

# Packages
library(highcharter)
library(dplyr)

# Load world map data
worldgeojson <- get_data_from_map(download_map_data("https://code.highcharts.com/mapdata/custom/world.geo.json"))

# Prepare data for destination points
dest_points <- data.frame(
  lat = top_destinations$Lat_PAIS,
  lon = top_destinations$Long_PAIS,
  name = top_destinations$Nome_Pais,
  US_FOB = top_destinations$US_FOB
)

# Prepare data for connections (maplines)
connections <- lapply(1:nrow(top_destinations), function(i) {
  list(
    from = list(lat = top_destinations$Lat_Municipio[i], lon = top_destinations$Long_Municipio[i]),
    to = list(lat = top_destinations$Lat_PAIS[i], lon = top_destinations$Long_PAIS[i])
  )
})

# Create the map with Highcharts
highchart(type = "map") %>%
  # Add base map
  hc_add_series(
    mapData = worldgeojson,
    showInLegend = FALSE,
    nullColor = "#E0E0E0",
    borderWidth = 0
  ) %>%
  hc_mapNavigation(enabled = TRUE) %>%
  # Add São Paulo as the origin point
  hc_add_series(
    type = "mappoint",
    data = data.frame(
      lat = top_destinations$Lat_Municipio[1],
      lon = top_destinations$Long_Municipio[1],
      name = "São Paulo",
      color = "#3d9970",
      US_FOB = sum(top_destinations$US_FOB)
    ),
    marker = list(symbol = "circle", radius = 4),
    tooltip = list(pointFormat = "City: {point.name}<br>Export Value: {point.US_FOB}")
  ) %>%
  # Add destination points
  hc_add_series(
    type = "mappoint",
    name = "Destinations",
    data = dest_points,
    color = "#d35400",
    marker = list(symbol = "circle", radius = 3),
    tooltip = list(pointFormat = "Country: {point.name}<br>Export Value: {point.US_FOB}")
  ) %>%
  # Add connections between São Paulo and destinations
  hc_add_series(
    type = "mapline",
    data = connections,
    lineWidth = 1.5,
    opacity = 0.8,
    showInLegend = FALSE,
    enableMouseTracking = FALSE
  ) %>%
  # Title and tooltip settings
  hc_title(text = "Main Export Destinations from São Paulo") %>%
  hc_tooltip(useHTML = TRUE)

Solution

  • I believe you can only use mapline type with geo_json or geo_list objects of {geojsonio} (ref: hc_add_series for geo_json & geo_list objects ).

    I'm using sf to build a set of linestrings which will be converted for highcharter with geojson_list(). And as I need source & destination points to be sf / sfc objects anyway, I switched to sf's spatial data frames and GeoJSON for all data objects, this also caused a couple of small changes on tooltips.

    For whatever reason highcharts.com is not severing world.geo.json for me, so using base map from {giscoR} instead.

    library(highcharter)
    library(geojsonio)
    library(dplyr)
    library(sf)
    #> Linking to GEOS 3.12.1, GDAL 3.8.4, PROJ 9.3.1; sf_use_s2() is TRUE
    
    worldgeojson <-
      giscoR::gisco_countries %>%
      geojsonio::geojson_json()
    
    # sf object with a source point
    src_point_sf <- 
      top_destinations %>%
      summarise(
        name = "São Paulo", 
        US_FOB = sum(US_FOB),
        lat = first(Lat_Municipio),
        lon = first(Long_Municipio)
      ) |> 
      st_as_sf(coords = c("lon", "lat"), crs = "WGS84")
    
    # Prepare data for destination points
    dest_points_sf <- 
      top_destinations %>%
      st_as_sf(coords = c("Long_PAIS", "Lat_PAIS"), crs = "WGS84") %>%
      select(name = Nome_Pais, US_FOB)
    
    # sf object with connection lines (generate point pair, union, cast to line)
    connections_sf <- 
      lapply(
        st_geometry(dest_points_sf), 
        \(to) st_union(c(st_geometry(src_point_sf)[[1]], to)) %>% st_cast("LINESTRING") ) %>% 
      st_as_sfc(crs = st_crs(dest_points_sf)) %>% 
      st_sf(geometry = _) %>% 
      # with just 2 endpoints we'd get straight lines,
      # adding a vertex at every 1000km introduces curves once linestrings are projected
      st_segmentize(dfMaxLength = 1000*1000)
    
    # Create the map with Highcharts
    highchart(type = "map") %>%
      # Add base map
      hc_add_series(
        mapData = worldgeojson, 
        showInLegend = FALSE,
        nullColor = "#E0E0E0",
        borderWidth = 0
      ) %>%
      hc_mapNavigation(enabled = TRUE) %>%
      # Add São Paulo as the origin point
      hc_add_series(
        data = geojson_list(src_point_sf),
        type = "mappoint",
        marker = list(symbol = "circle", radius = 4),
        color = "#3d9970" ,
        tooltip = list(pointFormat = "City: {point.properties.name}<br>Export Value: {point.properties.US_FOB}")
      ) %>%
      # Add destination points
      hc_add_series(
        data = geojson_list(dest_points_sf),
        type = "mappoint",
        name = "Destinations",
        color = "#d35400",
        marker = list(symbol = "circle", radius = 3),
        tooltip = list(pointFormat = "Country: {point.properties.name}<br>Export Value: {point.properties.US_FOB}")
      ) %>%  
      # Add connections between São Paulo and destinations
      hc_add_series(
        data = geojson_list(connections_sf),
        type = "mapline",
        lineWidth = 1.5,
        opacity = 0.8,
        showInLegend = FALSE,
        enableMouseTracking = FALSE
      ) %>%
      # Title and tooltip settings
      hc_title(text = "Main Export Destinations from São Paulo") %>%
      hc_tooltip(useHTML = TRUE)
    

    Example data:

    # df
    top_destinations <- tibble(
      Cidade = rep("São Paulo - SP", 10),
      Nome_Pais = c("China", "Canadá", "Estados Unidos", "Itália", "Bélgica", 
                    "Emirados Árabes Unidos", "Suíça", "Argentina", "Índia", "Argélia"),
      Lat_Municipio = rep(-23.6, 10),
      Long_Municipio = rep(-46.6, 10),
      Lat_PAIS = c(33.4, 59.1, 39.6, 42.7, 50.6, 23.5, 46.9, -35.4, 21.0, 28.2),
      Long_PAIS = c(107.0, -109.0, -98.6, 12.8, 4.66, 54.2, 7.91, -65.2, 78.5, 2.63),
      US_FOB = c(1e9, 3.46e8, 3.08e8, 2.26e8, 2.04e8, 1.63e8, 1.14e8, 1.08e8, 9.3e7, 8.12e7)
    )