How to apply a grid transformation in sf with PROJ7? - grid

I try to apply a gridded transformation of coordinates in sf, but it does not work. Is there something else to do apart from calling sf_proj_network(TRUE)? Here is the code I used:
pt_27572 <- data.frame(X=55824.4970,Y=2394454.2120) # set coordinates of one point in EPSG:27572
pt_2154 <- data.frame(X=107242.8310,Y=6832277.1820) # known accurate coordinates of the same point in EPSG:2154
ptSf_27572 <- st_as_sf(pt_27572, coords = c("X", "Y"), crs = 27572) # build sf object
ptSf_2154 <- st_as_sf(pt_2154, coords = c("X", "Y"), crs = 2154) # build sf object
sf_proj_network(TRUE) # allow search for online datum grids in the PROJ CDN
[1] "https://cdn.proj.org"
sf_proj_pipelines("EPSG:27572", "EPSG:2154") # grids (fr_ign_gr3df97a.tif) seem to be found for accurate transformation from EPSG:27572 to EPSG:2154
Candidate coordinate operations found: 3
Strict containment: FALSE
Axis order auth compl: FALSE
Source: EPSG:27572
Target: EPSG:2154
Best instantiable operation has accuracy: 1 m
Description: Inverse of Lambert zone II + NTF (Paris) to NTF (1) + NTF to
RGF93 (1) + Lambert-93
Definition: +proj=pipeline +step +inv +proj=lcc +lat_1=46.8 +lat_0=46.8
+lon_0=0 +k_0=0.99987742 +x_0=600000 +y_0=2200000
+ellps=clrk80ign +pm=paris +step +proj=push +v_3
+step +proj=cart +ellps=clrk80ign +step
+proj=xyzgridshift +grids=fr_ign_gr3df97a.tif
+grid_ref=output_crs +ellps=GRS80 +step +inv
+proj=cart +ellps=GRS80 +step +proj=pop +v_3 +step
+proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44
+x_0=700000 +y_0=6600000 +ellps=GRS80
ptSf_2154_grid <- st_transform(ptSf_27572,crs=2154) # apply transformation
st_distance(ptSf_2154_grid,ptSf_2154) # incorrect (ungridded) transformation, the distance should be zero. 3.777 m is the known error for the ungridded transformation.
Units: [m]
[,1]
[1,] 3.777346
Thanks for your help.

See https://github.com/r-spatial/sf/issues/1815. The underlying reason is a PROJ regression for versions 7.2.1-8.1.1 fixed by https://github.com/OSGeo/PROJ/pull/2884.

Related

Reprojecting & CRS of Shapefile using sf Package in R

I am a new R user. I have a shapfile in R which I want to use to calculate the K nearest neighbor. Each polygon's centroid (Inside_X & Inside_Y) was calculated in arcmap (Example: X: 32570914 Y: 5791442). I used the sf package to get a CRS for my coordinates. Ideally, I would like to use the proj of my shapefile but it seems invalid in R. Below is my code. My problem is that the knearneighbor considers knearneigh: coordinates are not geographical and st_is_longlat gives[FALSE] even after setting a CRS. Please help.
>data<- readOGR (dsn = ".", layer ="My_Data")
>summary(data)
Object of class SpatialPolygonsDataFrame
Coordinates:
min max
x 32568756 32641426
y 5723944 5853056
Is projected: TRUE
proj4string :
[+proj=tmerc +lat_0=0 +lon_0=9 +k=0.9996 +x_0=32500000 +y_0=0 +ellps=GRS80 +units=m +no_defs]
Data attributes:
>data.sf <- st_as_sf(data)
## Set CRS
>data.crs <- st_transform(data.sf, crs = 4839)
>st_crs(data.crs)
data.coord <- cbind(data.crs$INSIDE_X, data.crs$INSIDE_Y)
## K Nearest neighbor
>library(spdep)
>data.knn <- knearneigh(data.coord, k = 10, longlat = TRUE)
Warning message:
In knearneigh(data.coord, k = 10, longlat = TRUE) :
knearneigh: coordinates are not geographical: longlat argument wrong
Use EPSG 4326 instead of 4839, 4839 is project https://epsg.io/4839

Error with Plotting Cartogram - 'This function does not give correct centroids and distances for longitude/latitude data.'

