jsimpsno
jsimpsno

Reputation: 460

Rotate Lines To True North or Vertical

I have a dataset that contains polylines which run diagonal and I would like to rotate the lines to vertical or true north in order to show the x axis data correctly. I've looked at the Rodrigues Formula but it's way over my head and was wondering if their is a package in R that I could use. See the example below, which I would need to rotate.

library(sf)
library(ggplot2)

ex<-structure(list(OBJECTID = c(10526, 10913), geometry = structure(list(
    structure(c(-103.47406, -103.46268, 31.47367, 31.48499), .Dim = c(2L, 
    2L), class = c("XY", "LINESTRING", "sfg")), structure(c(-103.46525, 
    -103.4788333, 31.4879722000001, 31.4748056), .Dim = c(2L, 
    2L), class = c("XY", "LINESTRING", "sfg"))), n_empty = 0L, crs = structure(list(
    epsg = 4267L, proj4string = "+proj=longlat +datum=NAD27 +no_defs"), class = "crs"), class = c("sfc_LINESTRING", 
"sfc"), precision = 0, bbox = structure(c(xmin = -103.4788333, 
ymin = 31.47367, xmax = -103.46268, ymax = 31.4879722000001), class = "bbox"))), row.names = c(NA, 
-2L), sf_column = "geometry", agr = structure(c(OBJECTID = NA_integer_), .Label = c("constant", 
"aggregate", "identity"), class = "factor"), class = c("sf", 
"tbl_df", "tbl", "data.frame"))

ggplot(ex)+geom_sf()

Included a diagram of what I'm after

Rotation thanks!

Upvotes: 4

Views: 761

Answers (1)

SymbolixAU
SymbolixAU

Reputation: 26258

Under the assumption all the lines are parallel, and the point of rotation is the centroid of all the lines, a way to do this is

  1. Find the centroid of the lines
  2. Find the bearing between the lines and due-north
  3. Rotate each coordinate around the centroid
  4. Done.
library(sf)
library(sfheaders)
library(ggplot2)
library(geosphere)

## get the centre of the lines
centre <- sf::st_centroid( sf::st_union( ex ) )

## remove class so we just have coordinates as a vector
centre <- unclass( centre[[1]] )

## get each coordinate of the lines. These will each be rotated
coords <- sf::st_coordinates( ex )

## to know the angle of rotation, we need to know due-north from a given point
## under the assumption all lines are parallel, we just need the bearing between the 
## start of a line and the end
##
## you're using lon / lat values, so we can use geosphere package to get the bearing
bearing <- geosphere::bearing(
  p1 = coords[1, c("X","Y")]
  , p2 = coords[2, c("X","Y")]
  )


theta <- bearing * pi / 180 ## in radians

#' rotate
#' function to rotate x and y coordinates around a point
#' theta - angle of rotation
#' p - point(s) to rotate
#' centre - centre point
rotate <- function( theta, p, centre ) {
  new_x <- cos( theta ) * ( p[, 1] - centre[1] ) - sin( theta ) * ( p[, 2] - centre[2] ) + centre[1]
  new_y <- sin( theta ) * ( p[, 1] - centre[1] ) + cos( theta ) * ( p[, 2] - centre[2] ) + centre[2] 
  return( matrix( c( new_x, new_y ), ncol = 2 ) ) 
}

## calculate the rotated points
coords_new <- rotate( theta, coords, centre )

## we've kept order in tact, so we can cbind the L1 id back on
coords_new <- cbind( coords_new, coords[, "L1"])

## new sf object (using library(sfheaders) )
sf_new <- sfheaders::sf_linestring( obj = coords_new, linestring_id = 3)
sf::st_crs( sf_new ) <- sf::st_crs( ex )

## plot to verify
ggplot() + geom_sf( data = sf_new ) + 
  geom_sf( data = ex ) + 
  geom_sf( data = sf::st_centroid( sf::st_union( ex ) ) )

enter image description here

Upvotes: 5

Related Questions