【问题标题】:How to find the longest chord perpendicular to the maximum chord through a polygon如何通过多边形找到垂直于最大弦的最长弦
【发布时间】:2019-09-15 04:20:00
【问题描述】:

我有由一组点定义的不规则多边形。我可以找到最大弦的位置和长度,但我不太确定如何分析这些点以找到垂直于最大弦的最长弦位置和长度。

这是我到目前为止所得到的,一些用于定义多边形的点的示例数据:

points_ex <- structure(list(V1 = c(68L, 67L, 66L, 66L, 65L, 65L, 64L, 63L, 
                                   62L, 61L, 61L, 60L, 59L, 58L, 57L, 56L, 56L, 55L, 55L, 55L, 55L, 
                                   54L, 54L, 54L, 54L, 54L, 54L, 54L, 53L, 53L, 53L, 52L, 52L, 52L, 
                                   51L, 51L, 50L, 50L, 49L, 49L, 49L, 48L, 48L, 47L, 47L, 46L, 46L, 
                                   45L, 45L, 45L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 43L, 43L, 42L, 
                                   42L, 41L, 41L, 41L, 41L, 40L, 40L, 40L, 40L, 40L, 40L, 39L, 39L, 
                                   38L, 38L, 38L, 38L, 37L, 37L, 36L, 36L, 36L, 35L, 35L, 35L, 35L, 
                                   35L, 34L, 34L, 34L, 33L, 33L, 33L, 32L, 32L, 32L, 32L, 32L, 32L, 
                                   32L, 32L, 32L, 32L, 32L, 32L, 32L, 32L, 32L, 32L, 32L, 32L, 32L, 
                                   32L, 32L, 32L, 32L, 32L, 32L, 32L, 32L, 32L, 32L, 32L, 33L, 33L, 
                                   33L, 33L, 33L, 33L, 33L, 33L, 33L, 33L, 33L, 33L, 33L, 33L, 33L, 
                                   33L, 33L, 32L, 32L, 32L, 32L, 32L, 32L, 32L, 31L, 31L, 31L, 31L, 
                                   31L, 31L, 31L, 31L, 31L, 31L, 31L, 31L, 31L, 31L, 31L, 31L, 31L, 
                                   30L, 30L, 30L, 30L, 30L, 30L, 30L, 30L, 30L, 30L, 30L, 30L, 30L, 
                                   30L, 30L, 30L, 30L, 30L, 30L, 30L, 30L, 30L, 30L, 30L, 31L, 31L, 
                                   31L, 31L, 31L, 31L, 31L, 31L, 31L, 31L, 31L, 31L, 31L, 31L, 31L, 
                                   31L, 31L, 31L, 31L, 31L, 31L, 31L, 31L, 31L, 32L, 32L, 33L, 33L, 
                                   34L, 34L, 35L, 36L, 36L, 37L, 38L, 39L, 40L, 41L, 42L, 43L, 43L, 
                                   44L, 44L, 45L, 46L, 46L, 47L, 47L, 48L, 48L, 49L, 49L, 50L, 51L, 
                                   51L, 51L, 52L, 52L, 52L, 52L, 53L, 53L, 53L, 53L, 54L, 54L, 54L, 
                                   54L, 54L, 54L, 54L, 53L, 53L, 53L, 53L, 53L, 53L, 52L, 52L, 52L, 
                                   51L, 51L, 51L, 51L, 51L, 51L, 51L, 51L, 52L, 52L, 52L, 52L, 52L, 
                                   53L, 53L, 54L, 54L, 55L, 55L, 56L, 57L, 58L, 59L, 60L, 61L, 62L, 
                                   63L, 64L, 65L, 66L, 67L, 68L, 69L, 70L, 71L, 72L, 73L, 74L, 75L, 
                                   76L, 77L, 78L, 79L, 80L, 81L, 82L, 83L, 84L, 84L, 85L, 85L, 86L, 
                                   86L, 86L, 87L, 87L, 88L, 88L, 88L, 89L, 89L, 90L, 90L, 91L, 91L, 
                                   92L, 93L, 94L, 95L, 96L, 97L, 98L, 99L, 100L, 101L, 102L, 103L, 
                                   104L, 105L, 105L, 106L, 106L, 107L, 108L, 109L, 109L, 110L, 111L, 
                                   112L, 113L, 114L, 115L, 116L, 117L, 118L, 118L, 119L, 120L, 121L, 
                                   122L, 123L, 123L, 124L, 125L, 126L, 126L, 127L, 127L, 127L, 127L, 
                                   127L, 128L, 128L, 128L, 128L, 128L, 128L, 128L, 128L, 128L, 129L, 
                                   129L, 129L, 129L, 129L, 129L, 130L, 130L, 130L, 130L, 131L, 131L, 
                                   131L, 131L, 131L, 132L, 132L, 132L, 132L, 132L, 133L, 133L, 133L, 
                                   133L, 133L, 133L, 133L, 132L, 132L, 132L, 131L, 131L, 130L, 130L, 
                                   129L, 129L, 128L, 128L, 128L, 128L, 128L, 128L, 128L, 128L, 128L, 
                                   129L, 129L, 129L, 129L, 129L, 129L, 129L, 129L, 129L, 129L, 128L, 
                                   127L, 126L, 125L, 125L, 124L, 123L, 123L, 122L, 121L, 120L, 120L, 
                                   119L, 119L, 118L, 117L, 117L, 116L, 115L, 115L, 115L, 114L, 114L, 
                                   113L, 113L, 112L, 111L, 111L, 110L, 110L, 109L, 109L, 109L, 109L, 
                                   108L, 108L, 108L, 108L, 107L, 107L, 107L, 107L, 106L, 106L, 106L, 
                                   106L, 105L, 105L, 105L, 105L, 104L, 104L, 104L, 104L, 103L, 103L, 
                                   103L, 103L, 103L, 102L, 102L, 102L, 102L, 102L, 102L, 102L, 102L, 
                                   102L, 102L, 102L, 102L, 101L, 101L, 101L, 101L, 101L, 101L, 101L, 
                                   100L, 100L, 100L, 100L, 100L, 100L, 100L, 99L, 99L, 99L, 99L, 
                                   99L, 98L, 98L, 98L, 98L, 98L, 98L, 98L, 98L, 98L, 98L, 97L, 97L, 
                                   97L, 97L, 97L, 97L, 96L, 96L, 96L, 96L, 96L, 96L, 96L, 96L, 96L, 
                                   96L, 95L, 95L, 95L, 95L, 95L, 95L, 95L, 95L, 95L, 95L, 95L, 95L, 
                                   94L, 94L, 94L, 94L, 93L, 93L, 92L, 92L, 91L, 90L, 90L, 89L, 89L, 
                                   89L, 88L, 88L, 88L, 88L, 88L, 87L, 87L, 87L, 86L, 86L, 86L, 85L, 
                                   84L, 83L, 82L, 81L, 80L, 79L, 78L, 77L, 76L, 75L, 74L, 73L, 72L, 
                                   71L, 70L, 69L), V2 = c(20L, 21L, 22L, 23L, 24L, 25L, 26L, 27L, 
                                                          28L, 29L, 30L, 31L, 32L, 33L, 34L, 35L, 36L, 37L, 38L, 39L, 40L, 
                                                          41L, 42L, 43L, 44L, 45L, 46L, 47L, 48L, 49L, 50L, 51L, 52L, 53L, 
                                                          54L, 55L, 56L, 57L, 58L, 59L, 60L, 61L, 62L, 63L, 64L, 65L, 66L, 
                                                          67L, 68L, 69L, 70L, 71L, 72L, 73L, 74L, 75L, 76L, 77L, 78L, 79L, 
                                                          80L, 81L, 82L, 83L, 84L, 85L, 86L, 87L, 88L, 89L, 90L, 91L, 92L, 
                                                          93L, 94L, 95L, 96L, 97L, 98L, 99L, 100L, 101L, 102L, 103L, 104L, 
                                                          105L, 106L, 107L, 108L, 109L, 110L, 111L, 112L, 113L, 114L, 115L, 
                                                          116L, 117L, 118L, 119L, 120L, 121L, 122L, 123L, 124L, 125L, 126L, 
                                                          127L, 128L, 129L, 130L, 131L, 132L, 133L, 134L, 135L, 136L, 137L, 
                                                          138L, 139L, 140L, 141L, 142L, 143L, 144L, 145L, 146L, 147L, 148L, 
                                                          149L, 150L, 151L, 152L, 153L, 154L, 155L, 156L, 157L, 158L, 159L, 
                                                          160L, 161L, 162L, 163L, 164L, 165L, 166L, 167L, 168L, 169L, 170L, 
                                                          171L, 172L, 173L, 174L, 175L, 176L, 177L, 178L, 179L, 180L, 181L, 
                                                          182L, 183L, 184L, 185L, 186L, 187L, 188L, 189L, 190L, 191L, 192L, 
                                                          193L, 194L, 195L, 196L, 197L, 198L, 199L, 200L, 201L, 202L, 203L, 
                                                          204L, 205L, 206L, 207L, 208L, 209L, 210L, 211L, 212L, 213L, 214L, 
                                                          215L, 216L, 217L, 218L, 219L, 220L, 221L, 222L, 223L, 224L, 225L, 
                                                          226L, 227L, 228L, 229L, 230L, 231L, 232L, 233L, 234L, 235L, 236L, 
                                                          237L, 238L, 239L, 240L, 241L, 242L, 243L, 244L, 245L, 246L, 247L, 
                                                          248L, 249L, 250L, 251L, 252L, 253L, 254L, 255L, 256L, 257L, 258L, 
                                                          259L, 260L, 261L, 262L, 263L, 264L, 265L, 266L, 267L, 268L, 269L, 
                                                          270L, 271L, 272L, 273L, 274L, 275L, 276L, 277L, 278L, 279L, 280L, 
                                                          281L, 282L, 283L, 284L, 285L, 286L, 287L, 288L, 289L, 290L, 291L, 
                                                          292L, 293L, 294L, 295L, 296L, 297L, 298L, 299L, 300L, 301L, 302L, 
                                                          303L, 304L, 305L, 306L, 307L, 308L, 308L, 308L, 309L, 309L, 309L, 
                                                          310L, 310L, 310L, 311L, 311L, 312L, 312L, 312L, 312L, 312L, 312L, 
                                                          312L, 311L, 311L, 310L, 310L, 310L, 310L, 309L, 308L, 307L, 306L, 
                                                          305L, 304L, 303L, 302L, 301L, 300L, 299L, 298L, 297L, 296L, 295L, 
                                                          294L, 293L, 292L, 291L, 290L, 289L, 288L, 287L, 286L, 285L, 284L, 
                                                          283L, 282L, 281L, 280L, 279L, 279L, 278L, 277L, 276L, 275L, 274L, 
                                                          273L, 272L, 271L, 270L, 269L, 268L, 267L, 266L, 265L, 264L, 263L, 
                                                          262L, 261L, 260L, 259L, 258L, 257L, 256L, 255L, 254L, 253L, 252L, 
                                                          252L, 251L, 250L, 249L, 248L, 247L, 246L, 245L, 244L, 243L, 242L, 
                                                          241L, 240L, 239L, 238L, 237L, 236L, 235L, 234L, 233L, 232L, 231L, 
                                                          230L, 229L, 228L, 227L, 226L, 225L, 224L, 223L, 222L, 221L, 220L, 
                                                          219L, 218L, 217L, 216L, 215L, 214L, 213L, 212L, 211L, 210L, 209L, 
                                                          208L, 207L, 206L, 205L, 204L, 203L, 202L, 201L, 200L, 199L, 198L, 
                                                          197L, 196L, 195L, 194L, 193L, 192L, 191L, 190L, 189L, 188L, 187L, 
                                                          186L, 185L, 184L, 183L, 182L, 181L, 180L, 179L, 178L, 177L, 176L, 
                                                          175L, 174L, 173L, 172L, 171L, 170L, 169L, 168L, 167L, 166L, 165L, 
                                                          164L, 163L, 162L, 161L, 160L, 159L, 158L, 157L, 156L, 155L, 154L, 
                                                          153L, 152L, 151L, 150L, 149L, 148L, 147L, 146L, 145L, 144L, 143L, 
                                                          142L, 141L, 140L, 139L, 138L, 137L, 136L, 135L, 134L, 133L, 132L, 
                                                          131L, 130L, 129L, 128L, 127L, 126L, 125L, 124L, 123L, 122L, 121L, 
                                                          120L, 119L, 118L, 117L, 116L, 115L, 114L, 113L, 112L, 111L, 110L, 
                                                          109L, 108L, 107L, 106L, 105L, 104L, 103L, 102L, 101L, 100L, 99L, 
                                                          98L, 97L, 96L, 95L, 94L, 93L, 92L, 91L, 90L, 89L, 88L, 87L, 86L, 
                                                          85L, 84L, 83L, 82L, 81L, 80L, 79L, 78L, 77L, 76L, 75L, 74L, 73L, 
                                                          72L, 71L, 70L, 69L, 68L, 67L, 66L, 65L, 64L, 63L, 62L, 61L, 60L, 
                                                          59L, 58L, 57L, 56L, 55L, 54L, 53L, 52L, 51L, 50L, 49L, 48L, 47L, 
                                                          46L, 45L, 44L, 43L, 42L, 41L, 40L, 39L, 38L, 37L, 36L, 35L, 34L, 
                                                          33L, 32L, 31L, 30L, 29L, 28L, 27L, 26L, 26L, 26L, 25L, 25L, 24L, 
                                                          24L, 23L, 23L, 22L, 22L, 21L, 21L, 20L, 20L, 20L)), class = "data.frame", row.names = c(NA, 
                                                                                                                                                  -613L))

