I repeat the question because it seems that the code did not get attached. Dear R users, I am struggling to understand *curveGrob* and *annotation_custom* command in *ggplot*. In brief my issue can be approximated to drawing a downward sloping arrow curve from point (5,5) to (10,0) but keep on getting (5,0) to (10,5) |=> see attached arrows.png require(grid) g<-qplot(c(0,10),c(0,10)) myCurve<-curveGrob(0, 0, 1, 1, default.units = "npc", curvature = 0.3, angle = 90, ncp = 20, shape = 1, square = FALSE, squareShape = 1, inflect = FALSE, arrow = arrow(), open = TRUE, debug = FALSE, name = NULL, gp = gpar(col="blue"), vp = NULL) myCurve2<-curveGrob(0, 0, 1, 1, default.units = "npc", curvature = -0.3, angle = 60, ncp = 10, shape = 1, square = FALSE, squareShape = 1, inflect = FALSE, arrow = arrow(), open = TRUE, debug = FALSE, name = NULL, gp = gpar(), vp = NULL) g + annotation_custom(grob=myCurve,0,10,0,10) + # plot from 0,0 to 10,10 *annotation_custom(grob=myCurve,5,10,5,0) + # !!!!!this should draw from (5,5) to (10,0) but it does not* annotation_custom(grob=myCurve2,2.5,6,2.5,10) # plot from 2.5,2.5 to 6,6 In more detail: I am building a shiny application (like in shiny_pic.png). after choosing country 1 and country 2, R would plot a map of Europe, highlight the two selected countries with different colors and draw a curved arrow from country 1 in its respective color towards country2 and vice versa from country 2 to country 1 in the color of country 2. size of the arrow would correspond with trade between the two countries. since it is a shiny application I need a code that works fast. map does not have to be beautiful, the speed of the application is a priority. for the curved arrows I tried geom_curve but either the arrow was not drawn at all or would only appear after a wait for several minutes... I searched stackoverflow and have found a very similar problem: http://stackoverflow.com/questions/20216179/plot-curved-lines-between-two-locations-in-ggplot2 and although it works very well for a couple of countries like Italy-Poland (the only thing I had to change inside curveGrov2(arrow(ends="first")) for the arrow head to appear at the beginning, not the end. but the problem starts with country combinations like Austria-Cyprus or France-Greece. It seems that x-coordinates are respected, but y-coordinates inverted to draw an *upward* looking curve. it has been a while that I am looking at this problem so perhaps I have lost a fresh eye. is there some parameter I need to tweak in curveGrob function? fyi: my map does not have to be plotted with ggplot2. I am open to other solutions such as spplot etc. as long as it works fast (shiny) and draws the arrows as desired with every country combination and code execution. I would be grateful for any help and I am open to every feedback if there is something I can improve in my code please fell free... Milena aka suzukiblue -------------- next part -------------- ### rm(list = ls()) rm(list=lsf.str()) library(rgeos) library(stringr) library(reshape) library(maptools) library(ggplot2) library(SmarterPoland) library(sp) library(grid) library(rgdal) # choose your working directory for shapefile download setwd("C:/Users/Milena/Documents/R") current.dir <- getwd() #download the shapefile download.file("http://thematicmapping.org/downloads/TM_WORLD_BORDERS-0.3.zip", destfile="TM_WORLD_BORDERS-0.3.zip") #unzip to SpatialPolygonsDataFrame unzip("TM_WORLD_BORDERS-0.3.zip") world <- readOGR(dsn = current.dir, layer = "TM_WORLD_BORDERS-0.3") # extract from the world shapefile only the EU countries c.eu <- c("AUT", "BEL", "BGR", "HRV", "CYP", "CZE", "DNK", "EST", "FIN", "FRA", "DEU", "GRC", "HUN", "IRL", "ITA", "LVA", "LTU", "LUX", "MLT", "NLD", "POL", "PRT", "ROU", "SVK", "SVN", "ESP", "SWE", "GBR") vec.data.eu <- rep(0, times=length(c.eu)) for (i in 1:length(c.eu) ) {vec.data.eu[i] = which(world at data[, "ISO3"]==c.eu[i])} world.eu1 <- world[vec.data.eu,] world.eu <- world.eu1 # country centroids (middle points in each EU country) LAT <- c(47.33, 50.83, 43.00, 45.17, 35.00, 49.75, 56.00, 59.00, 64.00, 46.00, 51.50, 39.00, 47.00, 53.00, 42.83, 57.00, 56.00, 49.75, 35.92, 52.50, 52.00, 39.50, 46.00, 48.67, 46.25, 40.00, 62.00, 54.00) LONG <- c(13.33, 4.00, 25.00, 15.50, 33.00, 15.00, 10.00, 26.00, 26.00, 2.00, 10.50, 22.00, 20.00, -8.00, 12.83, 25.00, 24.00, 6.17, 14.43, 5.75, 20.00, -8.00, 25.00, 19.50, 15.17, -4.00, 15.00, -4.00) cent <- cbind(LAT,LONG) rownames(cent) <- c.eu # choose two countries and display them in two different colors (color A and color B) # I create a vector of ones length equal to the number of countries (28) world.eu <- world.eu1 a <- rep(1, length(c.eu)) # and overwrite the value for the 1st selected country with value 2 # and the 2nd selected country with 3 cor1 <<- 15 #Italy cor2 <<- 21 #Poland a[cor1] <- 2 a[cor2] <- 3 # combine the vector of levels with country names # and call the factor column "score" b <- cbind(c.eu, a) dataframe2 <- data.frame(b) colnames(dataframe2) <-c("Country.Code", "score") # merge score with the spatial points data frame # and pass it to ggplot for visualization matched.indices.eu <- match(world.eu at data[, "ISO3"], dataframe2[, "Country.Code"]) world.eu at data <- data.frame(world.eu at data, dataframe2[matched.indices.eu, ]) world.f.eu <- fortify(world.eu, region = "ISO3") world.m.eu <- merge(world.f.eu, world.eu at data, by.x = "id", by.y = "Country.Code") # draw curved arrows from country A to country B in color A (an arrow head pointing at country B) # and from country B to country A in color B (an arrow head pointing at country A) # the size of the arrow should correspond with the size of trade flow # from country A to B and from B to A # (that is however not the object of that question and could be done later) # since the ggplot will be encapsulated in a shiny application # code should draw correct arrows in every country A and country B combination # and should execute really fast. # I made a trial with geom_curve but the arrows either did not appear at all # (even though no error message was displayed) # or if appear that would be after a long wait # I found function curveGrob (which worked quite fast on my machine) # and annotation_custom to pass it to ggplot # I thought that it would be a solution. # my problem is similar I also want to visualize export between countries # all I had to change was the curvature of the first arrow to the -0.3 # and assign the arrow head of to the beginning not the end of the arrow myCurve<-curveGrob(0, 0, 1, 1, default.units = "npc", curvature = -0.3, angle = 60, ncp = 20, shape = 1, square = FALSE, squareShape = 1, inflect = FALSE, arrow = arrow(length = unit(0.15, "inches"), type="closed"), open = TRUE, debug = FALSE, name = NULL, gp = gpar(col="blue", lwd=10, lineend="round", fill="blue"), vp = NULL) myCurve2<-curveGrob(0, 0, 1, 1, default.units = "npc", curvature = 0.3, angle = 60, ncp = 20, shape = 1, square = FALSE, squareShape = 1, inflect = FALSE, arrow = arrow(length = unit(0.15, "inches"), type="closed", ends="first"), open = TRUE, debug = FALSE, name = NULL, gp = gpar(col="magenta", lwd=2, lineend="round", fill="magenta"), vp = NULL) ggplot(world.m.eu, aes(long, lat, group = group))+ geom_polygon(aes(fill = world.m.eu$score),show.legend=FALSE)+ geom_polygon(data = world.m.eu, aes(long,lat), fill="NA", color = "white", size=0.01) + coord_cartesian(xlim = c(-17, 37), ylim = c(34, 72))+ scale_fill_manual(values = c("lightblue", "blue", "magenta")) + annotation_custom(grob=myCurve, xmin=cent[cor1,2], xmax=cent[cor2,2], ymin=cent[cor1,1], ymax=cent[cor2,1]) + annotation_custom(grob=myCurve2,xmin=cent[cor2,2], xmax=cent[cor1,2], ymin=cent[cor2,1], ymax=cent[cor1,1]) # however what worked for the combination Italy - Poland # and perhaps a few others # does not work for example for Austria - Cyprus # or France - Greece. # it seems that regardless the assignment of xmin and xmax # or ymin and ymax # it looks like the ys are switched # for the lines to be drawed upwards ### Austria - Cyprus world.eu <- world.eu1 a <- rep(1, length(c.eu)) cor1 <<- 1 #Austria cor2 <<- 5 #Cyprus a[cor1] <- 2 a[cor2] <- 3 b <- cbind(c.eu, a) dataframe2 <- data.frame(b) colnames(dataframe2) <-c("Country.Code", "score") matched.indices.eu <- match(world.eu at data[, "ISO3"], dataframe2[, "Country.Code"]) world.eu at data <- data.frame(world.eu at data, dataframe2[matched.indices.eu, ]) world.f.eu <- fortify(world.eu, region = "ISO3") world.m.eu <- merge(world.f.eu, world.eu at data, by.x = "id", by.y = "Country.Code") myCurve<-curveGrob(0, 0, 1, 1, default.units = "npc", curvature = -0.3, angle = 60, ncp = 20, shape = 1, square = FALSE, squareShape = 1, inflect = FALSE, arrow = arrow(length = unit(0.15, "inches"), type="closed"), open = TRUE, debug = FALSE, name = NULL, gp = gpar(col="blue", lwd=10, lineend="round", fill="blue"), vp = NULL) myCurve2<-curveGrob(0, 0, 1, 1, default.units = "npc", curvature = 0.3, angle = 60, ncp = 20, shape = 1, square = FALSE, squareShape = 1, inflect = FALSE, arrow = arrow(length = unit(0.15, "inches"), type="closed", ends="first"), open = TRUE, debug = FALSE, name = NULL, gp = gpar(col="magenta", lwd=2, lineend="round", fill="magenta"), vp = NULL) ggplot(world.m.eu, aes(long, lat, group = group))+ geom_polygon(aes(fill = world.m.eu$score),show.legend=FALSE)+ geom_polygon(data = world.m.eu, aes(long,lat), fill="NA", color = "white", size=0.01) + coord_cartesian(xlim = c(-17, 37), ylim = c(34, 72))+ scale_fill_manual(values = c("lightblue", "blue", "magenta")) + annotation_custom(grob=myCurve, xmin=cent[cor1,2], xmax=cent[cor2,2], ymin=cent[cor1,1], ymax=cent[cor2,1]) + annotation_custom(grob=myCurve2,xmin=cent[cor2,2], xmax=cent[cor1,2], ymin=cent[cor2,1], ymax=cent[cor1,1]) ### France - Greece world.eu <- world.eu1 a <- rep(1, length(c.eu)) cor1 <<-10 #France cor2 <<-12 #Greece a[cor1] <- 2 a[cor2] <- 3 b <- cbind(c.eu, a) dataframe2 <- data.frame(b) colnames(dataframe2) <-c("Country.Code", "score") matched.indices.eu <- match(world.eu at data[, "ISO3"], dataframe2[, "Country.Code"]) world.eu at data <- data.frame(world.eu at data, dataframe2[matched.indices.eu, ]) world.f.eu <- fortify(world.eu, region = "ISO3") world.m.eu <- merge(world.f.eu, world.eu at data, by.x = "id", by.y = "Country.Code") myCurve<-curveGrob(0, 0, 1, 1, default.units = "npc", curvature = -0.3, angle = 60, ncp = 20, shape = 1, square = FALSE, squareShape = 1, inflect = FALSE, arrow = arrow(length = unit(0.15, "inches"), type="closed"), open = TRUE, debug = FALSE, name = NULL, gp = gpar(col="blue", lwd=10, lineend="round", fill="blue"), vp = NULL) myCurve2<-curveGrob(0, 0, 1, 1, default.units = "npc", curvature = 0.3, angle = 60, ncp = 20, shape = 1, square = FALSE, squareShape = 1, inflect = FALSE, arrow = arrow(length = unit(0.15, "inches"), type="closed", ends="first"), open = TRUE, debug = FALSE, name = NULL, gp = gpar(col="magenta", lwd=2, lineend="round", fill="magenta"), vp = NULL) ggplot(world.m.eu, aes(long, lat, group = group))+ geom_polygon(aes(fill = world.m.eu$score),show.legend=FALSE)+ geom_polygon(data = world.m.eu, aes(long,lat), fill="NA", color = "white", size=0.01) + coord_cartesian(xlim = c(-17, 37), ylim = c(34, 72))+ scale_fill_manual(values = c("lightblue", "blue", "magenta")) + annotation_custom(grob=myCurve, xmin=cent[cor1,2], xmax=cent[cor2,2], ymin=cent[cor1,1], ymax=cent[cor2,1]) + annotation_custom(grob=myCurve2,xmin=cent[cor2,2], xmax=cent[cor1,2], ymin=cent[cor2,1], ymax=cent[cor1,1]) ### -------------- next part -------------- A non-text attachment was scrubbed... Name: arrows.png Type: image/png Size: 5692 bytes Desc: not available URL: <https://stat.ethz.ch/pipermail/r-help/attachments/20160120/e980bd99/attachment-0005.png> -------------- next part -------------- A non-text attachment was scrubbed... Name: shiny_pic.png Type: image/png Size: 89577 bytes Desc: not available URL: <https://stat.ethz.ch/pipermail/r-help/attachments/20160120/e980bd99/attachment-0006.png> -------------- next part -------------- A non-text attachment was scrubbed... Name: Italy-Poland.png Type: image/png Size: 11819 bytes Desc: not available URL: <https://stat.ethz.ch/pipermail/r-help/attachments/20160120/e980bd99/attachment-0007.png> -------------- next part -------------- A non-text attachment was scrubbed... Name: Austria-Cyprus.png Type: image/png Size: 12047 bytes Desc: not available URL: <https://stat.ethz.ch/pipermail/r-help/attachments/20160120/e980bd99/attachment-0008.png> -------------- next part -------------- A non-text attachment was scrubbed... Name: France-Greece.png Type: image/png Size: 11894 bytes Desc: not available URL: <https://stat.ethz.ch/pipermail/r-help/attachments/20160120/e980bd99/attachment-0009.png>