我正在尝试使用 Highcharter 包在 R 中创建世界地图。目标是绘制圣保罗与前 10 个出口目的地之间的连接(线),每个连接由地图上的一条线(地图线类型)表示。但是,线条未显示。
我尝试了多种方式格式化数据,但线条仍然没有显示在地图上。下面是我正在使用的代码,以及数据集结构和我的预期输出的描述。
# 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)
我相信您只能将
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)
)
你已经得到了一个与 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});
}"
)