R/calculate_PPA.R

Defines functions PPA calc_PPA calcPPA_STP calcPPA_STP_Track calcPPA_STP_Tinterval in_time_range in_time_range_incl chull_poly

Documented in PPA

## Functions for calculating the Potetial Path Area
## check correctness for acitivty time and roughsets and combinations
#' @title PPA
#' @description Function for calculating the Potetial Path Area(PPA) of a STP_track.
#' This function can calculate the PPA for the entire trajectory, a specfic moment in time, or a time range.
#' The PPA for a specific moment in time is always based on the two points that before and after the time parameter.
#' If time is outside time range of the \link{STP_Track} but within time uncertainty limits,
#' the method will return the PPA based on the two closest points in time.
#' @param STP_track The STP_track for which the PPA needs to be calculated
#' @param time Time("POSIXct" or"POSIXt") for which the PPA needs to be calculated.
#' Use time = c(time1,time2) to calculate PPA for a time range. Default is NULL: calculate PPA for entire STP_track
#' @param points The points used for the PPA calculation given as a vector of integers. Default is NULL: calculate PPA for entire STP_track
#' @param x_density Paramter used for calculating the PPA of entire STPs.
#' The amount of x coordinates for which the corresponding y coordinate(s) will be calculated.
#' Only relevant if the PPA for at least 1 complete STP needs to be calculated.
#' @param time_interval The time interval in minutes used for calculating the PPA.
#' Only used for calculating the PPA for a specfic moment in time and
#' if only a part of the PPA of a STP needs to be calculated.
#' Default is every minute
#' @param quadsegs Passed to buffer. Number of line segments to use to approximate a quarter circle.
#' Only used where paramter time_interval is relavant.
#' @return The Potential Path Area as SpatialPolygons.
#' If time is equal to time of a space-time point and rough_sets (time and location uncertainty points) are 0, method returns NA because there is no PPA
#' @author Mark ten Vregelaar
#' @importMethodsFrom raster bind
#' @importFrom rgeos gBuffer gIntersection gUnaryUnion
#' @import plyr rgdal
#' @export
#' @examples
#'library(spacetime)
#'library(sp)
#'#--------------------------create a STP_Track--------------------------
#'# set time
#'t1 <- strptime("01/01/2017 00:00:00", "%m/%d/%Y %H:%M:%S")
#'t2 <- t1+5*60*60
#'time<-seq(t1,t2,30*60)
#'# set coordinates
#'x=c(seq(0,25,5),seq(27.5,37.5,2.5))
#'y=sample(-2:2, 11,replace = TRUE)
#'
#'n = length(x)
#'crs_NL = CRS("+init=epsg:28992")
#'
#'# create class STIDF
#'stidf1 = STIDF(SpatialPoints(cbind(x,y),crs_NL), time, data.frame(co2 = rnorm(n),O2=rnorm(n)))
#'
#'# Track-class {trajectories}
#'my_track1<-Track(stidf1)
#'
#'# set maximum speed
#'v1<-getVmaxtrack(my_track1)+0.00015
#'
#'# STP_track class
#'STP_track1<-STP_Track(my_track1,v1)
#'#------------------------------example 1------------------------------
#'## PPA entire track
#'#calculate PPA
#'PPA1<-PPA(STP_track1)
#'
#'# plot results
#'plot(STP_track1,type='b')
#'plot(PPA1,add=TRUE)
#'#------------------------------example 2------------------------------
#'## PPA only using every second point
#'# calculate PPA
#'PPA2<-PPA(STP_track1,points = seq(1,11,2))
#'
#'# plot results
#'plot(STP_track1,type='b')
#'plot(PPA2,add=TRUE)
#'#------------------------------example 3------------------------------
#'## PPA of a specfic moment in time
#'# calculate PPA
#'time <- strptime("01/01/2017 01:15:00", "%m/%d/%Y %H:%M:%S")
#'PPA3<-PPA(STP_track1,time = time)
#'
#'# plot results
#'plot(STP_track1,type='b')
#'plot(PPA3,add=TRUE)
#'#------------------------------example 4------------------------------
#'## PPA for a time range
#'# calculate PPA
#'timerange1 <- c(t1,strptime("01/01/2017 02:15:00", "%m/%d/%Y %H:%M:%S"))
#'PPA4<-PPA(STP_track1,time = timerange1)
#'
#'# plot results
#'plot(STP_track1,type='b')
#'plot(PPA4,add=TRUE)
PPA <-
  function(STP_track, time = NULL, points = NULL, x_density = 250, time_interval = 1,
           quadsegs = 12) {

    # Time uncertainty in seconds
    tu<-STP_track@rough_sets$time_uncertainty*60
    # Subset STP_Track to relevant points
    if (!is.null(points)) {
      STP_track <- STP_track[points, '']
    }
    ## PPA for specifc moment in time
    if (length(time) == 1) {
      # If time not in time range, stop method
      if(!in_time_range_incl(time[1],c(STP_track@endTime[1]-tu,tail(STP_track@endTime,1)+tu))){
        stop('Could not calculate PPA. Time not in time range of STP_track')
      }

      # Calculate PPA and return result if time is equeal to time control point and tu=0
      # Because location uncertainty already done within calc_PPA method if time is equal to time control points
      result <-  calc_PPA(STP_track, time[1], qs = quadsegs)
      if (time[1] %in% STP_track@endTime & isS4(result) & tu==0){

        return(result)
      }}
    ## PPA for a time interval
    # Check if time interval is in time range
    else if (length(time) == 2) {
      if(!in_time_range_incl(time[1],c(STP_track@endTime[1]-tu,tail(STP_track@endTime,1)+tu)) |
         !in_time_range_incl(time[2],c(STP_track@endTime[1]-tu,tail(STP_track@endTime,1)+tu))){
        stop('Could not calculate PPA. Time not in time range of STP_track')
      }
      # Check if time2 is bigger than time1
      if (time[2]<=time[1]){
        stop("error in time. time[2] is smaller or eaual to time[1]")
      }
      else if(time_interval > difftime(time[2],time[1],units = 'mins')){
        stop("error in time. time interval is bigger than time difference between space-time points")
        # claculate PPA
      }else{
        result <- calcPPA_STP_Tinterval(STP_track, time, x_density, time_interval, quadsegs)
      }
    }
    else{
      # If no time is provided, calculate PPA entire trajectory
      result <- calcPPA_STP_Track(STP_track, x_density)

    }
    # Show waning if no PPA was calculated
    if (!isS4(result)) {
      warning(
        'Could not calculate PPA. Maximum speed might be to low or time is equal to time of a
        space-time point in which case the PPA is a point. Returning NA'
      )
      # If there is location uncertainty generate buffer
    } else if (STP_track@rough_sets$location_uncertainty > 0) {
      result <- gBuffer(result, width = STP_track@rough_sets$location_uncertainty)
    }
    #return result
    return(result)

  }

