三角形内的热图

Heatmap within a triangle

考虑以下示例:

triangle_lines <- data.frame(
  X   = c(0,0,1,1,0.5,0.5),
  Y   = c(0,0,0,0,1,  1),
  grp = c(1,2,1,3,2,3)
)

df <- 
  matrix(c(c(0.2,0.5,0.8), c(0.3,0.5,0.1), c(1,5,10)), ncol=3) %>%
  as.data.frame()
colnames(df) <- c("x","y", "amount")

ggplot(df, aes(x=x, y=y)) +
  geom_point(aes(colour = amount)) +
  geom_line(data=triangle_lines, aes(X, Y, group = grp)) +
  theme_void() 

我们有三个值:x坐标、y坐标和决定散点颜色值的amount。这些点之间的距离不等。

另外,我有一个三角形。我正在尝试仅在这个三角形 内部 创建一个渐变热图,用基于 df$amount 的热图颜色填充整个三角形。知道如何解决这个问题吗?

P.S。虽然我在这里只是举例说明三个点,但这个图在实际应用中将有几十个或数百个点。

不是最干净的解决方案,但它有效。

首先我们启动一个空列表。

然后我们创建一系列 lambda 值,每个轴一个,用于在该尺度上的两个点(即 0 和 1 之间)之间创建加权平均值。在此示例中,每个轴有 101 个 lambda,创建 101^2 次迭代。

然后我们检查生成的坐标是否位于 sp::point.in.polygon 的三角形内。请注意,此方法适用于更多形状,而不仅仅是三角形,因此此解决方案适用于多种形状。

如果lambda值生成的坐标在多边形内部,那么我们计算这个坐标到df中每个坐标的距离。请注意,我们将 sqrt(2) 减去距离,因为距离越小,该点应承载的权重越大。因此,我们取最大距离 (sqrt(2)) 并减去距离。 sqrt(2) 不是必须设置的确定数字,但它确实可以防止出现负值。其他值提供其他结果。

在下一步中,我们缩放距离,使它们的总和为 1。这样我们就可以创建一个加权平均值,它在 amount 中定义。

在 运行 循环之后,我们将列表绑定到数据框中并创建绘图。

为了确保边缘光滑,我们制作了一些带有圆边的粗白线。

gradient_list <- list()
for (lambda_x in seq(0,1,by=0.01)) {
  for (lambda_y in seq(0,1,by=0.01)) {
    x_value <- lambda_x*0 + (1-lambda_x)*1
    y_value <- lambda_y*0 + (1-lambda_y)*1
    
    inside_polygon <- sp::point.in.polygon(x_value, y_value, triangle_lines$X, triangle_lines$Y) %>% as.logical()
    
    if (inside_polygon) {
      point <- c(x_value, y_value)
      distances <- sqrt(2) - sqrt((df$x - point[1])^2 + (df$y - point[2])^2)
      weighted_distances <- distances/sum(distances)
      amount <- sum(weighted_distances * df$z)
      gradient_list <- append(gradient_list, list(c(point, amount)))
    }
  }
}

gradient_df <- do.call(rbind, gradient_list) %>% as.data.frame()
colnames(gradient_df) <- c("x","y","amount")


ggplot(gradient_df, aes(x=x, y=y)) + 
  geom_point(aes(colour = amount), size=2) +
  theme_void() + 
  geom_line(data=triangle_lines, aes(X, Y, group = grp), size=3, colour="white", lineend="round")