Tracer les points à l'extérieur de la grille sous forme de flèches pointant vers les données avec ggplot2 en R

je génère des cartes avec des données à l'échelle mondiale, puis zoomant dans certaines régions. Sur la vue agrandie, je voudrais montrer qu'il y a d'autres points de données en dehors de la zone de limitation, en mettant des pointes de flèche qui point du centre de la zone à l'endroit où le point de données est dans le monde extérieur.

Remarque: Je n'ai pas besoin que ce soit un" grand cercle", juste des vecteurs XY dans la projection Mercator, parce que j'imagine que ce sera utile pour les tracés "normaux" comme bien.

par exemple, voici la carte du monde montrant l'étendue des données:

enter image description here

et voici le zoom en vue, avec des flèches magenta ajoutées manuellement pour montrer ce que je voudrais générer.

close-up

ci-Dessous le code et les données j'utilise pour générer ces deux parcelles. J'ai besoin d'un moyen de générer les pointes de flèche.

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)
16
demandé sur beroe 2015-05-08 22:02:38

2 réponses

Cette solution utilise sp et rgeos paquets pour manipuler les données spatiales, le principal point d'intersection étant les lignes et une boîte polygone pour obtenir les points de bord pour les flèches. Alors si vous dessinez des flèches geom_segment et de largeur zéro, la ligne est invisible et seule la tête de flèche reste.

cette fonction calcule les intersections ligne-boîte:

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

Et renvoie le geom avec des flèches:

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")
}

donc votre exemple, le tracé de base est:

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)

puis ajouter les flèches avec:

sp + wherelse(c(-128,-114), c(30,52), te)

enter image description here

pas sûr s'il y a une option pour dessiner les flèches exactement comme vous les voulez!

6
répondu Spacedman 2015-05-14 22:08:22

Voici ma tentative. C'est le plus proche que je suis arrivé. J'ai utilisé gcIntermediate() pour calculer la distance la plus courte entre le point central de votre carte américaine et les points de données qui restent en dehors de la bbox. Par conséquent, les positions des flèches peuvent ne pas être quelque chose que vous voulez. J'espère que quelqu'un d'autre fournira une meilleure solution basée sur cette tentative.

j'ai d'abord arrangé votre df (i.e., te) avec le point central de la carte zoomée américaine. J'ai ensuite choisi des points de données qui ne sont pas dans la bbox de la carte des états-unis. Ensuite, ajoutez deux colonnes pour indiquer le point central de la carte américaine. Renommer deux colonnes et calculer la plus courte distance avec 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 contient 100 points de données pour tracer la ligne respective. J'ai choisi des points de données qui restent proches de la limite de la bbox. Je cherchais spécifiquement deux points de données pour chaque ligne afin que je puisse utiliser geom_segment() plus tard. J'admets que j'ai un peu joué avec la condition du filtre. En fin de compte, je n'ai pas de sous-ensemble de données en utilisant lat ce cas.

filter(foo, between(long, -128, -126.5) | between(long, -115.5, -114)) %>%
group_by(group) %>%
slice(c(1,n())) -> mydf

dans la prochaine étape, j'ai réarrangé la base de données basée sur ce lien

mutate(mydf, end_long = lag(long), end_lat = lag(lat)) %>%
slice(n()) -> mydf2

finalement j'ai dessiné la carte avec des flèches. J'espère que cela vous fournira une sorte de base. J'espère également que d'autres utilisateurs de SO fourniront de meilleures solutions.

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

6
répondu jazzurro 2017-05-23 10:27:59