# @title calc_PPA
# @description This function calcualtes the Potential Path Area(PPA) for a given moment in time.
# @param STP A STP_Track
# @param time Time("POSIXct" or"POSIXt") for which the PPA needs to be calculated.
# @param points The points used for the PPA calculation given as a vector of integers.
# Default are the space-time points are directlty before and after space-time point
# @return The Potential Path Area as SpatialPolygons for the provided time
# @author Mark ten Vregelaar
# @importFrom rgeos gBuffer gIntersection gUnaryUnion
#
calc_PPA <- function(STP,t,points=NULL,qs=12){
  # time uncertainty in seconds
  tu <- STP@rough_sets$time_uncertainty*60
  # If there are no points provided, find the two points before and after t.
  if (is.null(points)){
    i=1
    search=T
    while (i+1<=length(STP) & search){
      if (in_time_range_incl(t,c(STP@endTime[i],STP@endTime[i+1]))){
        p1=i
        p2=i+1
        search = F
      }
      i=i+1

    }
    if(is.null(points)){
      n=length(STP)
      if (t>=(STP@endTime[1]-tu) & t<=(STP@endTime[1])){
        p1=1
        p2=2
      }else{
        if(t<=(STP@endTime[n]+tu) & t>=(STP@endTime[n])){
          p1=n-1
          p2=n

        }
      }

    }
  }
  else{
    p1 = points[1]
    p2 = points[2]
  }



  # maximum speed
  v = STP@connections$vmax[p1]
  # activity_time
  at <- STP@connections$activity_time[p1]*60
  # location uncertainty
  lu <- STP@rough_sets$location_uncertainty


  STP@endTime[p1]<-STP@endTime[p1]-tu
  STP@endTime[p2]<-STP@endTime[p2]+tu

  # get time difference of the time between the two points and t
  t1 = abs(difftime(STP@endTime[p1],t,units = 'secs'))
  t2 = abs(difftime(STP@endTime[p2],t,units = 'secs'))

  #calculate the maximum travel dictance strating form the two points in m
  # now assuming meters as unit for the projecition<-----------------------------------------
  s1 = v*as.numeric(t1)
  s2 = v*as.numeric(t2)

  # get coords of start and end point
  startpoint <- STP@sp[p1,]
  endpoint <- STP@sp[p2,]
  #calculate and return the PPA, the intersection of the two buffers
  if(t==STP@endTime[p1] & lu>0){
    return(gBuffer(startpoint,width = lu))
  }
  if(t==STP@endTime[p2] & lu>0){
    return(gBuffer(endpoint,width = lu))
  }
  tryCatch(
    {
      if (t1>0 & t2 >0){
        # calculate area that can be reached from each point
        buffer1<-gBuffer(startpoint,width=s1,quadsegs=qs)#default 6, first test 7
        buffer2<-gBuffer(endpoint,width=s2,quadsegs=qs)
        PPA1<-gIntersection(buffer1,buffer2)
        if(at>0){
          #restore time
          STP@endTime[p1]<-STP@endTime[p1]+tu
          STP@endTime[p2]<-STP@endTime[p2]-tu
          PPA1<-gIntersection(PPA1,PPA(STP[p1:p2,'']))
        }
        return(PPA1)}
      else {
        NA}

    },
    error=function(cond) {
      message("No intersection")
      message("Here's the original error message:")
      message(cond)
      # Choose a return value in case of error
      return(NA)
    },
    warning=function(cond) {
      message("functions caused a warning:")
      message("Here's the original warning message:")
      message(cond)
      # return PPA
      return(PPA1)
    })
}

