График точек вне сетки как стрелки, указывающие на данные с ggplot2 в R
Я создаю карты с данными мирового масштаба, а затем приближаюсь к определенным регионам. При увеличенном представлении я хотел бы показать, что есть другие точки данных за пределами ограничивающей рамки, путем размещения стрелок, которые указывают от центра поля до точки данных во внешнем мире.
Примечание: Мне не нужно, чтобы это был путь "большого круга", просто векторы XY в проекции Меркатора, потому что я думаю, что это будет полезно и для "нормальных" графиков.
В качестве примера, вот карта мира, показывающая объем данных:
![enter image description here]()
И вот увеличенное изображение, с добавлением пурпурных стрелок, чтобы показать, что я хотел бы сгенерировать.
![close-up]()
Ниже приведен код и данные, которые я использую для создания этих двух основных графиков. Мне нужен способ генерации стрелок.
require(ggplot2)
te = structure(list(lat = c(33.7399, 32.8571, 50.2214, 36.96263, 33.5835,
33.54557, 47.76147, 48, 59.40289, 35.93411, 32.87962, 38.3241,
50.03844, 37.44, 50.07774, 50.26668, 36.5944), lng = c(-118.37608,
-117.25746, -5.3865, -122.00809, -117.86159, -117.79805, -124.45055,
-126, -146.35157, -122.931472, -117.25285, -123.07331, -5.26339,
25.4, -5.709894, -3.86828, -121.96201)), .Names = c("lat", "lng"
), class = "data.frame", row.names = c(NA, -17L))
all_states = map_data("world")
# world version:
wp = ggplot() +
geom_polygon(data = all_states, aes(x = long, y = lat, group = group), colour = "gray",
fill = "gray") +
coord_cartesian(ylim = c(0, 80), xlim = c(-155, 45)) +
geom_point(data = te, aes(x = lng, y = lat), color = "blue", size = 5,alpha = 0.6)
print(wp)
#states plot
sp = ggplot() +
geom_polygon(data = all_states, aes(x = long, y = lat, group = group), colour = "gray", fill = "gray") +
coord_cartesian(ylim = c(30, 52), xlim = c(-128, -114)) +
geom_point(data = te, aes(x = lng, y = lat), color = "blue", size = 5, alpha = 0.6)
print(sp)
Ответы
Ответ 1
В этом решении используются пакеты sp
и rgeos
для управления пространственными данными, основными пересекающимися линиями которых являются пересекающиеся линии и многоугольник коробки, чтобы получить граничные точки для стрелок. Затем, если вы нарисуете стрелки с geom_segment
и нулевой шириной, линия невидима и останется только стрелка.
Эта функция вычисляет пересечения строк:
boxint <- function(xlim, ylim, xp, yp){
## build box as SpatialPolygons
box = cbind(xlim[c(1,2,2,1,1)],
ylim[c(1,1,2,2,1)])
box <- sp::SpatialPolygons(list(sp::Polygons(list(sp::Polygon(box)),ID=1)))
## get centre of box
x0=mean(xlim)
y0=mean(ylim)
## construct line segments to points
sl = sp::SpatialLines(
lapply(1:length(xp),
function(i){
sp::Lines(list(sp::Line(cbind(c(x0,xp[i]),c(y0,yp[i])))),ID=i)
}
)
)
## intersect lines segments with boxes to make points
pts = rgeos::gIntersection(sl, as(box, "SpatialLines"))
as.data.frame(sp::coordinates(pts), row.names=1:length(xp))
}
И это возвращает geom
со стрелками:
wherelse <- function(xlim, ylim, points){
## get points outside bounding box
outsides = points[!(
points$lng>=xlim[1] &
points$lng <= xlim[2] &
points$lat >= ylim[1] &
points$lat <= ylim[2]),]
npts = nrow(outsides)
## get centre point of box
x = rep(mean(xlim),npts)
y = rep(mean(ylim),npts)
## compute box-point intersections
pts = boxint(xlim, ylim, outsides$lng, outsides$lat)
pts$x0=x
pts$y0=y
## create arrow segments as invisible lines with visible arrowheads
ggplot2::geom_segment(data=pts, aes(x=x0,y=y0,xend=x,yend=y),
lwd=0, arrow=grid::arrow(length=unit(0.5,"cm"),
type="closed"),col="magenta")
}
Итак, ваш пример, основной сюжет:
sp = ggplot() +
geom_polygon(
data=all_states,
aes(x=long, y=lat, group = group),colour="gray",fill="gray" ) +
coord_cartesian(ylim=c(30, 52), xlim=c(-128,-114)) +
geom_point(data=te,aes(x=lng,y=lat),color="blue",size=5,alpha=0.6)
а затем добавьте стрелки с помощью:
sp + wherelse(c(-128,-114), c(30,52), te)
![enter image description here]()
Не уверен, что есть возможность рисовать стрелки точно так, как вы хотите их!
Ответ 2
Вот моя попытка. Это самое близкое, что я получил. Я использовал gcIntermediate()
для вычисления кратчайшего расстояния между центральной точкой вашей карты США и точками данных, которые остаются за пределами bbox. Следовательно, позиции стрелок могут быть не такими, какие вы хотите. Я надеюсь, что кто-то другой предоставит лучшее решение, основанное на этой попытке.
Сначала я разместил ваш df (т.е. te) с центральной точкой на увеличенной карте США. Затем я выбрал точки данных, которые не находятся в bbox на карте США. Затем добавьте два столбца, чтобы указать центральную точку карты США. Переименуйте два столбца и вычислите кратчайшее расстояние с помощью gcIntermediate.
library(dplyr)
library(ggplot2)
library(geosphere)
filter(te, !between(lng, -128, -114) | !between(lat, 30, 52)) %>%
mutate(start_long = (-128 - 114) / 2,
start_lat = (30 + 52) / 2) %>%
rename(end_lat = lat, end_long = lng) %>%
do(fortify(as(gcIntermediate(.[,c("start_long", "start_lat")],
.[,c("end_long", "end_lat")],
100,
breakAtDateLine = FALSE,
addStartEnd = TRUE,
sp = TRUE), "SpatialLinesDataFrame"))) -> foo
foo
содержит 100 точек данных для рисования соответствующей строки. Я выбрал точки данных, которые находятся близко к границе bbox. Я специально искал две точки данных для каждой строки, чтобы позже использовать geom_segment()
. Я признаю, что немного играл с фильтром. В конце концов, я не подмножал данные, используя lat в этом случае.
filter(foo, between(long, -128, -126.5) | between(long, -115.5, -114)) %>%
group_by(group) %>%
slice(c(1,n())) -> mydf
На следующем шаге я перестроил фрейм данных на основе этой ссылки
mutate(mydf, end_long = lag(long), end_lat = lag(lat)) %>%
slice(n()) -> mydf2
Наконец, я нарисовал карту со стрелками. Надеюсь, это предоставит вам какую-то основу. Я также надеюсь, что другие пользователи SO предоставят лучшие решения.
ggplot() +
geom_polygon(data = all_states, aes(x = long, y = lat, group = group),
colour = "gray", fill = "gray" ) +
coord_cartesian(ylim = c(30, 52), xlim = c(-128,-114)) +
geom_point(data = te, aes(x = lng,y = lat), color = "blue", size = 5,alpha = 0.6) +
geom_segment(data = mydf2, aes(x = end_long, xend = long,
y = end_lat, yend = lat, group = group),
arrow = arrow(length = unit(0.2, "cm"), ends = "last"))
![enter image description here]()