1
+ # ' Put Points in Sequence
2
+ # '
3
+ # ' The \code{point.in.sequence} function takes numeric input vectors \code{x} and \code{y} or a
4
+ # ' \code{\link{data.frame}} object, and orders the values in such way that they are correctly sequenced by the angle subtended between each point,
5
+ # ' and, the centroid of the total set. If the data is provided in the format of a \code{data.frame}, then it must
6
+ # ' containing columns named \code{x} and \code{y}, else an error will be thrown.
7
+ # '
8
+ # ' The arguments \code{x} and \code{y} represent cartesian coordinates. This is useful if a path is sought that
9
+ # ' passes through each point in the ordered set, however, no two lines in the total path cross over each other.
10
+ # ' Uses the \code{\link{atan2}} function to determine the angle (theta) between each point (x,y) and the centroid
11
+ # ' of the data, it then orders based on increasing values of theta.
12
+ # '
13
+ # ' @param x vector of numeric \code{x} values
14
+ # ' @param y vector of numeric \code{y} values
15
+ # ' @param ... not used
16
+ # ' @param df data.frame containing colums \code{x} and \code{y}
17
+ # ' @param close logical value (default \code{FALSE}), as to whether the set should be closed by adding (duplicating)
18
+ # ' the first row (after ordering) to the end of the set.
19
+ # ' @return \code{data.frame} object containing the re-ordered input set.
20
+ # ' @examples
21
+ # ' \donttest{
22
+ # ' #Load plotting library
23
+ # ' library(ggplot2)
24
+ # '
25
+ # ' #For reproducability
26
+ # ' set.seed(1)
27
+ # '
28
+ # ' #Build data in an approximate loop
29
+ # ' theta <- seq(0,2*pi,by=pi/100)
30
+ # ' r <- 1 + (runif(length(theta))-0.5)/5
31
+ # ' df2 <- data.frame(x=r*cos(theta),y=r*sin(theta))
32
+ # '
33
+ # ' #Randomise the order of the data
34
+ # ' df2 <- df2[sample(nrow(df2)),]
35
+ # '
36
+ # ' #Function to plot data
37
+ # ' demo <- function(ret){
38
+ # ' ggplot(data=ret,aes(x,y)) +
39
+ # ' geom_path() +
40
+ # ' geom_point(fill="yellow",color="black",shape=21)
41
+ # ' }
42
+ # '
43
+ # ' #Demonstrate how the data would plot WITHOUT sorting
44
+ # ' demo(df2)
45
+ # '
46
+ # ' #Demonstrate how the data would plot WITH sorting
47
+ # ' demo(point.in.sequence(df=df2,close=TRUE))
48
+ # ' }
49
+ # ' @export
50
+ point.in.sequence <- function (x ,y ,... ,df = data.frame (x = x ,y = y ),close = FALSE ){
51
+ # If first argument is provided as data.frame, re-assign to df
52
+ if (! missing(x ))
53
+ if (class(x ) == " data.frame" )
54
+ df = x
55
+
56
+ # Check df is dataframe
57
+ if (class(df ) != " data.frame" )
58
+ stop(" df must be a data.frame" ,call. = F )
59
+
60
+ # Check 2 or more unique rows exist.
61
+ if (nrow(unique(df )) < = 1 )
62
+ stop(" df must contain at least two unique rows" ,call. = FALSE )
63
+
64
+ # Check correct columns exist
65
+ sapply(c(" x" ," y" ),function (X ){
66
+ if (! X %in% colnames(df ))
67
+ stop(paste(" df must contain column" ,X ),call. = F )
68
+ if (! is.numeric(df [,X ]))
69
+ stop(paste(" df must contain column" ,X ," and must be numeric" ),call. = F )
70
+ })
71
+
72
+ # Check close argument is logical
73
+ close = ifthenelse(is.logical(close ),close [1 ],FALSE )
74
+
75
+ # Center point
76
+ c.x = mean(df $ x )
77
+ c.y = mean(df $ y )
78
+
79
+ # Determine angle with center point
80
+ df $ theta <- apply(df [,c(" x" ," y" )],1 ,function (r ) atan2(r [2 ] - c.y ,r [1 ] - c.x )* 180 / pi )
81
+
82
+ # Put in correct order and
83
+ df <- df [with(df ,base :: order(theta )), ]
84
+
85
+ # Make a closed loop if desired
86
+ # by duplicating the first row on the bottom
87
+ if (close )
88
+ df <- rbind(df ,df [1 ,])
89
+
90
+ # Remove theta column
91
+ df $ theta = NULL
92
+
93
+ # And return
94
+ df
95
+ }
0 commit comments