# @title calcPPA_STP
# @description This function calcualtes the Potential Path Area(PPA) of Space-Time Prism(STP).
# A STP is a set of space-time points with a maximum speed
# @param STP A STP_Track
# @param x_density The amount of x coordinates for which the corresponding y coordinate(s) will be calculated.
# @return The Potential Path Area of the STP as SpatialPolygons
# @author Mark ten Vregelaar
#
calcPPA_STP <- function(STP,x_density=250){
  # get coordiantes of STP
  x1<-STP@sp@coords[1,1]
  y1<-STP@sp@coords[1,2]
  x2<-STP@sp@coords[2,1]
  y2<-STP@sp@coords[2,2]

  # Downsize x and y. For accurate calculation of the PPA the values have to be small
  xmin<-min(x1,x2)
  ymin<-min(y1,y2)
  x1<-x1-xmin
  y1<-y1-ymin
  x2<-x2-xmin
  y2<-y2-ymin

  # time_uncertainty in seconds
  tu<-STP@rough_sets$time_uncertainty*60

  # activity time in seconds
  at<-STP@connections$activity_time*60

  # maximum speed
  v<-STP@connections$vmax

  ## calculate the x coordinates for which the y coordinates need to be calculated
  # total time budget
  t=as.numeric(difftime(STP@endTime[2]+tu,STP@endTime[1]-tu,units = 'secs'))-at

  # total distance that can be covered
  s=v*t
  # distance that can be reached
  dist <- s/2

  # caclulate values for the x coordinate usingthe remaining distance
  if(x1<x2){
    xrange<-c(x1-dist,x2+dist)
  }else{
    xrange<-c(x2-dist,x1+dist)
  }
  xpoints<-seq(xrange[1],xrange[2],length=x_density)

  # calculate y coordinates. y_segment1 and y_segment2 are both half a ellipse or circle
  suppressWarnings(yCoords<-sapply(xpoints,simplify = "array", function(x0) {
    y_segment1 <- ((s*sqrt(s^4+((-2*y2^2) + 4*y1*y2 - 2*x2^2 + 4*x0*x2 - 2*y1^2 - 2*x1^2 + 4*x0*x1 - 4*x0^2)
                           *s^2 + y2^4 - 4*y1*y2^3 + (2*x2^2 - 4*x0*x2 + 6*y1^2 + 2*x1^2 - 4*x0*x1 + 4*x0^2)
                           *y2^2 + ((-4*y1*x2^2) + 8*x0*y1*x2 - 4*y1^3+((-4*x1^2) + 8*x0*x1 - 8*x0^2)*y1)*y2 +
                             x2^4 - 4*x0*x2^3 + (2*y1^2 - 2*x1^2 + 4*x0*x1 + 4*x0^2)*x2^2 + ((-4*x0*y1^2) + 4*x0*x1^2 - 8*x0^2*x1)
                           *x2 + y1^4 + (2*x1^2 - 4*x0*x1 + 4*x0^2)*y1^2 + x1^4 - 4*x0*x1^3 + 4*x0^2*x1^2)
                    + (y2+y1) *s^2 - y2^3 + y1*y2^2 + ((-x2^2) +2*x0*x2 + y1^2 + x1^2 - 2*x0*x1)*y2 + y1*x2^2 - 2*x0*y1*x2 -y1^3 + (2*x0*x1 - x1^2)*y1)/
                     (2*s^2 - 2*y2^2 + 4*y1*y2 - 2*y1^2))

    y_segment2 <- ((-(s*sqrt(s^4+((-2*y2^2) + 4*y1*y2 - 2*x2^2 + 4*x0*x2 - 2*y1^2 - 2*x1^2 + 4*x0*x1 - 4*x0^2)
                             *s^2 + y2^4 - 4*y1*y2^3 + (2*x2^2 - 4*x0*x2 + 6*y1^2 + 2*x1^2 - 4*x0*x1 + 4*x0^2)
                             *y2^2 + ((-4*y1*x2^2) + 8*x0*y1*x2 - 4*y1^3+((-4*x1^2) + 8*x0*x1 - 8*x0^2)*y1)*y2 +
                               x2^4 - 4*x0*x2^3 + (2*y1^2 - 2*x1^2 + 4*x0*x1 + 4*x0^2)*x2^2 + ((-4*x0*y1^2) + 4*x0*x1^2 - 8*x0^2*x1)
                             *x2 + y1^4 + (2*x1^2 - 4*x0*x1 + 4*x0^2)*y1^2 + x1^4 - 4*x0*x1^3 + 4*x0^2*x1^2)
                      +((-y2)-y1)*s^2 + y2^3 - y1*y2^2 + (x2^2 - 2*x0*x2 - y1^2 - x1^2 + 2*x0*x1)*y2 - y1*x2^2 + 2*x0*y1*x2 + y1^3 + (x1^2 - 2*x0*x1)*y1))/
                     (2*s^2 - 2*y2^2 + 4*y1*y2 - 2*y1^2))

    c(y_segment1,y_segment2)
  }))

  # put results in data frame and remove NANs
  coords<-data.frame(x=rep(xpoints,2),y=c(yCoords[1,],yCoords[2,]))
  coords<-coords[complete.cases(coords),]
  # take convex hull
  PPA<-coords[chull(coords),]

  # relocate PPA to orginal position
  PPA$x<-PPA$x+xmin
  PPA$y<-PPA$y+ymin
  # covert PPA to SpatialPolygons class and return result
  Poly <- Polygons(list(Polygon(PPA)),'1')
  PPApoly <- SpatialPolygons(list(Poly),proj4string = STP@sp@proj4string)
  return(PPApoly)
}
# @title calcPPA_STP_Track
# @description This function calcualtes the Potential Path Area(PPA) of an entire STP_track
# @param STP_track The STP_Track for which the PPA will be calculated
# @param x_density The amount of x coordinates for which the corresponding y coordinate(s) will be calculated.
# @return The Potential Path Area of the STP as SpatialPolygons
# @author Mark ten Vregelaar
# @importMethodsFrom raster bind
calcPPA_STP_Track <- function(STP_track,x_density=250){
  if (length(STP_track)==2){

    return(calcPPA_STP(STP_track,x_density))
  }
  ## calculate PPA of entire track
  # Calculate PPA for every STP of the trajectory

  PPAlist<-lapply((1:(length(STP_track)-1)), FUN=function(p1) {

    STP<-STP_track[p1:(p1+1),'']
    # calculate PPA of STP
    calcPPA_STP(STP,x_density)
  })

  #convertlist of PPA to one SpatialPolygons
  PPA_polygons<-do.call(bind,PPAlist)
  PPA<-gUnaryUnion(PPA_polygons)

  #return results
  return(PPA)#<---------------------------------------------------------------------------------------------------------

}