看起来像这样:

我可以找到最大的和弦并画出来:

# draw max dimension points and line
suppressPackageStartupMessages(library(tidyverse))
df_dist = data.frame(as.matrix(dist(cbind(points_ex$V1,points_ex$V2))))
df_dist_x = df_dist %>% 
  mutate(row.1 = 1:nrow(df_dist)) %>% 
  mutate(Y = paste0("Y", row_number())) %>%
  gather(X,  distance, X1:nrow(.)) %>% 
  select(X, Y, distance) %>% 
  mutate_at(vars(X, Y), parse_number)

df_dist_x_max <- 
  df_dist_x %>% 
  dplyr::filter(distance == max(distance)) 

points(points_ex[df_dist_x_max$X[1],], col = "red", cex = 2)
points(points_ex[df_dist_x_max$X[2],], col = "red", cex = 2.5)

segments(points_ex[df_dist_x_max$X[1], 'V1'], 
         points_ex[df_dist_x_max$X[1], 'V2'],
         points_ex[df_dist_x_max$X[2], 'V1'], 
         points_ex[df_dist_x_max$X[2], 'V2'],
         col = "green")

这就是我一直试图让最长的弦垂直于最大长度的弦:

# transform the points and lines into spatial objects
library(sf)
library(sp)
library(rgeos)

