-3

I would like to apply the distancePointSegment function to all points in my vector, both are given in the code snippet below. The function takes in 6 values, 2 of which are dynamic (column/row specific) and 4 are static.

# Function that I want to apply:
distancePointSegment <- function(px, py, x1, y1, x2, y2) {
  ## px,py is the point to test.
  ## x1,y1,x2,y2 is the line to check distance.
  ##
  ## Returns distance from the line, or if the intersecting point on the line nearest
  ## the point tested is outside the endpoints of the line, the distance to the
  ## nearest endpoint.
  ##
  ## Returns 9999 on 0 denominator conditions.
  lineMagnitude <- function(x1, y1, x2, y2) sqrt((x2-x1)^2+(y2-y1)^2)
  ans <- NULL
  ix <- iy <- 0   # intersecting point
  lineMag <- lineMagnitude(x1, y1, x2, y2)
  if( lineMag < 0.00000001) {
    warning("short segment")
    return(9999)
  }
  u <- (((px - x1) * (x2 - x1)) + ((py - y1) * (y2 - y1)))
  u <- u / (lineMag * lineMag)
  if((u < 0.00001) || (u > 1)) {
    ## closest point does not fall within the line segment, take the shorter distance
    ## to an endpoint
    ix <- lineMagnitude(px, py, x1, y1)
    iy <- lineMagnitude(px, py, x2, y2)
    if(ix > iy)  ans <- iy
    else ans <- ix
  } else {
    ## Intersecting point is on the line, use the formula
    ix <- x1 + u * (x2 - x1)
    iy <- y1 + u * (y2 - y1)
    ans <- lineMagnitude(px, py, ix, iy)
  }
  ans
}

# my data points
vector <- c(134.2, 156.1, 165.2, 186, 220.8, 237.1, 239.6, 327.7, 376.2, 
396.1, 424.5, 460.2, 563.4, 565.7, 818.6, 819.7, 1120.4, 1279.5, 
1640.7)
point_coords <- rbind(1:length(cl$height), vector)

x1 = point_coords[1,1]
y1 = point_coords[2,1]
x2 = point_coords[1,length(vector)]
y2 = point_coords[2,length(vector)]

I tried these two syntaxes but neither works:

apply(point_coords, MARGIN=2, FUN= function(col) {distancePointSegment(col[1], col[2], x1, y1, x2, y2)}, x1=x1, x2=x2,y1=y1,y2=y2)

apply(point_coords, MARGIN=2, FUN= distancePointSegment, px=x[1], py=x[2], x1=x1, x2=x2, y1=y1, y2=y2)

Can someone point me in the right direction? What was the right logic here?

P.S: This question is linked to an earlier one, but is a more generic case with multiple dynamic and static parameters.

Community
  • 1
  • 1
Zhubarb
  • 11,432
  • 18
  • 75
  • 114

1 Answers1

3

You can use mapply:

mapply(FUN= distancePointSegment, point_coords[1,], point_coords[2,],
       MoreArgs = list(x1=x1, x2=x2, y1=y1, y2=y2))

Or change your function and use apply:

# Function that I want to apply:
distancePointSegment <- function(p, x1, y1, x2, y2) {
  px <- p[1] #the coordinates are passed as a vector to the function
  py <- p[2]
  ## px,py is the point to test.
  ## x1,y1,x2,y2 is the line to check distance.
  ##
  ## Returns distance from the line, or if the intersecting point on the line nearest
  ## the point tested is outside the endpoints of the line, the distance to the
  ## nearest endpoint.
  ##
  ## Returns 9999 on 0 denominator conditions.
  lineMagnitude <- function(x1, y1, x2, y2) sqrt((x2-x1)^2+(y2-y1)^2)
  ans <- NULL
  ix <- iy <- 0   # intersecting point
  lineMag <- lineMagnitude(x1, y1, x2, y2)
  if( lineMag < 0.00000001) {
    warning("short segment")
    return(9999)
  }
  u <- (((px - x1) * (x2 - x1)) + ((py - y1) * (y2 - y1)))
  u <- u / (lineMag * lineMag)
  if((u < 0.00001) || (u > 1)) {
    ## closest point does not fall within the line segment, take the shorter distance
    ## to an endpoint
    ix <- lineMagnitude(px, py, x1, y1)
    iy <- lineMagnitude(px, py, x2, y2)
    if(ix > iy)  ans <- iy
    else ans <- ix
  } else {
    ## Intersecting point is on the line, use the formula
    ix <- x1 + u * (x2 - x1)
    iy <- y1 + u * (y2 - y1)
    ans <- lineMagnitude(px, py, ix, iy)
  }
  ans
}

# my data points
vector <- c(134.2, 156.1, 165.2, 186, 220.8, 237.1, 239.6, 327.7, 376.2, 
            396.1, 424.5, 460.2, 563.4, 565.7, 818.6, 819.7, 1120.4, 1279.5, 
            1640.7)
point_coords <- rbind(seq_along(vector), vector) #changed for reproducibility

x1 = point_coords[1,1]
y1 = point_coords[2,1]
x2 = point_coords[1,length(vector)]
y2 = point_coords[2,length(vector)]

apply(point_coords, MARGIN=2, FUN= distancePointSegment,
      x1=x1, x2=x2, y1=y1, y2=y2)
Roland
  • 127,288
  • 10
  • 191
  • 288
  • Thanks ROland, I actually tried this as well, it gives: `Error in FUN(newX[, i], ...) : argument "py" is missing, with no default` – Zhubarb May 18 '15 at 09:26
  • 1
    Note that I've changed your function. – Roland May 18 '15 at 09:26
  • OK, I see what you have done. So is the answer, "one cannot feed in two dynamic (col/row specific parameters) in this format" ? (e.g. you need to package them into a single parameter) – Zhubarb May 18 '15 at 09:27
  • You can pass two dynamic parameters with `mapply`. – Roland May 18 '15 at 09:27
  • Thanks, would you mind giving the syntax for that as well (as it is likely I may come back to this question for future reference). I did not have any strong motivations to use `apply` instead of `mapply`. – Zhubarb May 18 '15 at 09:28