# @title calcPPA_STP_Tinterval
# @description Function for calculating PPA for specific time range
# @param STP_track The STP_track for which the PPA needs to be calculated
# @param time_range time range ("POSIXct" or"POSIXt") for which the PPA needs to be calculated.
# @param x_density Paramter used for calculating the PPA of entire STPs.
# The amount of x coordinates for which the corresponding y coordinate(s) will be calculated.
# Only relevant if the PPA for at least 1 complete STP needs to be calculated
# @param time_interval(minutes) The time interval used for calculating the PPA.
# Only used for calculating the PPA for a specfic moment in time and
# if only a part of the PPA of a STP needs to be calculated.
# Default is every minute
# @param quadsegs Passed to buffer. Number of line segments to use to approximate a quarter circle.
# Only used where paramter time_interval is relavant
# @return The Potential Path Area for the time range as SpatialPolygons
# @author Mark ten Vregelaar
#'
calcPPA_STP_Tinterval <- function(STP_track,time_range,x_density,time_interval, quadsegs){# can be done smarter

  # define frequently used vaiables
  crs<- STP_track@sp@proj4string
  t1<-time_range[1]
  t2<-time_range[2]


  # number of space-time points within time range
  points<-STP_track@endTime>=t1 & STP_track@endTime<=t2
  # if less than two calculate PPAs
  if (sum(points==T)<2){
    # times for which the PPA needs to be calculated
    PPAtimes<-seq(t1+(time_interval*60),t2,(time_interval*60))

    # calculate PPA for times in PPAtimes
    PPAs<-lapply(PPAtimes, function(x) {

      if ((!x %in% STP_track@endTime) | STP_track@rough_sets$location_uncertainty>0 | STP_track@rough_sets$time_uncertainty>0){
        calc_PPA(STP_track, x,qs = quadsegs)@polygons[[1]]@Polygons[[1]]@coords
      }
    })

    # test if a space-time point is within the time range
    TF_point <- lapply(STP_track@endTime, FUN=function(x) in_time_range(x,time_range))

    if (T %in% TF_point){
      ## CASE: only one space-time point in time range
      # get times before and after original space-time point
      subset1<-which(PPAtimes<STP_track@endTime[which(TF_point==T)])
      subset2<-which(PPAtimes>STP_track@endTime[which(TF_point==T)])
      # select PPAs for both before and after space-time point
      PPAs1 <-PPAs[subset1]
      PPAs2 <- PPAs[subset2]
      #calculate PPA by calculating convexhull of PPAS
      PPApoly1<-chull_poly(PPAs1,crs)
      PPApoly2<-chull_poly(PPAs2,crs)
      # comind the two PPAs
      PPA_polygons<-do.call(bind,c(PPApoly1,PPApoly2))
      PPA<-gUnaryUnion(PPA_polygons)
      # return final PPA
      return(PPA)
    }

    else{
      ## CASE: no point in between time range
      #calculate PPA by calculating convexhull of PPAS
      PPAs[sapply(PPAs, is.null)] <- NULL
      PPApoly <- chull_poly(PPAs,crs)
      # return final PPA
      return(PPApoly)
    }}
  else{
    # CASE: at least one complete STP in time-range
    # calculate PPA the complete part of STP_track
    comp_STP_track<-STP_track[1:length(STP_track),paste(time_range[1],time_range[2],sep="::")]
    PPA_track <- calcPPA_STP_Track(comp_STP_track,x_density)
    # initialize PPAs to NULL
    PPApoly1<-NULL
    PPApoly2<-NULL
    ## calc PPA for time before complete part of STP_track

    #lapply for calculating PPAs for different moments in time
    if ((t1+(time_interval*60))<(comp_STP_track@endTime[1])){
      PPAS1<-lapply(seq(t1+(time_interval*60),comp_STP_track@endTime[1]-(time_interval*60),(time_interval*60)), function(x) {
        calc_PPA(STP_track, x, qs = quadsegs)@polygons[[1]]@Polygons[[1]]@coords
      }
      )
      # convexhull of points
      PPApoly1<-chull_poly(PPAS1,crs)
    }
    ## calc PPA for time after complete part of STP_track

    #lapply for calculating PPAs for different moments in time
    if ((tail(comp_STP_track@endTime,n=1)+(time_interval*60))<t2){
      PPAS2<-lapply(seq(tail(comp_STP_track@endTime,n=1)+(time_interval*60),t2,(time_interval*60)), function(x)
        calc_PPA(STP_track, x, qs = quadsegs)@polygons[[1]]@Polygons[[1]]@coords)
      # convexhull of points
      PPApoly2<-chull_poly(PPAS2,crs)
    }
    if(is.null(PPApoly1) & is.null(PPApoly2)){
      ## CASE: a complete track
      # no parts before and after complete track
      return(PPA_track)
    }
    else{
      # combine before and after part with complete track
      PPA_polygons<-do.call(bind,c(PPApoly1,PPApoly2,PPA_track))
      PPA<-gUnaryUnion(PPA_polygons)
      return(PPA)
    }
  }
}