points_sf <- st_as_sf(points_ex, coords = c("V1", "V2"))

newline = matrix(c(points_ex[df_dist_x_max$X[1], 'V1'], 
                   points_ex[df_dist_x_max$X[1], 'V2'],
                   points_ex[df_dist_x_max$X[2], 'V1'], 
                   points_ex[df_dist_x_max$X[2], 'V2']), byrow = T, nrow = 2)

spline <- as(st_as_sfc(st_as_text(st_linestring(newline))), "Spatial") # there is probably a more straighforward solution...
position <- gProject(spline, as(points_sf, "Spatial"))
position <-  coordinates(gInterpolate(spline, position))
colnames(position) <- c("X2", "Y2")

segments <- 
  data.frame(st_coordinates(points_sf), position)  

segments$dist <- NULL
for(i in 1:nrow(segments)){
  segments$dist[i] <- 
    proxy::dist(data.frame(segments$X[i], segments$Y[i]),  
                data.frame(segments$X2[i], segments$Y2[i]))
}

# max width perpendicular to length axis
max_segment <- segments[which.max(segments$dist), ]
max_segment <- segments[segments$Y == max_segment$Y, ]

segments(max_segment$X[1], max_segment$Y[1],
         max_segment$X2[1], max_segment$Y2[1],
         col = "purple")

