Mapline 未在 Highcharts 地图上显示连接

问题描述 投票:0回答:2

我正在尝试使用 Highcharter 包在 R 中创建世界地图。目标是绘制圣保罗与前 10 个出口目的地之间的连接(线),每个连接由地图上的一条线(地图线类型)表示。但是,线条未显示。

我尝试了多种方式格式化数据,但线条仍然没有显示在地图上。下面是我正在使用的代码,以及数据集结构和我的预期输出的描述。

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)
r mapping maps r-highcharter
2个回答
3
投票

我相信您只能将

mapline
类型与
geo_json
geo_list
{geojsonio}
对象一起使用(参考:
hc_add_series
用于
geo_json
geo_list
对象
)。

我正在使用

sf
构建一组线串,这些线串将使用
highcharter
转换为
geojson_list()
。由于我需要源点和目标点为
sf
/
sfc
对象,因此我切换到
sf
的空间数据框和所有数据对象的 GeoJSON,这也导致了工具提示上的一些小变化。

无论出于何种原因,highcharts.com都没有为我切断world.geo.json,因此使用来自

{giscoR}
的底图。

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)

示例数据:

# 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)
)


2
投票

你已经得到了一个与 R 一致的很好的答案,但这里还有另一个选择。这将省略您的最终跟踪 - “地图线”跟踪,并使用

htmlwidget::onRender
在 Javascript 中添加跟踪。

我以不同的方式查询地图数据,因为我收到了错误。然而,这是相同的数据。

library(highcharter)
library(dplyr)
library(geojsonio)
library(jsonlite)
library(httr)

worldgeojson <- httr::GET("https://raw.githubusercontent.com/highcharts/map-collection-dist/refs/heads/master/custom/world.geo.json") %>% 
  content() %>% fromJSON(simplifyVector = F) %>% as.json()

我将

id
添加到您的
dest_points
数据框中,因为它显着减少了 Javascript 中所需的代码量。

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,
  id = top_destinations$Nome_Pais     # <--- I'm new!!
)
不再需要

connections
。除了删除一个系列之外,情节没有变化。

onRender
的调用中,我使用了 Highcharts 函数的修改版本来创建 SVG 线段。

Javascript 中的最后一个调用是创建

mapline
系列。您应该能够看到您在
mapline
中调用的所有原始参数都已添加到 JS 版本中。此外,我还为该系列提供了名称和工具提示,以便您可以看到它是如何添加的(如果您想添加的话)。

我在 JS 中添加了注释,以便您可以了解发生的情况。如果您有任何疑问,请告诉我。

# 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(
      id = "São Paulo",
      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) %>% 
  htmlwidgets::onRender(
    "function(el, x) { /* this is a derivation of the function create by Highcharts */
      function pointsToPath(fromP, toP, invertArc) {
        const 
          fr = {x: fromP.x, y: fromP.y},
          to = {x: toP.x, y: toP.y},
          curve = .05,
          arcPx = (fr.x + to.x) / (invertArc ? 2 + curve : 2 - curve),
          arcPy = (fr.y + to.y) / (invertArc ? 2 + curve : 2 - curve);
        return [['M', fr.x, fr.y], ['Q', arcPx, arcPy, to.x, to.y]];
      }
      x = Highcharts.charts[0].series[2].data; /* point data to connect to */
      y = [];
      x.forEach((e, i) => {                    /* for each destination */
        nm = Highcharts.charts[0].series[2].data[i].name;
        y.push({id: 'São Paulo - ' + nm, 
                path: pointsToPath(Highcharts.charts[0].get('São Paulo'), Highcharts.charts[0].get(nm))});
      });    /* creates SVG paths between origin and each destination */
      Highcharts.charts[0].addSeries(  /* add new series with your original selected options */
        {type: 'mapline', showInLegend: false, enableMouseTracking: true,
         tooltip: {pointFormat: 'Path between {point.id}'},
         name: 'Flight Paths',
         lineWidth: 1.5, opacity: .8, data: y});
    }"
  )

updated plot

© www.soinside.com 2019 - 2024. All rights reserved.