Fixing maps library data for Pacific centred (0°-360° longitude) display - r

I'm plotting some points on a map of the world using the R maps package, something like:
The command to draw the base map is:
map("world", fill=TRUE, col="white", bg="gray", ylim=c(-60, 90), mar=c(0,0,0,0))
But I need to display Pacific centred map. I use map("world2", etc to use the Pacific centred basemap from the maps package, and convert the coordinates of the data points in my dataframe (df) with:
df$longitude[df$longitude < 0] = df$longitude[df$longitude < 0] + 360
This works if I don't use the fill option, but with fill the polygons which cross 0° cause problems.
I guess I need to transform the polygon data from the maps library somehow to sort this out, but I have no idea how to get at this.
My ideal solution would be to draw a maps with a left boundary at -20° and a right boundary at -30° (i.e. 330°). The following gets the correct points and coastlines onto the map, but the crossing-zero problem is the same
df$longitude[df$longitude < -20] = df$longitude[d$longitude < -20] + 360
map("world", fill=TRUE, col="white", bg="gray", mar=c(0,0,0,0),
ylim=c(-60, 90), xlim=c(-20, 330))
map("world2", add=TRUE, col="white", bg="gray", fill=TRUE, xlim=c(180, 330))
Any help would be greatly appreciated.

You could use the fact that internally, a map object returned by the map() function can be recalculated and used again in the map() function. I'd create a list with individual polygons, check which ones have very different longitude values, and rearrange those ones. I gave an example of this approach in the function below*, which allows something like :
plot.map("world", center=180, col="white",bg="gray",
fill=TRUE,ylim=c(-60,90),mar=c(0,0,0,0))
to get
If I were you, I'd shift everything a bit more, like in :
plot.map("world", center=200, col="white",bg="gray",
fill=TRUE,ylim=c(-60,90),mar=c(0,0,0,0))
The function :
plot.map<- function(database,center,...){
Obj <- map(database,...,plot=F)
coord <- cbind(Obj[[1]],Obj[[2]])
# split up the coordinates
id <- rle(!is.na(coord[,1]))
id <- matrix(c(1,cumsum(id$lengths)),ncol=2,byrow=T)
polygons <- apply(id,1,function(i){coord[i[1]:i[2],]})
# split up polygons that differ too much
polygons <- lapply(polygons,function(x){
x[,1] <- x[,1] + center
x[,1] <- ifelse(x[,1]>180,x[,1]-360,x[,1])
if(sum(diff(x[,1])>300,na.rm=T) >0){
id <- x[,1] < 0
x <- rbind(x[id,],c(NA,NA),x[!id,])
}
x
})
# reconstruct the object
polygons <- do.call(rbind,polygons)
Obj[[1]] <- polygons[,1]
Obj[[2]] <- polygons[,2]
map(Obj,...)
}
*Note that this function only takes positive center values. It's easily adapted to allow for center values in both directions, but I didn't bother anymore as that's trivial.

install the latest version of maps (3.2.0).
do this:
d$lon2 <- ifelse(d$lon < -25, d$lon + 360, d$lon) # where d is your df
mapWorld <- map_data('world', wrap=c(-25,335), ylim=c(-55,75))
ggplot() +
geom_polygon(data = mapWorld, aes(x=long, y = lat, group = group)) +
geom_point(data = d, aes(x = lon2, y = lat))

A bit late, but you can also create a shifted map by using a projection (requires the mapproj package):
map("world", projection="rectangular", parameter=0,
orientation=c(90,0,180), wrap=TRUE, fill=T, resolution=0,col=0)
This will shift by 180 degrees. But the difference with 'world2' is that the longitude co-ordinate will be different ([-pi,pi]). All projections of this package put 0 at the centre. And in that case, the 'wrap' option detects the jump correctly.
'resolution=0' helps to get cleaner borders.
You can easily change the centre longitude by changing the '180' value in the projection description.

What about this solution?
xlims = c(0, 359) # these are the limits you want to change
ylims = c(-55,75)
mapWorld <- map_data('world', wrap=xlims, ylim=ylims)
head(mapWorld)
g1 <- ggplot() +
geom_polygon(data = mapWorld, aes(x=long, y = lat, group = group)) +
coord_map("rectangular", lat0=0, xlim=xlims, ylim=ylims)
g1

Related

How to shift coordinates in ggplot2 map for any arbitrary longitude range

I'm making a map using ggplot and I want to be able to center the map around the Pacific Ocean while plotting points on the map.
It turns out I can do the map shifting by using the wrap option of maps::map. However, I'm not sure exactly how to shift the points to get them to match to my new shifted map. I found how to do that when I recreate a Pacific centered map with wrap(0, 360) but I'm not sure how to accomplish this for arbitrary shift units. I'm sure this is pretty straightforward but I can't seem to figure it out. Any ideas?
library(maps)
library(tidyverse)
# Pacific centered map
shift_value_1 <- 0
shift_value_2 <- 360
# Regular map, how about new values of shift_value_1 and shift_value_2? (e.g. -20, 325)
shift_value_1 <- -180
shift_value_2 <- 180
map_world_df <- map_data('world', wrap=c(shift_value_1, shift_value_2)) %>%
dplyr::filter(region != "Antarctica")
country_shapes <- geom_polygon(data = map_world_df,
aes(x=long, y = lat, group = group),
fill = "gainsboro",
color = "gainsboro",
size = 0.15)
nodes <- data.frame(names = c("A", "B", "C", "D"),
lat = c(64.220241, 10.278386, 64.710869, 19.432564),
lon = c(135.75572, 34.33927, -151.20003, -99.13323))
nodes$lon[nodes$lon <0] <- nodes$lon[nodes$lon <0] + (shift_value_1 + shift_value_2)
ggplot() +
country_shapes +
geom_point(data = nodes, aes(x=lon, y = lat))
I would love to be able to have a way of selecting arbitrary longitude ranges and shift the points accordingly, instead I get incorrectly placed points.
First of all, when using wrapping with two limits, you should make sure they add up correctly. Wrapping is not the same as just setting boundaries. For instance, in you're comment to the code you are asking the map to continue from -25 to 320, which is inconsistent. The resulting map will have some strange artefacts (more visible if you try larger errors, e.g. (-25, 150) ). You should always have shift_value2 - shift_value1 == 360, so for instance (-25, 335). Shifting longitudes by any other value will always give errors. You can add xlim=c(-25, 320) to the map() call if you wish, of course. But this should be done in a separate map() call (as explained in the documentation: xlim is applied before the wrapping, so combining them results in part of the map being dropped). So for a limited map, you should probably do
mymap <- map(wrap=c(-25, 335), fill=TRUE, plot=FALSE)
map(mymap, xlim=c(-25, 250),...)
But that is not necessary when using ggplot2 for plotting the map, because those map limits are applied after the call to map() and the wrapping.
Shifting an arbitrary point just means adding (or subtracting) 360 until it falls between the two values. In most cases, the following should work:
lon[lon < shift_value1] <- lon[lon < shift_value1] + 360
lon[lon > shift_value2] <- lon[lon > shift_value2] - 360

R: ggmap: How do I draw lines using only known angles on a ggmap? My geom_spoke method is not working

I am attempting to draw lines in polar coordinates on a ggmap. The general method works fine in ggplot:
library(ggmap)
library(rgdal)
library(ggplot2)
#####################################
## build data frame with triangulation data
t1 <- c(534325, 4858925, 338, 0955)
t2 <- c(534383, 4859261, 290, 1010)
t3 <- c(534386, 4859011, 301, 1015)
df <- as.data.frame(rbind(t1, t2, t3))
colnames(df) <- c("Easting", "Northing", "Azimuth", "Time")
df$Time <- as.character(df$Time)
## plot coordinates with triangulation
ggplot(df, aes(x=Easting, y=Northing, label=Time)) +
geom_point() +
geom_text(nudge_x = 50) +
geom_spoke(aes(angle = Azimuth, radius = -500)) +
theme_bw()
Triangulation with no base map:
This is what I want, but with no base map in the background.
However, when I attempt this method in ggmap:
## convert utms to lat long
utmcoor <- SpatialPoints(cbind(df$Easting,df$Northing),
proj4string=CRS("+proj=utm +zone=12"))
lonlatcoor <- as.data.frame(spTransform(utmcoor,CRS("+proj=longlat")))
colnames(lonlatcoor) <- c("lon", "lat")
df$lon <- lonlatcoor$lon
df$lat <- lonlatcoor$lat
## plot on base map
zoom <- 15
meanlon <- mean(df$lon)
meanlat <- mean(df$lat)
basemap <- get_googlemap(center=c(lon=meanlon, lat=meanlat), zoom=zoom,
maptype="hybrid")
ggmap(basemap) +
geom_point(aes(x=lon, y=lat), colour="red", size=2, data=df) +
geom_spoke(aes(x=lon, y=lat, angle = Azimuth), data=df, radius=-200) +
theme_void()
I get the error
Warning message:
Removed 3 rows containing missing values (geom_segment).
The code works fine without the geom_spoke line, resulting in basemap without triangulations:
I understand that this function is generally used for things like quiver plots. Does ggmap not support geom_spoke? Is there a better function altogether that I am not aware of?
Thanks in advance.
The issue is with the coordinates: You're giving geom_spoke a radius of -200, but the coordinates you're working with are several orders of magnitude smaller. I took out the theme_void just to remind myself of how small the scale is; you want a radius of something like 0.01. Mess around with the radius value I put here.
geom_spoke basically does a little trigonometry behind the scenes to calculate the endpoint of each spoke, then draws a segment. If that endpoint is out of the bounds of the plot, you get that missing values warning. I'm not sure if -200 is the radius you're sticking with, or just a dummy value, but you could use your original coordinates and angles to calculate the endpoints, then project those points to figure out the radius. Or just use those endpoints to draw the geom_segment yourself. I'll leave those calculations to you, or it might make for another question. I'm pretty partial to sf and have a feeling that could provide an easy enough way to get those points.
library(ggmap)
library(rgdal)
library(ggplot2)
# .....
# everything up until ggmap is the same as in question
ggmap(basemap) +
geom_point(aes(x=lon, y=lat), colour="red", size=2, data=df) +
geom_spoke(aes(x=lon, y=lat, angle = Azimuth), data=df, radius = -1e-2)
Created on 2018-07-18 by the reprex package (v0.2.0).

Underlay a vector image to a grid for kriging in R

After searching around a lot, asking, and doing some code, I kinda got the bare minimum for doing kriging in R's gstat.
Using 4 points (I know, totally bad), I kriged the unsampled points located between them. But in actuality, I don't need all of those points. Inside that area, there is a smaller subarea... this area is the one I actually need.
Long story short.. I have measurements taken from 4 weather stations that report rainfall data. The lat and long coordinates for these points are:
lat long
7.16 124.21
8.6 123.35
8.43 124.28
8.15 125.08
My road to kriging can be seen through my previous questions on StackOverflow.
This: Create variogram in R's gstat package
And this: Create Grid in R for kriging in gstat
I know that the image in has the coordinates (at least according to my estimates):
Leftmost: 124 13ish 0 E(DMS)
Rightmost : 124 20ish 0 E
Topmost corrdinates: 124 17ish 0 E
Bottommost coordinates: 124 16ish 0 E
Conversion will take place for that but that doesn't matter I think, or easier to deal with later.
The image is also irregular (but aren't they all though).
Think of it like a doughnut, you krige the the whole circular shape of the doughnut but you only need the area covered by the hole so you remove or at least disregard the values you got from the doughnut itself.
I have an image (.jpg) of the area in question, I will have to convert the image into a shapefile or some other vector format using QGIS or similar software. After that, I will have to insert that vector image inside the 4 point kriged area, so I know which coordinates to actually consider and which ones to remove.
Finally, I take the values of the area covered by the image and store them into a csv or database.
Anybody know how I can start with this? Total noob at R and statistics. Thanks to anyone who responds.
I just want to know if its possible and if it is provide some tips. Thanks again.
Might as well also post my script:
suppressPackageStartupMessages({
library(sp)
library(gstat)
library(RPostgreSQL)
library(dplyr) # for "glimpse"
library(ggplot2)
library(scales) # for "comma"
library(magrittr)
library(gridExtra)
library(rgdal)
library(raster)
library(leaflet)
library(mapview)
})
drv <- dbDriver("PostgreSQL")
con <- dbConnect(drv, dbname="Rainfall Data", host="localhost", port=5432,
user="postgres", password="postgres")
day_1 <- dbGetQuery(con, "SELECT lat, long, rainfall FROM cotabato.sample")
coordinates(day_1) <- ~ lat + long
plot(day_1)
x.range <- as.integer(c(7.0,9.0))
y.range <- as.integer(c(123.0,126.0))
grid <- expand.grid(x=seq(from=x.range[1], to=x.range[2], by=0.05),
y=seq(from=y.range[1], to=y.range[2], by=0.05))
coordinates(grid) <- ~x+y
plot(grid, cex=1.5)
points(day_1, col='red')
title("Interpolation Grid and Sample Points")
day_1.vgm <- variogram(rainfall~1, day_1, width = 0.02, cutoff = 1.8)
day_1.fit <- fit.variogram(day_1.vgm, model=vgm("Sph", psill = 8000, range = 1))
plot(day_1.vgm, day_1.fit)
plot1 <- day_1 %>% as.data.frame %>%
ggplot(aes(lat, long)) + geom_point(size=1) + coord_equal() +
ggtitle("Points with measurements")
plot(plot1)
############################
plot2 <- grid %>% as.data.frame %>%
ggplot(aes(x, y)) + geom_point(size=1) + coord_equal() +
ggtitle("Points at which to estimate")
plot(plot2)
grid.arrange(plot1, plot2, ncol = 2)
coordinates(grid) <- ~ x + y
############################
day_1.kriged <- krige(rainfall~1, day_1, grid, model=day_1.fit)
day_1.kriged %>% as.data.frame %>%
ggplot(aes(x=x, y=y)) + geom_tile(aes(fill=var1.pred)) + coord_equal() +
scale_fill_gradient(low = "yellow", high="red") +
scale_x_continuous(labels=comma) + scale_y_continuous(labels=comma) +
theme_bw()
write.csv(day_1.kriged, file = "Day_1.csv")
EDIT: The code has changed since the last time. But that doesn't matter I guess, I just want to know if its possible and can anybody provide the simplest example of it being possible. I can derive the solution to the example to my own problem from there.
Let me know if you find this useful:
"Think of it like a doughnut, you krige the the whole circular shape of the doughnut but you only need the area covered by the hole so you remove or at least disregard the values you got from the doughnut itself."
For this you load your vectorial data:
donut <- rgdal::readOGR('/variogram', 'donut')
day_1#proj4string#projargs <- "+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0" # Becouse donut shape have this CRS
plot(donut, axes = TRUE, col = 3)
plot(day_1, col = 2, pch = 20, add = TRUE)
Then you delete the 'external ring' and keep the insider. Also indicates that the second isn't a hole anymore:
hole <- donut # for keep original shape
hole#polygons[1][[1]]#Polygons[1] <- NULL
hole#polygons[1][[1]]#Polygons[1][[1]]#hole <- FALSE
plot(hole, axes = TRUE, col = 4, add = TRUE)
After that you chek whicch points are inside 'hole' new blue vector layer:
over.pts <- over(day_1, hole)
day_1_subset <- day_1[!is.na(over.pts$Id), ]
plot(donut, axes = TRUE, col = 3)
plot(hole, col = 4, add = TRUE)
plot(day_1, col = 2, pch = 20, add = TRUE)
plot(day_1_subset, col = 'white', pch = 1, cex = 2, add = TRUE)
write.csv(day_1_subset#data, 'myfile.csv') # write intersected points table
write.csv(as.data.frame(coordinates(day_1_subset)), 'myfile.csv') # write intersected points coords
writeOGR(day_1_subset, 'path', 'mysubsetlayer', driver = 'ESRI Shapefile') # write intersected points shape
With this code you can solve the 'ring' or doughnut 'hole' if you already have the shapefile.
If you have an image and want to clip it try the follow:
In the case you load a raster (get basemap image from web):
coordDf <- as.data.frame(coordinates(day_1)) # get basemap from points
# coordDf <- data.frame(hole#polygons[1][[1]]#Polygons[1][[1]]#coords) # get basemap from hole
colnames(coordDf) <- c('x', 'y')
imag <- dismo::gmap(coordDf, lonlat = TRUE)
myimag <- raster::crop(day_1.kriged, hole)
plot(myimag)
plot(day_1, add = TRUE, col = 2)
In case you use day_1.kriged:
myCropKrig<- raster::crop(day_1.kriged, hole)
myCropKrig %>% as.data.frame %>%
ggplot(aes(x=x, y=y)) + geom_tile(aes(fill=var1.pred)) + coord_equal() +
scale_fill_gradient(low = "yellow", high="red") +
scale_x_continuous(labels=comma) + scale_y_continuous(labels=comma) +
geom_point(data=coordDf[!is.na(over.pts$Id), ], aes(x=x, y=y), color="blue", size=3, shape=20) +
theme_bw()
And "Finally, I take the values of the area covered by the image and store them into a csv or database."
write.csv(as.data.frame(myCropKrig), 'myCropKrig.csv')
Hope you find this useful and I respond your meaning
To simplify your question:
You want to delineate an area based on an image that is not georeferenced.
You want to extract results of a interpolation only for this area
Few steps are required
You need to use QGIS to georeference your image (Raster > Georeferencer). You need to have a georeferenced map in background to help. This creates a raster object with spatial information.
Two possibilities.
2.a. The central part of your image has a color than can be directly used as a mask in R (Ex. All green pixels in middle of red pixels).
2.b. If not, you need to use QGIS to delineate manually a Polygon of the area (Layer > Create Layer > New Shapefile > Polygon)
Import your raster or polygon shapefile in R
Use function raster::mask to extract values of your interpolation using the raster image or the SpatialPolygon.

R Data points perimeter buffer

I need to create a buffer zone on the set of data points with x and y coordinates (grey points on the graph).
Unfortunately, I don’t have a perimeter border of the points, from which to create a buffer.
I was trying to calculate the perimeter using chull function, however it is not working properly (orange area).
I can calculate the border points using max/min functions for the data by some step (let's say 10 m, red dots), and try to calculate the buffer from those points.
Is someone aware of more correct and clean way to calculate the buffer zone for set of points.
You could do a tesselation around the points. Points at the border will have much larger polygons.
library(deldir)
library(ggplot2)
triang <- deldir(data$x, data$y)
border <- triang$summary
border$Selected <- border$dir.area > 260
ggplot(border[order(border$Selected), ], aes(x = x, y = y, colour = Selected)) + geom_point()
thanks a lot for your suggestions and comments.
Indeed, It was my fault omitting the alphahull package.
After identifying the border with ashape I create a buffer polygon and identified the data that lies inside and outside the buffer. Challenge was to correctly extract the polygon from ashap, but solution of RPubs safe me.
You can see also the graphical example here.
Best
## load
library(ggplot2); library(alphahull);
library(igraph); library(rgeos)
## Load the data
data.df<-read.csv("Data/Cencus/Lyford_meta.csv",sep=",",header=TRUE)
#Remove the duplicates in the data to do the chull calculation
data <- data.df[!duplicated(paste(data.df$xsite, data.df$ysite, sep ="_")), c("xsite","ysite") ]
#calculate the chull with alpha 20
data.chull <- ashape(data, alpha = 20)
## Below is the code to extract polygon from the ashape chull function
## credit to: http://rpubs.com/geospacedman/alphasimple
order.chull <- graph.edgelist(cbind(as.character(data.chull$edges[, "ind1"]), as.character(data.chull$edges[,"ind2"])), directed = FALSE)
cutg <- order.chull - E(order.chull)[1]
ends <- names(which(degree(cutg) == 1))
path <- get.shortest.paths(cutg, ends[1], ends[2])[[1]]
pathX <- as.numeric(V(order.chull)[unlist(path[[1]])]$name)
pathX = c(pathX, pathX[1])
data.chull <- as.data.frame(data.chull$x[pathX, ])
## Create a spatial object from the polygon and apply a buffer to
## Then extract the data to the dataframe.
data.chull.poly <- SpatialPolygons(list(Polygons(list(Polygon(as.matrix(data.chull))),"s1")))
data.chull.poly.buff <- gBuffer(data.chull.poly, width = -10)
data.buffer <- fortify(data.chull.poly.buff)[c("long","lat")]
## Identidfy the data that are inside the buffer polygon
data$posit <- "Outside"
data$posit[point.in.polygon(data$x,data$y,data.buffer$long,data.buffer$lat) %in% c(1,2,3)] <- "Inside"
## Plot the results
ggplot()+
theme_bw()+xlab("X coordinates (m)")+ylab("Y coordinates (m)") +
geom_point(data = data, aes(xsite, ysite, color = posit))+
geom_polygon(data = data.chull, aes(V1, V2), color = "black", alpha = 0)+
geom_polygon(data = data.buffer, aes(long, lat), color = "blue", alpha = 0)

maps r plot distances with color on these

perhaps you have an idea and could help me. I have following data:
lon.x <- c(11.581981, 13.404954, 9.993682, 7.842104 , 11.741185)
lat.x <- c(48.135125, 52.520007, 53.551085, 47.999008, 48.402880)
lon.y <- c(8.801694, 7.842104 , 11.581981, 13.404954, 7.842104 )
lat.y <- c(53.079296,47.999008, 48.135125, 52.520007, 47.999008)
pred <- c(1,2,3,4,5)
data <- data.frame(cbind(lon.x, lat.x, lon.y, lat.y, pred))
where "lon.x" and "lat.x" are longitude-latitude points of a city and "lon.y" and "lat.y" of another city. So there are pairs of cities.
Now, I would like to make a map in R, with
(1) the direct distances between the x and y coordinates as a line
(2) which will receive a different color based on the variable "pred", this could be red for higher values and blue for lower, or thicker lines with higher values of "pred".
The result should be a simple map, with lines between the cities, that are shaped based on the variable "pred". For instance, the line between the first pair of cities would be thinner, while the last one would be thicker. Is that possible?
I have currently only made to receive a (very complicated) google map of Germany:
library(mapproj)
map <- get_map(location = 'Germany', zoom = 6.2)
ggmap(map)
But I am not sure how to plot points and especially relations between the points that differ based on "pred". Also a very simple map (not so detailed google map) would be best! Any idea? THANKS!
You can use ggplot2 to add lines onto the plot.
library(ggplot2)
library(ggmap)
map <- get_map(location = 'Germany', zoom = 6)
ggmap(map) +
geom_segment(data=data, aes(x=lon.x, xend=lon.y, y=lat.x, yend=lat.y, color=pred), size=2) +
scale_color_continuous(high="red", low="blue")
As for the simpler map, you can download shape files (just the outlines of countries) from www.gadm.org. Level 0 maps are just the country, level 1 have state boundaries, etc. To use one of these, download the file from the website and use this code:
load("DEU_adm0.RData")
gadm <- fortify(gadm)
ggplot(gadm) +
geom_path(aes(x=long, y=lat, group=group)) +
geom_segment(data=data, aes(x=lon.x, xend=lon.y, y=lat.x, yend=lat.y, color=pred), size=2) +
scale_color_continuous(high="red", low="blue")

Resources