segments(max_segment$X[2], max_segment$Y[2],
         max_segment$X2[2], max_segment$Y2[2],
         col = "purple")

看起来大致没问题,但我的问题是,这种找到垂直于最大弦的最长弦的方法只查看到多边形边缘的最长距离的最大弦的一侧。

我不知道如何测量垂直于最大弦的多边形从边缘到边缘的每一个距离。

这意味着我的方法根本不能很好地概括,这里它被应用于另一个多边形(这里的数据:https://pastebin.com/XpiB6UnX 因为dput 的输出使这篇文章太长了)

显然这很糟糕,因为两个紫色段不应位于绿色段的同一侧,而且对于垂直于最大弦的最长弦而言,它看起来根本不是正确的位置。

如何稳健地找到垂直于最大弦的最长弦?

【问题讨论】:

  • 试图消化这个。这是正确的:最长的弦必须在两个多边形顶点之间。垂直于最长弦的最长线必须从多边形顶点开始,但可能在顶点之间结束。我会使用矢量算术创建一条长线,与依次通过每个顶点的最长弦垂直,然后使用 st_intersection 并保存最长的弦。如果多边形是凹的,并且这些线中的任何一条线都不止一个线段怎么办?
  • 这是一个两线式,它找到矩阵的最大值的 i,j 来替换所有那些难以理解的管道:findmax &lt;- function(m){ v = which.max(m) - 1; c(v %% nrow(m)+1, v %/% nrow(m)+1) }

标签: r geometry


【解决方案1】:

这是一个完整的解决方案。首先是一些功能:

## this returns the i,j of the largest elements in matrix `m`
findmax <- function(m){
    v = which.max(m) - 1
    c(v %% nrow(m)+1, v %/% nrow(m)+1)
}

## Return an sf line through a point at an angle of a given length
pline <- function(pt, angle, length){
    st_linestring(
        cbind(
            pt[1] + c(length,-length) * sin(angle),
            pt[2] + c(length,-length) * cos(angle)
        )
    )
}

## return the line that is the chord at angle perp.angle of length through any of the polygon vertices
max_perp_chord <- function(polygon, perp.angle, length){
    ## get polygon vertices
    pts = st_coordinates(polygon)[,c(1,2)]
    ## return the perpendicular lines
    perplines = lapply(1:nrow(pts), function(i){
        ## through the i-th vertex
        xy = pts[i,,drop=FALSE]
        perpline = pline(xy, perp.angle, length)
        ## intersect it with the polygon
        inters = st_intersection(polygon, perpline)
        inters
    }
    )

    ## get the vector of intersection lengths, find the largest
    perplengths = unlist(lapply(perplines, st_length))
    longest = which.max(perplengths)
    ## return the longest line
    perplines[[longest]]

}

### find the max length chord across all pairs of vertex points
max_chord <- function(polygon){
    ## get polygon coordinates
    xy = st_coordinates(polygon)[,1:2]

    ## compute the distance matrix and find largest element
    df_dist = as.matrix(dist(xy))
    maxij = findmax(df_dist)

    ## those elements define the largest chord
    chord = rbind(
        xy[maxij[1],],
        xy[maxij[2],]
    )
    chord
}


find_max_chord <- function(spolygon, chord=max_chord(spolygon)){

    ## Now compute the length and angle of the longest chord
    chord.length = sqrt(diff(chord[,1])^2 + diff(chord[,2])^2)
    chord.theta = atan2(diff(chord[,1]), diff(chord[,2]))

    ## The perpendicular is at this angle plus pi/2 radians
    perp = chord.theta + pi/2
    max_perp_chord(spolygon, perp, chord.length)
}

这是如何结合在一起的。唯一的依赖是sf - 只使用了base R:

library(sf)

接下来将您的数据处理成一个sf 多边形对象:

## construct an sf polygon from points:
polygon = st_polygon(list(as.matrix(rbind(points_ex, points_ex[1,]))))

获取顶点之间的最大长度弦:

chord = max_chord(polygon)

绘制多边形和弦以及弦点:

plot(polygon)
points(chord, col="red",cex=2)
lines(chord,col="green",lwd=2)

现在是肉。通过多边形顶点之一获得垂直于最大弦的弦:

## get the max perpendicular chord
pchord = find_max_chord(polygon, chord)

画出来。

plot(pchord,add=TRUE)

我已经在另一个示例中对此进行了测试,但不是您发布的第二个示例...应该可以但是...

确实……

【讨论】:

  • 哇,是的,这太完美了!非常感谢。我也试图得到垂直于最大弦的弦,在其中点与最大弦相交。让我们看看在这里使用你的框架我能走多远。再次感谢!
  • 轻松获得chord 的中点(X 的平均值,两个弦点的 Y 的平均值),然后使用pline 得到一个通过该点的长垂线。代码中没有解释的一件事是,垂直线最初的长度是length 的两倍,并且它以最长弦的长度为食。由于垂直弦比最长弦短,这保证了初始垂直线在用 st_intersect 裁剪时不会低于多边形
【解决方案2】:

如果你的多边形是单调的,你可以应用下一个方法:

我假设多边形被旋转以使最大长度的弦垂直(如您的两张图片所示)。

从顶点开始。左转CCW,右转顺时针。

关键时刻:最长的弦必须接触到某个多边形顶点(因为在边的两个内点之间的线段上无法达到最大值)。

所以获取具有最高 Y 坐标的顶点(在左侧或右侧)。计算对面边缘的点。获取长度。

获取下一个 Y 坐标的顶点。计算对面边缘的点。获取长度。继续。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-23
    • 1970-01-01
    • 1970-01-01
    • 2022-01-18
    • 2013-06-25
    • 1970-01-01
    • 2014-03-13
    • 1970-01-01
    相关资源
    最近更新 更多