# @title in_time_range
# @description Function that checks if the time is within the time_range
# @param time time (POSIXct): time
# @param time_range time_range(two POSIXct): the time range
#
# @return True or False(logical): True if time within time_Range
in_time_range <- function(time, time_range){

  stopifnot(length(time_range) == 2L)

  time>time_range[1] & time<time_range[2]

}

# @title in_time_range_incl
# @description Function that checks if the time is within the time_range
# @param time time (POSIXct): time
# @param time_range time_range(two POSIXct): the time range
#
# @return True or False(logical): True if time within time_Range
in_time_range_incl <- function(time, time_range){

  stopifnot(length(time_range) == 2L)

  time>=time_range[1] & time<=time_range[2]

}

chull_poly <- function(PPAcoords,crs){
  # Function that calculates PPA based on convex hull of PPAcoords
  #
  #   Arg:
  #          PPAcoords(matrix): x and y coordiantes
  #          crs(CRS): crs of coordinates
  #
  #    Return:
  #         PPA(SpatialPolygons): Potential Path Area
  coords<-plyr::rbind.fill.matrix(PPAcoords)
  #coords<-do.call(rbind,PPAS)#<------ other method maybe be faster<________________________
  # convexhull of coords
  coords<-coords[chull(coords),]
  # covert PPA to SpatialPolygons class and return result
  Poly <- Polygons(list(Polygon(coords)),'1')
  PPApoly <- SpatialPolygons(list(Poly),proj4string = crs)
  return(PPApoly)
}
markvregel/STPtrajectories documentation built on May 21, 2019, 12:25 p.m.