I'm trying to use the wrld_simple data from the maptools package to plot a Cartogram that involves some European countries based on their Population in 2005:
countries = c('Austria', 'Belgium', 'Bulgaria', 'Cyprus', 'Czech Republic', 'Denmark', 'Estonia', 'Finland', 'France', 'Germany', 'Greece', 'Hungary', 'Iceland', 'Ireland', 'Italy', 'Latvia', 'Lithuania', 'Luxembourg', 'Malta', 'Netherlands', 'Norway', 'Poland', 'Portugal', 'Slovenia', 'Spain', 'Sweden', 'United Kingdom')
Using the maptools package, I got the wrld_simpl data, and retrieved the data for those particular countries:
data(wrld_simpl)
mapsimple = wrld_simpl[wrld_simpl$NAME %in% countries]
plot(mapsimple)
So far so good, but when I plot it out:
cartogram= cartogram(mapping1, "POP2005", itermax = 7)
Please use cartogram_cont() instead of cartogram().
Error: Using an unprojected map. This function does not give correct centroids and distances for longitude/latitude data:
Use "st_transform()" to transform coordinates to another projection.
I try using cartogram_cont:
> cartogram= cartogram_cont(mapping1, "POP2005", itermax = 7)
Error: Using an unprojected map. This function does not give correct centroids and distances for longitude/latitude data:
Use "st_transform()" to transform coordinates to another projection.
I tried transforming it to sf and then doing the st_transform(), but to no avail:
sfno = st_as_sf(mapping1)
sfnoproj = st_transform(sfno, coords = c("lon", "lat"), crs = "+proj=longlat +datum=WGS84", agr = "constant")
cartogram= cartogram_cont(sfnoproj, "POP2005", itermax = 7)
Error: Using an unprojected map. This function does not give correct centroids and distances for longitude/latitude data:
Use "st_transform()" to transform coordinates to another projection.
I'm assuming I don't have the correct values for the projection right. How would I know to set it better? If I were to use other values besides 'POP2005', how do I go about it? What does the 'itermax' in cartogram mean (I don't quite understand the given definition)
Any help would be greatly appreciated as I'm quite new to this. Thank you!!!!
Some inconsistencies in your sample data (mapsimple v. mapping1 and a missing comma), so updated below:
library(maptools)
library(sf)
library(ggplot2)
countries = c('Austria', 'Belgium', 'Bulgaria', 'Cyprus', 'Czech Republic', 'Denmark', 'Estonia', 'Finland', 'France', 'Germany', 'Greece', 'Hungary', 'Iceland', 'Ireland', 'Italy', 'Latvia', 'Lithuania', 'Luxembourg', 'Malta', 'Netherlands', 'Norway', 'Poland', 'Portugal', 'Slovenia', 'Spain', 'Sweden', 'United Kingdom')
data(wrld_simpl)
mapsimple = wrld_simpl[wrld_simpl$NAME %in% countries,]
As the error indicated, your data was unprojected. It uses latitude/longitude, which doesn't work for cartogram_cont().
sfno <- st_as_sf(mapsimple)
st_crs(sfno)
Coordinate Reference System:
EPSG: 4326
proj4string: "+proj=longlat +datum=WGS84 +no_defs"
Your st_transform() was reprojecting sfno as the same thing: EPSG 4326:
sfnoproj <- st_transform(sfno, coords = c("lon", "lat"),
crs = "+proj=longlat +datum=WGS84",
agr = "constant")
st_crs(sfnoproj)
Coordinate Reference System:
EPSG: 4326
proj4string: "+proj=longlat +datum=WGS84 +no_defs"
Instead, choose a projected coordinate system for Europe, like 23038.
sfproj <- st_transform(sfno, crs = 23038)
st_crs(sfproj)
Coordinate Reference System:
EPSG: 23038
proj4string: "+proj=utm +zone=38 +ellps=intl +towgs84=-87,-98,-121,0,0,0,0 +units=m +no_defs"
Then, we can make and plot the cartogram:
cartogram <- cartogram_cont(sfproj, "POP2005", itermax = 7)
ggplot() +
geom_sf(data = cartogram, aes(fill = POP2005))

Mapview in R and problem with projections/crs in Pacific area

I am trying to plot data in R using mapView for the grid in the Pacific that crosses the longitude 180deg. The native crs is "+proj=merc +lon_0=150 +lat_ts=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0".
The example of equivalent data is:
test.coords<-as.data.frame(cbind(c(runif(15,-180,-130),runif(5,160,180)),runif(20,40,60)))
test.sp <- SpatialPointsDataFrame(coords = cbind(test.coords$V1,test.coords$V2), data = test.coords,
proj4string = CRS("+proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0"))
test.sp <- spTransform(test.sp,
"+proj=merc +lon_0=150 +lat_ts=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0")
plot(test.sp)
mapview(test.sp)
When using plot function, the points are centered, while using mapView they get split between two sides of the map (linked image). Can the view using mapView be centered on a different longitude?
Using native crs for this map does not help. I get an error.
mapview(plot, native.crs=TRUE)
Warning message:
sf layer is not long-lat data
Thank you
mapView image Pacific
Ok, so this is a semi-optimal solution to your question for several reasons:
this will only work for point data
it may not scale well for large point collections
projecting to another CRS may not work anymore (though that is not really related, as the issue at hand is about visualising)
For sets of lines or polygons, we would need another approach, but I am currently not aware of a sp/sf native solution to round-tripping coordinates of an object.
library(sp)
library(sf)
#> Linking to GEOS 3.6.1, GDAL 2.2.3, PROJ 4.9.3
library(mapview)
test.coords<-as.data.frame(cbind(c(runif(15,-180,-130),runif(5,160,180)),runif(20,40,60)))
test.sp <- SpatialPointsDataFrame(coords = cbind(test.coords$V1,test.coords$V2), data = test.coords,
proj4string = CRS("+proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0"))
test_sf = st_as_sf(test.sp)
shift = function(x) {
geom = st_geometry(x)
st_geometry(x) = st_sfc(
lapply(seq_along(geom), function(i) {
geom[[i]][1] = ifelse(geom[[i]][1] < 0, geom[[i]][1] + 360, geom[[i]][1])
return(geom[[i]])
})
, crs = st_crs(geom)
)
return(x)
}
mapview(shift(test_sf)) +
mapview(test_sf, col.regions = "orange")
Created on 2019-12-11 by the reprex package (v0.3.0)
I chose to use sf rather than sp for this because mapview uses sf internally and was hoping to find a general approach to this problem which could potentially be integrated.
Here is a workaround for polygons - it produces some warnings, but overall does the job.
# transform grid to standard latitude and longitude
# original grid in crs "+proj=merc +lon_0=150 +lat_ts=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0"
grid<-spTransform(grid,"+init=epsg:4326")
summary(coordinates(grid))
# V1 V2
# Min. :-179.8 Min. :37.17
# 1st Qu.:-168.8 1st Qu.:51.70
# Median :-154.3 Median :55.02
# Mean :-118.0 Mean :54.65
# 3rd Qu.:-131.4 3rd Qu.:58.31
# Max. : 179.8 Max. :65.56
out <- lapply(1:length(grid), function(i) grid[i, ])
for (i in 1:length(grid)) {
cds <- slot(slot(slot(out[[i]], "polygons")[[1]], "Polygons")[[1]], "coords")
cds_polygon <- Polygons(list(Polygon(cds)), ID="out.x")
out.x <- SpatialPolygons(list(cds_polygon))
proj4string(out.x) <- CRS("+init=epsg:4326")
if (cds[1,1]>0) { # depending to which side one want to move polygons that are on both sides of International Date Line
out.y <- elide(out.x, shift=c(-360,0))
} else {
out.y<-out.x}
if(i==1){grid.shifted<-out.y} else {
grid.shifted<-raster::union(grid.shifted,out.y)
}
}
# add data in the original grid
grid.shifted <- SpatialPolygonsDataFrame(grid.shifted, grid#data, match.ID = F)
mapview(grid.shifted)

Why is sf::st_transform() returning an object with a different projection than used in the call?

I want to re-project my sf object using sf::st_transform(), but the projection of the transformed object is not the same as the projection I specified in the transformation call. Why?
library(sf)
#> Linking to GEOS 3.6.1, GDAL 2.2.3, PROJ 4.9.3
# target proj4string
my_crs <- "+proj=lcc +lat_1=40.66666666666666 +lat_2=41.03333333333333 +lat_0=40.16666666666666 +lon_0=-74 +x_0=300000.0000000001 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=us-ft +no_defs"
# create test sfc
p1 <- st_sfc(st_point(c(1,2)), crs = "+init=epsg:3857")
# re-project p1 to `my_crs`
p2 <- st_transform(p1, crs = my_crs)
all.equal(my_crs, st_crs(p2)$proj4string)
#> [1] "1 string mismatch"
st_crs(p2)
#> Coordinate Reference System:
#> No EPSG code
#> proj4string: "+proj=lcc +lat_1=40.66666666666666 +lat_2=41.03333333333333 +lat_0=40.16666666666666 +lon_0=-74 +x_0=300000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=us-ft +no_defs"
The only difference between the projections is the +x_0 element in the proj4string. The trailing 1e-10 has been removed in p2's projection.
sf::st_transform() uses GDAL for projections. The help page states:
Some PROJ.4 projections are not supported by GDAL, e.g. "+proj=wintri" because it does
not have an inverse projection. Projecting to unsupported projections can be done by
st_transform_proj, part of package lwgeom. Note that the unsupported proj4string cannot
be passed as argument to st_crs, but has to be given as character string.
If you use PROJ.4 for the projection in your example it works correctly:
library(sf)
#> Linking to GEOS 3.6.1, GDAL 2.2.3, PROJ 4.9.3
library(lwgeom)
#> Linking to liblwgeom 2.5.0dev r16016, GEOS 3.6.1, PROJ 4.9.3
my_crs <- "+proj=lcc +lat_1=40.66666666666666 +lat_2=41.03333333333333 +lat_0=40.16666666666666 +lon_0=-74 +x_0=300000.0000000001 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=us-ft +no_defs"
p1 <- st_sfc(st_point(c(1,2)), crs = "+init=epsg:3857")
p2 <- lwgeom::st_transform_proj(p1, crs = my_crs)
all.equal(my_crs, st_crs(p2)$proj4string)
#> [1] TRUE
I don't know enough about cartography to say if the difference is due to the inverse projection issue mentioned in the help page or something else.
Related:
https://stackoverflow.com/a/51663647/1707525
https://github.com/r-spatial/sf/issues/810
https://github.com/r-spatial/sf/issues/1019

Converting UTM's to Lat/Long in R

I have a .csv file of 9,000+ UTM coordinates that I would like to convert into decimal degrees and I am having a bit of trouble. I have searched through several of the posts that have been posted here and elsewhere and I can't seem find a solution that transforms my set of UTM's into usable and accurate lat/long's.
I essentially have two questions: 1) does anyone see any issues with my code; and 2) is anyone familiar with forgoing transformation of UTM's into lat/long's and just using UTM's in the Rgooglemaps package?
Here are some examples of my code and data:
Data:
>head(utm)
-Northing Easting
1 4236576 615805
2 4236576 615805
3 4236576 615805
4 4236576 615805
5 4236576 615805
6 4236576 615805
Code thus far:
utm <- read.csv(file="utm.csv", header=TRUE, sep=",")
library(rgdal)
utm <- utm[complete.cases(utm),]
utm1 <- data.frame(x=utm$Northing,y=utm$Easting)
coordinates(utm1) <- ~x+y
class(utm1)
proj4string(utm1) <- CRS("+proj=utm +zone=10 +datum=WGS84 +units=m +ellps=WGS84")
utm2 <- spTransform(utm1,CRS("+proj=longlat +datum=WGS84"))
Results
> head(utm2)
SpatialPoints:
x y
[1,] -91.08516 4.727323
[2,] -91.08516 4.727323
[3,] -91.08516 4.727323
[4,] -91.08516 4.727323
[5,] -91.08516 4.727323
[6,] -91.08516 4.727323
Coordinate Reference System (CRS) arguments: +proj=longlat +datum=WGS84 +ellps=WGS84
+towgs84=0,0,0
So, I am getting some output, but I am not getting sensible output. Is there something I am missing here? Also, for what its worth, I was planning on using the "Rgooglemaps" package for creating some heat maps and kernel density plots.
I'm using the following code to convert from UTM to Lat/Long. It's working for the London area
wgs84 = "+init=epsg:4326"
bng = '+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000
+ellps=airy +datum=OSGB36 +units=m +no_defs'
ConvertCoordinates <- function(easting,northing) {
out = cbind(easting,northing)
mask = !is.na(easting)
sp <- sp::spTransform(sp::SpatialPoints(list(easting[mask],northing[mask]),proj4string=sp::CRS(bng)),sp::CRS(wgs84))
out[mask,]=sp#coords
out
}
I think you just need to swap X and Y--Northing as Y, Easting as X. The original code worked for me with this change.

Resources