inst/GWmodelVisApp/server.R

# server.R

# 服务器端代码目录 全局变量
code_dir <- getwd()
# 修改shiny工作目录
# setwd(code_dir)
# 临时路径
dir <- "temp"
if (!dir.exists(dir)) dir.create(dir)
temp_dir <- file.path(code_dir, "temp")  # 拼接 temp 路径
# 打印路径调试
cat("file path: ", code_dir, "\n")
cat("temp path: ", temp_dir, "\n")

# 设置最大上传文件大小限制(以字节为单位)
options(shiny.maxRequestSize = 100 * 1024^2)  # 100 MB

# 全局变量,存储服务器对象
file_server <- NULL
# 启动文件服务器(for gwboxplot)
start_servr <- function() {
  # 停止已存在的服务器
  running_servers <- servr::daemon_list()
  if (length(running_servers) > 0) {
    lapply(running_servers, servr::daemon_stop)
    cat("Stopped all existing servers.\n")
  }

  # 动态分配端口
  port <- 8000
  while (port <= 9000) {
    tryCatch({
      server <- servr::httd(dir = temp_dir, port = port, daemon = TRUE)
      cat("Server started at port:", port, "\n")
      return(server)
    }, error = function(e) {
      cat("Port", port, "is in use. Trying next port...\n")
      port <- port + 1
    })
  }

  stop("Failed to start server: All ports are in use.")
}
# 停止文件服务器
stop_servr <- function(server) {
  if (!is.null(server) && server$server %in% names(servr::daemon_list())) {
    servr::daemon_stop(server$server)
    cat("R HTTP server stopped.\n")
  } else {
    cat("No running server found or invalid server object.\n")
  }
}

# gw_boxplot function
findmedian <- function(x, w) {
    xo <- sort(x)
    wo <- w[order(x)]

  # 找到累积和首次大于0.5的位置
    idx <- which(cumsum(wo) >= 0.5, arr.ind = TRUE)
    if (length(idx) == 0) {
        # 如果没有找到合适的索引,则返回最后一个元素
        idx <- length(xo)
    } else {
        idx <- idx[1]
    }
    xo[idx]
}
findQ1 <- function(x,w){
    xo <- sort(x)
    wo <- w[order(x)]

    # 找到累积和首次大于0.25的位置
    idx <- which(cumsum(wo) >= 0.25, arr.ind = TRUE)
    if (length(idx) == 0) {
        # 如果没有找到合适的索引,则返回最后一个元素
        idx <- length(xo)
    } else {
        idx <- idx[1]
    }
    xo[idx]
}
findQ3 <- function(x,w){
    xo <- sort(x)
    wo <- w[order(x)]

    # 找到累积和首次大于0.75的位置
    idx <- which(cumsum(wo) >= 0.75, arr.ind = TRUE)
    if (length(idx) == 0) {
        # 如果没有找到合适的索引,则返回最后一个元素
        idx <- length(xo)
    } else {
        idx <- idx[1]
    }
    xo[idx]
}
findMin <- function(Q1,Q3){
       IQR=Q3-Q1
       Min=Q1-1.5*IQR
       Min
}
findMax <- function(Q1,Q3){
       IQR=Q3-Q1
       Max=Q3+1.5*IQR
       Max
}
gw_boxplot <- function(bw, X, kernel, adaptive, dp.locat, p, theta, longlat, calibrationPoint, gwbp_bpcolor) {
  # 初始化默认值
  if (is.null(p)) p <- 2
  if (is.null(longlat)) longlat <- TRUE
  theta <- 0
  # 确保 dp.locat 和 calibrationPoint 是矩阵
  if (!is.matrix(dp.locat)) dp.locat <- as.matrix(dp.locat)
  if (!is.null(calibrationPoint) && !is.matrix(calibrationPoint)) calibrationPoint <- as.matrix(calibrationPoint)

  # 根据是否有 calibrationPoint 决定使用的循环点
  loop_points <- if (!is.null(calibrationPoint)) calibrationPoint else dp.locat

  # 检查 dp.locat 和 loop_points 是否有效
  if (nrow(dp.locat) == 0 || ncol(dp.locat) != 2) {
    stop("dp.locat must be a non-empty matrix with 2 columns (longitude, latitude).")
  }
  if (nrow(loop_points) == 0 || ncol(loop_points) != 2) {
    stop("loop_points must be a non-empty matrix with 2 columns (longitude, latitude).")
  }
  # 数据点数量
  point_count <- nrow(loop_points)

  # GWBoxplot Display---------------------------------------------------------------------------
  # 初始化结果存储
  result_data <- data.frame(
    FID = 1:point_count,
    Min = numeric(point_count),
    Q1 = numeric(point_count),
    Median = numeric(point_count),
    Q3 = numeric(point_count),
    Max = numeric(point_count),
    longitude = loop_points[, 1],
    latitude = loop_points[, 2]
  )

   # 计算 Boxplot 数据
  calculate_boxplot <- function(i) {
    # 计算距离和权重
    dist.vi <- GWmodel::gw.dist(dp.locat = dp.locat, rp.locat = loop_points, focus = i, p = p, theta = theta, longlat = longlat)
    W.i <- matrix(GWmodel::gw.weight(dist.vi, bw, kernel, adaptive), nrow = 1)
    Wi <- W.i / sum(W.i)  # 归一化权重

    # 计算统计值
    median <- findmedian(X, w = c(Wi))
    Q1 <- findQ1(X, w = c(Wi))
    Q3 <- findQ3(X, w = c(Wi))
    min_val <- findMin(Q1, Q3)
    max_val <- findMax(Q1, Q3)
    max_min_diff <- max_val - min_val

    return(list(
       min_val = min_val, Q1 = Q1, median = median, Q3 = Q3, max_val = max_val, max_min_diff = max_min_diff
    ))
  }

  # 逐点计算统计值
  boxplot_data <- lapply(1:point_count, calculate_boxplot)
  global_min <- min(sapply(boxplot_data, function(x) x$min_val))
  global_max <- max(sapply(boxplot_data, function(x) x$max_val))

  # 批量绘制 Boxplot
  generate_boxplot <- function(i, stats) {

    # 获取统计数据
    min_val <- stats$min_val
    Q1 <- stats$Q1
    median <- stats$median
    Q3 <- stats$Q3
    max_val <- stats$max_val
    
    # 统一全局高度,确保所有 Boxplot 处于相同数轴
    total_height <- 1.5 
    
    # 归一化函数,使所有 Boxplot 统一在 (global_min, global_max) 范围内缩放
    normalize <- function(val) {
      return ((val - global_min) / (global_max - global_min) * total_height)
    }

    # 计算归一化 y 坐标
    y_min <- normalize(min_val)
    y_Q1 <- normalize(Q1)
    y_median <- normalize(median)
    y_Q3 <- normalize(Q3)
    y_max <- normalize(max_val)

    # 设定坐标位置
    x_center <- 0
    box_width <- 0.4  # 箱体宽度
    x_range <- c(x_center - 0.5, x_center + 0.5)  # 固定x轴范围

    # 定义 Boxplot 的绘制数据
    # 箱体(矩形主体)
    box <- data.frame(
      x = c(x_center - box_width / 2, x_center + box_width / 2, 
            x_center + box_width / 2, x_center - box_width / 2),
      y = c(y_Q1, y_Q1, y_Q3, y_Q3),
      label = factor(i)
    )
    # 须线(whiskers)和极值线
    lines <- list(
      line_min = data.frame(x = c(x_center, x_center), y = c(y_min, y_Q1), label = factor(i)),
      line_minl = data.frame(x = c(x_center - box_width / 2, x_center + box_width / 2), y = c(y_min, y_min), label = factor(i)),
      line_max = data.frame(x = c(x_center, x_center), y = c(y_Q3, y_max), label = factor(i)),
      line_maxl = data.frame(x = c(x_center - box_width / 2, x_center + box_width / 2), y = c(y_max, y_max), label = factor(i)),
      line_median = data.frame(x = c(x_center - box_width / 2, x_center + box_width / 2), y = c(y_median, y_median), label = factor(i))
    )

    # 绘制 Boxplot
    p <- ggplot2::ggplot() +
      # 固定x轴显示范围确保宽度一致
      ggplot2::xlim(x_range[1], x_range[2]) +
      ggforce::geom_shape(data = box, ggplot2::aes(x, y, group = label), alpha = 0.7, linewidth = 0.5, fill = gwbp_bpcolor, colour = "black") +
      # geom_rect(data = box, ggplot2::aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax, fill = gwbp_bpcolor), color = "black", alpha = 0.7) + 
      ggplot2::geom_line(data = lines$line_min, ggplot2::aes(x, y, group = label), linetype = 1, linewidth = 0.5) +
      ggplot2::geom_line(data = lines$line_minl, ggplot2::aes(x, y, group = label), linetype = 1, linewidth = 0.5) +
      ggplot2::geom_line(data = lines$line_maxl, ggplot2::aes(x, y, group = label), linetype = 1, linewidth = 0.5) +
      ggplot2::geom_line(data = lines$line_max, ggplot2::aes(x, y, group = label), linetype = 1, linewidth = 0.5) +
      ggplot2::geom_line(data = lines$line_median, ggplot2::aes(x, y, group = label), linetype = 1, linewidth = 1) +
      # geom_glowline(data = lines$line_median, ggplot2::aes(x, y, group = label),color = "cyan", size = 0.5, alpha = 0.8, shadowcolor = "cyan", shadowalpha = 0.3, shadowwidth = 0.5) +
      ggplot2::theme_void() +
      ggplot2::theme(legend.position = "none")  # 移除图例

    ggplot2::ggsave(
      filename = paste0(code_dir, "/temp/gw_boxplot_", i, ".png"), 
      plot = p, 
      width = 2,  # 固定宽度 + 边距
      height = total_height  + 2,  # 动态高度 + 边距
      units = "in", 
      dpi = 300)
  }

  # 批量绘制并保存结果
  for (i in 1:point_count) {
    generate_boxplot(i, boxplot_data[[i]])
    result_data[i, 2:6] <- unlist(boxplot_data[[i]][1:5])  # 保存结果
  }

  return(result_data)
}
gw_boxplot_outliers <- function(bw, X, kernel, adaptive, dp.locat, p = 2, theta = 0, longlat = TRUE) {
  if (!is.matrix(dp.locat)) dp.locat <- as.matrix(dp.locat)
  # 数据点数量
  dp.n <- nrow(dp.locat)

  # 初始化结果存储
  result <- data.frame(
    FID = numeric(dp.n),
    Min = numeric(dp.n),
    Q1 = numeric(dp.n),
    Median = numeric(dp.n),
    Q3 = numeric(dp.n),
    Max = numeric(dp.n),
    Outliers = numeric(dp.n),
    TorF_outlier = logical(dp.n),
    longitude = dp.locat[, 1],
    latitude = dp.locat[, 2]
  )

  # 核心函数:计算距离、权重和统计值
  calculate_outlier <- function(i) {
    # 计算距离
    dist.vi <- GWmodel::gw.dist(dp.locat = dp.locat, focus = i, p = p, theta = theta, longlat = longlat)
    # 计算权重
    W.i <- GWmodel::gw.weight(dist.vi, bw, kernel, adaptive)
    if (any(is.nan(W.i))) stop("The weight vector contains NaN values.")
    Wi <- W.i / sum(W.i)  # 归一化权重

    # 计算统计值
    median <- findmedian(X, w = Wi)
    Q1 <- findQ1(X, w = Wi)
    Q3 <- findQ3(X, w = Wi)
    min_val <- findMin(Q1, Q3)
    max_val <- findMax(Q1, Q3)
    outlier_flag <- (X[i] < min_val) || (X[i] > max_val)

    # 返回结果
    return(list(
      min = min_val,
      Q1 = Q1,
      median = median,
      Q3 = Q3,
      max = max_val,
      outlier = X[i],
      outlier_flag = outlier_flag
    ))
  }

  # 遍历每个点,计算结果
  outlier_results <- lapply(1:dp.n, calculate_outlier)

  # 填充结果到数据框
  result$FID <- 1:dp.n
  result$Median <- sapply(outlier_results, function(x) x$median)
  result$Q1 <- sapply(outlier_results, function(x) x$Q1)
  result$Q3 <- sapply(outlier_results, function(x) x$Q3)
  result$Min <- sapply(outlier_results, function(x) x$min)
  result$Max <- sapply(outlier_results, function(x) x$max)
  result$Outliers <- sapply(outlier_results, function(x) x$outlier)
  result$TorF_outlier <- sapply(outlier_results, function(x) x$outlier_flag)

  return(result)
}

# point_card function is used to generate the card content for each point on the map
point_card <- function (gwbp_Name, gwbp_price, gwbp_longitude, gwbp_latitude, gwbp_max, gwbp_Q3, gwbp_median, gwbp_Q1, gwbp_min) {
  card_content <- paste0("
                    <style>
                    div.leaflet-popup-content {width:auto !important;}
                        .flip-card {
                          background-color: transparent;
                          width: 300px;
                          height: 330px;
                          perspective: 1000px;
                        }

                        .flip-card-inner {
                          position: relative;
                          width: 100%;
                          height: 100%;
                          text-align: center;
                          transition: transform 0.6s;
                          transform-style: preserve-3d;
                          box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
                        }

                        .flip-card:hover .flip-card-inner {
                          transform: rotateY(180deg);
                        }

                        .flip-card-front, .flip-card-back {
                          position: absolute;
                          width: 100%;
                          height: 100%;
                          backface-visibility: hidden;
                        }

                        .flip-card-front {
                          background-color: #CCE4EF;
                          color: black;
                          z-index: 2;
                        }

                        .flip-card-back {
                          background-color: #92B5CA;
                          color: white;
                          transform: rotateY(180deg);
                          z-index: 1;
                        }
                    </style>",
                    "<table style='width:100%; background-color: #CCE4EF;'>",
                    "<tr>",
                    "<th><b><h1 style='text-align: left;'>",gwbp_Name,"</h1></b></th>",
                    "</tr>",
                    "</table>",
                    "<div class='flip-card'>",
                      "<div class='flip-card-inner'>",
                        "<div class='flip-card-front'>",
                          "<h3>Variable parameter</h3>",
                          "<hr>",
                          "<table style='width:100%;'>",
                            "<tr>",
                            "</tr>",
                            "<tr>",
                              "<td style='text-align: left; padding-left: 20px;'><h4>GWBoxplot_MAX:</h4></td>",
                              "<td><h4>",gwbp_max,"</h4></td>",
                            "</tr>",
                            "<tr>",
                              "<td style='text-align: left; padding-left: 20px;'><h4>GWBoxplot_Q3:<h4></td>",
                              "<td><h4>",gwbp_Q3,"</h4></td>",
                            "</tr>",
                            "<tr>",
                              "<td style='text-align: left; padding-left: 20px;'><h4>GWBoxplot_Median:<h4></td>",
                              "<td><h4>",gwbp_median,"</h4></td>",
                            "</tr>",
                            "<tr>",
                              "<td style='text-align: left; padding-left: 20px;'><h4>GWBoxplot_Q1:<h4></td>",
                              "<td><h4>",gwbp_Q1,"</h4></td>",
                            "</tr>",
                            "<tr>",
                              "<td style='text-align: left; padding-left: 20px;'><h4>GWBoxplot_MIN:<h4></td>",
                              "<td><h4>",gwbp_min,"</h4></td>",
                            "</tr>",
                          "</table>",
                          "</div>",
                        "<div class='flip-card-back'>",
                          "<h3>GW_Boxplot parameter</h3>",
                          "<hr>",
                          "<table style='width:80%;'>",
                            "<tr>",
                              "<td style='padding: 5px;'><h4><b>Variable: </b>",gwbp_price,"</h4></td>",
                            "</tr>",
                            "<tr>",
                              "<td style='padding: 5px;'><h4><b>Longitude: </b>",gwbp_longitude,"</h4></td>",
                            "</tr>",
                            "<tr>",
                              "<td style='padding: 5px;'><h4><b>Latitude: </b>",gwbp_latitude,"</h4></td>",
                            "</tr>",
                          "</table>",
                          "</div>",
                      "</div>",
                    "</div>"
  )
  return(card_content)
}
# small outliers popup function
small_outliers_popup <- function(x_field,Outliers) {
  content <- paste0('<body style="background-color: white;">
                      <div style="background-color: white; text-align:center;">
                      <h4>',x_field,'</h4>
                      <b>', Outliers, '</b>
                      </div>
                      </body>')
  return(content)
}
# 定义颜色渐变函数
color_gradients <- list(
  "green-red" = c(low = "green", high = "red"),
  "blue-yellow" = c(low = "blue", high = "yellow"),
  "purple-orange" = c(low = "purple", high = "orange"),
  "magenta-cyan" = c(low = "magenta", high = "cyan"),
  "pink-blue" = c(low = "pink", high = "blue"),
  "viridis" = c(low = "viridis", high = NA)  # 使用 viridis 色系
)
# 通用绘图函数
generate_plot <- function(data, sdf_col, bandwidth, output_path, color_palette, extended_min, extended_max, fixed_breaks) {
  # 判断几何类型
  geom_type <- unique(sf::st_geometry_type(data))
  is_polygon <- "POLYGON" %in% geom_type || "MULTIPOLYGON" %in% geom_type
  is_point <- "POINT" %in% geom_type || "MULTIPOINT" %in% geom_type
  
  # 根据用户选择的色阶动态设置颜色映射
  if (color_palette == "viridis") {
    color_mapping <- if (is_polygon) {
      ggplot2::scale_fill_viridis_c(
        limits = c(extended_min, extended_max),  # 扩展范围
        breaks = fixed_breaks,                  # 固定分割点
        oob = scales::squish                    # 超出范围的值压缩到边界颜色
      )
    } else if (is_point) {
      ggplot2::scale_color_viridis_c(
        limits = c(extended_min, extended_max),
        breaks = fixed_breaks,
        oob = scales::squish
      )
    } else {
      stop("Unsupported geometry type for plotting.")
    }
  } else {
    gradient <- color_gradients[[color_palette]]
    color_mapping <- if (is_polygon) {
      ggplot2::scale_fill_gradient(
        low = gradient["low"], 
        high = gradient["high"],
        limits = c(extended_min, extended_max),  # 扩展范围
        breaks = fixed_breaks,                  # 固定分割点
        oob = scales::squish                    # 超出范围的值压缩到边界颜色
      )
    } else if (is_point) {
      ggplot2::scale_color_gradient(
        low = gradient["low"], 
        high = gradient["high"],
        limits = c(extended_min, extended_max),
        breaks = fixed_breaks,
        oob = scales::squish
      )
    } else {
      stop("Unsupported geometry type for plotting.")
    }
  }

  # 绘制并保存图像
  p <- ggplot2::ggplot(data) +
    {if (is_polygon) {
      ggplot2::geom_sf(ggplot2::aes(fill = value))  # 使用 fill 来绘制多边形
    } else if (is_point) {
      ggplot2::geom_sf(ggplot2::aes(color = value))  # 使用 color 来绘制点数据
    }}  +
    color_mapping +
    ggplot2::labs(title = paste0(sdf_col, "  (Bandwidth: ", bandwidth, ")")) +
    ggspatial::annotation_north_arrow(
      location = "tl",  # 左上角
      which_north = "true",
      pad_x = ggplot2::unit(0.2, "cm"),
      pad_y = ggplot2::unit(0.2, "cm"),
      style = ggspatial::north_arrow_fancy_orienteering()
    ) +
    ggspatial::annotation_scale(
      location = "bl",  # 左下角
      width_hint = 0.3
    ) +
    ggplot2::theme(
      panel.background = ggplot2::element_rect(fill = "white", color = NA),
      panel.grid = ggplot2::element_blank(),
      plot.background = ggplot2::element_rect(fill = "white", color = NA)
    )
  
  # 保存图片
  ggplot2::ggsave(
    filename = output_path,
    plot = p,
    width = 5, height = 5, units = "in", dpi = 150
  )
}
# 通用Web Map生成音频(根据系数)
generate_audio <- function(value, filename,x_min = NULL, x_max = NULL) {

  # waveform_ratio = c(0.7, 0.2, 0.1)
  # adsr = c(attack = 0.05, decay = 0.1, sustain = 0.6, release = 0.15)
  # filter_cutoff = 0.4
  # loudness_factor = 2
  waveform_ratio = c(0.6, 0.3, 0.1)  # 降低方波占比,增强二次谐波
  adsr = c(attack = 0.05, decay = 0.1, sustain = 0.5, release = 0.2)  # 让音色更平滑
  filter_cutoff = 0.1  # **降低低通滤波器截止频率**
  loudness_factor = 0.2 # **降低音量归一化系数**
  sampling_rate = 44100
  duration_per_note = 0.5
  
  # 异常值处理
  if (is.na(value) || is.null(value)) value <- 0
  
  # --- 频率映射 ---
  if (is.null(x_min)) x_min <- min(value)
  if (is.null(x_max)) x_max <- max(value)
  
  a <- (1000 - 440) / (x_max - x_min)
  b <- 440 - a * x_min
  freq <- a * value + b  
  
  # --- 波形生成参数 ---
  note_samples <- round(duration_per_note * sampling_rate)
  t <- seq(0, duration_per_note, length.out = note_samples)

  tryCatch({
    # 基波(正弦波)
    base_wave <- sin(2 * pi * freq * t)

    # 谐波成分
    harmonic1 <- sin(2 * pi * 2 * freq * t) * waveform_ratio[2]
    harmonic2 <- sin(2 * pi * 3 * freq * t) * waveform_ratio[3]

    # 混合波形
    mixed_wave <- (waveform_ratio[1] * base_wave) + harmonic1 + harmonic2

    # --- ADSR包络 ---
    apply_envelope <- function(signal, adsr_params) {
      n <- length(signal)
      attack_len <- floor(adsr_params["attack"] * n)
      decay_len <- floor(adsr_params["decay"] * n)
      release_len <- floor(adsr_params["release"] * n)
      sustain_len <- n - attack_len - decay_len - release_len
      
      envelope <- c(
        seq(0, 1, length.out = attack_len),
        seq(1, adsr_params["sustain"], length.out = decay_len),
        rep(adsr_params["sustain"], sustain_len),
        seq(adsr_params["sustain"], 0, length.out = release_len)
      )
      length(envelope) <- n  # 长度对齐
      signal * envelope
    }
    
    enveloped_wave <- apply_envelope(mixed_wave, adsr)

    # --- 低通滤波 ---
    bf <- signal::butter(4, filter_cutoff, type = "low")
    filtered_wave <- signal::filtfilt(bf, enveloped_wave)

    # --- 规范化处理(降低音量)---
    normalized_wave <- filtered_wave / max(abs(filtered_wave)) * loudness_factor
    normalized_wave <- pmin(pmax(normalized_wave, -0.5), 0.5)  # **进一步限制振幅**

    # --- 生成Wave对象 ---
    audio_wave <- tuneR::Wave(
      left = as.integer(normalized_wave * 32767),
      right = as.integer(normalized_wave * 32767),
      samp.rate = sampling_rate,
      bit = 16
    )

    # 保存文件
    tuneR::writeWave(audio_wave, filename)
    return(filename)

  }, error = function(e) {
    warning("音频生成失败:", e$message)
    return(NULL)
  })
}
# 合成音频的函数(依赖 tuneR 包)
concatenate_audio <- function(audio_files, output_file) {
  if(length(audio_files) == 0) return(NULL)
  library(tuneR)
  combined <- tuneR::readWave(audio_files[1])
  if(length(audio_files) > 1) {
    for(file in audio_files[-1]){
      wav <- tuneR::readWave(file)
      # 简单拼接音频
      combined <- bind(combined, wav)
    }
  }
  tuneR::writeWave(combined, output_file)
}
# 检查字符串是否包含中文字符的函数
has_chinese <- function(x) {
  grepl("[\u4e00-\u9fff]", x)
}
musicIcon <- leaflet::makeIcon(
      iconUrl = "www/music_note.png",  # 确保在www目录放置音乐图标
      iconWidth = 24, 
      iconHeight = 24,
      iconAnchorX = 12,
      iconAnchorY = 12
  )

# Server
server <- function(input, output,session) {
  #--------------------------# 全局变量 #--------------------------#
  running <- reactiveVal(TRUE)
  point_cards_reactive <- reactiveVal(data.frame()) # 初始化反应式值来存储marker点数据
  addResourcePath("www", "www")  # 注册 www 目录

  #--------------------------# Home #--------------------------#
  observeEvent(input$btn_gwss, {
    updateTabItems(session, "tabs", "gwss")
  })
  observeEvent(input$btn_gw_boxplot, {
    updateTabItems(session, "tabs", "gw_boxplot")
  })
  observeEvent(input$btn_gwr_basic, {
    updateTabItems(session, "tabs", "gwr")
  })
  observeEvent(input$btn_gwr_multiscale, {
    updateTabItems(session, "tabs", "mgwr")
  })
  observeEvent(input$btn_gwpca, {
    updateTabItems(session, "tabs", "gwpca")
  })
  #--------------------------# GWSS #--------------------------#
  # 按钮跳转
  observeEvent(input$gwss_execute, {
    updateTabsetPanel(session, "gwss_tabs", selected = "Summary Information")
  })
  observeEvent(input$gwss_video_button, {
    updateTabsetPanel(session, "gwss_tabs", selected = "Video")
  })
  # shp读取函数
  gwss_shapefile_data <- reactive({
    req(input$gwss_shapefile)  # 确保用户已上传文件

    # 获取上传文件的路径和扩展名
    file_path <- input$gwss_shapefile$datapath
    file_ext <- tolower(tools::file_ext(input$gwss_shapefile$name))  # 获取文件扩展名,转为小写
    file_name <- tools::file_path_sans_ext(input$gwss_shapefile$name)

    # 设置 GDAL 配置选项
    Sys.setenv(SHAPE_RESTORE_SHX = "YES")

    if (file_ext == "zip") {
      # 如果是 ZIP 文件,解压缩并查找 .shp 文件
      temp_dir <- tempdir()
      unzip(file_path, exdir = temp_dir)
      shp_files <- list.files(temp_dir, pattern = paste0("^", file_name, "\\.shp$"), full.names = TRUE)
      dbf_files <- list.files(temp_dir, pattern = paste0("^", file_name, "\\.dbf$"), full.names = TRUE)

      if (length(shp_files) > 0 && length(dbf_files) > 0) {
        shp_file <- shp_files[1]
        dbf_file <- sub("\\.shp$", ".dbf", shp_file)
        shp_data <- tryCatch(
          {
            sf::st_read(shp_file, options = "ENCODING=GBK")
          },
          error = function(e) {
            shiny::showNotification("Error reading Shapefile from ZIP. Please check the file content.", type = "error")
            return(NULL)
          }
        )
        # 处理 CRS(避免警告)
        if (!is.null(sf::st_crs(shp_data))) {
          shp_data <- sf::st_transform(shp_data, 4326)  # 如果已有 CRS,重新投影
        } else {
          sf::st_crs(shp_data) <- 4326  # 如果没有 CRS,先赋值再投影
          shp_data <- sf::st_transform(shp_data, 4326)
        }
        return(shp_data)
      } else {
        shiny::showNotification("No Shapefile found in the ZIP file.", type = "error")
        return(NULL)
      }
    } else {
      # 如果上传的文件既不是 .shp 也不是 .zip,显示错误通知
      shiny::showNotification("Unsupported file type. Please upload .zip file.", type = "error")
      return(NULL)
    }
  })
  # 更新自变量&图层选项
  observeEvent(gwss_shapefile_data(), {
    # 获取变量名并按首字母排序(不区分大小写)
    var_names <- names(gwss_shapefile_data())
    
    # 检查变量名是否为空
    if (length(var_names) == 0) {
      sorted_vars <- character(0)  # 空字符向量
    } else {
      # 不区分大小写排序
      sorted_vars <- var_names[order(tolower(var_names))]
    }
    
    # 确保默认选中项有效(至少有两个变量时才选中前两个)
    selected_vars <- if (length(sorted_vars) >= 2) {
      sorted_vars[1:2]
    } else {
      sorted_vars  # 不足两个时选中所有可用变量
    }
    updateSelectInput(session, "gwss_vars", choices = sorted_vars, selected = selected_vars)
  })
  # 输出自变量选择控件
  output$gwss_vars <- renderUI({
    selectInput("gwss_vars", "Choose variables", choices = NULL, multiple = TRUE, selected = NULL)
  })
  # 更新带宽滑块
  observe({
      req(input$gwss_shapefile)  # 确保文件已上传
      shp_data <- gwss_shapefile_data()  # 读取 shapefile 数据
      shp_data <- sf::st_make_valid(shp_data)      # 确保数据有效
      req(shp_data)

      # 计算最大距离(米),确保单位正确
      if (sf::st_is_longlat(shp_data)) {
          max_distance <- as.integer(max(sf::st_distance(shp_data)))  # 计算球面距离
      } else {
          coords <- sf::st_coordinates(shp_data)
          max_distance <- as.integer(max(dist(coords)))  # 计算欧式距离
      }

      # 读取是否为自适应带宽
      gwss_adaptive <- as.logical(input$gwss_adaptive)

      # 设置带宽的最大值和单位
      if (gwss_adaptive) {
          max_bw <- nrow(shp_data)  # 以点的个数为单位
          label <- "Bandwidth (Number of points)"
      } else {
          max_bw <- max_distance  # 以最大距离为单位
          label <- "Bandwidth (meter)"
      }

      # 更新滑块
      updateSliderInput(session, "gwss_bandwidth",
                        max = max_bw,
                        label = label)
  })
  # gwss结果对象
  gwss_bw_data <- reactiveValues(
    bw = NULL,          
  )
  # 地图可视化
  output$gwss_mapPlot <- leaflet::renderLeaflet({
    map <- leaflet::leaflet()%>%
          leaflet::clearMarkers() %>%
          leaflet::addProviderTiles("CartoDB.Positron")

    # 返回最终的map对象
    map
  })
  #--------------------------# GWSS Analysis #--------------------------#
  # 运行GWSS分析
  gwss_result <- eventReactive(input$gwss_execute, {
    # 保证参数存在
    req(input$gwss_shapefile)
    req(input$gwss_vars)
    req(input$gwss_kernel, input$gwss_adaptive)

    # 显示自定义模态窗口
    showModal(modalDialog(
      title = "Processing GWSS Analysis...",
      tags$div(
        style = "text-align: center;",
        shinyWidgets::progressBar(
          id = "gwss_progress",
          value = 0,
          display_pct = TRUE,
          status = "info",
          striped = TRUE
        )
      ),
      footer = tagList(
        modalButton("Cancel")  # 添加关闭按钮
      ),
      easyClose = FALSE
    ))

    # 更新进度条10%
    shinyWidgets::updateProgressBar(session = session, id = "gwss_progress", value = 10)

    # 获取实际的数据和坐标
    gwss_shp_data <- gwss_shapefile_data()
    # 检查数据是否为 sf 对象
    if (!inherits(gwss_shp_data, "sf")) {
      shiny::showNotification("Unsupported data types. Input is not an sf object.", type = "error", duration = NULL)
      return()
    }
    # 修复可能的无效几何数据
    gwss_shp_data <- sf::st_make_valid(gwss_shp_data)

    # 更新进度条20%
    shinyWidgets::updateProgressBar(session = session, id = "gwss_progress", value = 20)

    # 初始化 dp.locat
    dp.locat <- NULL
    # 提取坐标点
    tryCatch({
      geometry_type <- unique(sf::st_geometry_type(gwss_shp_data))  # 获取所有几何类型
      if ("MULTIPOLYGON" %in% geometry_type || "POLYGON" %in% geometry_type) {
        centroids <- sf::st_centroid(sf::st_geometry(gwss_shp_data))  # 仅计算几何的质心
        coords <- sf::st_coordinates(centroids)
        dp.locat <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
      } else if ("POINT" %in% geometry_type || "MULTIPOINT" %in% geometry_type) {
        coords <- sf::st_coordinates(gwss_shp_data)
        dp.locat <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
      } else if ("LINESTRING" %in% geometry_type) {
        # 如果是线状几何,可以取中点(或其他逻辑)
        midpoints <- sf::st_line_sample(sf::st_geometry(gwss_shp_data), n = 1)
        coords <- sf::st_coordinates(midpoints)
        dp.locat <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
      } else {
        stop(paste("Unsupported geometry type(s):", paste(geometry_type, collapse = ", ")))
      }
    }, error = function(e) {
      shiny::showNotification(paste("Error extracting coordinates:", e$message), type = "error", duration = NULL)
      shiny::removeModal()
      return(NULL)
    })
    # 检查 dp.locat 是否为空
    if (is.null(dp.locat)) {
      shiny::showNotification("Coordinates could not be extracted from the shapefile.", type = "error", duration = NULL)
      return()
    }
    dp.locat <- na.omit(dp.locat)

    # 检查并生成 FID 列
    if (!"FID" %in% names(gwss_shp_data)) {
      dp.n <- nrow(dp.locat)
      gwss_shp_data$FID <- seq_len(dp.n)
    }

    # 更新进度条到 30%
    shinyWidgets::updateProgressBar(session = session, id = "gwss_progress", value = 30)

    # 转换为 sp::SpatialPointsDataFrame
    spdf <- tryCatch({
      sp::SpatialPointsDataFrame(
        coords = dp.locat,
        data = as.data.frame(gwss_shp_data),  # 转换 sf 对象为 data.frame
        proj4string = sp::CRS("+proj=longlat +datum=WGS84")
      )
    }, error = function(e) {
      shiny::showNotification(paste("Error creating SpatialPointsDataFrame:", e$message), type = "error", duration = NULL)
      shiny::removeModal()
      return(NULL)
    })

    # 更新进度条到 40%
    shinyWidgets::updateProgressBar(session = session, id = "gwss_progress", value = 40)

    # 获取用户选定的变量
    gwss_vars <- as.character(input$gwss_vars)
    # 获取数据框并检查选定列的数据类型
    data_check <- spdf@data[gwss_vars]
    non_numeric_cols <- sapply(data_check, function(x) !is.numeric(x))

    if (any(non_numeric_cols)) {
      non_numeric_vars <- names(data_check)[non_numeric_cols]
      shiny::showNotification(
        paste("Error running GWSS: Non-numeric variables detected -", 
              paste(non_numeric_vars, collapse = ", ")),
        type = "error",
        duration = NULL
      )
      shiny::removeModal()
      return(NULL)
    }
    # 获取选项
    gwss_kernel <- as.character(input$gwss_kernel)
    gwss_adaptive <- as.logical(input$gwss_adaptive)
    # 选择带宽
    # bandwidth <- as.numeric(input$gwss_bandwidth) # 用户输入带宽
    gwss_bw_data$bw <- as.numeric(input$gwss_bandwidth)

    # 更新进度条到 50%
    shinyWidgets::updateProgressBar(session = session, id = "gwss_progress", value = 50)

    # 执行 GWSS 模型
    model <- tryCatch({
      GWmodel::gwss(data = spdf, vars = gwss_vars, bw = gwss_bw_data$bw, kernel = gwss_kernel, adaptive = gwss_adaptive)
    }, error = function(e) {
      shiny::showNotification(paste("Error running GWSS:", e$message), type = "error", duration = NULL)
      shiny::removeModal()
      return(NULL)
    })

    # 模型成功后检查FID列
    if (!is.null(model)) {
      # 获取空间数据框架
      sdf <- model$SDF
      # 检查是否存在FID列
      if (!"FID" %in% names(sdf)) {
        # 获取数据点数
        dp.n <- nrow(sdf@data)
        # sdf@data$FID <- seq_len(dp.n)
        sdf$FID <- seq_len(dp.n)
        # 更新模型结果
        model$SDF <- sdf
      }
    }

    # 函数执行完成后,关闭等待窗口
    # 更新进度条到 100%
    shinyWidgets::updateProgressBar(session = session, id = "gwss_progress", value = 100)
    Sys.sleep(1)  # 等待一秒,确保用户看到完成的进度条
    shiny::removeModal()

    return(model)
  })
  # 更新图层选择控件
  observeEvent(gwss_result(), {
    req(gwss_result())

    # 获取 GWSS 结果
    gwss_result <- gwss_result()
    sdf <- gwss_result$SDF

    # 触发重新渲染 UI 控件
    output$gwss_map_layer <- renderUI({
        # 提取有效图层名称
        layer_names <- names(sdf)
        layer_names <- layer_names[!grepl("_LCV$", layer_names)]
        layer_names <- layer_names[!grepl("^Cov_", layer_names)]
        layer_names <- layer_names[!grepl("^Spearman", layer_names)]
        layer_names <- layer_names[!grepl("FID", layer_names)]

        # 确保至少有一个可用图层
        if (length(layer_names) == 0) {
          return(NULL)  # 没有可用图层时,不渲染 UI
        }

        # 创建下拉框
        selectInput(
            inputId = "gwss_map_layer",
            label = "Select Map Layer to Display",
            choices = layer_names,
            selected = layer_names[1]  # 默认选择第一个图层
        )
    })

    # 动态更新变量选择选项
    output$gwss_columns <- renderUI({
      # 筛选列名,排除不需要的列
      names <- names(sdf)
      names <- names[!grepl("_LCV$", names)]
      names <- names[!grepl("^Cov_", names)]
      names <- names[!grepl("^Spearman", names)]
      names <- names[!grepl("FID", names)]

      # 更新选择框的选项
      selectInput(
        inputId = "gwss_columns",
        label = "Choose Variables",
        choices = names,
        multiple = TRUE,
        selected = names[1]  # 默认不选中任何选项
      )
    })

    # **自动触发地图更新,确保默认图层直接显示**
    observe({
      req(input$gwss_map_layer)  # 确保 gwss_map_layer 不是 NULL
      shinyjs::delay(1000, shinyjs::click("gwss_map_layer_execute"))  
    })
  })
  #--------------------------# Web Map #-------------------------#
  # 更新地图上的图层
  observeEvent(input$gwss_map_layer_execute, {
    req(gwss_result())
    req(input$gwss_map_layer)
    req(gwss_shapefile_data())
    req(input$gwss_color_palette)

    # 每更新一次图层清除www/temp下的音频文件
    prefixes_to_remove <- c("^gwss_map_audio_",
                            "^gwss_map_audio_composite_",
                            "^gwss_map_waveform_",
                            "gwss_map_sound_video.mp4")
    for (prefix in prefixes_to_remove) {
      files_to_remove <- list.files(temp_dir, pattern = prefix, full.names = TRUE)
      www_dir <- file.path(code_dir, "www")
      file_to_remove <- list.files(www_dir, pattern = prefix, full.names = TRUE)
      if (length(files_to_remove) > 0) {
        file.remove(files_to_remove)
      } else if (length(file_to_remove) > 0) {
        file.remove(file_to_remove)}
    }

    # 获取底图map对象
    map <- leaflet::leafletProxy("gwss_mapPlot", session)
    if(!is.null(map)){
      # 获取result和sdf
      result <- gwss_result()
      if(!is.null(result$SDF)){sdf <- result$SDF}else{print("result sdf is null")} 
      # 获取sdf的坐标
      coords_sdf <- sp::coordinates(sdf)
      # 获取图层名称layer_to_plot
      layer_to_plot <- input$gwss_map_layer
      # 获取shp数据
      shp_data <- gwss_shapefile_data()

      library(leaflet.extras)
      library(RColorBrewer)

      # 输出图层选择控件
      if (inherits(shp_data, "sf")) {shp_data <- sf::st_make_valid(shp_data)}
      # 根据几何类型进行不同专题图显示
      if (sf::st_geometry_type(shp_data)[1] == "MULTIPOLYGON" || sf::st_geometry_type(shp_data)[1] == "POLYGON"){
          # 根据用户选择动态设置颜色映射
          selected_palette <- input$gwss_color_palette
          if (selected_palette == "viridis") {
            color_palette <- leaflet::colorNumeric(palette = "viridis", domain = c(
              min(sdf[[layer_to_plot]], na.rm = TRUE),
              max(sdf[[layer_to_plot]], na.rm = TRUE)
            ))
          } else {
            gradient <- color_gradients[[selected_palette]]
            color_palette <- leaflet::colorNumeric(
              palette = c(gradient["low"], gradient["high"]),
              domain = c(min(sdf[[layer_to_plot]], na.rm = TRUE),
                        max(sdf[[layer_to_plot]], na.rm = TRUE))
            )
          }
          map %>%
                  leaflet::clearGroup("Polygons") %>%
                  leaflet::clearGroup("Circles") %>%
                  leaflet::removeControl("Polygons") %>%
                  leaflet::removeControl("Circles") %>%
                  leaflet::clearGroup("Significant") %>%
                  leaflet::removeControl("Significant") %>%
                  # setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 9) %>%
                  leaflet::addPolygons(
                    data = shp_data,
                    fillColor = ~color_palette(sdf[[layer_to_plot]]),
                    color = "black",
                    fillOpacity = 0.4,
                    weight = 0.3,
                    opacity = 0.5,
                    # popup = paste("FID : ",sdf[["FID"]], "<br>",layer_to_plot, " : ",  sdf[[layer_to_plot]]),
                    group = "Polygons",
                    layerId = sdf[["FID"]] 
                  )%>%
                  leaflet::addLegend(
                    position = "bottomright",
                    pal = color_palette,                                # 图例位置
                    values = sdf[[layer_to_plot]],
                    title = layer_to_plot,                              # 图例标题
                    labFormat = labelFormat(prefix = "", suffix = ""),  # 可选:标签格式
                    opacity = 1,                                         # 图例的不透明度
                    layerId = "Polygons"
                  )%>%
                  # 动态计算地图边界
                  leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                            lat1 = min(coords_sdf[, 2]),
                            lng2 = max(coords_sdf[, 1]),
                            lat2 = max(coords_sdf[, 2]))

      } else if (sf::st_geometry_type(shp_data)[1] == "POINT" || sf::st_geometry_type(shp_data)[1] == "MULTIPOINT"){
          # 根据用户选择动态设置颜色映射
          domain <- c(min(sdf[[layer_to_plot]], na.rm = TRUE),
          max(sdf[[layer_to_plot]], na.rm = TRUE))
          selected_palette <- input$gwss_color_palette
          if (selected_palette == "viridis") {
            color_palette <- leaflet::colorNumeric(palette = "viridis", domain = domain)
          } else {
            colors <- color_gradients[[selected_palette]]
            color_palette <- leaflet::colorNumeric(palette = colors, domain = domain)
          }
          # 设置半径大小
          point_radius <- 50
          map %>%
                leaflet::clearGroup("Polygons") %>%
                leaflet::clearGroup("Circles") %>%
                leaflet::removeControl("Polygons") %>%
                leaflet::removeControl("Circles") %>%
                leaflet::clearGroup("Significant") %>%
                leaflet::removeControl("Significant") %>%
                # setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 11) %>%
                leaflet::addCircles(
                          data = sdf,
                          weight = 1,
                          radius = point_radius,
                          color = ~color_palette(sdf[[layer_to_plot]]),
                          fillOpacity = 1,
                          lng = coords_sdf[, 1],
                          lat = coords_sdf[, 2],
                          # popup = paste("FID : ",sdf[["FID"]], "<br>",layer_to_plot, " : ",  sdf[[layer_to_plot]]),
                          opacity = 1,
                          group = "Circles",
                          layerId = sdf[["FID"]])%>%
                leaflet::addLegend(
                  position = "bottomright",
                  pal = color_palette,                                # 图例位置
                  values = sdf[[layer_to_plot]],
                  title = layer_to_plot,                              # 图例标题
                  labFormat = labelFormat(prefix = "", suffix = ""),  # 可选:标签格式
                  opacity = 1,                                         # 图例的不透明度
                  layerId = "Circles"
                )%>%
                # 动态计算地图边界
                leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                          lat1 = min(coords_sdf[, 2]),
                          lng2 = max(coords_sdf[, 1]),
                          lat2 = max(coords_sdf[, 2]))
      }
    }else{print("map is null")}

  })
  # 用 reactiveValues 存储用户点击的点和生成的短音频文件路径
  gwss_rv <- reactiveValues(
    clicked_points = list(),  # 用于存放点击的经纬度(仅连线模式时使用)
    audio_files = character() # 存放每个点击生成的短音频文件路径
  )
  # 点击地图产生音频
  observeEvent(input$gwss_mapPlot_click, {
    req(gwss_result())
    click <- input$gwss_mapPlot_click
    shp_data <- gwss_shapefile_data() %>% 
        sf::st_as_sf() %>% 
        sf::st_make_valid() %>% 
        sf::st_set_crs(4326)
    geom_type <- sf::st_geometry_type(shp_data) %>% 
      as.character() %>% 
      unique() %>% 
      dplyr::first()

    # 选择模式:点击模式直接生成短音频
    if(input$gwss_audio_mode == "click"){
      # 获取必要数据
      gwss_sdf <- gwss_result()$SDF
      layer_to_plot <- input$gwss_map_layer
      
      # 这里假设已将 gwss_sdf 转换为 sf 对象,或直接使用 spatial 包的 nearest feature 方法
      gwss_sdf_sf <- sf::st_as_sf(gwss_sdf)
      click_point <- sf::st_sfc(sf::st_point(c(click$lng, click$lat)), crs = 4326)
      selected_id <- sf::st_nearest_feature(click_point, gwss_sdf_sf)
      selected_row <- gwss_sdf[selected_id, ]
      
      # 生成 popup 内容
      popup_content <- paste("FID :", selected_row$FID, "<br>", 
                            layer_to_plot, ":", round(selected_row[[layer_to_plot]], 4))
      if(geom_type %in% c("POLYGON", "MULTIPOLYGON")) {
        # 找到包含点击点的 Polygon
        selected_feature <- shp_data[sf::st_contains(shp_data, click_point, sparse = FALSE), ]
        if (nrow(selected_feature) == 0) {
          shiny::showNotification("No polygon data found", type = "error")
          return(NULL)
        }

        leaflet::leafletProxy("gwss_mapPlot") %>%
          leaflet::clearPopups() %>%
          # addPopups(lng = click$lng, lat = click$lat, popup = popup_content) %>%
          leaflet::clearGroup("highlighted_features") %>%  # 清除之前的高亮
          leaflet::addPolygons(
            data = selected_feature,
            group = "highlighted_features",
            fillColor = "yellow",
            color = "#FF0000",
            fillOpacity = 0,
            weight = 2,
            highlightOptions = leaflet::highlightOptions(
              weight = 5,
              color = "#FF0000",
              bringToFront = TRUE
              )
            ) %>%
          leaflet::addMarkers(  
                  lng = click$lng, 
                  lat = click$lat, 
                  popup = popup_content,
                  group = "highlighted_features",
                  icon = musicIcon # 使用自定义图标
              )
      }else if(geom_type %in% c("POINT", "MULTIPOINT")) {
        # 找到最近的点
        selected_id <- sf::st_nearest_feature(click_point, shp_data)
        selected_feature <- shp_data[selected_id, ]
        if (nrow(selected_feature) == 0) {
          shiny::showNotification("No point data found", type = "error")
          return(NULL)
        }
        leaflet::leafletProxy("gwss_mapPlot") %>%
          leaflet::clearPopups() %>%
          leaflet::clearGroup("highlighted_features") %>%  # 清除之前的高亮
          # addPopups(lng = click$lng, lat = click$lat, popup = popup_content) %>%
          leaflet::addCircleMarkers(
            data = selected_feature,
            group = "highlighted_features",
            color = "red",
            fillOpacity = 0,
            radius = 8,
            stroke = TRUE,
            weight = 2
          ) %>%
          leaflet::addMarkers( 
                    lng = click$lng, lat = click$lat,
                    group = "highlighted_features",
                    popup = popup_content,
                    icon = musicIcon # 使用自定义图标
                )
      } else {
        shiny::showNotification("The current geometry type is not supported", type = "error")
        return(NULL)
      }
      
      
      # 生成短音频文件
      coeff <- ifelse(is.na(selected_row[[layer_to_plot]]), 0, selected_row[[layer_to_plot]])
      short_filename <- paste0("www/gwss_map_audio_", as.integer(Sys.time()), "_", sample(1:1000, 1), ".wav")
      generate_audio(value = coeff,
                    filename = short_filename,
                    x_min = min(gwss_sdf[[layer_to_plot]], na.rm = TRUE),
                    x_max = max(gwss_sdf[[layer_to_plot]], na.rm = TRUE))
      
      # 更新 audio 控件,播放短音频
      shinyjs::runjs(sprintf("document.getElementById('gwss_map_audio').src='%s'; document.getElementById('gwss_map_audio').play();", short_filename))
      
    } else if(input$gwss_audio_mode == "line") {
      # 连线模式:记录点击点,不直接生成音频
      gwss_rv$clicked_points <- append(gwss_rv$clicked_points, list(c(click$lng, click$lat)))
      # 在地图上添加标记以便确认顺序
      leaflet::leafletProxy("gwss_mapPlot") %>%
        leaflet::addMarkers(
                  lng = click$lng, 
                  lat = click$lat,
                  popup = paste("point", length(gwss_rv$clicked_points)),
                  group = "point",
                  icon = musicIcon 
                  )
    }
  })
  # 处理连线模式下的确认按钮:生成长音频
  observeEvent(input$gwss_confirm_audio, {
    req(length(gwss_rv$clicked_points) > 0)
    # 仅在连线模式时触发检查
    if (input$gwss_audio_mode == "line") {
      # 检查点击点数量是否足够
      if (length(gwss_rv$clicked_points) < 2) {
        shiny::showNotification("Choose at least two points", type = "error")
        return() # 不满足条件时提前退出
      }
    }

    gwss_buffer_length <- as.numeric(input$gwss_buffer_length)
    layer_to_plot <- input$gwss_map_layer

    # 显示自定义模态窗口
    showModal(modalDialog(
      title = "Processing GWSS Audio...",
      tags$div(
        style = "text-align: center;",
        shinyWidgets::progressBar(
          id = "gwss_audio_progress",
          value = 0,
          display_pct = TRUE,
          status = "info",
          striped = TRUE
        )
      ),
      footer = tagList(
        modalButton("Cancel")  # 添加关闭按钮
      ),
      easyClose = FALSE
    ))

    # 1. 获取用户点击的坐标并绘制连线
    clicked_matrix <- do.call(rbind, gwss_rv$clicked_points)
    leaflet::leafletProxy("gwss_mapPlot") %>%
      leaflet::addPolylines(
                  lng = clicked_matrix[,1], 
                  lat = clicked_matrix[,2],
                  color = "red",
                  weight = gwss_buffer_length,
                  opacity = 0.8,
                  group = "point_line") %>%
      leaflet::clearGroup("highlighted_features")  # 清除之前的高亮
    
    # 2. 创建 sf 线对象并确保坐标系统一
    clicked_line <- sf::st_linestring(clicked_matrix) %>%
      sf::st_sfc(crs = 4326) %>%
      sf::st_make_valid()
    line_buffer <- sf::st_buffer(clicked_line, dist = gwss_buffer_length)  # 可调整缓冲区大小
  
    # 3. 处理空间数据
    gwss_sdf <- gwss_result()$SDF
    shp_data <- gwss_shapefile_data() %>% 
      sf::st_as_sf() %>% 
      sf::st_make_valid() %>% 
      sf::st_set_crs(4326)

    # 更新进度条
    shinyWidgets::updateProgressBar(session = session, id = "gwss_audio_progress", value = 10)

    # 4. 转换Spatial对象为sf
    gwss_sdf_sf <- sf::st_as_sf(gwss_sdf) 
    
    # 将gwss计算结果合并到原始shp属性(假设行顺序一致)
    if(nrow(shp_data) == nrow(gwss_sdf_sf)) {
      shp_data_with_gwss <- shp_data %>%
        dplyr::bind_cols(sf::st_drop_geometry(gwss_sdf_sf)) %>%  # 使用正确的属性数据
        sf::st_sf()
    } else {
      shiny::showNotification("The number of data rows is inconsistent", type = "error")
      shiny::removeModal()
      return()
    }

    geom_type <- sf::st_geometry_type(shp_data_with_gwss) %>% 
      as.character() %>% 
      unique() %>% 
      dplyr::first()

    if(geom_type %in% c("POLYGON", "MULTIPOLYGON")) {
      # 精确相交检测
      intersects <- tryCatch({
        sf::st_filter(shp_data_with_gwss, line_buffer)
      }, error = function(e) {
        shiny::showNotification("Fail in intersects", type = "error")
        shiny::removeModal()
        return(NULL)
      })
      
      intersect_features <- intersects
      # **绘制调试图,检查是否有交集**
      plot(sf::st_geometry(shp_data_with_gwss), col = "blue", border = "black", main = "空间要素 vs. 线")
      plot(sf::st_geometry(line_buffer), col = "red", lwd = gwss_buffer_length, add = TRUE)

      leaflet::leafletProxy("gwss_mapPlot") %>%
        leaflet::addPolygons(
          data = intersects,
          group = "highlighted_features",
          color = "#FF0000",
          fillOpacity = 0,
          weight = 2,
          highlightOptions = leaflet::highlightOptions(
            weight = 5,
            color = "#FF0000",
            bringToFront = TRUE
            )
          )
    }else if(geom_type %in% c("POINT", "MULTIPOINT")) {
      # 点处理逻辑
      # intersect_idx  <- st_intersects(gwss_sdf_sf, line_buffer, sparse = FALSE)
      # intersect_features <- gwss_sdf_sf[which(intersect_idx, arr.ind = TRUE), ]
      intersects <- tryCatch({
        sf::st_filter(shp_data_with_gwss, line_buffer)
      }, error = function(e) {
        shiny::showNotification("Fail in intersects", type = "error")
        shiny::removeModal()
        return(NULL)
      })
      intersect_features <- intersects

      if(nrow(intersect_features) == 0) {
        shiny::showNotification("The line does not pass through any features", type = "warning")
        shiny::removeModal()
        return()
      }
      # **绘制调试图,检查是否有交集**
      plot(sf::st_geometry(gwss_sdf_sf), col = "blue", border = "black", main = "空间要素 vs. 线")
      plot(sf::st_geometry(line_buffer), col = "red", lwd = gwss_buffer_length, add = TRUE)

      leaflet::leafletProxy("gwss_mapPlot") %>%
        leaflet::addCircleMarkers(
          data = intersect_features,
          group = "highlighted_features",
          color = "red",
          fillOpacity = 0,
          radius = 8,
          stroke = TRUE,
          weight = 2
        )

    } else {
      shiny::showNotification("The current geometry type is not supported", type = "error")
      shiny::removeModal()
      return()
    }
    
    # 更新进度条
    shinyWidgets::updateProgressBar(session = session, id = "gwss_audio_progress", value = 20)

    # 6. 沿路径排序(确保交点顺序与点击点一致)
    if (nrow(intersect_features) > 0) {
      start_point <- sf::st_sfc(sf::st_point(clicked_matrix[1, ]), crs = 4326)
      intersect_features <- intersect_features %>%
        dplyr::mutate(
          dist_to_start = as.numeric(sf::st_distance(geometry, start_point))
        ) %>%
        dplyr::arrange(dist_to_start)
    }

    # 7. 生成音频
    gwss_rv$audio_files <- character()  # 重置音频存储

    # 更新进度条
    shinyWidgets::updateProgressBar(session = session, id = "gwss_audio_progress", value = 30 )

    # 8. 遍历要素,生成音频
    for (i in seq_len(nrow(intersect_features))) {
      feature <- intersect_features[i, ]
      coeff <- ifelse(is.na(feature[[layer_to_plot]]), 0, feature[[layer_to_plot]])
      short_filename <- paste0("www/gwss_map_audio_", i, ".wav")

      generate_audio(
        value = coeff,
        filename = short_filename,
        x_min = min(gwss_sdf[[layer_to_plot]], na.rm = TRUE),
        x_max = max(gwss_sdf[[layer_to_plot]], na.rm = TRUE)
      )

      gwss_rv$audio_files <- c(gwss_rv$audio_files, short_filename)

      # 更新进度条
      shinyWidgets::updateProgressBar(session = session, id = "gwss_audio_progress", value = 30 + (i/nrow(intersect_features)) * 30)
    }
    
    # 9. 合成所有短音频文件为一个长音频
    composite_filename <- paste0("www/gwss_map_audio_composite.wav")
    concatenate_audio(gwss_rv$audio_files, composite_filename)
    # 确保 FFmpeg 使用合成后的音频
    sound_road <- composite_filename  # 这里修正

    # 提取系数数据
    ranges <- seq_len(nrow(intersect_features))  # 用于 X 轴
    coeff_values <- intersect_features[[layer_to_plot]]  # 提取回归系数
    ranges_length <- length(ranges)
    # 生成初始曲线图
    waveform_plot <- ggplot2::ggplot(data.frame(x = ranges, y = coeff_values), ggplot2::aes(x = x, y = y)) +
      ggplot2::geom_point(color = "blue", size = 2) +  
      ggplot2::geom_smooth(method = "loess", color = "blue", span = 0.5, se = FALSE) +  
      ggplot2::theme_minimal() +
      ggplot2::theme(
        panel.background = ggplot2::element_rect(fill = "white", color = NA),  
        plot.background = ggplot2::element_rect(fill = "white", color = NA)  
      ) +
      ggplot2::ggtitle("Coefficient Variation Over Features") +
      ggplot2::xlab("Feature Index") + 
      ggplot2::ylab("Coefficient Value")

    # 逐帧生成视频
    waveform_images <- c()
    for (i in seq_along(ranges)) {
      frame_plot <- waveform_plot +
        ggplot2::geom_vline(xintercept = ranges[i], color = "red", linetype = "dashed", size = 1) +
        ggplot2::ggtitle(paste("Feature:", i, "  Coefficient:", round(coeff_values[i], 4)))

      frame_image <- file.path(temp_dir, paste0("gwss_map_waveform_", i, ".png"))
      ggplot2::ggsave(frame_image, frame_plot, width = 6, height = 4, dpi = 150)
      waveform_images <- c(waveform_images, frame_image)

      shinyWidgets::updateProgressBar(session, "gwss_video_progress", value = 60 + (i / ranges_length) * 30)
    }

    # 计算音频时长
    audio_info <- tuneR::readWave(composite_filename)
    total_frames <- length(waveform_images)
    total_audio_duration <- length(audio_info@left) / audio_info@samp.rate
    # 计算帧率,使得视频时长与音频一致
    frame_rate <- total_frames / total_audio_duration
    # 确保 framerate 合理(避免异常)
    if (frame_rate < 1) frame_rate <- 1
    if (frame_rate > 30) frame_rate <- 30  # 限制在 1-30 fps,防止帧率过大或过小
    # 生成视频
    waveform_video <- file.path(temp_dir, "gwss_map_waveform_video.mp4")
    av::av_encode_video(
      input = waveform_images,
      output = waveform_video,
      framerate = frame_rate,
      codec = "libx264",
      vfilter = "scale=720:720,format=yuv420p",
      audio = NULL,
      verbose = TRUE
    )

    # 合成音视频
    sound_video <- file.path("www", "gwss_map_sound_video.mp4")
    # sound_road <- gwss_rv$audio_files  # 获取之前生成的音频
    ffmpeg_command <- paste(
      "ffmpeg",
      "-y", 
      "-i", shQuote(waveform_video),
      "-i", shQuote(sound_road),
      "-ac 2 -c:v libx264 -c:a aac -b:a 192k -strict experimental",
      shQuote(sound_video)
    )
    ffmpeg_result <- tryCatch({
      system(ffmpeg_command, intern = FALSE)
    }, error = function(e) {
      cat("FFmpeg command failed:", e$message, "\n")
      NA
    })
    if (!is.na(ffmpeg_result) && ffmpeg_result != 0) {
      shiny::showNotification("The command execution of FFmpeg failed.Please check (1) whether ffmpeg is installed (2) whether the system path is configured.", type = "error")
      shiny::removeModal()
      return()
    }
    cat(format(Sys.time(), "%Y-%m-%d %H:%M:%S"), " 合成波形图和音频完成,文件保存为: ", sound_video, "\n")

    # 在地图左下角显示视频
    leaflet::leafletProxy("gwss_mapPlot") %>%
      leaflet::removeControl( layerId = "map_video_control") %>%
      leaflet::addControl(
         HTML(sprintf(
          '<video id="gwss_video_player" width="300" autoplay controls>
            <source src="%s" type="video/mp4">
            Your browser does not support the video tag.
          </video>
          <audio id="gwss_audio" src="%s"></audio>
          <script>
            document.getElementById("gwss_video_player").onplay = function() {
              var audio = document.getElementById("gwss_audio");
              if (audio) {
                audio.play();
              }
            };
            document.getElementById("gwss_video_player").onpause = function() {
              var audio = document.getElementById("gwss_audio");
              if (audio) {
                audio.pause();
              }
            };
            document.getElementById("gwss_video_player").ontimeupdate = function() {
              var audio = document.getElementById("gwss_audio");
              if (audio) {
                audio.currentTime = this.currentTime;
              }
            };
          </script>', sound_video, composite_filename
        )),
        position = "bottomleft",
        layerId = "map_video_control" 
      )

    # 设置音频源
    # shinyjs::runjs(sprintf("document.getElementById('gwss_map_audio').src='%s';", composite_filename))

    # 重置记录,便于下次操作
    gwss_rv$clicked_points <- list()
    gwss_rv$audio_files <- character()

    shinyWidgets::updateProgressBar(session = session, id = "gwss_audio_progress", value = 100)
    Sys.sleep(1)
    shiny::removeModal()
  })
  # 处理连线的图标
  observeEvent(input$gwss_confirm_audio_clear, {
    # 重置记录,便于下次操作
    gwss_rv$clicked_points <- list()
    gwss_rv$audio_files <- character()

    leaflet::leafletProxy("gwss_mapPlot") %>%
      leaflet::removeControl( layerId = "map_video_control") %>%
      leaflet::clearGroup("point") %>%
      leaflet::clearGroup("point_line") %>%
      leaflet::clearGroup("highlighted_features")  # 清除高亮
  })
  #--------------------------# Table View #-------------------------#
  # 显示汇总表
  output$gwss_summaryTable <- DT::renderDataTable({
    result <- gwss_result()
    sdf <- as.data.frame(result$SDF)
    DT::datatable(
      sdf,
      extensions = 'Buttons',  # 关键要素1:声明使用扩展
      options = list(
        pageLength = 10,        # 默认每页显示10条
        lengthMenu = c(10, 15, 20, 25),  # 每页显示条目数选项
        searching = TRUE,       # 启用搜索框
        scrollX = TRUE,          # 启用水平滚动
        dom = 'Blfrtip',          # 控制界面元素布局
        buttons = list(       # 关键要素3:按钮嵌套配置
          list(extend = 'copy', text = 'Copy'),
          list(extend = 'csv', text = 'Export CSV'),
          list(extend = 'excel', text = 'Export Excel')
        )
      ),
      rownames = FALSE,         # 不显示行号
      class = "'display nowrap hover" 
    )
  })
  #--------------------------# Summary Information #-------------------------#
  # 显示模型诊断信息
  output$gwss_modelDiagnostics <- renderPrint({
    gwss_result()
  })
  #--------------------------# Multiple Visualizations #-------------------------#
  # 动态生成图片
  observe({
    req(input$gwss_columns)  # 确保用户已经选择了变量
    req(input$gwss_bandwidth)
    req(input$gwss_color_palette)  # 确保用户已经选择了色阶

    # 显示等待窗口
    showModal(modalDialog(
      title = "Please wait",
      "The GWSS result is drawing...",
      easyClose = FALSE
    ))

    bw <- round(as.numeric(gwss_bw_data$bw), 3)

    shp_data <- gwss_shapefile_data()
    if (!inherits(shp_data, "sf")) {shp_data <- sf::st_as_sf(shp_data)}

    # 获取 GWSS 结果
    gwss_result <- gwss_result()
    sdf <- gwss_result$SDF

    # 设置高分辨率参数
    dpi <- 100  # 设置DPI值
    width_px <- 1500  # 设置宽度像素
    height_px <- 1500 # 设置高度像素

    # 遍历用户选择的列并动态生成图片
    selected_columns <- input$gwss_columns
    for (col in selected_columns) {
      local({
        column_name <- col  # 需要使用 local() 避免循环中的闭包问题
        # 动态创建 UI 输出控件
        output[[paste0("plot_", column_name)]] <- renderPlot({

          # 确保列存在于数据框中
          req(column_name %in% names(sdf))

          # 创建专题图
          target_data <- shp_data
          target_data$value <- sdf[[column_name]]  # 将数据加入到 sf 对象中,以便于绘制专题图

          # 判断数据类型,动态选择绘图样式
          geom_type <- unique(sf::st_geometry_type(target_data))
          # 根据选择的色阶动态设置颜色
          gradient <- color_gradients[[input$gwss_color_palette]]

          if ("POINT" %in% geom_type || "MULTIPOINT" %in% geom_type) {  # 点数据:使用 color 映射
              if (input$gwss_color_palette == "viridis") {
                ggplot2::ggplot(target_data) +
                  ggplot2::geom_sf(ggplot2::aes(color = value)) +
                  ggplot2::scale_color_viridis_c() +  # 使用 Viridis 色阶
                  ggplot2::labs(title = paste0(column_name, "  (Bandwidth: ", bw, ")")) +
                  ggspatial::annotation_north_arrow(
                    location = "tl",  # 左上角
                    which_north = "true",
                    pad_x = ggplot2::unit(0.3, "cm"),
                    pad_y = ggplot2::unit(0.3, "cm"),
                    style = ggspatial::north_arrow_fancy_orienteering()
                  ) +
                  ggspatial::annotation_scale(
                    location = "bl",  # 左下角
                    width_hint = 0.3
                  ) +
                  ggplot2::theme(
                    panel.background = ggplot2::element_rect(fill = "white", color = NA),
                    panel.grid = ggplot2::element_blank(),
                    plot.background = ggplot2::element_rect(fill = "white", color = NA)
                  )
              } else {
                ggplot2::ggplot(target_data) +
                  ggplot2::geom_sf(ggplot2::aes(color = value)) +
                  ggplot2::scale_color_gradient(low = gradient["low"], high = gradient["high"]) +
                  ggplot2::labs(title = paste0(column_name, "  (Bandwidth: ", bw, ")")) +
                  ggspatial::annotation_north_arrow(
                    location = "tl",  # 左上角
                    which_north = "true",
                    pad_x = ggplot2::unit(0.3, "cm"),
                    pad_y = ggplot2::unit(0.3, "cm"),
                    style = ggspatial::north_arrow_fancy_orienteering()
                  ) +
                  ggspatial::annotation_scale(
                    location = "bl",  # 左下角
                    width_hint = 0.3
                  ) +
                  ggplot2::theme(
                    panel.background = ggplot2::element_rect(fill = "white", color = NA),
                    panel.grid = ggplot2::element_blank(),
                    plot.background = ggplot2::element_rect(fill = "white", color = NA)
                  )
              }
          } else if ("POLYGON" %in% geom_type || "MULTIPOLYGON" %in% geom_type) {  # 面数据:使用 fill 映射
            if (input$gwss_color_palette == "viridis") {
              ggplot2::ggplot(target_data) +
                ggplot2::geom_sf(ggplot2::aes(fill = value)) +
                ggplot2::scale_fill_viridis_c() +  # 使用 Viridis 色阶
                ggplot2::labs(title = paste0(column_name, "  (Bandwidth: ", bw, ")")) +
                ggspatial::annotation_north_arrow(
                    location = "tl",  # 左上角
                    which_north = "true",
                    pad_x = ggplot2::unit(0.3, "cm"),
                    pad_y = ggplot2::unit(0.3, "cm"),
                    style = ggspatial::north_arrow_fancy_orienteering()
                  ) +
                ggspatial::annotation_scale(
                    location = "bl",  # 左下角
                    width_hint = 0.3
                  ) +
                ggplot2::theme(
                  panel.background = ggplot2::element_rect(fill = "white", color = NA),
                  panel.grid = ggplot2::element_blank(),
                  plot.background = ggplot2::element_rect(fill = "white", color = NA)
                )
            } else {
              ggplot2::ggplot(target_data) +
                ggplot2::geom_sf(ggplot2::aes(fill = value)) +
                ggplot2::scale_fill_gradient(low = gradient["low"], high = gradient["high"]) +
                ggplot2::labs(title = paste0(column_name, "  (Bandwidth: ", bw, ")")) +
                ggspatial::annotation_north_arrow(
                    location = "tl",  # 左上角
                    which_north = "true",
                    pad_x = ggplot2::unit(0.3, "cm"),
                    pad_y = ggplot2::unit(0.3, "cm"),
                    style = ggspatial::north_arrow_fancy_orienteering()
                  ) +
                ggspatial::annotation_scale(
                    location = "bl",  # 左下角
                    width_hint = 0.3
                  ) +
                ggplot2::theme(
                  panel.background = ggplot2::element_rect(fill = "white", color = NA),
                  panel.grid = ggplot2::element_blank(),
                  plot.background = ggplot2::element_rect(fill = "white", color = NA)
                )
            }
          } else {
            # 如果既不是点也不是面,抛出警告
            shiny::showNotification("Unsupported geometry type for plotting.", type = "error")
            shiny::removeModal()
            return()
          }        
        })
      })
    }

    # # 动态图片生成完成后关闭等待窗口
    shiny::removeModal()
  })
  # 动态显示图片
  output$gwss_Plot <- renderUI({
    req(input$gwss_columns)  # 确保用户已经选择了变量

    # 每行的图片数量
    images_per_row <- min(2, length(input$gwss_columns))  # 每行最多显示 2 张图片

    plot_outputs <- lapply(seq_along(input$gwss_columns), function(i) {
      column_name <- input$gwss_columns[i]

      # 使用 column 动态调整宽度,每行最多显示 images_per_row 张图片
      column(
        width = 12 / images_per_row,  # 动态设置列宽(12 栅格系统)
        plotOutput(outputId = paste0("plot_", column_name), height = "500px", width = "100%")
      )
    })

    # 将图片控件布局在 fluidRow 中
    do.call(fluidRow, plot_outputs)
  })
  #--------------------------# Video #-------------------------#
  # 更新范围图层选项
  gwss_range_layer <- eventReactive(input$gwss_vars, {
    req(input$gwss_vars)
    gwss_vars <- input$gwss_vars

    names <- gwss_vars

    # 初始化choices列表
    choices <- list()
    suffixes <- c("_LM", "_LSD", "_LVar", "_LSKe")  # 后缀列表

    # 动态添加回归系数选项
    for (var in names) {
      for (suffix in suffixes) {
        choice_name <- paste0(var, suffix)  # 生成选项
        choices[[choice_name]] <- choice_name
      }
    }

    # 设置默认选项(第一个变量加第一个后缀作为默认选项)
    default_choice <- paste0(names[1], suffixes[1])

    # 返回choices列表和默认选项
    list(choices = choices, default_choice = default_choice)
  }, ignoreNULL = FALSE)
  # 监听范围图层选项的变化并更新图层选项
  observeEvent(gwss_range_layer(), {
    layer_info <- gwss_range_layer()
    updateSelectInput(session, "gwss_range_layer", choices = layer_info$choices, selected = layer_info$default_choice)
  })
  # 步长
  observeEvent(input$gwss_range, {
    min_range <- as.integer(input$gwss_range[1])
    max_range <- as.integer(input$gwss_range[2])
    updateNumericInput(session, "gwss_step_length", max = max_range-min_range)
  })
  # 生成视频
  observeEvent(input$gwss_video_button, {
    # 保证参数存在
    req(input$gwss_shapefile)
    req(input$gwss_vars)
    req(input$gwss_kernel, input$gwss_adaptive)
    req(input$gwss_color_palette)

    # 带宽范围
    range_min <- input$gwss_range[1]
    range_max <- input$gwss_range[2]
    # 步长
    step_length <- input$gwss_step_length
    # 颜色盘
    color_palette <- input$gwss_color_palette

    # 显示自定义模态窗口
    showModal(modalDialog(
      title = "Processing GWSS Video...",
      tags$div(
        style = "text-align: center;",
        shinyWidgets::progressBar(
          id = "gwss_video_progress",
          value = 0,
          display_pct = TRUE,
          status = "info",
          striped = TRUE
        )
      ),
      footer = tagList(
        modalButton("Cancel")  # 添加关闭按钮
      ),
      easyClose = FALSE
    ))
    # 更新进度条5%
    shinyWidgets::updateProgressBar(session = session, id = "gwss_video_progress", value = 5)
    cat("0% ",format(Sys.time(), "%Y-%m-%d %H:%M:%S"),"\n")
    time1 <- as.POSIXct(format(Sys.time(), "%Y-%m-%d %H:%M:%S"))  # 第一个时间点

    # 在开始生成前,确保删除 temp_dir/www_dir 文件夹下以特定前缀开头的所有文件
    prefixes_to_remove <- c("^gwss_image_video_", 
                            "gwss_image_video.mp4",
                            "^gwss_sound_audio_",
                            "^gwss_waveform_",
                            "gwss_waveform_video.mp4",
                            "gwss_waveform_overview.png",
                            "^gwss_sound_video_",
                            "gwss_sound_video.mp4",
                            "gwss_final_video.mp4")
    for (prefix in prefixes_to_remove) {
      files_to_remove <- list.files(temp_dir, pattern = prefix, full.names = TRUE)
      www_dir <- file.path(code_dir, "www")
      file_to_remove <- list.files(www_dir, pattern = prefix, full.names = TRUE)
      if (length(files_to_remove) > 0) {
        file.remove(files_to_remove)
      } else if (length(file_to_remove) > 0) {
        file.remove(file_to_remove)}
    }

    # 获取实际的数据和坐标
    shp_data <- gwss_shapefile_data()
    spdf <- as(shp_data,"Spatial")
    # 获取选项
    kernel <- as.character(input$gwss_kernel)
    adaptive <- as.logical(input$gwss_adaptive)
    # 获取变量Vars
    vars <- as.list(input$gwss_vars)
    # 获取变量
    range_layer <- as.character(input$gwss_range_layer)
    # 确保数据是 sf 对象
    cp_shp_data <- NULL
    if (!inherits(shp_data, "sf")) {shp_data <- sf::st_as_sf(shp_data)}
    # 确保数据中有有效的几何列
    if (is.null(sf::st_geometry(shp_data))){ 
      shiny::showNotification("shp_data does not contain geometry.", type = "error")
      shiny::removeModal()
      return()
    }
     # 计算模型结果
    target_shp_data <- shp_data
  
    # 频率数组
    intercept_vars <- c()
    # 根据步长生成序列
    ranges <- seq(
      from = range_min,  # 起始值
      to = range_max,    # 结束值
      by = step_length   # 步长
    )
    ranges_length <- length(ranges)
    # 更新进度条5%
    shinyWidgets::updateProgressBar(session = session, id = "gwss_video_progress", value = 5)

    cat(format(Sys.time(),"%Y-%m-%d %H:%M:%S")," 开始生成专题图图片...","\n")
    # 专题图视频
    # 几何类型计算
    geometry_type <- sf::st_geometry_type(target_shp_data)[1]
    is_polygon <- geometry_type == "MULTIPOLYGON" || geometry_type == "POLYGON"
    is_point <- geometry_type == "POINT" || geometry_type == "MULTIPOLYGON"
    # 确定全局范围:计算所有 range_layer 数据的最小值和最大值
    global_min <- Inf
    global_max <- -Inf
    range_now_fir <- 1
    for (range in ranges) {
      bandwidth <- range
      # GWSS 模型计算
      model <- GWmodel::gwss(data = spdf, vars = vars, kernel = kernel, adaptive = adaptive, bw = bandwidth)
      # 提取模型结果
      sdf <- model$SDF
      range_values <- sdf[[range_layer]]
      global_min <- min(global_min, min(range_values, na.rm = TRUE))
      global_max <- max(global_max, max(range_values, na.rm = TRUE))
      
      # 更新进度条
      shinyWidgets::updateProgressBar(
        session = session,
        id = "gwss_video_progress",
        value = as.integer(5 + (range_now_fir / ranges_length) * 15)
      )
      range_now_fir <- range_now_fir + 1
    }
    # 确保全局范围有效
    if (global_min == Inf || global_max == -Inf) {
      shiny::showNotification("No valid range values found for plotting.", type = "error")
      shiny::removeModal()
      return()
    }
    # 扩展比例
    padding_ratio <- 0.1  # 扩展范围的比例(10%)
    global_min <-  min(range_values)
    global_max <- max(range_values)
    print(paste0("global_min",global_min,"global_max",global_max))
    # 调整全局最小值和最大值
    range_span <- global_max - global_min
    extended_min <- global_min - range_span * padding_ratio
    extended_max <- global_max + range_span * padding_ratio
    print(paste0("extended_min",extended_min,"extended_max",extended_max))
    fixed_breaks <- seq(global_min, global_max, length.out = 7)  # 创建7份区间
    # 图片主循环
    range_now <- 1
    for (range in ranges) {
      bandwidth <- range
      # GWSS 模型计算
      model <- GWmodel::gwss(data = spdf, vars = vars, kernel = kernel, adaptive = adaptive, bw = bandwidth)
      
      # 提取模型结果
      sdf <- model$SDF
      result_Intercept <- as.list(sdf[[range_layer]])
      result_Intercept_vector <- unlist(result_Intercept)
      intercept_vars <- c(intercept_vars, var(result_Intercept_vector, na.rm = TRUE))

      # 绘图
      target_shp_data$value <- sdf[[range_layer]]  # 添加绘图列
      output_path <- file.path(temp_dir, paste0("gwss_image_video_", range, ".png"))
      generate_plot(target_shp_data, range_layer, bandwidth, output_path,color_palette,extended_min, extended_max,fixed_breaks)

      # 更新进度条
      shinyWidgets::updateProgressBar(
        session = session,
        id = "gwss_video_progress",
        value = as.integer(20 + (range_now / ranges_length) * 60)
      )
      range_now <- range_now + 1
    }
    cat(format(Sys.time(),"%Y-%m-%d %H:%M:%S")," 专题图图片生成完成","\n")

    cat(format(Sys.time(),"%Y-%m-%d %H:%M:%S")," 开始生成波形图和音频...","\n")
    # --- 音频参数配置 ---
    sampling_rate <- 44100  # 采样率
    duration_per_note <- 0.3  # 每个音符的持续时间(秒)
    note_samples <- duration_per_note * sampling_rate  # 每个音符样本数

    # --- 音色控制参数 ---
    waveform_ratio <- c(0.7, 0.2, 0.1)  # 基波/二次谐波/三次谐波振幅比例 (增大谐波比例(如 c(0.5,0.3,0.2))会让音色更尖锐)
    adsr <- c(attack=0.05,   # 起音时间比例(0-1)  增大 attack 会产生渐强的起始效果
              decay=0.1,     # 衰减时间比例
              sustain=0.6,   # 延音电平(0-1)
              release=0.15)  # 释音时间比例   增加 release 会让音符结束时有更长的衰减尾音
    filter_cutoff <- 0.4          # 低通滤波器截止频率(比例,0-1对应0-Nyquist) 范围 0.1-0.8,值越小高频衰减越明显
    loudness_factor <- 2          # 响度增益因子

    # --- 频率映射(保持原逻辑)---
    intercept_vars <- na.omit(intercept_vars)
    x_min <- min(intercept_vars)
    x_max <- max(intercept_vars)
    a <- (1000 - 440) / (x_max - x_min)
    b <- 440 - a * x_min
    Intercept_values <- round(a * intercept_vars + b)

    # --- 定义ADSR包络函数 ---
    apply_envelope <- function(signal, adsr_params) {
      n <- length(signal)
      attack_len <- floor(adsr_params["attack"] * n)
      decay_len <- floor(adsr_params["decay"] * n)
      release_len <- floor(adsr_params["release"] * n)
      sustain_len <- n - attack_len - decay_len - release_len
      
      # 构造包络曲线
      envelope <- c(
        seq(0, 1, length.out = attack_len),                     # 起音
        seq(1, adsr_params["sustain"], length.out = decay_len), # 衰减
        rep(adsr_params["sustain"], sustain_len),               # 延音
        seq(adsr_params["sustain"], 0, length.out = release_len) # 释音
      )
      
      # 确保长度匹配
      length(envelope) <- n  # 自动截断多余部分
      return(signal * envelope)
    }
    
    # --- 生成复合波形 ---
    audio_signal <- do.call(c, lapply(Intercept_values, function(freq) {
      # 生成时间序列
      t <- seq(0, duration_per_note, length.out = note_samples)
      # 方波生成(核心波形)
      square_wave <- ifelse(sin(2 * pi * freq * t) > 0, 1, -1)
      # 谐波叠加
      harmonic1 <- 0.3 * sin(2 * pi * 2 * freq * t)   # 二次谐波
      harmonic2 <- 0.2 * sin(2 * pi * 3 * freq * t)   # 三次谐波
      # 混合波形
      mixed_wave <- waveform_ratio[1] * square_wave + 
                    waveform_ratio[2] * harmonic1 + 
                    waveform_ratio[3] * harmonic2
      # 应用包络
      apply_envelope(mixed_wave, adsr)
    }))

    # --- 后处理 ---
    # 低通滤波器(软化高频)
    bf <- signal::butter(4, filter_cutoff, type = "low")  # 4阶巴特沃斯低通
    audio_filtered <- signal::filtfilt(bf, audio_signal)

    # 规范化处理
    audio_normalized <- audio_filtered / max(abs(audio_filtered)) * loudness_factor
    audio_normalized[audio_normalized > 1] <- 1   # 硬限幅
    audio_normalized[audio_normalized < -1] <- -1

    # --- 生成Wave对象 ---
    audio_wave <- tuneR::Wave(
      left = as.integer(audio_normalized * 32767),
      right = as.integer(audio_normalized * 32767),
      samp.rate = sampling_rate,
      bit = 24
    )

    # 保存为WAV文件
    sound_road <- file.path(temp_dir,paste0("gwss_sound_audio_", range_min, "_", range_max, ".wav"))
    tuneR::writeWave(audio_wave, sound_road)
    cat(format(Sys.time(),"%Y-%m-%d %H:%M:%S")," 音频文件已保存为",sound_road,"\n")

    # 音频波形图
    # 生成总的点图并连成曲线图
    # y为该带宽下的总体系数方差
    waveform_plot <- ggplot2::ggplot(data.frame(x = ranges, y = intercept_vars), ggplot2::aes(x = x, y = y)) +
      ggplot2::geom_point(color = "blue", size = 2) +  # 添加离散点
      ggplot2::geom_smooth(method = "loess", color = "blue", span = 0.5, se = FALSE) +  # 平滑曲线
      ggplot2::theme_minimal() +
      ggplot2::theme(
        panel.background = ggplot2::element_rect(fill = "white", color = NA),  # 设定绘图区域背景为白色
        plot.background = ggplot2::element_rect(fill = "white", color = NA)  # 设定整体背景为白色
      ) +
      ggplot2::ggtitle("Frequency Variation Over Ranges") +
      ggplot2::xlab("Ranges") + 
      ggplot2::ylab("Intercept Values")

    # 保存总波形图
    ggplot2::ggsave(file.path(temp_dir, "gwss_waveform_overview.png"), waveform_plot, width = 6, height = 4, dpi = 150)
    # 生成逐帧图片
    i = 1
    for (range in ranges) {
      single_waveform_plot <- waveform_plot +
        ggplot2::geom_vline(xintercept = range, color = "red", linetype = "dashed", size = 1) +
        ggplot2::ggtitle(paste("Range:", range, "  Variance of variable coefficients:", as.character(round(intercept_vars[i],4))))

      waveform_image <- file.path(temp_dir, paste0("gwss_waveform_", as.character(range), ".png"))
      ggplot2::ggsave(waveform_image, single_waveform_plot, width = 6, height = 4, dpi = 150)
      shinyWidgets::updateProgressBar(
        session = session,
        id = "gwss_video_progress",
        value = as.integer(80 + (i / ranges_length) * 15)
      )
      i = i + 1
    }
    waveform_images <- file.path(temp_dir, paste0("gwss_waveform_", as.character(ranges), ".png"))
    waveform_video <- file.path(temp_dir, "gwss_waveform_video.mp4")
    av::av_encode_video(
      input = waveform_images,
      output = waveform_video,
      framerate = 10,
      codec = "libx264",
      vfilter = "scale=720:720,format=yuv420p",  # 强制使用兼容像素格式
      audio = NULL,
      verbose = TRUE  # 显示详细日志
    )
    cat(format(Sys.time(),"%Y-%m-%d %H:%M:%S")," 波形图文件已保存为",waveform_video,"\n")

    # 确保总音频时长与图片生成的视频时长一致  
    # 计算音频总时长
    audio_duration <- length(audio_signal) / sampling_rate  # 音频总时长 (秒)
    # 确保视频帧率适配音频时长
    # 图片数量(即范围跨度)
    num_images <- length(waveform_images)
    # 动态计算帧率,使视频总时长与音频时长一致
    framerate <- num_images / audio_duration
    # 打印调试信息
    cat("音频时长:", audio_duration, "秒\n")
    cat("图片数量:", num_images, "\n")
    cat("视频帧率:", framerate, "帧/秒\n")

    # 专题图视频(无音频)
    image_files <- file.path(temp_dir, paste0("gwss_image_video_", ranges, ".png"))
    image_video <- file.path("www", "gwss_image_video.mp4")
    av::av_encode_video(
      input = image_files,
      output = image_video,
      framerate = framerate,
      codec = "libx264",
      vfilter = "scale=720:720,format=yuv420p",  # 强制使用兼容像素格式
      audio = NULL,
      verbose = TRUE  # 显示详细日志
    )
    # 波形图和音频
    cat(format(Sys.time(),"%Y-%m-%d %H:%M:%S")," 开始合成波形图和音频...","\n")
    video_file <- file.path(temp_dir, paste0("gwss_sound_video_", range_min, "_", range_max, ".mp4"))
    av::av_encode_video(
      input = waveform_images,
      output = video_file,
      framerate = framerate,
      codec = "libx264",
      vfilter = "scale=720:720,format=yuv420p",  # 强制使用兼容像素格式
      audio = NULL,
      verbose = TRUE  # 显示详细日志
    )
    # 使用FFmpeg合成波形图和音频
    sound_video <- file.path("www", "gwss_sound_video.mp4")
    # 合成音视频
    ffmpeg_command <- paste(
      "ffmpeg",
      "-y", # 添加 -y 强制覆盖文件
      "-i", shQuote(video_file),
      "-i", shQuote(sound_road),
      "-ac 2 -c:v libx264 -c:a aac -b:a 192k -strict experimental",
      shQuote(sound_video)
    )
    # 调用系统命令运行 FFmpeg
    ffmpeg_result <- tryCatch({
      system(ffmpeg_command, intern = FALSE)
    }, error = function(e) {
      cat("FFmpeg command failed:", e$message, "\n")
      NA  # 返回一个默认值
    })
    if (!is.na(ffmpeg_result) && ffmpeg_result != 0) {
      shiny::showNotification("The command execution of FFmpeg failed.Please check (1) whether ffmpeg is installed (2) whether the system path is configured.", type = "error")
      shiny::removeModal()
      return()
    }
    # 打印合成结果
    cat(paste0(format(Sys.time(),"%Y-%m-%d %H:%M:%S")," 合成波形图和音频完成,文件保存为: ", sound_video, "\n"))
    time2 <- as.POSIXct(format(Sys.time(),"%Y-%m-%d %H:%M:%S"))  # 第二个时间点
    time_diff <- difftime(time2, time1, units = "mins")  # 单位:分钟
    print(paste0("波形图和音频合成时间:", time_diff," mins"))

    # 最终视频
    cat(format(Sys.time(),"%Y-%m-%d %H:%M:%S")," 开始合成最终视频文件已保存为","\n")
    final_video <- file.path("www", "gwss_final_video.mp4")
    overlay_filter <- "[1:v]scale=200:150[ovrl];[0:v][ovrl]overlay=W-w-10:H-h-10"
    # "[1:v]scale=200:150[ovrl]":将小视频缩放到200x150的大小,并将其输出到一个名为ovrl的临时虚拟视频。
    # "[0:v][ovrl]overlay=W-w-10:10":将主视频和小视频叠加在一起。W-w-10表示小视频在主视频的右上角,距离右边和上边各10像素。
    # 左上角:overlay=10:10
    # 左下角:overlay=10:H-h-10
    # 右下角:overlay=W-w-10:H-h-10
    # 使用 FFmpeg 合并两个视频(水平分屏)
    ffmpeg_combine <- paste(
      "ffmpeg -y",
      "-i", shQuote(image_video),
      "-i", shQuote(sound_video),
      "-filter_complex", shQuote(overlay_filter),
      "-c:v libx264",
      shQuote(final_video)
    )
    # 执行命令并检查结果
    tryCatch({
      exit_code <- system(ffmpeg_combine, intern = FALSE, ignore.stderr = FALSE)
  
      # 防御性处理:确保 exit_code 是数值类型
      if (is.null(exit_code) || is.na(exit_code)) {
        shiny::showNotification("The command execution of FFmpeg failed.Please check (1) whether ffmpeg is installed (2) whether the system path is configured.", type = "error")
        shiny::removeModal()
        return()
      }
      
      if (exit_code != 0) {
        shiny::showNotification(paste0("FFmpeg exited with code ", exit_code))
        shiny::removeModal()
        return()
      }
      
      # 显式检查文件路径
      final_video_abs <- normalizePath(final_video, mustWork = FALSE)
      if (!file.exists(final_video_abs)) {
        shiny::showNotification(paste0("最终视频文件未生成: ", final_video_abs))
      } else {
        cat("最终视频路径验证成功:", final_video_abs, "\n")
      }
    }, error = function(e) {
      cat("!!! 最终视频合成失败:", e$message, "\n")
      shinyalert::shinyalert("致命错误", paste("视频合成失败:", e$message), type = "error")
      shiny::removeModal()  # 强制关闭进度窗口
      return()
    })

    cat(format(Sys.time(),"%Y-%m-%d %H:%M:%S")," 最终视频文件已保存为",final_video,"\n")
    time3 <- as.POSIXct(format(Sys.time(),"%Y-%m-%d %H:%M:%S"))  # 第三个时间点
    time_diff2 <- difftime(time3, time2, units = "mins")  # 单位:分钟
    print(paste0("最后视频合成时间:", time_diff2," mins"))

    # 函数执行完成后,关闭等待窗口
    shinyWidgets::updateProgressBar(session = session, id = "gwss_video_progress", value = 100)
    Sys.sleep(1)
    shiny::removeModal()

    cat("检查最终视频文件是否存在:", final_video, "\n")
    cat("文件是否存在:", file.exists(final_video), "\n")
    cat("文件大小:", file.size(final_video), "字节\n")

    # 输出视频控件
    output$gwss_image_video <- renderUI({
      timestamp <- as.integer(Sys.time())
      tags$video(
          src = paste0("gwss_image_video.mp4?", timestamp),
          controls = TRUE,
          width = "300px"
      )
    })
    output$gwss_sound_video <- renderUI({
      timestamp <- as.integer(Sys.time())
      tags$video(
          src = paste0("gwss_sound_video.mp4?", timestamp),
          controls = TRUE,
          width = "300px"
      )
    })
    output$gwss_final_video <- renderUI({
      timestamp <- as.integer(Sys.time())
      tags$video(
          src = paste0("gwss_final_video.mp4?", timestamp),
          controls = TRUE,
          width = "600px"
      )
    })
  })


  #--------------------------# GW_Boxplot #--------------------------#
  # 启动文件服务器
  file_server <<- start_servr()
  # 回归点 shapefile 数据的临时文件路径缓存
  gwbp_cp_temp_files <- reactiveVal(NULL)
  # gwbp结果对象
  gwbp_data <- reactiveValues(
    results = NULL,           # 主分析结果
    outliers_true = NULL,     # 离群点
    outliers_false = NULL,     # 非离群点
    results_outliers = NULL    # 异常值计算
  )
  # shp读取函数
  gwbp_shapefile_data <- reactive({
    req(input$gwbp_shapefile)  # 确保用户已上传文件
    # 设置 GDAL 配置选项
    Sys.setenv(SHAPE_RESTORE_SHX = "YES")

    # 获取上传文件的路径和扩展名
    file_path <- input$gwbp_shapefile$datapath
    file_ext <- tolower(tools::file_ext(input$gwbp_shapefile$name))  # 获取文件扩展名,转为小写
    file_name <- tools::file_path_sans_ext(input$gwbp_shapefile$name)

    # 定义临时目录
    temp_dir <- tempdir()

    if (file_ext == "zip") {
      # 如果是 ZIP 文件,解压缩并查找 .shp 文件
      unzip(file_path, exdir = temp_dir)
      shp_files <- list.files(temp_dir, pattern = paste0("^", file_name, "\\.shp$"), full.names = TRUE)
      dbf_files <- list.files(temp_dir, pattern = paste0("^", file_name, "\\.dbf$"), full.names = TRUE)

      if (length(shp_files) > 0 && length(dbf_files) > 0) {
        # 缓存解压文件路径
        shp_file <- shp_files[1]
        dbf_file <- sub("\\.shp$", ".dbf", shp_file)
        shp_data <- tryCatch({
            sf::st_read(shp_file, options = "ENCODING=GBK", quiet = FALSE)
          }, error = function(e) {
            shiny::showNotification("Error reading Shapefile from ZIP. Please check the file content.", type = "error")
            return(NULL)
          }
        )
        # 处理 CRS(避免警告)
        if (!is.null(sf::st_crs(shp_data))) {
          shp_data <- sf::st_transform(shp_data, 4326)  # 如果已有 CRS,重新投影
        } else {
          sf::st_crs(shp_data) <- 4326  # 如果没有 CRS,先赋值再投影
          shp_data <- sf::st_transform(shp_data, 4326)
        }
        return(shp_data)
      } else {
        shiny::showNotification("No Shapefile found in the ZIP file.", type = "error")
        return(NULL)
      }
    } else {
      # 如果上传的文件既不是 .shp 也不是 .zip,显示错误通知
      shiny::showNotification("Unsupported file type. Please upload .zip file.", type = "error")
      return(NULL)
    }
  })
  # cp_shp读取函数
  gwbp_cp_shapefile_data <- reactive({
    req(input$gwbp_cp_shapefile)  # 确保用户已上传文件

   # 设置 GDAL 配置选项
    Sys.setenv(SHAPE_RESTORE_SHX = "YES")

    # 获取上传文件的路径和扩展名
    file_path <- input$gwbp_cp_shapefile$datapath
    file_ext <- tolower(tools::file_ext(input$gwbp_cp_shapefile$name))  # 获取文件扩展名,转为小写
    file_name <- tools::file_path_sans_ext(input$gwbp_cp_shapefile$name)

    # 定义临时目录
    temp_dir <- tempdir()

    if (file_ext == "zip") {
      # 如果是 ZIP 文件,解压缩并查找 .shp 文件
      unzip(file_path, exdir = temp_dir)
      shp_files <- list.files(temp_dir, pattern = paste0("^", file_name, "\\.shp$"), full.names = TRUE)
      dbf_files <- list.files(temp_dir, pattern = paste0("^", file_name, "\\.dbf$"), full.names = TRUE)
      other_files <- list.files(temp_dir, pattern = paste0(file_name, ".*"), full.names = TRUE)

      if (length(shp_files) > 0 && length(dbf_files) > 0) {
        # 缓存解压文件路径
        gwbp_cp_temp_files(other_files)
        shp_file <- shp_files[1]
        dbf_file <- sub("\\.shp$", ".dbf", shp_file)
        shp_data <- tryCatch({
            sf::st_read(shp_file, options = "ENCODING=GBK")
          },error = function(e) {
            shiny::showNotification("Error reading Shapefile from ZIP. Please check the file content.", type = "error")
            return(NULL)
          }
        )
        # 处理 CRS(避免警告)
        if (!is.null(sf::st_crs(shp_data))) {
          shp_data <- sf::st_transform(shp_data, 4326)  # 如果已有 CRS,重新投影
        } else {
          sf::st_crs(shp_data) <- 4326  # 如果没有 CRS,先赋值再投影
          shp_data <- sf::st_transform(shp_data, 4326)
        }
        return(shp_data)
      } else {
        shiny::showNotification("No Shapefile found in the ZIP file.", type = "error")
        return(NULL)
      }
    } else {
      # 如果上传的文件既不是 .shp 也不是 .zip,显示错误通知
      shiny::showNotification("Unsupported file type. Please upload .zip file.", type = "error")
      return(NULL)
    }
  })
  # # 监听 Clear 按钮,清除回归点 shapefile 数据
  # observeEvent(input$gwbp_cp_shapefile_clear_button, {
  #   cat("Clearing regression points shapefile data...\n")
  #   # 删除缓存的临时文件
  #   temp_files <- gwbp_cp_temp_files()
  #   if (!is.null(temp_files)) {
  #     cat("Deleting cp temporary files:\n", paste(temp_files, collapse = "\n"), "\n")
  #     unlink(temp_files, recursive = TRUE)
  #   }
  #   gwbp_cp_temp_files(NULL)  # 清空缓存

  #   # 清空文件输入框
  #   # updateFileInput(session, "gwbp_cp_shapefile", value = NULL)
  #   js$resetFileInput(id = "gwbp_cp_shapefile")

  #   shiny::showNotification("Regression points Shapefile data cleared.", type = "message")
  # })
  # 更新变量选项
  observeEvent(gwbp_shapefile_data(), {
    # 获取变量名并按首字母排序(不区分大小写)
    var_names <- names(gwbp_shapefile_data())
    
    # 检查变量名是否为空
    if (length(var_names) == 0) {
      sorted_vars <- character(0)  # 空字符向量
    } else {
      # 不区分大小写排序
      sorted_vars <- var_names[order(tolower(var_names))]
    }

    updateSelectInput(session, "gwbp_X", choices = sorted_vars)
  })
  # 输出X变量选择控件
  output$gwbp_X <- renderUI({
    selectInput("gwbp_X", "Choose variable:", choices = NULL)
  })
  # 更新带宽滑块
  observe({
      req(input$gwbp_shapefile)  # 确保文件已上传
      shp_data <- gwbp_shapefile_data()  # 读取 shapefile 数据
      shp_data <- sf::st_make_valid(shp_data)      # 确保数据有效
      req(shp_data)

      # 计算最大距离(米),确保单位正确
      if (sf::st_is_longlat(shp_data)) {
          max_distance <- as.integer(max(sf::st_distance(shp_data)))  # 计算球面距离
      } else {
          coords <- sf::st_coordinates(shp_data)
          max_distance <- as.integer(max(dist(coords)))  # 计算欧式距离
      }

      # 读取是否为自适应带宽
      gwbp_adaptive <- as.logical(input$gwbp_adaptive)

      # 设置带宽的最大值和单位
      if (gwbp_adaptive) {
          max_bw <- nrow(shp_data)  # 以点的个数为单位
          label <- "Bandwidth (Number of points)"
      } else {
          max_bw <- max_distance  # 以最大距离为单位
          label <- "Bandwidth (meter)"
      }

      # 更新滑块
      updateSliderInput(session, "gwbp_bandwidth",
                        max = max_bw,
                        label = label)
  })
  # execute按钮执行gwboxplot分析
  observeEvent(input$gwbp_execute, {
    # 跳转分页
    updateTabsetPanel(session, "gwbp_tabs", selected = "Web Map")

    # 在开始生成前,确保删除 temp_dir 文件夹下以特定前缀开头的所有文件
    prefixes_to_remove <- c("^gw_boxplot_")
    for (prefix in prefixes_to_remove) {
      files_to_remove <- list.files(temp_dir, pattern = prefix, full.names = TRUE)
      if (length(files_to_remove) > 0) {
        file.remove(files_to_remove)
      }
    }

    # 判断必填项是否为空
    if(is.null(input$gwbp_shapefile)){
      shiny::showNotification("The SHP file selected to construct a geo-weighted box map cannot be empty", type = "error", duration = NULL)
      return()}
    
    current_map <- leaflet::leaflet()%>%
                      leaflet::addProviderTiles("CartoDB.Positron")

    # 获取shp数据的坐标
    req(gwbp_shapefile_data())

    # 显示自定义模态窗口
    showModal(modalDialog(
      title = "Processing GW Boxplot Analysis...",
      tags$div(
        style = "text-align: center;",
        shinyWidgets::progressBar(
          id = "gwbp_progress",
          value = 0,
          display_pct = TRUE,
          status = "info",
          striped = TRUE
        )
      ),
      footer = tagList(
        modalButton("Cancel")  # 添加关闭按钮
      ),
      easyClose = FALSE
    ))
    # 更新进度条10%
    shinyWidgets::updateProgressBar(session = session, id = "gwbp_progress", value = 10)

    gwbp_shp_data <- gwbp_shapefile_data()
    # 检查数据是否为 sf 对象
    if (!inherits(gwbp_shp_data, "sf")) {
      shiny::showNotification("Unsupported data types. Input is not an sf object.", type = "error", duration = NULL)
      shiny::removeModal()
      return()
    }
    # 修复可能的无效几何数据
    gwbp_shp_data <- sf::st_make_valid(gwbp_shp_data)
    # 初始化 dp.locat
    dp.locat <- NULL

    # 更新进度条20%
    shinyWidgets::updateProgressBar(session = session, id = "gwbp_progress", value = 20)

    # 提取坐标点
    tryCatch({
      geometry_type <- unique(sf::st_geometry_type(gwbp_shp_data))  # 获取所有几何类型
      if ("MULTIPOLYGON" %in% geometry_type || "POLYGON" %in% geometry_type) {
        centroids <- sf::st_centroid(sf::st_geometry(gwbp_shp_data))  # 仅计算几何的质心
        coords <- sf::st_coordinates(centroids)
        dp.locat <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
      } else if ("POINT" %in% geometry_type || "MULTIPOINT" %in% geometry_type) {
        coords <- sf::st_coordinates(gwbp_shp_data)
        dp.locat <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
      } else if ("LINESTRING" %in% geometry_type) {
        # 如果是线状几何,可以取中点(或其他逻辑)
        midpoints <- sf::st_line_sample(sf::st_geometry(gwbp_shp_data), n = 1)
        coords <- sf::st_coordinates(midpoints)
        dp.locat <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
      } else {
        shiny::showNotification(paste("Unsupported geometry type(s):", paste(geometry_type, collapse = ", ")), type = "error")
        shiny::removeModal()
        return()
      }
    }, error = function(e) {
      shiny::showNotification(paste("Error extracting coordinates:", e$message), type = "error", duration = NULL)
      shiny::removeModal()
      return(NULL)
    })
    # 检查 dp.locat 是否为空
    if (is.null(dp.locat)) {
      shiny::showNotification("Coordinates could not be extracted from the shapefile.", type = "error", duration = NULL)
      return()
    }
    dp.locat <- na.omit(dp.locat)

    # 更新进度条30%
    shinyWidgets::updateProgressBar(session = session, id = "gwbp_progress", value = 30)

    # 获取 calibrationPoint 数据
    if (is.null(input$gwbp_cp_shapefile)) {
      cp_shp_data <- gwbp_shp_data
      calibrationPoint <- dp.locat
    } else {
      cp_shp_data <- gwbp_cp_shapefile_data()
      cp_shp_data <- sf::st_make_valid(cp_shp_data)
      # 提取坐标点
      tryCatch({
        geometry_type <- unique(sf::st_geometry_type(cp_shp_data))  # 获取所有几何类型
        if ("MULTIPOLYGON" %in% geometry_type || "POLYGON" %in% geometry_type) {
          centroids <- sf::st_centroid(sf::st_geometry(cp_shp_data))  # 仅计算几何的质心
          coords <- sf::st_coordinates(centroids)
          calibrationPoint <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
        } else if ("POINT" %in% geometry_type || "MULTIPOINT" %in% geometry_type) {
          coords <- sf::st_coordinates(cp_shp_data)
          calibrationPoint <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
        } else if ("LINESTRING" %in% geometry_type) {
          # 如果是线状几何,可以取中点(或其他逻辑)
          midpoints <- sf::st_line_sample(sf::st_geometry(cp_shp_data), n = 1)
          coords <- sf::st_coordinates(midpoints)
          calibrationPoint <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
        } else {
          shiny::showNotification(paste("Unsupported geometry type(s):", paste(geometry_type, collapse = ", ")), type = "error")
          shiny::removeModal()
          return()
        }
      }, error = function(e) {
        shiny::showNotification(paste("Error extracting coordinates:", e$message), type = "error", duration = NULL)
        shiny::removeModal()
        return(NULL)
      })
    }
    calibrationPoint <- na.omit(calibrationPoint)

    # 检查 dp.locat 和 calibrationPoint 是否有效
    if (nrow(dp.locat) == 0 || nrow(calibrationPoint) == 0) {
      shiny::showNotification("No valid points found in the shapefile or calibration data.", type = "error", duration = NULL)
      return()
    }

    # 获取选中的Kernel、adaptive、longlat、outliers选项
    gwbp_kernel <- as.character(input$gwbp_kernel)
    gwbp_adaptive <- as.logical(input$gwbp_adaptive)
    gwbp_longlat <- as.logical(input$gwbp_longlat)
    # gwbp_checkbox_outlier <- input$gwbp_outliers_checkbox
    gwbp_bw <- as.numeric(input$gwbp_bandwidth) # 获取滑块当前带宽值
    gwbp_bpcolor <- as.character(input$gwbp_bpcolor)

    # 获取 X 列的数据
    x_field <- as.character(input$gwbp_X)
    # 字段存在性检查
    if (!x_field %in% names(gwbp_shp_data)) {
      shiny::showNotification("The selected field does not exist in the shapefile.", type = "error", duration = NULL)
      return()
    }
    # 数值类型检查
    selected_col <- gwbp_shp_data[[x_field]]
    if (!is.numeric(selected_col)) {
      shiny::showNotification(
        paste("Error running GWBP: Selected variable", x_field, "is not numeric type"),
        type = "error",
        duration = NULL
      )
      shiny::removeModal()
      return()
    }
    # 数值转换与缺失值处理
    X <- na.omit(as.numeric(selected_col))

    # 更新进度条40%
    shinyWidgets::updateProgressBar(session = session, id = "gwbp_progress", value = 40)

    cat("开始GWboxplot分析\n")
    # 进行GWboxplot分析,生成对应boxplot图片在temp目录下
    results <- gw_boxplot(bw = gwbp_bw, X = X, kernel = gwbp_kernel, adaptive = gwbp_adaptive, dp.locat = dp.locat, p = 2, theta = 0,
                          longlat = gwbp_longlat, calibrationPoint = calibrationPoint, gwbp_bpcolor = gwbp_bpcolor)
    cat("结束GWboxplot分析\n")

    # 更新进度条60%
    shinyWidgets::updateProgressBar(session = session, id = "gwbp_progress", value = 60)

    # 获取outliers结果
    results_outliers <- gw_boxplot_outliers(bw = gwbp_bw, X = X, kernel = gwbp_kernel, adaptive = gwbp_adaptive, dp.locat = dp.locat, p = 2,
                                            theta = 0,longlat = gwbp_longlat)
    results_outliers <- as.data.frame(na.omit(results_outliers))
    outliers_true <- results_outliers[results_outliers$TorF_outlier == TRUE, ]
    outliers_false <- results_outliers[results_outliers$TorF_outlier == FALSE, ]

    # #------------------------# 在地图上标记markers,显示point card
    # 更新点数据
    results <- as.data.frame(results)
    # 检查并生成 FID 列
    if (!"FID" %in% names(gwbp_shp_data)) {
      dp.n <- nrow(dp.locat)
      gwbp_shp_data$FID <- seq_len(dp.n)
    }
    if (!"FID" %in% names(calibrationPoint)) {
      cp.n <- nrow(calibrationPoint)
      calibrationPoint$FID <- seq_len(cp.n)
    }
    # 检查字段是否存在
    if (is.null(input$gwbp_cp_shapefile)) {
      data <- NULL
      # 提取字段数据
      if ("Name" %in% names(gwbp_shp_data) && x_field %in% names(gwbp_shp_data)) {
        data <- gwbp_shp_data[, c("FID", "Name", x_field)]
      } else if (x_field %in% names(gwbp_shp_data)) {
        data <- gwbp_shp_data[, c("FID", x_field)]
      }
      # 合并数据
      if (!is.null(data)) {
        data <- as.data.frame(data)  # 确保为 data.frame
        results <- merge(results, data, by = "FID", all.x = TRUE)
      }
    } else {
      # 如果存在 cp_shapefile,则移除无关列
      results$Name <- NULL
      results$Price <- NA
    }
    # 关键修改部分:补充缺失的经纬度列
    if (!("longitude" %in% names(results)) || !("latitude" %in% names(results))) {
      # 确保 calibrationPoint 包含必要列
      required_cols <- c("FID", "longitude", "latitude")
      if (all(required_cols %in% names(calibrationPoint))) {
        # 根据 FID 合并校准点坐标
        results <- merge(
          results,
          calibrationPoint[, required_cols],
          by = "FID",
          all.x = TRUE
        )
      } else {
        shiny::showNotification("Data miss longitude and latitude columns", type = "error", duration = NULL)
        shiny::removeModal()
      }
    }

    gwbp_data$results <- results
    gwbp_data$results_outliers <- results_outliers
    gwbp_data$outliers_true <- outliers_true
    gwbp_data$outliers_false <- outliers_false

    # 更新进度条到 100%
    shinyWidgets::updateProgressBar(session = session, id = "gwbp_progress", value = 100)
    Sys.sleep(1)  # 等待一秒,确保用户看到完成的进度条
    shiny::removeModal()

    running(FALSE)
  })
  # 显示map地图,并更新shapefile
  output$gwbp_mapPlot <- leaflet::renderLeaflet({
      current_map <- leaflet::leaflet()%>%
                      leaflet::clearMarkers() %>%
                      leaflet::addProviderTiles("CartoDB.Positron")
      # 获取当前的 leaflet 地图对象
      if(!is.null(input$gwbp_shapefile)){
          shp_data <- gwbp_shapefile_data()
          print(paste0("shp_data class:",sf::st_geometry_type(shp_data)[1]))
          # 检查 gwbp_shp_data 是否为 sf 对象
          if (inherits(shp_data, "sf")) {
            if(sf::st_geometry_type(shp_data)[1] == "MULTIPOLYGON" || sf::st_geometry_type(shp_data)[1] == "POLYGON"){
              current_map <- current_map %>%
                  leaflet::clearMarkers() %>%
                  leaflet::addPolygons(data = shp_data,
                              fillColor = "#C7DFF0",
                              stroke = TRUE,
                              weight = 0.3,
                              opacity = 0.5,
                              fillOpacity = 0.4  # 透明度
                              )
            }
          }
        }
      if(!is.null(input$gwbp_cp_shapefile)){
            cp_shp_data <- gwbp_cp_shapefile_data()
            print(paste0("cp_shp_data class:",sf::st_geometry_type(cp_shp_data)[1]))
            if (inherits(cp_shp_data, "sf")) {
              if(sf::st_geometry_type(cp_shp_data)[1] == "MULTIPOLYGON" || sf::st_geometry_type(shp_data)[1] == "POLYGON"){
                current_map <- current_map %>%
                    leaflet::clearMarkers() %>%
                    leaflet::addPolygons(data = cp_shp_data,
                                fillColor = "#C7DFF0",
                                stroke = TRUE,
                                weight = 0.3,
                                opacity = 0.5,
                                fillOpacity = 0.4,  # 透明度
                                group = "GWBP-Polygons"
                                )
            }
          }
        } else {
          current_map <- current_map %>%
                          leaflet::clearGroup("GWBP-Polygons")
        }

      
      req(gwbp_data$results)

      results <- gwbp_data$results 
      results_outliers <- gwbp_data$results_outliers
      outliers_true <- gwbp_data$outliers_true
      outliers_false <- gwbp_data$outliers_false
      x_field <- as.character(input$gwbp_X)
      print(paste0("gwbp_data results:",results))

      # 设置适合的缩放级别
      # 计算所有点的边界框
      # 检查是否存在经度和纬度列,并有效计算范围
      if (all(c("longitude", "latitude") %in% names(results))) {
          # 检查是否有非NA的值
          if (any(!is.na(results$longitude)) && any(!is.na(results$latitude))) {
              # 计算min和max,移除NA
              lon_min <- min(results$longitude, na.rm = TRUE)
              lat_min <- min(results$latitude, na.rm = TRUE)
              lon_max <- max(results$longitude, na.rm = TRUE)
              lat_max <- max(results$latitude, na.rm = TRUE)
              # 确保结果有效(非Inf)
              if (all(is.finite(c(lon_min, lat_min, lon_max, lat_max)))) {
                  bounds <- cbind(lon_min, lat_min, lon_max, lat_max)
              } else {
                  # 无效数值,使用shp_data的边界
                  shp_bbox <- sf::st_bbox(shp_data)
                  bounds <- cbind(shp_bbox["xmin"], shp_bbox["ymin"], shp_bbox["xmax"], shp_bbox["ymax"])
              }
          } else {
              # 全为NA,使用shp_data的边界
              shp_bbox <- sf::st_bbox(shp_data)
              bounds <- cbind(shp_bbox["xmin"], shp_bbox["ymin"], shp_bbox["xmax"], shp_bbox["ymax"])
          }
      } else {
          # 列不存在,使用shp_data的边界
          shp_bbox <- sf::st_bbox(shp_data)
          bounds <- cbind(shp_bbox["xmin"], shp_bbox["ymin"], shp_bbox["xmax"], shp_bbox["ymax"])
      }

      icon_url <- paste0("http://127.0.0.1:8000/gw_boxplot_", as.character(results$FID), ".png")

      # 添加标记点
      current_map <- current_map %>%
        leaflet::clearGroup("point_cards_markers") %>%
        leaflet::clearGroup("Outliers") %>%
        leaflet::clearGroup("Non-outliers") %>%
        leaflet::clearControls() %>%
        leaflet::addMarkers(
          data = results,
          lng = results$longitude,
          lat = results$latitude,
          clusterOptions = markerClusterOptions(), # 使用聚类标记
          popup = ~as.character(point_card(results$Name,results[[x_field]],results$longitude,results$latitude,
                                          results$Max,results$Q3,results$Median,results$Q1,results$Min)),
          icon = leaflet::makeIcon(
            iconUrl = icon_url,
            iconWidth = 80,
            iconHeight = 100,
            iconAnchorX = 10,
            iconAnchorY = 10),
          group = "point_cards_markers"
        )%>%
        leaflet::fitBounds(lng1 = bounds[1], lat1 = bounds[2],
                  lng2 = bounds[3], lat2 = bounds[4])

      # 处理 outliers 的点
      current_map <- current_map %>%
        leaflet::addCircles(
          data = outliers_true,
          lng = ~longitude,
          lat = ~latitude,
          popup = ~as.character(small_outliers_popup(x_field, outliers_true$Outliers)),
          color = "red",
          fillOpacity = 0.5,
          stroke = TRUE,
          group = "Outliers"
      ) %>%
      leaflet::addCircles(
        data = outliers_false,
        lng = ~longitude,
        lat = ~latitude,
        popup = ~as.character(small_outliers_popup(x_field, outliers_false$Outliers)),
        color = "blue",
        fillOpacity = 0.5, # 半透明
        stroke = TRUE,
        group = "Non-outliers"
      )%>%
      # 添加组合图例
      leaflet::addLegend(
        position = "bottomright",
        colors = c("red", "blue"),
        labels = c(paste("Outliers (n =", nrow(outliers_true),")"),
                  paste("Non-outliers (n =", nrow(outliers_false),")")),
        title = "Outliers Display",
        opacity = 0.8,
        group = "outliers_Legend"
      ) %>%
      leaflet::addLayersControl(
        overlayGroups = c("Outliers", "Non-outliers"),
        options = layersControlOptions(collapsed = FALSE)
      )%>%
      leaflet::fitBounds(lng1 = bounds[1], lat1 = bounds[2],
                lng2 = bounds[3], lat2 = bounds[4])
    

      current_map

  })
  # 响应用户点击marker产生point card
  lat_tolerance <- 0.0005  # 容差范围
  lng_tolerance <- 0.0005
  observeEvent(input$gwbp_mapPlot_marker_click, { # 当点击点时加载point card
      req(point_cards_reactive(), input$gwbp_mapPlot_marker_click)
      clicked_data <- point_cards_reactive()
      click_lat <- input$gwbp_mapPlot_marker_click$lat
      click_lng <- input$gwbp_mapPlot_marker_click$lng  # 从input$parksMap_marker_click获取点击的标记的信息,并将其存储在pin变量中。这个pin变量通常包含有关点击位置的坐标(如lat和lng)
      selectedPoint <- clicked_data[
        (clicked_data$latitude >= click_lat - lat_tolerance) &
        (clicked_data$latitude <= click_lat + lat_tolerance) &
        (clicked_data$longitude >= click_lng - lng_tolerance) &
        (clicked_data$longitude <= click_lng + lng_tolerance),
      ] # 点击的pin的经纬度lng/lat与数据points的经纬度相匹配
      if (!nrow(selectedPoint) == 0) {
        # 更新leaflet地图的弹出窗口
        leaflet::leafletProxy("gwbp_mapPlot") %>%
          leaflet::clearPopups() %>%
          addPopups(
                  lng = click_lng,
                  lat = click_lat, #指定了弹出窗口的位置
                  popup = as.character(point_card(selectedPoint$Name, selectedPoint$Price, selectedPoint$longitude, selectedPoint$latitude,
                  selectedPoint$Max, selectedPoint$Q3,selectedPoint$Median,selectedPoint$Q1,selectedPoint$Min)),
              )
      }
  })
  #--------------------------# Table View #-------------------------#
  # 显示汇总表
  output$gwbp_summaryTable <- DT::renderDataTable({
    result <- gwbp_data$results
    sdf <- as.data.frame(result)
    DT::datatable(
      sdf,
      extensions = 'Buttons',  # 关键要素1:声明使用扩展
      options = list(
        pageLength = 10,        # 默认每页显示10条
        lengthMenu = c(10, 15, 20, 25),  # 每页显示条目数选项
        searching = TRUE,       # 启用搜索框
        scrollX = TRUE,          # 启用水平滚动
        dom = 'Blfrtip',          # 控制界面元素布局
        buttons = list(       # 关键要素3:按钮嵌套配置
          list(extend = 'copy', text = 'Copy'),
          list(extend = 'csv', text = 'Export CSV'),
          list(extend = 'excel', text = 'Export Excel')
        )
      ),
      rownames = FALSE,         # 不显示行号
      class = "'display nowrap hover" 
    )
  })
  #--------------------------# GWR #--------------------------#
  # 按钮跳转
  observeEvent(input$gwr_execute, {
    updateTabsetPanel(session, "gwr_tabs", selected = "Scatter Plot")
  })
  observeEvent(input$gwr_video_button, {
    updateTabsetPanel(session, "gwr_tabs", selected = "Video")
  })
  observeEvent(input$gwr_predict_execute, {
    updateTabsetPanel(session, "gwr_tabs", selected = "Prediction")
  })
  # shp读取函数
  gwr_shapefile_data <- reactive({
    req(input$gwr_shapefile)  # 确保用户已上传文件

    # 获取上传文件的路径和扩展名
    file_path <- input$gwr_shapefile$datapath
    file_ext <- tolower(tools::file_ext(input$gwr_shapefile$name))  # 获取文件扩展名,转为小写
    file_name <- tools::file_path_sans_ext(input$gwr_shapefile$name)

    # 设置 GDAL 配置选项
    Sys.setenv(SHAPE_RESTORE_SHX = "YES")

    if (file_ext == "zip") {
      # 如果是 ZIP 文件,解压缩并查找 .shp 文件
      temp_dir <- tempdir()
      unzip(file_path, exdir = temp_dir)
      shp_files <- list.files(temp_dir, pattern = paste0("^", file_name, "\\.shp$"), full.names = TRUE)
      dbf_files <- list.files(temp_dir, pattern = paste0("^", file_name, "\\.dbf$"), full.names = TRUE)

      if (length(shp_files) > 0 && length(dbf_files) > 0) {
        shp_file <- shp_files[1]
        dbf_file <- sub("\\.shp$", ".dbf", shp_file)
        shp_data <- tryCatch(
          {
            sf::st_read(shp_file, options = "ENCODING=GBK")
          },
          error = function(e) {
            shiny::showNotification("Error reading Shapefile from ZIP. Please check the file content.", type = "error")
            return(NULL)
          }
        )
        # 处理 CRS(避免警告)
        if (!is.null(sf::st_crs(shp_data))) {
          shp_data <- sf::st_transform(shp_data, 4326)  # 如果已有 CRS,重新投影
        } else {
          sf::st_crs(shp_data) <- 4326  # 如果没有 CRS,先赋值再投影
          shp_data <- sf::st_transform(shp_data, 4326)
        }
        return(shp_data)
      } else {
        shiny::showNotification("No Shapefile found in the ZIP file.", type = "error")
        return(NULL)
      }
    } else {
      # 如果上传的文件既不是 .shp 也不是 .zip,显示错误通知
      shiny::showNotification("Unsupported file type. Please upload .zip file.", type = "error")
      return(NULL)
    }
  })
  # cp_shp读取函数
  gwr_cp_shapefile_data <- reactive({
    req(input$gwr_cp_shapefile)  # 确保用户已上传文件

    # 获取上传文件的路径和扩展名
    file_path <- input$gwr_cp_shapefile$datapath
    file_ext <- tolower(tools::file_ext(input$gwr_cp_shapefile$name))  # 获取文件扩展名,转为小写
    file_name <- tools::file_path_sans_ext(input$gwr_cp_shapefile$name)

    # 设置 GDAL 配置选项
    Sys.setenv(SHAPE_RESTORE_SHX = "YES")

    temp_dir <- tempdir()

    if (file_ext == "zip") {
      # 如果是 ZIP 文件,解压缩并查找 .shp 文件
      unzip(file_path, exdir = temp_dir)
      shp_files <- list.files(temp_dir, pattern = paste0("^", file_name, "\\.shp$"), full.names = TRUE)
      dbf_files <- list.files(temp_dir, pattern = paste0("^", file_name, "\\.dbf$"), full.names = TRUE)

      if (length(shp_files) > 0 && length(dbf_files) > 0) {
        shp_file <- shp_files[1]
        dbf_file <- sub("\\.shp$", ".dbf", shp_file)
        shp_data <- tryCatch({
            sf::st_read(shp_file, options = "ENCODING=GBK")
          },error = function(e) {
            shiny::showNotification("Error reading Shapefile from ZIP. Please check the file content.", type = "error")
            return(NULL)
          }
        )
        # 处理 CRS(避免警告)
        if (!is.null(sf::st_crs(shp_data))) {
          shp_data <- sf::st_transform(shp_data, 4326)  # 如果已有 CRS,重新投影
        } else {
          sf::st_crs(shp_data) <- 4326  # 如果没有 CRS,先赋值再投影
          shp_data <- sf::st_transform(shp_data, 4326)
        }
        return(shp_data)
      } else {
        shiny::showNotification("No Shapefile found in the ZIP file.", type = "error")
        return(NULL)
      }
    } else {
      # 如果上传的文件既不是 .shp 也不是 .zip,显示错误通知
      shiny::showNotification("Unsupported file type. Please upload .zip file.", type = "error")
      return(NULL)
    }
  })
  # gwr结果对象
  gwr_bw_data <- reactiveValues(
    bw = NULL,          
  )
  # 带宽滑块显示隐藏
  observeEvent(input$gwr_best_bandwidth, {
    if (input$gwr_best_bandwidth) {
      shinyjs::hide("bandwidth_div")  # 隐藏带宽滑块
    } else {
      shinyjs::show("bandwidth_div")  # 显示带宽滑块
    }
  })
  # 显著性图例显示隐藏(面/点) 还要加是面的判断
  shinyjs::hide("Significant_line_div")
  shinyjs::hide("Significant_point_div")
  observeEvent(input$gwr_map_layer_execute, {
    # 获取shp数据
    shp_data <- gwr_shapefile_data()
    shp_data <- sf::st_make_valid(shp_data)
    # 初始化 cp_shp_data
    cp_shp_data <- NULL

    # 如果用户上传了regression shapefile,则获取数据
    if (!is.null(input$gwr_cp_shapefile)){
      cp_shp_data <- gwr_cp_shapefile_data()
      cp_shp_data <- sf::st_make_valid(cp_shp_data)
    }
    # 判断是否是面/点数据
    if ((sf::st_geometry_type(shp_data)[1] == "MULTIPOLYGON" || sf::st_geometry_type(shp_data)[1] == "POLYGON")||
        (!is.null(cp_shp_data) && (sf::st_geometry_type(cp_shp_data)[1] == "MULTIPOLYGON" || sf::st_geometry_type(cp_shp_data)[1] == "POLYGON"))) {

      # 面数据:隐藏 Significant_point_div,显示或隐藏 Significant_line_div
      shinyjs::hide("Significant_point_div")
      if (input$gwr_significance_show) {
        shinyjs::show("Significant_line_div")
      } else {
        shinyjs::hide("Significant_line_div")
      }

    } else if (sf::st_geometry_type(shp_data)[1] == "POINT") {

      # 点数据:隐藏 Significant_line_div,显示或隐藏 Significant_point_div
      shinyjs::hide("Significant_line_div")
      if (input$gwr_significance_show) {
        shinyjs::show("Significant_point_div")
      } else {
        shinyjs::hide("Significant_point_div")
      }

    } else {
      # 如果几何类型不在 MULTIPOLYGON 或 POINT 中,隐藏所有结果
      shinyjs::hide("Significant_line_div")
      shinyjs::hide("Significant_point_div")
    }
  })
  # 更新带宽滑块
  observe({
      req(input$gwr_shapefile)  # 确保文件已上传
      shp_data <- gwr_shapefile_data()  # 读取 shapefile 数据
      shp_data <- sf::st_make_valid(shp_data)      # 确保数据有效
      req(shp_data)

      # 计算最大距离(米),确保单位正确
      if (sf::st_is_longlat(shp_data)) {
          max_distance <- as.integer(max(sf::st_distance(shp_data)))  # 计算球面距离
      } else {
          coords <- sf::st_coordinates(shp_data)
          max_distance <- as.integer(max(dist(coords)))  # 计算欧式距离
      }

      # 读取是否为自适应带宽
      gwr_adaptive <- as.logical(input$gwr_adaptive)

      # 设置带宽的最大值和单位
      if (gwr_adaptive) {
          max_bw <- nrow(shp_data)  # 以点的个数为单位
          label <- "Bandwidth (Number of points)"
      } else {
          max_bw <- max_distance  # 以最大距离为单位
          label <- "Bandwidth (meter)"
      }

      # 设置滑块n和步长的最大值为数据的数量
      updateSliderInput(session, "gwr_bandwidth", max = max_bw,label = label)
      updateSliderInput(session, "gwr_range", max = max_bw,label = label)
  })
  # 更新因变量选项(独立模块)
  observeEvent(gwr_shapefile_data(), {
    req(gwr_shapefile_data())
    var_names <- names(gwr_shapefile_data()) 
    # 按首字母排序(不区分大小写)
    sorted_vars <- if (length(var_names) > 0) {
      var_names[order(tolower(var_names))]
    } else {
      character(0)
    }
    # 设置默认因变量(示例:选第一个变量)
    default_dependent <- if (length(sorted_vars) >= 1) sorted_vars[1] else character(0)
    
    # 更新因变量下拉框
    updateSelectInput(session,"gwr_dependentVar",choices = sorted_vars,selected = default_dependent)
  })
  # 更新自变量选项(监听因变量和数据变化)
  observeEvent(list(gwr_shapefile_data(), input$gwr_dependentVar), {
    req(gwr_shapefile_data())
    
    # 获取所有变量名并排序
    all_vars <- names(gwr_shapefile_data())
    sorted_all_vars <- all_vars[order(tolower(all_vars))]
    
    # 安全获取当前因变量(处理未选择的情况)
    dependent_var <- if (is.null(input$gwr_dependentVar)) {
      character(0)
    } else {
      input$gwr_dependentVar
    }
    
    # 排除因变量(若存在)
    independent_vars <- if (dependent_var %in% sorted_all_vars) {
      setdiff(sorted_all_vars, dependent_var)
    } else {
      sorted_all_vars  # 因变量无效时显示全部变量
    }
    
    # 设置默认自变量(选前两个有效变量)
    selected_independent <- if (length(independent_vars) >= 2) {
      independent_vars[1:2]
    } else if (length(independent_vars) == 1) {
      independent_vars[1]
    } else {
      character(0)
    }
    
    # 更新自变量下拉框
    updateSelectInput(session,"gwr_independentVars", choices = independent_vars, selected = selected_independent)
  })
  # 输出因变量选择控件
  output$gwr_dependentVar <- renderUI({
    selectInput("gwr_dependentVar", "Choose dependentVar", choices = NULL)
  })
  # 输出自变量选择控件
  output$gwr_independentVars <- renderUI({
    selectInput("gwr_independentVars", "Choose independentVars", choices = NULL, multiple = TRUE, selected = NULL)
  })
  #--------------------------# Scatter Plot #-------------------------#
  # 绘制散点图
  observeEvent(input$gwr_execute, {
    req(input$gwr_dependentVar, input$gwr_independentVars)
    dependent_var <- input$gwr_dependentVar
    independent_vars <- input$gwr_independentVars

    # 遍历每个自变量生成散点图
    lapply(independent_vars, function(independent_var) {
      plotname <- paste0("scatter_plot_", independent_var)

      output[[plotname]] <- renderPlot({
        shp_data <- gwr_shapefile_data()

        # 检查数据是否存在自变量和因变量
        req(independent_var %in% names(shp_data))
        req(dependent_var %in% names(shp_data))

        # 创建回归模型
        formula <- as.formula(paste(dependent_var, "~", independent_var))
        fit <- lm(formula, data = shp_data)

        # 提取回归模型的相关系数和显著性水平
        fit_summary <- summary(fit)
        r_squared <- fit_summary$r.squared
        p_value <- coef(fit_summary)[2, "Pr(>|t|)"]

        # 创建散点图并添加回归线、置信区间及注释
        ggplot2::ggplot(shp_data, ggplot2::aes_string(x = independent_var, y = dependent_var)) +
          ggplot2::geom_point(color = "blue", alpha = 0.6) +  # 散点
          ggplot2::geom_smooth(method = "lm", se = TRUE, color = "red", fill = "orange", alpha = 0.2) +  # 回归线与置信区间
          ggplot2::labs(
            x = independent_var,
            y = dependent_var,
            title = paste(dependent_var, "vs", independent_var),
            subtitle = paste0(
              "R² = ", round(r_squared, 3),
              ", P-value = ", format.pval(p_value, digits = 3, eps = 0.001)
            )
          ) +
          ggplot2::theme_minimal()
      })
    })

    # 动态生成 UI 输出控件
    output$gwr_ScatterPlot <- renderUI({
      plot_outputs <- lapply(independent_vars, function(independent_var) {
        plotname <- paste0("scatter_plot_", independent_var)
        plotOutput(plotname, height = "400px")
      })
      do.call(tagList, plot_outputs)
    })
  })
  #--------------------------# GWR Analysis #-------------------------#
  # 运行GWR分析
  gwr_result <- eventReactive(input$gwr_execute, {
    # 保证参数存在
    req(input$gwr_shapefile)
    req(input$gwr_dependentVar, input$gwr_independentVars)
    req(input$gwr_kernel, input$gwr_adaptive)

    # 显示自定义模态窗口
    showModal(modalDialog(
      title = "Processing GWR Analysis...",
      tags$div(
        style = "text-align: center;",
        shinyWidgets::progressBar(
          id = "gwr_progress",
          value = 0,
          display_pct = TRUE,
          status = "info",
          striped = TRUE
        )
      ),
      footer = tagList(
        modalButton("Cancel")  # 添加关闭按钮
      ),
      easyClose = FALSE
    ))
    # 更新进度条20%
    shinyWidgets::updateProgressBar(session = session, id = "gwr_progress", value = 20)

    # 获取实际的数据和坐标
    gwr_shp_data <- gwr_shapefile_data()
    spdf <- as(gwr_shp_data, "Spatial")

    # 获取用户选定的变量
    gwr_dependentVar <- as.character(input$gwr_dependentVar)
    gwr_independentVars <- as.character(input$gwr_independentVars)

    # 1. 检查变量存在性
    all_vars <- c(gwr_dependentVar, gwr_independentVars)
    missing_vars <- all_vars[!all_vars %in% names(gwr_shp_data)]
    if (length(missing_vars) > 0) {
      shiny::showNotification(
        paste("Missing variables:", paste(missing_vars, collapse = ", ")),
        type = "error",
        duration = NULL
      )
      shiny::removeModal()
      return()
    }
    # 2. 检查数值类型
    check_numeric <- function(var_name) {
      var_data <- gwr_shp_data[[var_name]]
      if (!is.numeric(var_data)) {
        return(var_name)
      }
      return(NULL)
    }
    # 检查所有变量
    non_numeric_vars <- unlist(lapply(all_vars, check_numeric))
    if (length(non_numeric_vars) > 0) {
      shiny::showNotification(
        paste("Non-numeric variables:", paste(non_numeric_vars, collapse = ", ")),
        type = "error",
        duration = NULL
      )
      shiny::removeModal()
      return()
    }

    # 初始化 dp.locat
    dp.locat <- NULL
    # 提取坐标点
    tryCatch({
      geometry_type <- unique(sf::st_geometry_type(gwr_shp_data))  # 获取所有几何类型
      if ("MULTIPOLYGON" %in% geometry_type || "POLYGON" %in% geometry_type) {
        centroids <- sf::st_centroid(sf::st_geometry(gwr_shp_data))  # 仅计算几何的质心
        coords <- sf::st_coordinates(centroids)
        dp.locat <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
      } else if ("POINT" %in% geometry_type || "MULTIPOINT" %in% geometry_type) {
        coords <- sf::st_coordinates(gwr_shp_data)
        dp.locat <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
      } else if ("LINESTRING" %in% geometry_type) {
        # 如果是线状几何,可以取中点(或其他逻辑)
        midpoints <- sf::st_line_sample(sf::st_geometry(gwr_shp_data), n = 1)
        coords <- sf::st_coordinates(midpoints)
        dp.locat <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
      } else {
        shiny::showNotification(paste("Unsupported geometry type(s):", paste(geometry_type, collapse = ", ")), type = "error")
        shiny::removeModal()
        return()
      }
    }, error = function(e) {
      shiny::showNotification(paste("Error extracting coordinates:", e$message), type = "error", duration = NULL)
      shiny::removeModal()
      return(NULL)
    })
    # 检查 dp.locat 是否为空
    if (is.null(dp.locat)) {
      shiny::showNotification("Coordinates could not be extracted from the shapefile.", type = "error", duration = NULL)
      return()
    }
    dp.locat <- na.omit(dp.locat)
    # 检查并生成 FID 列
    if (!"FID" %in% names(gwr_shp_data)) {
      dp.n <- nrow(dp.locat)
      gwr_shp_data$FID <- seq_len(dp.n)
    }

    formula <- as.formula(paste(gwr_dependentVar, "~", paste(gwr_independentVars, collapse = "+")))

    # 更新进度条40%
    shinyWidgets::updateProgressBar(session = session, id = "gwr_progress", value = 40)

    # 获取选项
    gwr_kernel <- as.character(input$gwr_kernel)
    gwr_adaptive <- as.logical(input$gwr_adaptive)
    # 选择带宽
    if (input$gwr_best_bandwidth) {
      bandwidth <- GWmodel::bw.gwr(formula, data = spdf, kernel = gwr_kernel ,adaptive = gwr_adaptive) # 软件计算带宽
    } else {
      bandwidth <- as.numeric(input$gwr_bandwidth) # 用户输入带宽
    }
    gwr_bw_data$bw <- bandwidth

    # 更新进度条50%
    shinyWidgets::updateProgressBar(session = session, id = "gwr_progress", value = 50)

    gwr_cp_shp_data <- NULL
    cp_spdf <- NULL
    # 选择regression_points
    if(!is.null(input$gwr_cp_shapefile)){
      gwr_cp_shp_data <- gwr_cp_shapefile_data()
      cp_spdf <- as(gwr_cp_shp_data, "Spatial")
    }
    if(is.null(input$gwr_cp_shapefile)){
      # 执行GWR模型
      model <- GWmodel::gwr.basic(formula, data = spdf, kernel = gwr_kernel, adaptive = gwr_adaptive, bw = bandwidth)
    }else{
      # 执行GWR模型
      model <- GWmodel::gwr.basic(formula, data = spdf, regression.points = cp_spdf,  kernel = gwr_kernel, adaptive = gwr_adaptive, bw = bandwidth)
    }

    # 模型成功后检查FID列
    if (!is.null(model)) {
      # 获取空间数据框架
      sdf <- model$SDF
      # 检查是否存在FID列
      if (!"FID" %in% names(sdf)) {
        # 获取数据点数
        dp.n <- nrow(sdf@data)
        # sdf@data$FID <- seq_len(dp.n)
        sdf$FID <- seq_len(dp.n)
        # 更新模型结果
        model$SDF <- sdf
      }
    }

    # 更新进度条100%
    shinyWidgets::updateProgressBar(session = session, id = "gwr_progress", value = 100)
    Sys.sleep(1)  # 等待一秒,确保用户看到完成的进度条
    shiny::removeModal()

    return(model)  # 返回
  })
  # 更新图层选择控件
  observeEvent(gwr_result(), {
    req(gwr_result())

    result <- gwr_result()
    sdf <- result$SDF

    # 触发重新渲染 UI 控件
    output$gwr_map_layer <- renderUI({
        # 提取有效图层名称
        layer_names <- names(sdf)
        layer_names <- layer_names[!grepl("_SE$", layer_names)]
        layer_names <- layer_names[!grepl("y", layer_names)]
        layer_names <- layer_names[!grepl("yhat", layer_names)]
        layer_names <- layer_names[!grepl("_TV$", layer_names)]
        layer_names <- layer_names[!grepl("FID", layer_names)]

        # 创建下拉框
        selectInput(
            inputId = "gwr_map_layer",
            label = "Select Map Layer to Display",
            choices = layer_names,
            selected = layer_names[1]  # 默认选择第一个图层
        )
    })

    # 动态更新变量选择选项
    output$gwr_columns <- renderUI({
      # 筛选列名,排除不需要的列
      names <- names(sdf)
      names <- names[!grepl("_SE$", names)]
      names <- names[!grepl("y", names)]
      names <- names[!grepl("yhat", names)]
      names <- names[!grepl("_TV$", names)]
      names <- names[!grepl("FID", names)]

      # 更新选择框的选项
      selectInput(
        inputId = "gwr_columns",
        label = "Choose Variables",
        choices = names,
        multiple = TRUE,
        selected = names[1]  # 默认不选中任何选项
      )
    })
    
    # **自动触发地图更新,确保默认图层直接显示**
    observe({
      req(input$gwr_map_layer)  #
      shinyjs::delay(1000, shinyjs::click("gwr_map_layer_execute"))  
    })
  })
  #--------------------------# Table View #-------------------------#
  # 显示汇总表
  output$gwr_summaryTable <- DT::renderDataTable({
    result <- gwr_result()
    sdf <- as.data.frame(result$SDF)
    DT::datatable(
      sdf,
      extensions = 'Buttons',  # 关键要素1:声明使用扩展
      options = list(
        pageLength = 10,        # 默认每页显示10条
        lengthMenu = c(10, 15, 20, 25),  # 每页显示条目数选项
        searching = TRUE,       # 启用搜索框
        scrollX = TRUE,          # 启用水平滚动
        dom = 'Blfrtip',          # 控制界面元素布局
        buttons = list(       # 关键要素3:按钮嵌套配置
          list(extend = 'copy', text = 'Copy'),
          list(extend = 'csv', text = 'Export CSV'),
          list(extend = 'excel', text = 'Export Excel')
        )
      ),
      rownames = FALSE,         # 不显示行号
      class = "'display nowrap hover" 
    )
  })
  #--------------------------# Summary Information #-------------------------#
  # 显示模型诊断信息
  output$gwr_modelDiagnostics <- renderPrint({
    gwr_result()
  })
  #--------------------------# Web Map #-------------------------#
  # 地图可视化
  output$gwr_mapPlot <- leaflet::renderLeaflet({
    shinyjs::hide("Significant_line_div")
    shinyjs::hide("Significant_point_div")

    map <- leaflet::leaflet()%>%
          leaflet::clearMarkers() %>%
          leaflet::addProviderTiles("CartoDB.Positron")

    # 返回最终的map对象
    map
  })
  # 更新地图上的图层
  observeEvent(input$gwr_map_layer_execute, {
    req(gwr_result())
    req(input$gwr_map_layer)
    req(input$gwr_shapefile)
    req(input$gwr_independentVars)

    shinyjs::hide("Significant_point_div")
    shinyjs::hide("Significant_line_div")
    
    # 每更新一次图层清除音频文件
    prefixes_to_remove <- c("^gwr_map_audio_",
                            "^gwr_map_audio_composite_",
                            "^gwr_map_waveform_",
                            "gwr_map_sound_video.mp4")
    for (prefix in prefixes_to_remove) {
      files_to_remove <- list.files(temp_dir, pattern = prefix, full.names = TRUE)
      www_dir <- file.path(code_dir, "www")
      file_to_remove <- list.files(www_dir, pattern = prefix, full.names = TRUE)
      if (length(files_to_remove) > 0) {
        file.remove(files_to_remove)
      } else if (length(file_to_remove) > 0) {
        file.remove(file_to_remove)}
    }

    # 获取底图map对象
    map <- leaflet::leafletProxy("gwr_mapPlot", session)
    if(!is.null(map)){}
    else{print("map is null")}
    # 获取result和sdf
    result <- gwr_result()
    if(!is.null(result$SDF)){
      sdf <- result$SDF
    }else{print("result sdf is null")}

    # 获取sdf的坐标
    coords_sdf <- sp::coordinates(sdf)
    # 获取自变量independentVars
    independentvars <- as.character(input$gwr_independentVars)
    # 获取图层名称layer_to_plot
    layer_to_plot <- as.character(input$gwr_map_layer)
    # 获取shp数据
    shp_data <- gwr_shapefile_data()
    # 检查数据是否为 sf 对象
    if (!inherits(shp_data, "sf")) {
      shiny::showNotification("Unsupported data types. Input is not an sf object.", type = "error", duration = NULL)
      return()
    }
    # 修复可能的无效几何数据
    shp_data <- sf::st_make_valid(shp_data)

    library(leaflet.extras)
    library(RColorBrewer)

    cp_shp_data <- NULL
    # 修复可能的无效几何图形
    if (!is.null(input$gwr_cp_shapefile)){
        cp_shp_data <- gwr_cp_shapefile_data()
        if (!inherits(cp_shp_data, "sf")) {
          shiny::showNotification("Unsupported data types. Input is not an sf object.", type = "error", duration = NULL)
          return()
        }
        cp_shp_data <- sf::st_make_valid(cp_shp_data)
    }

    # 计算p值
    if(is.null(input$gwr_cp_shapefile)){
      result_enp <- result$GW.diagnostic$enp
      n<-nrow(sdf)
      rdf<-n-result_enp
      # 获取所有包含 "_TV" 的列名
      t_columns <- grep("_TV$", names(sdf), value = TRUE)
      for (col in t_columns){
        # 创建一个新的列名用于存储 p 值
        p_value_col <- gsub("_TV", "_p_value", col)
        sdf[[p_value_col]]<-2*pt(abs(sdf[[col]]), df=rdf, lower.tail = F)
      }
    } else( print(paste0("enp is null")))

    if (!(layer_to_plot %in% names(sdf))) {
      shiny::showNotification(paste("Error: Layer", layer_to_plot, "not found in dataset"), type = "error")
      return()
    }

    if (is.null(sdf[[layer_to_plot]]) || all(is.na(sdf[[layer_to_plot]]))) {
      shiny::showNotification(paste("Error: Layer", layer_to_plot, "contains only NA values"), type = "error")
      return()
    }
    # 根据几何类型进行不同专题图显示
    if ((!is.null(shp_data) && (sf::st_geometry_type(shp_data)[1] == "MULTIPOLYGON" || sf::st_geometry_type(shp_data)[1] == "POLYGON")) ||
        (!is.null(cp_shp_data) && (sf::st_geometry_type(cp_shp_data)[1] == "MULTIPOLYGON" || sf::st_geometry_type(cp_shp_data)[1] == "POLYGON"))) {
          # 根据用户选择动态设置颜色映射
          selected_palette <- input$gwr_color_palette
          if (selected_palette == "viridis") {
            color_palette <- leaflet::colorNumeric(palette = "viridis", domain = c(
              min(sdf[[layer_to_plot]], na.rm = TRUE),
              max(sdf[[layer_to_plot]], na.rm = TRUE)
            ))
          } else {
            gradient <- color_gradients[[selected_palette]]
            color_palette <- leaflet::colorNumeric(
              palette = c(gradient["low"], gradient["high"]),
              domain = c(min(sdf[[layer_to_plot]], na.rm = TRUE),
                        max(sdf[[layer_to_plot]], na.rm = TRUE))
            )
          }
          if (layer_to_plot == "Intercept" || layer_to_plot %in% independentvars) {
            shinyjs::hide("Significant_line_div")
            shinyjs::hide("Significant_point_div")
            # 添加多边形填充图&图例
            if (sf::st_geometry_type(shp_data)[1] == "MULTIPOLYGON" || sf::st_geometry_type(shp_data)[1] == "POLYGON"){
              map %>%
                  leaflet::clearGroup("Polygons") %>%
                  leaflet::clearGroup("Circles") %>%
                  leaflet::removeControl("Polygons") %>%
                  leaflet::removeControl("Circles") %>%
                  leaflet::clearGroup("Significant") %>%
                  leaflet::removeControl("Significant") %>%
                  # setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 9) %>%
                  leaflet::addPolygons(
                    data = shp_data,
                    fillColor = ~color_palette(sdf[[layer_to_plot]]),
                    color = "black",
                    fillOpacity = 0.4,
                    weight = 0.3,
                    opacity = 0.5,
                    # popup = paste("FID : ",sdf[["FID"]], "<br>", layer_to_plot, " : ",  sdf[[layer_to_plot]]),
                    group = "Polygons",
                    layerId = sdf[["FID"]]
                  )%>%
                  leaflet::addLegend(
                    position = "bottomright",
                    pal = color_palette,                                # 图例位置
                    values = sdf[[layer_to_plot]],
                    title = layer_to_plot,                              # 图例标题
                    labFormat = labelFormat(prefix = "", suffix = ""),  # 可选:标签格式
                    opacity = 1,                                         # 图例的不透明度
                    layerId = "Polygons"
                  )%>%
                  # 动态计算地图边界
                  leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                            lat1 = min(coords_sdf[, 2]),
                            lng2 = max(coords_sdf[, 1]),
                            lat2 = max(coords_sdf[, 2]))
            } else if (sf::st_geometry_type(cp_shp_data)[1] == "MULTIPOLYGON" || sf::st_geometry_type(cp_shp_data)[1] == "POLYGON"){
              map %>%
                  leaflet::clearGroup("Polygons") %>%
                  leaflet::clearGroup("Circles") %>%
                  leaflet::removeControl("Polygons") %>%
                  leaflet::removeControl("Circles") %>%
                  leaflet::clearGroup("Significant") %>%
                  leaflet::removeControl("Significant") %>%
                  # setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 9) %>%
                  leaflet::addPolygons(
                    data = cp_shp_data,
                    fillColor = ~color_palette(sdf[[layer_to_plot]]),
                    color = "black",
                    fillOpacity = 0.4,
                    weight = 0.3,
                    opacity = 0.5,
                    # popup = paste("FID : ",sdf[["FID"]], "<br>",layer_to_plot, " : ",  sdf[[layer_to_plot]]),
                    group = "Polygons",
                    layerId = sdf[["FID"]]
                  )%>%
                  leaflet::addLegend(
                    position = "bottomright",
                    pal = color_palette,                                # 图例位置
                    values = sdf[[layer_to_plot]],
                    title = layer_to_plot,                              # 图例标题
                    labFormat = labelFormat(prefix = "", suffix = ""),  # 可选:标签格式
                    opacity = 1,                                         # 图例的不透明度
                    layerId = "Polygons"
                  )%>%
                  # 动态计算地图边界
                  leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                            lat1 = min(coords_sdf[, 2]),
                            lng2 = max(coords_sdf[, 1]),
                            lat2 = max(coords_sdf[, 2]))
            }

            # Significance 图
            if(input$gwr_significance_show && is.null(input$gwr_cp_shapefile)){
              shinyjs::show("Significant_line_div")

              # 根据 p 值创建 dashArray 向量
              dash_array_vec <- function(p_value) {
                ifelse(p_value < 0.05, "10,1", "5, 10")
              }

              # 创建一个新的列名用于存储 p 值
              p_value_col <- paste0(layer_to_plot, "_p_value")
              # 添加多边形填充图&图例
              if (sf::st_geometry_type(shp_data)[1] == "MULTIPOLYGON" || sf::st_geometry_type(shp_data)[1] == "POLYGON"){
                map %>%
                    leaflet::clearGroup("Significant") %>%
                    leaflet::removeControl("Significant") %>%
                    # setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 9) %>%
                    leaflet::addPolygons(
                      data = shp_data,
                      color = "black",
                      weight = 1,
                      dashArray = dash_array_vec(sdf[[p_value_col]]), # 粗线条为实线,细线条为虚线
                      fillOpacity = 0,
                      opacity = 1,
                      # popup = paste(layer_to_plot, " : ",  sdf[[layer_to_plot]]),
                      group = "Significant"
                    )%>%
                    # 动态计算地图边界
                    leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                              lat1 = min(coords_sdf[, 2]),
                              lng2 = max(coords_sdf[, 1]),
                              lat2 = max(coords_sdf[, 2]))
              } else if (sf::st_geometry_type(cp_shp_data)[1] == "MULTIPOLYGON" || sf::st_geometry_type(cp_shp_data)[1] == "POLYGON"){
                map %>%
                    leaflet::clearGroup("Significant") %>%
                    leaflet::removeControl("Significant") %>%
                    # setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 9) %>%
                    leaflet::addPolygons(
                      data = cp_shp_data,
                      color = "black",
                      weight = 1,
                      dashArray = dash_array_vec, # 粗线条为实线,细线条为虚线
                      fillOpacity = 0,
                      opacity = 1,
                      group = "Significant"
                    )%>%
                    # 动态计算地图边界
                    leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                              lat1 = min(coords_sdf[, 2]),
                              lng2 = max(coords_sdf[, 1]),
                              lat2 = max(coords_sdf[, 2]))
              }
            }else{shinyjs::hide("Significant_line_div")}
          } else if (layer_to_plot == "residual" || layer_to_plot == "CV_Score" ||
                     layer_to_plot == "Stud_residual" || layer_to_plot == "Local_R2") {
            shinyjs::hide("Significant_point_div")
            shinyjs::hide("Significant_line_div")
            # 添加多边形填充图&图例
            if (sf::st_geometry_type(shp_data)[1] == "MULTIPOLYGON" || sf::st_geometry_type(shp_data)[1] == "POLYGON"){
              map %>%
                  leaflet::clearGroup("Polygons") %>%
                  leaflet::clearGroup("Circles") %>%
                  leaflet::removeControl("Polygons") %>%
                  leaflet::removeControl("Circles") %>%
                  leaflet::clearGroup("Significant") %>%
                  leaflet::removeControl("Significant") %>%
                  # setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 9) %>%
                  leaflet::addPolygons(
                    data = shp_data,
                    fillColor = ~color_palette(sdf[[layer_to_plot]]),
                    color = "black",
                    fillOpacity = 0.4,
                    weight = 0.3,
                    opacity = 0.5,
                    # popup = paste("FID : ",sdf[["FID"]], "<br>", layer_to_plot,  " : ", sdf[[layer_to_plot]]),
                    group = "Polygons"
                  )%>%
                  leaflet::addLegend(
                    position = "bottomright",
                    pal = color_palette,                                # 图例位置
                    values = sdf[[layer_to_plot]],
                    title = layer_to_plot,                              # 图例标题
                    labFormat = labelFormat(prefix = "", suffix = ""),  # 可选:标签格式
                    opacity = 1,                                         # 图例的不透明度
                    layerId = "Polygons"
                  )%>%
                  # 动态计算地图边界
                  leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                            lat1 = min(coords_sdf[, 2]),
                            lng2 = max(coords_sdf[, 1]),
                            lat2 = max(coords_sdf[, 2]))
            } else if (sf::st_geometry_type(cp_shp_data)[1] == "MULTIPOLYGON" || sf::st_geometry_type(cp_shp_data)[1] == "POLYGON"){
              map %>%
                  leaflet::clearGroup("Polygons") %>%
                  leaflet::clearGroup("Circles") %>%
                  leaflet::removeControl("Polygons") %>%
                  leaflet::removeControl("Circles") %>%
                  leaflet::clearGroup("Significant") %>%
                  leaflet::removeControl("Significant") %>%
                  # setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 9) %>%
                  leaflet::addPolygons(
                    data = cp_shp_data,
                    fillColor = ~color_palette(sdf[[layer_to_plot]]),
                    color = "black",
                    fillOpacity = 0.4,
                    weight = 0.3,
                    opacity = 0.5,
                    # popup = paste("FID : ",sdf[["FID"]], "<br>", layer_to_plot,  " : ", sdf[[layer_to_plot]]),
                    group = "Polygons"
                  )%>%
                  leaflet::addLegend(
                    position = "bottomright",
                    pal = color_palette,                                # 图例位置
                    values = sdf[[layer_to_plot]],
                    title = layer_to_plot,                              # 图例标题
                    labFormat = labelFormat(prefix = "", suffix = ""),  # 可选:标签格式
                    opacity = 1,                                         # 图例的不透明度
                    layerId = "Polygons"
                  )%>%
                  # 动态计算地图边界
                  leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                            lat1 = min(coords_sdf[, 2]),
                            lng2 = max(coords_sdf[, 1]),
                            lat2 = max(coords_sdf[, 2]))
            }
          }
      } else if ((is.null(cp_shp_data) && (sf::st_geometry_type(shp_data)[1] == "POINT" || sf::st_geometry_type(shp_data)[1] == "MULTIPOINT") ) ||
                 (!is.null(cp_shp_data) && !is.null(input$gwr_cp_shapefile) && (sf::st_geometry_type(cp_shp_data)[1] == "POINT" || sf::st_geometry_type(cp_shp_data)[1] == "MULTIPOINT"))){
                  # 根据用户选择动态设置颜色映射
                  domain <- c(min(sdf[[layer_to_plot]], na.rm = TRUE),
                  max(sdf[[layer_to_plot]], na.rm = TRUE))
                  selected_palette <- input$gwr_color_palette
                  if (selected_palette == "viridis") {
                    color_palette <- leaflet::colorNumeric(palette = "viridis", domain = domain)
                  } else {
                    colors <- color_gradients[[selected_palette]]
                    color_palette <- leaflet::colorNumeric(palette = colors, domain = domain)
                  }

                  if (layer_to_plot == "Intercept" || layer_to_plot %in% independentvars) {
                    shinyjs::hide("Significant_line_div")
                    shinyjs::hide("Significant_point_div")
                    # 设置半径大小
                    point_radius <- 50
                    # 添加气泡图&图例
                    map %>%
                        leaflet::clearGroup("Polygons") %>%
                        leaflet::clearGroup("Circles") %>%
                        leaflet::removeControl("Polygons") %>%
                        leaflet::removeControl("Circles") %>%
                        leaflet::clearGroup("Significant") %>%
                        leaflet::removeControl("Significant") %>%
                        # setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 11) %>%
                        leaflet::addCircles(
                                  data = sdf,
                                  weight = 3,
                                  radius = point_radius,
                                  color = ~color_palette(sdf[[layer_to_plot]]),
                                  fillOpacity = 1,
                                  lng = coords_sdf[, 1],
                                  lat = coords_sdf[, 2],
                                  # popup = paste("FID : ",sdf[["FID"]], "<br>", layer_to_plot, " : ",  sdf[[layer_to_plot]]),
                                  opacity = 1,
                                  group = "Circles",
                                  layerId = sdf[["FID"]])%>%
                        leaflet::addLegend(
                          position = "bottomright",
                          pal = color_palette,                                # 图例位置
                          values = sdf[[layer_to_plot]],
                          title = layer_to_plot,                              # 图例标题
                          labFormat = labelFormat(prefix = "", suffix = ""),  # 可选:标签格式
                          opacity = 1,                                         # 图例的不透明度
                          layerId = "Circles"
                        )%>%
                        # 动态计算地图边界
                        leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                                  lat1 = min(coords_sdf[, 2]),
                                  lng2 = max(coords_sdf[, 1]),
                                  lat2 = max(coords_sdf[, 2]))

                    # Significance 图
                    if(input$gwr_significance_show && is.null(input$gwr_cp_shapefile)){
                      shinyjs::show("Significant_point_div")
                      # 创建一个新的列名用于存储 p 值
                      p_value_col <- paste0(layer_to_plot, "_p_value")

                      # 假设sdf是一个SpatialPointsDataFrame,coords_sdf是其坐标矩阵
                      sdf_df <- as.data.frame(sdf@data)

                      # 将坐标添加到数据框中
                      sdf_df$lng <- coords_sdf[, 1]
                      sdf_df$lat <- coords_sdf[, 2]
                      # 过滤数据
                      p_values <- sdf_df %>% dplyr::pull(!!dplyr::sym(p_value_col))
                      sdf_significant <- sdf_df[p_values < 0.05 & !is.na(p_values), ]
                      sdf_not_significant <- sdf_df[p_values >= 0.05 & !is.na(p_values), ]

                      # 为两组数据创建不同的图标
                      icon_not_significant <-  leaflet::makeIcon(
                                                iconUrl = "https://s2.loli.net/2024/10/29/sih4RPljFwvVT5S.png",
                                                iconWidth = 15, iconHeight = 15
                                                # iconAnchorX = 0, iconAnchorY = 94 https://s2.loli.net/2024/10/29/sih4RPljFwvVT5S.png X.png
                                              )

                      # 添加气泡图&图例
                      map %>%
                          leaflet::clearGroup("Significant") %>%
                          leaflet::removeControl("Significant") %>%
                          setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 11) %>%
                          leaflet::addMarkers(data = sdf_not_significant,
                                            lng = ~lng, lat = ~lat,
                                            icon = icon_not_significant,
                                            group = "Significant")
                    }
                  } else if (layer_to_plot == "residual" || layer_to_plot == "CV_Score" ||
                            layer_to_plot == "Stud_residual" || layer_to_plot == "Local_R2") {
                    shinyjs::hide("Significant_point_div")
                    shinyjs::hide("Significant_line_div")
                    # 设置半径大小
                    point_radius <- 50
                    # 添加气泡图&图例
                    map %>%
                        leaflet::clearGroup("Polygons") %>%
                        leaflet::clearGroup("Circles") %>%
                        leaflet::removeControl("Polygons") %>%
                        leaflet::removeControl("Circles") %>%
                        leaflet::clearGroup("Significant") %>%
                        leaflet::removeControl("Significant") %>%
                        # setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 11) %>%
                        leaflet::addCircles(
                                  data = sdf,
                                  weight = 3,
                                  radius = point_radius,
                                  color = ~color_palette(sdf[[layer_to_plot]]),
                                  fillOpacity = 1,
                                  lng = coords_sdf[, 1],
                                  lat = coords_sdf[, 2],
                                  # popup = paste("FID : ",sdf[["FID"]], "<br>", layer_to_plot, " : ",  sdf[[layer_to_plot]]),
                                  opacity = 1,
                                  group = "Circles",
                                  layerId = sdf[["FID"]])%>%
                        leaflet::addLegend(
                          position = "bottomright",
                          pal = color_palette,                                # 图例位置
                          values = sdf[[layer_to_plot]],
                          title = layer_to_plot,                              # 图例标题
                          labFormat = labelFormat(prefix = "", suffix = ""),  # 可选:标签格式
                          opacity = 1,                                         # 图例的不透明度
                          layerId = "Circles"
                        )%>%
                        # 动态计算地图边界
                        leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                                  lat1 = min(coords_sdf[, 2]),
                                  lng2 = max(coords_sdf[, 1]),
                                  lat2 = max(coords_sdf[, 2]))
                  }
      }
  })
  # 清除显著性图层
  observeEvent(input$gwr_significance_show,{
    # 获取底图map对象
    map <- leaflet::leafletProxy("gwr_mapPlot", session)
    if(!is.null(map)){}
    else{print("map is null")}
    if(!input$gwr_significance_show){
      shinyjs::hide("Significant_line_div")
      shinyjs::hide("Significant_point_div")
      # 清除所有图层
      map %>%
          leaflet::clearGroup("Significant") %>%
          leaflet::removeControl("Significant")
    }
    map
  })
  # 用 reactiveValues 存储用户点击的点和生成的短音频文件路径
  gwr_rv <- reactiveValues(
    clicked_points = list(),  # 用于存放点击的经纬度(仅连线模式时使用)
    audio_files = character() # 存放每个点击生成的短音频文件路径
  )
  # 点击地图产生音频
  observeEvent(input$gwr_mapPlot_click, {
    req(gwr_result())
    click <- input$gwr_mapPlot_click

    # 选择模式:点击模式直接生成短音频
    if(input$gwr_audio_mode == "click"){
      # 获取必要数据
      gwr_sdf <- gwr_result()$SDF
      layer_to_plot <- input$gwr_map_layer

      # 这里假设已将 gwr_sdf 转换为 sf 对象,或直接使用 spatial 包的 nearest feature 方法
      gwr_sdf_sf <- sf::st_as_sf(gwr_sdf)
      click_point <- sf::st_sfc(sf::st_point(c(click$lng, click$lat)), crs = 4326)
      selected_id <- sf::st_nearest_feature(click_point, gwr_sdf_sf)
      selected_row <- gwr_sdf[selected_id, ]
      # 生成 popup 内容
      popup_content <- paste("FID :", selected_row$FID, "<br>", 
                            layer_to_plot, ":", round(selected_row[[layer_to_plot]], 4))
      if(is.null(input$gwr_cp_shapefile)){
        shp_data <- gwr_shapefile_data() %>% 
              sf::st_as_sf() %>% 
              sf::st_make_valid() %>% 
              sf::st_set_crs(4326)
      } else {
        shp_data <- gwr_cp_shapefile_data() %>% 
              sf::st_as_sf() %>% 
              sf::st_make_valid() %>% 
              sf::st_set_crs(4326)
      }
      geom_type <- sf::st_geometry_type(shp_data) %>% 
        as.character() %>% 
        unique() 
      print(paste0("geom_type",geom_type))
      if(geom_type %in% c("POLYGON", "MULTIPOLYGON")) {
        # 找到包含点击点的 Polygon
        selected_feature <- shp_data[sf::st_contains(shp_data, click_point, sparse = FALSE), ]
        if (nrow(selected_feature) == 0) {
          shiny::showNotification("No polygon data found", type = "error")
          return(NULL)
        }

        leaflet::leafletProxy("gwr_mapPlot") %>%
          leaflet::clearPopups() %>%
          # addPopups(lng = click$lng, lat = click$lat, popup = popup_content) %>%
          leaflet::clearGroup("highlighted_features") %>%  # 清除之前的高亮
          leaflet::addPolygons(
            data = selected_feature,
            group = "highlighted_features",
            fillColor = "yellow",
            color = "#FF0000",
            fillOpacity = 0,
            weight = 2,
            highlightOptions = leaflet::highlightOptions(
              weight = 5,
              color = "#FF0000",
              bringToFront = TRUE
              )
            )%>%
          leaflet::addMarkers(  
                  lng = click$lng, 
                  lat = click$lat, 
                  popup = popup_content,
                  group = "highlighted_features",
                  icon = musicIcon # 使用自定义图标
              )
      }else if(geom_type %in% c("POINT", "MULTIPOINT")) {
        # 找到最近的点
        selected_id <- sf::st_nearest_feature(click_point, shp_data)
        selected_feature <- shp_data[selected_id, ]
        if (length(selected_feature) == 0) {
          shiny::showNotification("No point data found", type = "error")
          return(NULL)
        }
        leaflet::leafletProxy("gwr_mapPlot") %>%
          leaflet::clearPopups() %>%
          leaflet::clearGroup("highlighted_features") %>%  # 清除之前的高亮
          # addPopups(lng = click$lng, lat = click$lat, popup = popup_content) %>%
          leaflet::addCircleMarkers(
            data = selected_feature,
            group = "highlighted_features",
            color = "red",
            fillOpacity = 0,
            radius = 8,
            stroke = TRUE,
            weight = 2
          )%>%
          leaflet::addMarkers(  
                  lng = click$lng, 
                  lat = click$lat, 
                  popup = popup_content,
                  group = "highlighted_features",
                  icon = musicIcon # 使用自定义图标
              )
      } else {
        shiny::showNotification("The current geometry type is not supported", type = "error")
        return(NULL)
      }
      
      # 生成短音频文件
      coeff <- ifelse(is.na(selected_row[[layer_to_plot]]), 0, selected_row[[layer_to_plot]])
      short_filename <- paste0("www/gwr_map_audio_", as.integer(Sys.time()), "_", sample(1:1000, 1), ".wav")
      generate_audio(value = coeff,
                    filename = short_filename,
                    x_min = min(gwr_sdf[[layer_to_plot]], na.rm = TRUE),
                    x_max = max(gwr_sdf[[layer_to_plot]], na.rm = TRUE))
      
      # 更新 audio 控件,播放短音频
      shinyjs::runjs(sprintf("document.getElementById('gwr_map_audio').src='%s'; document.getElementById('gwr_map_audio').play();", short_filename))
      
    } else if(input$gwr_audio_mode == "line") {
      # 连线模式:记录点击点,不直接生成音频
      gwr_rv$clicked_points <- append(gwr_rv$clicked_points, list(c(click$lng, click$lat)))
      # 在地图上添加标记以便确认顺序
      leaflet::leafletProxy("gwr_mapPlot") %>%
        leaflet::addMarkers(
                  lng = click$lng, 
                  lat = click$lat,
                  popup = paste("point", length(gwr_rv$clicked_points)),
                  group = "point",
                  icon = musicIcon
                  )
    }
  })
  # 处理连线模式下的确认按钮:生成长音频
  observeEvent(input$gwr_confirm_audio, {
    req(length(gwr_rv$clicked_points) > 0)
    # 仅在连线模式时触发检查
    if (input$gwr_audio_mode == "line") {
      # 检查点击点数量是否足够
      if (length(gwr_rv$clicked_points) < 2) {
        shiny::showNotification("Choose at least two points", type = "error")
        return() # 不满足条件时提前退出
      }
    }
    gwr_buffer_length <- as.numeric(input$gwr_buffer_length)
    layer_to_plot <- input$gwr_map_layer

    # 显示自定义模态窗口
    showModal(modalDialog(
      title = "Processing GWR Audio...",
      tags$div(
        style = "text-align: center;",
        shinyWidgets::progressBar(
          id = "gwr_audio_progress",
          value = 0,
          display_pct = TRUE,
          status = "info",
          striped = TRUE
        )
      ),
      footer = tagList(
        modalButton("Cancel")  # 添加关闭按钮
      ),
      easyClose = FALSE
    ))

    # 1. 获取用户点击的坐标并绘制连线
    clicked_matrix <- do.call(rbind, gwr_rv$clicked_points)
    leaflet::leafletProxy("gwr_mapPlot") %>%
      leaflet::addPolylines(
                  lng = clicked_matrix[,1], 
                  lat = clicked_matrix[,2],
                  color = "red",
                  weight = gwr_buffer_length,
                  opacity = 0.8,
                  group = "point_line") %>%
      leaflet::clearGroup("highlighted_features")  # 清除之前的高亮
    
    # 2. 创建 sf 线对象并确保坐标系统一
    clicked_line <- sf::st_linestring(clicked_matrix) %>%
      sf::st_sfc(crs = 4326) %>%
      sf::st_make_valid()
    line_buffer <- sf::st_buffer(clicked_line, dist = gwr_buffer_length)  # 可调整缓冲区大小
  
    # 3. 处理空间数据
    gwr_sdf <- gwr_result()$SDF
    if(is.null(input$gwr_cp_shapefile)){
      shp_data <- gwr_shapefile_data() %>% 
            sf::st_as_sf() %>% 
            sf::st_make_valid() %>% 
            sf::st_set_crs(4326)
    } else {
      shp_data <- gwr_cp_shapefile_data() %>% 
            sf::st_as_sf() %>% 
            sf::st_make_valid() %>% 
            sf::st_set_crs(4326)
    }

    # 更新进度条
    shinyWidgets::updateProgressBar(session = session, id = "gwr_audio_progress", value = 10)

    # 4. 转换Spatial对象为sf
    gwr_sdf_sf <- sf::st_as_sf(gwr_sdf) 
    gwr_sdf_sf <- gwr_sdf_sf %>%
      dplyr::rename_with(~paste0("gwr_", .), .cols = -geometry)  

    # 将gwr计算结果合并到原始shp属性(假设行顺序一致)
    if(nrow(shp_data) == nrow(gwr_sdf_sf)) {
      shp_data_with_gwr <- shp_data %>%
        dplyr::bind_cols(sf::st_drop_geometry(gwr_sdf_sf)) %>%  # 使用正确的属性数据
        sf::st_sf()
    } else {
      shiny::showNotification("The number of data rows is inconsistent", type = "error")
      shiny::removeModal()
      return()
    }

    layer_name <- paste0("gwr_",layer_to_plot)

    geom_type <- sf::st_geometry_type(shp_data_with_gwr) %>% 
      as.character() %>% 
      unique() %>% 
      dplyr::first()

    if(geom_type %in% c("POLYGON", "MULTIPOLYGON")) {
      # 精确相交检测
      intersects <- tryCatch({
        sf::st_filter(shp_data_with_gwr, line_buffer)
      }, error = function(e) {
        shiny::showNotification("Fail in intersects", type = "error")
        shiny::removeModal()
        return(NULL)
      })
      
      intersect_features <- intersects
      # **绘制调试图,检查是否有交集**
      plot(sf::st_geometry(shp_data_with_gwr), col = "blue", border = "black", main = "空间要素 vs. 线")
      plot(sf::st_geometry(line_buffer), col = "red", lwd = gwr_buffer_length, add = TRUE)

      leaflet::leafletProxy("gwr_mapPlot") %>%
        leaflet::addPolygons(
          data = intersects,
          group = "highlighted_features",
          color = "#FF0000",
          fillOpacity = 0,
          weight = 2,
          highlightOptions = leaflet::highlightOptions(
            weight = 5,
            color = "#FF0000",
            bringToFront = TRUE
            )
          )
    }else if(geom_type %in% c("POINT", "MULTIPOINT")) {
      intersects <- tryCatch({
        sf::st_filter(shp_data_with_gwr, line_buffer)
      }, error = function(e) {
        shiny::showNotification("Fail in intersects", type = "error")
        shiny::removeModal()
        return(NULL)
      })
      intersect_features <- intersects

      if(length(intersect_features) == 0) {
        shiny::showNotification("The line does not pass through any features", type = "warning")
        shiny::removeModal()
        return()
      }
      # **绘制调试图,检查是否有交集**
      plot(sf::st_geometry(shp_data_with_gwr), col = "blue", border = "black", main = "空间要素 vs. 线")
      plot(sf::st_geometry(line_buffer), col = "red", lwd = gwr_buffer_length, add = TRUE)

      leaflet::leafletProxy("gwr_mapPlot") %>%
        leaflet::addCircleMarkers(
          data = intersect_features,
          group = "highlighted_features",
          color = "red",
          fillOpacity = 0,
          radius = 8,
          stroke = TRUE,
          weight = 2
        )
    } else {
      shiny::showNotification("The current geometry type is not supported", type = "error")
      shiny::removeModal()
      return()
    }
    
    # 更新进度条
    shinyWidgets::updateProgressBar(session = session, id = "gwr_audio_progress", value = 20)

    # 6. 沿路径排序(确保交点顺序与点击点一致)
    if (length(intersect_features) > 0) {
      start_point <- sf::st_sfc(sf::st_point(clicked_matrix[1, ]), crs = 4326)
      intersect_features <- intersect_features %>%
        dplyr::mutate(
          dist_to_start = as.numeric(sf::st_distance(geometry, start_point))
        ) %>%
        dplyr::arrange(dist_to_start)
    }

    # 7. 生成音频
    gwr_rv$audio_files <- character()  # 重置音频存储

    # 更新进度条
    shinyWidgets::updateProgressBar(session = session, id = "gwr_audio_progress", value = 30 )

    # 8. 遍历要素,生成音频
    for (i in seq_len(nrow(intersect_features))) {
      feature <- intersect_features[i, ]
      coeff <- ifelse(is.na(feature[[layer_name]]), 0, feature[[layer_name]])
      short_filename <- paste0("www/gwr_map_audio_", i, ".wav")

      generate_audio(
        value = coeff,
        filename = short_filename,
        x_min = min(shp_data_with_gwr[[layer_name]], na.rm = TRUE),
        x_max = max(shp_data_with_gwr[[layer_name]], na.rm = TRUE)
      )

      gwr_rv$audio_files <- c(gwr_rv$audio_files, short_filename)

      # 更新进度条
      shinyWidgets::updateProgressBar(session = session, id = "gwr_audio_progress", value = 30 + (i/nrow(intersect_features)) * 30)
    }
    
    # 9. 合成所有短音频文件为一个长音频
    composite_filename <- paste0("www/gwr_map_audio_composite.wav")
    concatenate_audio(gwr_rv$audio_files, composite_filename)
    # 确保 FFmpeg 使用合成后的音频
    sound_road <- composite_filename  # 这里修正

    # 提取系数数据
    ranges <- seq_len(nrow(intersect_features))  # 用于 X 轴
    coeff_values <- intersect_features[[layer_name]]  # 提取回归系数
    ranges_length <- length(ranges)
    # 生成初始曲线图
    waveform_plot <- ggplot2::ggplot(data.frame(x = ranges, y = coeff_values), ggplot2::aes(x = x, y = y)) +
      ggplot2::geom_point(color = "blue", size = 2) +  
      ggplot2::geom_smooth(method = "loess", color = "blue", span = 0.5, se = FALSE) +  
      ggplot2::theme_minimal() +
      ggplot2::theme(
        panel.background = ggplot2::element_rect(fill = "white", color = NA),  
        plot.background = ggplot2::element_rect(fill = "white", color = NA)  
      ) +
      ggplot2::ggtitle("Coefficient Variation Over Features") +
      ggplot2::xlab("Feature Index") + 
      ggplot2::ylab("Coefficient Value")

    # 逐帧生成视频
    waveform_images <- c()
    for (i in seq_along(ranges)) {
      frame_plot <- waveform_plot +
        ggplot2::geom_vline(xintercept = ranges[i], color = "red", linetype = "dashed", size = 1) +
        ggplot2::ggtitle(paste("Feature:", i, "  Coefficient:", round(coeff_values[i], 4)))

      frame_image <- file.path(temp_dir, paste0("gwr_map_waveform_", i, ".png"))
      ggplot2::ggsave(frame_image, frame_plot, width = 6, height = 4, dpi = 150)
      waveform_images <- c(waveform_images, frame_image)

      shinyWidgets::updateProgressBar(session, "gwr_video_progress", value = 60 + (i / ranges_length) * 30)
    }

    # 计算音频时长
    audio_info <- tuneR::readWave(composite_filename)
    total_frames <- length(waveform_images)
    total_audio_duration <- length(audio_info@left) / audio_info@samp.rate
    # 计算帧率,使得视频时长与音频一致
    frame_rate <- total_frames / total_audio_duration
    # 确保 framerate 合理(避免异常)
    if (frame_rate < 1) frame_rate <- 1
    if (frame_rate > 30) frame_rate <- 30  # 限制在 1-30 fps,防止帧率过大或过小
    # 生成视频
    waveform_video <- file.path(temp_dir, "gwr_map_waveform_video.mp4")
    av::av_encode_video(
      input = waveform_images,
      output = waveform_video,
      framerate = frame_rate,
      codec = "libx264",
      vfilter = "scale=720:720,format=yuv420p",
      audio = NULL,
      verbose = TRUE
    )

    # 合成音视频
    sound_video <- file.path("www", "gwr_map_sound_video.mp4")
    # sound_road <- gwr_rv$audio_files  # 获取之前生成的音频
    ffmpeg_command <- paste(
      "ffmpeg",
      "-y", 
      "-i", shQuote(waveform_video),
      "-i", shQuote(sound_road),
      "-ac 2 -c:v libx264 -c:a aac -b:a 192k -strict experimental",
      shQuote(sound_video)
    )
    ffmpeg_result <- tryCatch({
      system(ffmpeg_command, intern = FALSE)
    }, error = function(e) {
      cat("FFmpeg command failed:", e$message, "\n")
      NA
    })
    if (!is.na(ffmpeg_result) && ffmpeg_result != 0) {
      shiny::showNotification("The command execution of FFmpeg failed.Please check (1) whether ffmpeg is installed (2) whether the system path is configured.", type = "error")
      shiny::removeModal()
      return()
    }
    cat(format(Sys.time(), "%Y-%m-%d %H:%M:%S"), " 合成波形图和音频完成,文件保存为: ", sound_video, "\n")

    # 在地图左下角显示视频
    leaflet::leafletProxy("gwr_mapPlot") %>%
      leaflet::removeControl(layerId ="map_video_control") %>%
      leaflet::addControl(
         HTML(sprintf(
          '<video id="gwr_video_player" width="300" autoplay controls>
            <source src="%s" type="video/mp4">
            Your browser does not support the video tag.
          </video>
          <audio id="gwr_audio" src="%s"></audio>
          <script>
            document.getElementById("gwr_video_player").onplay = function() {
              var audio = document.getElementById("gwr_audio");
              if (audio) {
                audio.play();
              }
            };
            document.getElementById("gwr_video_player").onpause = function() {
              var audio = document.getElementById("gwr_audio");
              if (audio) {
                audio.pause();
              }
            };
            document.getElementById("gwr_video_player").ontimeupdate = function() {
              var audio = document.getElementById("gwr_audio");
              if (audio) {
                audio.currentTime = this.currentTime;
              }
            };
          </script>', sound_video, composite_filename
        )),
        position = "bottomleft",
        layerId = "map_video_control" 
      )


    # 设置音频源
    # shinyjs::runjs(sprintf("document.getElementById('gwr_map_audio').src='%s';", composite_filename))

    # 重置记录,便于下次操作
    gwr_rv$clicked_points <- list()
    gwr_rv$audio_files <- character()

    shinyWidgets::updateProgressBar(session = session, id = "gwr_audio_progress", value = 100)
    Sys.sleep(1)
    shiny::removeModal()
  })
  # 处理连线的图标
  observeEvent(input$gwr_confirm_audio_clear, {
    # 重置记录,便于下次操作
    gwr_rv$clicked_points <- list()
    gwr_rv$audio_files <- character()

    leaflet::leafletProxy("gwr_mapPlot") %>%
      leaflet::removeControl(layerId ="map_video_control") %>%
      leaflet::clearGroup("point") %>%
      leaflet::clearGroup("point_line") %>%
      leaflet::clearGroup("highlighted_features")  # 清除高亮
  })
  #--------------------------# Video #-------------------------#
  # 更新范围图层选项
  gwr_range_layer <- eventReactive(input$gwr_independentVars, {
    req(input$gwr_independentVars)
    gwr_vars <- input$gwr_independentVars

    names <- c("Intercept", gwr_vars)

    # 初始化choices列表
    choices <- list()
    # 动态添加回归系数选项
    for (var in names) {
      choices[[var]] <- var
    }

    # 设置默认选项
    default_choice <- names[1]  # 假设第一个选项是默认选项

    # 返回choices列表和默认选项
    list(choices = choices, default_choice = default_choice)
  }, ignoreNULL = FALSE)
  # 监听范围图层选项的变化并更新图层选项
  observeEvent(gwr_range_layer(), {
    layer_info <- gwr_range_layer()
    updateSelectInput(session, "gwr_range_layer", choices = layer_info$choices, selected = layer_info$default_choice)
  })
  observeEvent(input$gwr_range, {
    min_range <- as.integer(input$gwr_range[1])
    max_range <- as.integer(input$gwr_range[2])
    updateNumericInput(session, "gwr_step_length", max = max_range-min_range)
  })
  # 生成视频
  observeEvent(input$gwr_video_button, {
    # 保证参数存在
    req(input$gwr_shapefile)
    req(input$gwr_dependentVar, input$gwr_independentVars)
    req(input$gwr_kernel, input$gwr_adaptive)
    req(input$gwr_color_palette)

    # 带宽范围
    range_min <- input$gwr_range[1]
    range_max <- input$gwr_range[2]
    # 步长
    step_length <- input$gwr_step_length
    # 颜色盘
    color_palette <- input$gwr_color_palette

    # 显示自定义模态窗口
    showModal(modalDialog(
      title = "Processing GWR Video...",
      tags$div(
        style = "text-align: center;",
        shinyWidgets::progressBar(
          id = "gwr_video_progress",
          value = 0,
          display_pct = TRUE,
          status = "info",
          striped = TRUE
        )
      ),
      footer = tagList(
        modalButton("Cancel")  # 添加关闭按钮
      ),
      easyClose = FALSE
    ))
    # 更新进度条5%
    shinyWidgets::updateProgressBar(session = session, id = "gwr_video_progress", value = 5)
    cat("0% ",format(Sys.time(), "%Y-%m-%d %H:%M:%S"),"\n")
    time1 <- as.POSIXct(format(Sys.time(), "%Y-%m-%d %H:%M:%S"))  # 第一个时间点

    # 在开始生成前,确保删除 temp_dir/www_dir 文件夹下以特定前缀开头的所有文件
    prefixes_to_remove <- c("^gwr_image_video_", 
                            "gwr_image_video.mp4",
                            "^gwr_sound_audio_",
                            "^gwr_waveform_",
                            "gwr_waveform_video.mp4",
                            "gwr_waveform_overview.png",
                            "^gwr_sound_video_",
                            "gwr_sound_video.mp4",
                            "gwr_final_video.mp4")
    for (prefix in prefixes_to_remove) {
      files_to_remove <- list.files(temp_dir, pattern = prefix, full.names = TRUE)
      www_dir <- file.path(code_dir, "www")
      file_to_remove <- list.files(www_dir, pattern = prefix, full.names = TRUE)
      if (length(files_to_remove) > 0) {
        file.remove(files_to_remove)
      } else if (length(file_to_remove) > 0) {
        file.remove(file_to_remove)}
    }

    # 获取实际的数据和坐标
    gwr_shp_data <- gwr_shapefile_data()
    spdf <- as(gwr_shp_data,"Spatial")
    formula <- as.formula(paste(input$gwr_dependentVar, "~", paste(input$gwr_independentVars, collapse = "+")))
    # 获取选项
    kernel <- as.character(input$gwr_kernel)
    adaptive <- as.logical(input$gwr_adaptive)
    # 获取自变量independentVars
    independentvars <- as.character(input$gwr_independentVars)
    # 获取变量
    range_layer <- as.character(input$gwr_range_layer)
    # 确保数据是 sf 对象
    cp_shp_data <- NULL
    shp_data <- gwr_shapefile_data()
    if (!inherits(shp_data, "sf")) {shp_data <- sf::st_as_sf(shp_data)}
    # 确保数据中有有效的几何列
    if (is.null(sf::st_geometry(shp_data))) 
    {
      shiny::showNotification("shp_data does not contain geometry.", type = "error")
      shiny::removeModal()
      return()
    }

    # 频率数组
    intercept_vars <- c()
    # 根据步长生成序列
    ranges <- seq(
      from = range_min,  # 起始值
      to = range_max,    # 结束值
      by = step_length   # 步长
    )
    ranges_length <- length(ranges)

    cat(format(Sys.time(),"%Y-%m-%d %H:%M:%S")," 开始生成专题图图片...","\n")
    # 专题图视频
    # 计算模型结果
    if (is.null(input$gwr_cp_shapefile)) {
      target_shp_data <- shp_data
      is_regression_point <- FALSE
    } else {
      cp_shp_data <- gwr_cp_shapefile_data()
      if (is.null(sf::st_geometry(cp_shp_data))) 
      {
        shiny::showNotification("cp_shp_data does not contain geometry.", type = "error")
        shiny::removeModal()
        return()
      }
      target_shp_data <- cp_shp_data
      is_regression_point <- TRUE
    }
    # 几何类型计算
    geometry_type <- sf::st_geometry_type(target_shp_data)[1]
    is_polygon <- geometry_type == "MULTIPOLYGON" || geometry_type == "POLYGON"
    is_point <- geometry_type == "POINT" || geometry_type == "MULTIPOLYGON"
    # 确定全局范围:计算所有 range_layer 数据的最小值和最大值
    global_min <- Inf
    global_max <- -Inf
    range_now_fir <- 1
    for (range in ranges) {
      bandwidth <- range
      #  GWR 模型计算
      model <- if (is_regression_point) {
        GWmodel::gwr.basic(formula = formula,data = spdf,regression.points = as(target_shp_data, "Spatial"),kernel = kernel,adaptive = adaptive,bw = bandwidth)
      } else {
        GWmodel::gwr.basic(formula = formula,data = spdf,kernel = kernel,adaptive = adaptive,bw = bandwidth)
      }
      # 提取模型结果
      sdf <- model$SDF
      range_values <- sdf[[range_layer]]
      global_min <- min(global_min, min(range_values, na.rm = TRUE))
      global_max <- max(global_max, max(range_values, na.rm = TRUE))
      
      # 更新进度条
      shinyWidgets::updateProgressBar(
        session = session,
        id = "gwr_video_progress",
        value = as.integer(5 + (range_now_fir / ranges_length) * 15)
      )
      range_now_fir <- range_now_fir + 1
    }
    # 确保全局范围有效
    if (global_min == Inf || global_max == -Inf) {
      shiny::showNotification("No valid range values found for plotting.", type = "error")
      shiny::removeModal()
      return()
    }
    # 扩展比例
    padding_ratio <- 0.1  # 扩展范围的比例(10%)
    global_min <-  min(range_values)
    global_max <- max(range_values)
    print(paste0("global_min",global_min,"global_max",global_max))
    # 调整全局最小值和最大值
    range_span <- global_max - global_min
    extended_min <- global_min - range_span * padding_ratio
    extended_max <- global_max + range_span * padding_ratio
    print(paste0("extended_min",extended_min,"extended_max",extended_max))
    fixed_breaks <- seq(global_min, global_max, length.out = 7)  # 创建7份区间
    # 图片主循环
    range_now <- 1
    for (range in ranges) {
      bandwidth <- range
      #  GWR 模型计算
      model <- if (is_regression_point) {
        GWmodel::gwr.basic(formula = formula,data = spdf,regression.points = as(target_shp_data, "Spatial"),kernel = kernel,adaptive = adaptive,bw = bandwidth)
      } else {
        GWmodel::gwr.basic(formula = formula,data = spdf,kernel = kernel,adaptive = adaptive,bw = bandwidth)
      }
      
      # 提取模型结果
      sdf <- model$SDF
      result_Intercept <- as.list(sdf[[range_layer]])
      result_Intercept_vector <- unlist(result_Intercept)
      intercept_vars <- c(intercept_vars, var(result_Intercept_vector, na.rm = TRUE))

      # 绘图
      target_shp_data$value <- sdf[[range_layer]]  # 添加绘图列
      output_path <- file.path(temp_dir, paste0("gwr_image_video_", range, ".png"))
      generate_plot(target_shp_data, range_layer, bandwidth, output_path,color_palette,extended_min, extended_max,fixed_breaks)

      # 更新进度条
      shinyWidgets::updateProgressBar(
        session = session,
        id = "gwr_video_progress",
        value = as.integer(20 + (range_now / ranges_length) * 60)
      )
      range_now <- range_now + 1
    }
    cat(format(Sys.time(),"%Y-%m-%d %H:%M:%S")," 专题图图片生成完成","\n")

    cat(format(Sys.time(),"%Y-%m-%d %H:%M:%S")," 开始生成波形图和音频...","\n")
    # --- 音频参数配置 ---
    sampling_rate <- 44100  # 采样率
    duration_per_note <- 0.3  # 每个音符的持续时间(秒)
    note_samples <- duration_per_note * sampling_rate  # 每个音符样本数

    # --- 音色控制参数 ---
    waveform_ratio <- c(0.7, 0.2, 0.1)  # 基波/二次谐波/三次谐波振幅比例 (增大谐波比例(如 c(0.5,0.3,0.2))会让音色更尖锐)
    adsr <- c(attack=0.05,   # 起音时间比例(0-1)  增大 attack 会产生渐强的起始效果
              decay=0.1,     # 衰减时间比例
              sustain=0.6,   # 延音电平(0-1)
              release=0.15)  # 释音时间比例   增加 release 会让音符结束时有更长的衰减尾音
    filter_cutoff <- 0.4          # 低通滤波器截止频率(比例,0-1对应0-Nyquist) 范围 0.1-0.8,值越小高频衰减越明显
    loudness_factor <- 2          # 响度增益因子

    # --- 频率映射(保持原逻辑)---
    intercept_vars <- na.omit(intercept_vars)
    x_min <- min(intercept_vars)
    x_max <- max(intercept_vars)
    a <- (1000 - 440) / (x_max - x_min)
    b <- 440 - a * x_min
    Intercept_values <- round(a * intercept_vars + b)

    # --- 定义ADSR包络函数 ---
    apply_envelope <- function(signal, adsr_params) {
      n <- length(signal)
      attack_len <- floor(adsr_params["attack"] * n)
      decay_len <- floor(adsr_params["decay"] * n)
      release_len <- floor(adsr_params["release"] * n)
      sustain_len <- n - attack_len - decay_len - release_len
      # 构造包络曲线
      envelope <- c(
        seq(0, 1, length.out = attack_len),                     # 起音
        seq(1, adsr_params["sustain"], length.out = decay_len), # 衰减
        rep(adsr_params["sustain"], sustain_len),               # 延音
        seq(adsr_params["sustain"], 0, length.out = release_len) # 释音
      )
      # 确保长度匹配
      length(envelope) <- n  # 自动截断多余部分
      return(signal * envelope)
    }

    # --- 生成复合波形 ---
    audio_signal <- do.call(c, lapply(Intercept_values, function(freq) {
      # 生成时间序列
      t <- seq(0, duration_per_note, length.out = note_samples)
      # 方波生成(核心波形)
      square_wave <- ifelse(sin(2 * pi * freq * t) > 0, 1, -1)
      # 谐波叠加
      harmonic1 <- 0.3 * sin(2 * pi * 2 * freq * t)   # 二次谐波
      harmonic2 <- 0.2 * sin(2 * pi * 3 * freq * t)   # 三次谐波
      # 混合波形
      mixed_wave <- waveform_ratio[1] * square_wave + 
                    waveform_ratio[2] * harmonic1 + 
                    waveform_ratio[3] * harmonic2
      # 应用包络
      apply_envelope(mixed_wave, adsr)
    }))

    # --- 后处理 ---
    # 低通滤波器(软化高频)
    bf <- signal::butter(4, filter_cutoff, type = "low")  # 4阶巴特沃斯低通
    audio_filtered <- signal::filtfilt(bf, audio_signal)
    # 规范化处理
    audio_normalized <- audio_filtered / max(abs(audio_filtered)) * loudness_factor
    audio_normalized[audio_normalized > 1] <- 1   # 硬限幅
    audio_normalized[audio_normalized < -1] <- -1

    # --- 生成Wave对象 ---
    audio_wave <- tuneR::Wave(
      left = as.integer(audio_normalized * 32767),
      right = as.integer(audio_normalized * 32767),
      samp.rate = sampling_rate,
      bit = 24
    )
    # 保存为WAV文件
    sound_road <- file.path(temp_dir,paste0("gwr_sound_audio_", range_min, "_", range_max, ".wav"))
    tuneR::writeWave(audio_wave, sound_road)
    cat(format(Sys.time(),"%Y-%m-%d %H:%M:%S")," 音频文件已保存为",sound_road,"\n")


     # 音频波形图
    # 生成总的点图并连成曲线图
    # y为该带宽下的总体系数方差
    waveform_plot <- ggplot2::ggplot(data.frame(x = ranges, y = intercept_vars), ggplot2::aes(x = x, y = y)) +
      ggplot2::geom_point(color = "blue", size = 2) +  # 添加离散点
      ggplot2::geom_smooth(method = "loess", color = "blue", span = 0.5, se = FALSE) +  # 平滑曲线
      ggplot2::theme_minimal() +
      ggplot2::theme(
        panel.background = ggplot2::element_rect(fill = "white", color = NA),  # 设定绘图区域背景为白色
        plot.background = ggplot2::element_rect(fill = "white", color = NA)  # 设定整体背景为白色
      ) +
      ggplot2::ggtitle("Frequency Variation Over Ranges") +
      ggplot2::xlab("Ranges") + ggplot2::ylab("Intercept Values")

    # 保存总波形图
    ggplot2::ggsave(file.path(temp_dir, "gwr_waveform_overview.png"), waveform_plot, width = 6, height = 4, dpi = 150)
    # 生成逐帧图片
    i = 1
    for (range in ranges) {
      single_waveform_plot <- waveform_plot +
        ggplot2::geom_vline(xintercept = range, color = "red", linetype = "dashed", size = 1) +
        ggplot2::ggtitle(paste("Range:", range, "  Variance of variable coefficients:", as.character(round(intercept_vars[i],4))))

      waveform_image <- file.path(temp_dir, paste0("gwr_waveform_", as.character(range), ".png"))
      ggplot2::ggsave(waveform_image, single_waveform_plot, width = 6, height = 4, dpi = 150)
      shinyWidgets::updateProgressBar(
        session = session,
        id = "gwr_video_progress",
        value = as.integer(80 + (i / ranges_length) * 15)
      )
      i = i + 1
    }
    waveform_images <- file.path(temp_dir, paste0("gwr_waveform_", as.character(ranges), ".png"))
    waveform_video <- file.path(temp_dir, "gwr_waveform_video.mp4")
    av::av_encode_video(waveform_images, output = waveform_video, framerate = 10, codec = "libx264", vfilter = "scale=720:720")
    cat(format(Sys.time(),"%Y-%m-%d %H:%M:%S")," 波形图文件已保存为",waveform_video,"\n")

    # 确保总音频时长与图片生成的视频时长一致  
    # 计算音频总时长
    audio_duration <- length(audio_signal) / sampling_rate  # 音频总时长 (秒)
    # 确保视频帧率适配音频时长
    # 图片数量(即范围跨度)
    num_images <- length(waveform_images)
    # 动态计算帧率,使视频总时长与音频时长一致
    framerate <- num_images / audio_duration
    # 打印调试信息
    cat("音频时长:", audio_duration, "秒\n")
    cat("图片数量:", num_images, "\n")
    cat("视频帧率:", framerate, "帧/秒\n")

    # 专题图视频(无音频)
    image_files <- file.path(temp_dir, paste0("gwr_image_video_", ranges, ".png"))
    image_video <- file.path("www", "gwr_image_video.mp4")
    av::av_encode_video(image_files, output = image_video, framerate = framerate, codec = "libx264", vfilter = "scale=720:720")

    # 波形图和音频
    cat(format(Sys.time(),"%Y-%m-%d %H:%M:%S")," 开始合成波形图和音频...","\n")
    video_file <- file.path(temp_dir, paste0("gwr_sound_video_", range_min, "_", range_max, ".mp4"))
    av::av_encode_video(waveform_images,
                        output = video_file,
                        framerate = framerate,
                        codec = "libx264",
                        vfilter = "scale=720:720")
    # 使用FFmpeg合成波形图和音频
    sound_video <- file.path("www", "gwr_sound_video.mp4")
    # 合成音视频
    ffmpeg_command <- paste(
      "ffmpeg",
      "-y", # 添加 -y 强制覆盖文件
      "-i", shQuote(video_file),
      "-i", shQuote(sound_road),
      "-ac 2 -c:v libx264 -c:a aac -b:a 192k -strict experimental",
      shQuote(sound_video)
    )
    # 调用系统命令运行 FFmpeg
    ffmpeg_result <- tryCatch({
      system(ffmpeg_command, intern = FALSE)
    }, error = function(e) {
      cat("FFmpeg command failed:", e$message, "\n")
      NA  # 返回一个默认值
    })
    if (!is.na(ffmpeg_result) && ffmpeg_result != 0) {
      shiny::showNotification("The command execution of FFmpeg failed.Please check (1) whether ffmpeg is installed (2) whether the system path is configured.", type = "error")
      shiny::removeModal()
      return()
    }
    # 打印合成结果
    cat(paste0(format(Sys.time(),"%Y-%m-%d %H:%M:%S")," 合成波形图和音频完成,文件保存为: ", sound_video, "\n"))
    time2 <- as.POSIXct(format(Sys.time(),"%Y-%m-%d %H:%M:%S"))  # 第二个时间点
    time_diff <- difftime(time2, time1, units = "mins")  # 单位:分钟
    print(paste0("波形图和音频合成时间:", time_diff," mins"))

    # 最终视频
    cat(format(Sys.time(),"%Y-%m-%d %H:%M:%S")," 开始合成最终视频文件已保存为","\n")
    final_video <- file.path("www", "gwr_final_video.mp4")
    overlay_filter <- "[1:v]scale=200:150[ovrl];[0:v][ovrl]overlay=W-w-10:H-h-10"
    # "[1:v]scale=200:150[ovrl]":将小视频缩放到200x150的大小,并将其输出到一个名为ovrl的临时虚拟视频。
    # "[0:v][ovrl]overlay=W-w-10:10":将主视频和小视频叠加在一起。W-w-10表示小视频在主视频的右上角,距离右边和上边各10像素。
    # 左上角:overlay=10:10
    # 左下角:overlay=10:H-h-10
    # 右下角:overlay=W-w-10:H-h-10
    # 使用 FFmpeg 合并两个视频(水平分屏)
    ffmpeg_combine <- paste(
      "ffmpeg -y",
      "-i", shQuote(image_video),
      "-i", shQuote(sound_video),
      "-filter_complex", shQuote(overlay_filter),
      "-c:v libx264",
      shQuote(final_video)
    )
    # system(ffmpeg_combine)
    # 调用系统命令运行 FFmpeg
    ffmpeg_result_conbine <- tryCatch({
      system(ffmpeg_combine, intern = FALSE)
    }, error = function(e) {
      cat("FFmpeg command failed:", e$message, "\n")
      NA  # 返回一个默认值
    })
    if (!is.na(ffmpeg_result_conbine) && ffmpeg_result_conbine != 0) {
      shiny::showNotification("The command execution of FFmpeg failed.Please check (1) whether ffmpeg is installed (2) whether the system path is configured.", type = "error")
      shiny::removeModal()
      return()
    }

    cat(format(Sys.time(),"%Y-%m-%d %H:%M:%S")," 最终视频文件已保存为",final_video,"\n")
    time3 <- as.POSIXct(format(Sys.time(),"%Y-%m-%d %H:%M:%S"))  # 第三个时间点
    time_diff2 <- difftime(time3, time2, units = "mins")  # 单位:分钟
    print(paste0("最后视频合成时间:", time_diff2," mins"))

    # 函数执行完成后,关闭等待窗口
    shinyWidgets::updateProgressBar(session = session, id = "gwr_video_progress", value = 100)
    Sys.sleep(1)
    shiny::removeModal()

     # 输出视频控件
    output$gwr_image_video <- renderUI({
        tags$video(
            src = image_video, 
            controls = TRUE,
            width = "300px"
        )
    })
    output$gwr_sound_video <- renderUI({
        tags$video(
            src = sound_video, 
            controls = TRUE,
            width = "300px"
        )
    })
    output$gwr_final_video <- renderUI({
        tags$video(
            src = final_video, 
            controls = TRUE,
            width = "600px"
        )
    })
  })
  #--------------------------# Multiple Visualizations #-------------------------#
  observe({
    req(input$gwr_columns)  # 确保用户已经选择了变量
    req(input$gwr_bandwidth)
    req(input$gwr_color_palette)  # 确保用户已经选择了色阶

    # 显示等待窗口
    showModal(modalDialog(
      title = "Please wait",
      "The GWR result is drawing...",
      easyClose = FALSE
    ))

    bw <- round(as.numeric(gwr_bw_data$bw), 3)

    shp_data <- gwr_shapefile_data()
    if (!inherits(shp_data, "sf")) {shp_data <- sf::st_as_sf(shp_data)}

    if (is.null(input$gwr_cp_shapefile)) {
      target_shp_data <- shp_data
      is_regression_point <- FALSE
    } else {
      cp_shp_data <- gwr_cp_shapefile_data()
      if (is.null(sf::st_geometry(cp_shp_data))) 
      {
        shiny::showNotification("cp_shp_data does not contain geometry.", type = "error")
        shiny::removeModal()
        return()
      }
      target_shp_data <- cp_shp_data
      is_regression_point <- TRUE
    }
    # 获取 gwr 结果
    gwr_result <- gwr_result()
    sdf <- gwr_result$SDF

    # 设置高分辨率参数
    dpi <- 100  # 设置DPI值
    width_px <- 1500  # 设置宽度像素
    height_px <- 1500 # 设置高度像素

    # 遍历用户选择的列并动态生成图片
    selected_columns <- input$gwr_columns
    for (col in selected_columns) {
      local({
        column_name <- col  # 需要使用 local() 避免循环中的闭包问题
        # 动态创建 UI 输出控件
        output[[paste0("gwr_plot_", column_name)]] <- renderPlot({

          # 确保列存在于数据框中
          req(column_name %in% names(sdf))

          # 创建专题图
          target_data <- target_shp_data
          target_data$value <- sdf[[column_name]]  # 将数据加入到 sf 对象中,以便于绘制专题图

          # 判断数据类型,动态选择绘图样式
          geom_type <- unique(sf::st_geometry_type(target_data))
          # 根据选择的色阶动态设置颜色
          gradient <- color_gradients[[input$gwr_color_palette]]

          if ("POINT" %in% geom_type || "MULTIPOINT" %in% geom_type) {  # 点数据:使用 color 映射
              if (input$gwr_color_palette == "viridis") {
                ggplot2::ggplot(target_data) +
                  ggplot2::geom_sf(ggplot2::aes(color = value)) +
                  ggplot2::scale_color_viridis_c() +  # 使用 Viridis 色阶
                  ggplot2::labs(title = paste0(column_name, "  (Bandwidth: ", bw, ")")) +
                  ggspatial::annotation_north_arrow(
                    location = "tl",  # 左上角
                    which_north = "true",
                    pad_x = ggplot2::unit(0.3, "cm"),
                    pad_y = ggplot2::unit(0.3, "cm"),
                    style = ggspatial::north_arrow_fancy_orienteering()
                  ) +
                  ggspatial::annotation_scale(
                    location = "bl",  # 左下角
                    width_hint = 0.3
                  ) +
                  ggplot2::theme(
                    panel.background = ggplot2::element_rect(fill = "white", color = NA),
                    panel.grid = ggplot2::element_blank(),
                    plot.background = ggplot2::element_rect(fill = "white", color = NA)
                  )
              } else {
                ggplot2::ggplot(target_data) +
                  ggplot2::geom_sf(ggplot2::aes(color = value)) +
                  ggplot2::scale_color_gradient(low = gradient["low"], high = gradient["high"]) +
                  ggplot2::labs(title = paste0(column_name, "  (Bandwidth: ", bw, ")")) +
                  ggspatial::annotation_north_arrow(
                    location = "tl",  # 左上角
                    which_north = "true",
                    pad_x = ggplot2::unit(0.3, "cm"),
                    pad_y = ggplot2::unit(0.3, "cm"),
                    style = ggspatial::north_arrow_fancy_orienteering()
                  ) +
                  ggspatial::annotation_scale(
                    location = "bl",  # 左下角
                    width_hint = 0.3
                  ) +
                  ggplot2::theme(
                    panel.background = ggplot2::element_rect(fill = "white", color = NA),
                    panel.grid = ggplot2::element_blank(),
                    plot.background = ggplot2::element_rect(fill = "white", color = NA)
                  )
              }
          } else if ("POLYGON" %in% geom_type || "MULTIPOLYGON" %in% geom_type) {  # 面数据:使用 fill 映射
            if (input$gwr_color_palette == "viridis") {
              ggplot2::ggplot(target_data) +
                ggplot2::geom_sf(ggplot2::aes(fill = value)) +
                ggplot2::scale_fill_viridis_c() +  # 使用 Viridis 色阶
                ggplot2::labs(title = paste0(column_name, "  (Bandwidth: ", bw, ")")) +
                ggspatial::annotation_north_arrow(
                    location = "tl",  # 左上角
                    which_north = "true",
                    pad_x = ggplot2::unit(0.3, "cm"),
                    pad_y = ggplot2::unit(0.3, "cm"),
                    style = ggspatial::north_arrow_fancy_orienteering()
                  ) +
                  ggspatial::annotation_scale(
                    location = "bl",  # 左下角
                    width_hint = 0.3
                  ) +
                ggplot2::theme(
                  panel.background = ggplot2::element_rect(fill = "white", color = NA),
                  panel.grid = ggplot2::element_blank(),
                  plot.background = ggplot2::element_rect(fill = "white", color = NA)
                )
            } else {
              ggplot2::ggplot(target_data) +
                ggplot2::geom_sf(ggplot2::aes(fill = value)) +
                ggplot2::scale_fill_gradient(low = gradient["low"], high = gradient["high"]) +
                ggplot2::labs(title = paste0(column_name, "  (Bandwidth: ", bw, ")")) +
                ggspatial::annotation_north_arrow(
                    location = "tl",  # 左上角
                    which_north = "true",
                    pad_x = ggplot2::unit(0.3, "cm"),
                    pad_y = ggplot2::unit(0.3, "cm"),
                    style = ggspatial::north_arrow_fancy_orienteering()
                  ) +
                  ggspatial::annotation_scale(
                    location = "bl",  # 左下角
                    width_hint = 0.3
                  ) +
                ggplot2::theme(
                  panel.background = ggplot2::element_rect(fill = "white", color = NA),
                  panel.grid = ggplot2::element_blank(),
                  plot.background = ggplot2::element_rect(fill = "white", color = NA)
                )
            }
          } else {
            shiny::showNotification("Unsupported geometry type for plotting.", type = "error")
            shiny::removeModal()
            return()
          }        
        })
      })
    }

    # # 动态图片生成完成后关闭等待窗口
    shiny::removeModal()
  })
  # 动态显示图片
  output$gwr_Plot <- renderUI({
    req(input$gwr_columns)  # 确保用户已经选择了变量

    # 每行的图片数量
    images_per_row <- min(2, length(input$gwr_columns))  # 每行最多显示 2 张图片

    plot_outputs <- lapply(seq_along(input$gwr_columns), function(i) {
      column_name <- input$gwr_columns[i]

      # 使用 column 动态调整宽度,每行最多显示 images_per_row 张图片
      column(
        width = 12 / images_per_row,  # 动态设置列宽(12 栅格系统)
        plotOutput(outputId = paste0("gwr_plot_", column_name), height = "500px", width = "100%")
      )
    })

    # 将图片控件布局在 fluidRow 中
    do.call(fluidRow, plot_outputs)
  })
  #--------------------------# Prediction #-------------------------#
  # 预测模块
  observeEvent(input$gwr_predict_execute, {
    req(gwr_result(), input$gwr_predict_datafile)

    # 显示自定义模态窗口
    showModal(modalDialog(
      title = "Processing GWR Prediction...",
      tags$div(
        style = "text-align: center;",
        shinyWidgets::progressBar(
          id = "gwr_predict_progress",
          value = 0,
          display_pct = TRUE,
          status = "info",
          striped = TRUE
        )
      ),
      footer = tagList(
        modalButton("Cancel")  # 添加关闭按钮
      ),
      easyClose = FALSE
    ))
    # 更新进度条
    shinyWidgets::updateProgressBar(session = session, id = "gwr_predict_progress", value = 10)
    
    # 读取预测数据
    df <- read.csv(input$gwr_predict_datafile$datapath)
    # 确保预测数据包含坐标
    if (!all(c("longitude", "latitude") %in% colnames(df))) {
      shiny::showNotification(
        paste("Predict data must include longitude and latitude column。"),
        type = "error",
        duration = NULL
      )
      shiny::removeModal()
      return()
    }
    # 转换为 sf 对象
    sf_obj <- sf::st_as_sf(df, coords = c("longitude", "latitude"), crs = 4326)
    # 转换为 sp 对象
    pred_data <- as(sf_obj, "Spatial")
  
    # 更新进度条
    shinyWidgets::updateProgressBar(session = session, id = "gwr_predict_progress", value = 30)

    # 获取实际的数据和坐标
    gwr_shp_data <- gwr_shapefile_data()
    # 修复可能的无效几何数据
    gwr_shp_data <- sf::st_make_valid(gwr_shp_data)
    spdf <- as(gwr_shp_data, "Spatial")
     # 获取用户选定的变量
    gwr_dependentVar <- as.character(input$gwr_dependentVar)
    gwr_independentVars <- as.character(input$gwr_independentVars)
    # 1. 检查变量存在性
    all_vars <- c(gwr_dependentVar, gwr_independentVars)
    missing_vars <- all_vars[!all_vars %in% names(gwr_shp_data)]
    if (length(missing_vars) > 0) {
      shiny::showNotification(
        paste("Missing variables:", paste(missing_vars, collapse = ", ")),
        type = "error",
        duration = NULL
      )
      shiny::removeModal()
      return()
    }
    # 2. 检查数值类型
    check_numeric <- function(var_name) {
      var_data <- gwr_shp_data[[var_name]]
      if (!is.numeric(var_data)) {
        return(var_name)
      }
      return(NULL)
    }
    # 检查所有变量
    non_numeric_vars <- unlist(lapply(all_vars, check_numeric))
    if (length(non_numeric_vars) > 0) {
      shiny::showNotification(
        paste("Non-numeric variables:", paste(non_numeric_vars, collapse = ", ")),
        type = "error",
        duration = NULL
      )
      shiny::removeModal()
      return()
    }
    # 获取用户选定的变量
    gwr_dependentVar <- as.character(input$gwr_dependentVar)
    gwr_independentVars <- as.character(input$gwr_independentVars)
    # # 验证因变量
    # validate(
    #   need(
    #     !has_chinese(gwr_dependentVar),
    #     "错误:因变量名包含中文字符!"
    #   )
    # )
    # # 分割自变量并验证
    # independent_list <- trimws(unlist(strsplit(gwr_independentVars, ",\\s*")))
    # if (length(independent_list) == 0) {
    #   validate(need(FALSE, "错误:请输入至少一个自变量!"))
    # }
    # invalid_vars <- sapply(independent_list, function(var) {
    #   has_chinese(var)
    # })
    # if (any(invalid_vars)) {
    #   invalid_names <- independent_list[invalid_vars]
    #   validate(
    #     need(
    #       FALSE,
    #       paste("错误:以下自变量包含中文字符:", paste(invalid_names, collapse = ", "))
    #     )
    #   )
    # }
    formula <- as.formula(paste(gwr_dependentVar, "~", paste(gwr_independentVars, collapse = "+")))
    bw <- gwr_bw_data$bw
    # 获取选项
    gwr_kernel <- as.character(input$gwr_kernel)
    gwr_adaptive <- as.logical(input$gwr_adaptive)

    # 更新进度条
    shinyWidgets::updateProgressBar(session = session, id = "gwr_predict_progress", value = 60)
    
    prediction <- tryCatch({
      GWmodel::gwr.predict(formula = formula, data = spdf, predictdata = pred_data, bw = bw, kernel = gwr_kernel, adaptive = gwr_adaptive)
    }, error = function(e) {
      shiny::showNotification(
        paste("GWR predict error: ", e$message),
        type = "error",
        duration = NULL
      )
      shiny::removeModal()
      return()
    })
    # 更新进度条
    shinyWidgets::updateProgressBar(session = session, id = "gwr_predict_progress", value = 80)

    # 预测数据地图
    output$gwr_predict_map <- leaflet::renderLeaflet({
      req(prediction, gwr_shp_data)  # 确保预测结果和原始数据存在
      # 检查 prediction 结果
      if (!"SDF" %in% names(prediction)) {
        return(NULL)
      }
      pred_values <- prediction$SDF$prediction
      if (is.null(pred_values)) {
        pred_values <- rep("No Prediction", nrow(df))  # 预防性填充
      }
      
      # 获取原始数据的几何类型
      geom_type <- sf::st_geometry_type(gwr_shp_data, by_geometry = FALSE) %>% 
        as.character() %>%
        unique() %>%
        dplyr::first()

      # 创建基础地图
      map <- leaflet::leaflet()%>%
        leaflet::addProviderTiles("CartoDB.Positron")

      # 添加原始数据图层(蓝色)
      if (geom_type %in% c("POINT", "MULTIPOINT")) {
        map <- map %>% 
          leaflet::addCircleMarkers(
            data = gwr_shp_data,
            radius = 3,
            color = "blue",
            fillOpacity = 0.5,
            group = "Train Data",
            popup = ~paste("Train Data:", get(input$gwr_dependentVar))
          )
      } else if (geom_type %in% c("POLYGON", "MULTIPOLYGON")) {
        map <- map %>% 
          leaflet::addPolygons(
            data = gwr_shp_data,
            color = "blue",
            weight = 1,
            fillOpacity = 0.3,
            group = "Train Data",
            popup = ~paste("Train Data:", get(input$gwr_dependentVar))
          )
      }

      # 添加预测数据图层(红色)
      if (exists("pred_values") && !all(is.na(pred_values))) {
        map <- map %>%
          leaflet::addCircleMarkers(
            data = df,
            lng = ~longitude,
            lat = ~latitude,
            radius = 5,
            color = "red",
            fillOpacity = 0.8,
            group = "Predict Data",
            popup = ~paste("Predict Data:", round(pred_values, 2))
          ) %>%
          leaflet::addLayersControl(
            overlayGroups = c("Train Data", "Predict Data"),
            options = layersControlOptions(collapsed = FALSE)
          )
      }

      # 添加图例
      map %>%
        leaflet::addLegend(
          position = "bottomright",
          colors = c("blue", "red"),
          labels = c("Train Data", "Predict Data"),
          opacity = 0.8
        )
    })

    # 显示预测模型诊断信息
    output$gwr_predict_information <- renderPrint({
      prediction
    })

    # 显示预测结果表
    output$gwr_predict_table <- DT::renderDataTable({
      result <- prediction
      sdf <- as.data.frame(result$SDF)
      DT::datatable(
        sdf,
        extensions = 'Buttons',  # 关键要素1:声明使用扩展
        options = list(
          pageLength = 10,        # 默认每页显示10条
          lengthMenu = c(10, 15, 20, 25),  # 每页显示条目数选项
          searching = TRUE,       # 启用搜索框
          scrollX = TRUE,          # 启用水平滚动
          dom = 'Blfrtip',          # 控制界面元素布局
          buttons = list(       # 关键要素3:按钮嵌套配置
            list(extend = 'copy', text = 'Copy'),
            list(extend = 'csv', text = 'Export CSV'),
            list(extend = 'excel', text = 'Export Excel')
          )
        ),
        rownames = FALSE,         # 不显示行号
        class = "'display nowrap hover" 
      )
    })

    # 更新进度条
    shinyWidgets::updateProgressBar(session = session, id = "gwr_predict_progress", value = 100)
    Sys.sleep(1)
    shiny::removeModal()
  })

  #--------------------------# MGWR #--------------------------#
  # 显著性图例显示隐藏(面/点) 还要加是面的判断
  shinyjs::hide("mgwr_Significant_line_div")
  shinyjs::hide("mgwr_Significant_point_div")
  # 按钮跳转
  observeEvent(input$mgwr_execute, {
    updateTabsetPanel(session, "mgwr_tabs", selected = "Summary Information")
  })
  # shp读取函数
  mgwr_shapefile_data <- reactive({
    req(input$mgwr_shapefile)  # 确保用户已上传文件

    # 获取上传文件的路径和扩展名
    file_path <- input$mgwr_shapefile$datapath
    file_ext <- tolower(tools::file_ext(input$mgwr_shapefile$name))  # 获取文件扩展名,转为小写
    file_name <- tools::file_path_sans_ext(input$mgwr_shapefile$name)

    # 设置 GDAL 配置选项
    Sys.setenv(SHAPE_RESTORE_SHX = "YES")

    if (file_ext == "zip") {
      # 如果是 ZIP 文件,解压缩并查找 .shp 文件
      temp_dir <- tempdir()
      unzip(file_path, exdir = temp_dir)
      shp_files <- list.files(temp_dir, pattern = paste0("^", file_name, "\\.shp$"), full.names = TRUE)
      dbf_files <- list.files(temp_dir, pattern = paste0("^", file_name, "\\.dbf$"), full.names = TRUE)

      if (length(shp_files) > 0 && length(dbf_files) > 0) {
        shp_file <- shp_files[1]
        dbf_file <- sub("\\.shp$", ".dbf", shp_file)
        shp_data <- tryCatch(
          {
            sf::st_read(shp_file, options = "ENCODING=GBK")
          },
          error = function(e) {
            shiny::showNotification("Error reading Shapefile from ZIP. Please check the file content.", type = "error")
            return(NULL)
          }
        )
        # 处理 CRS(避免警告)
        if (!is.null(sf::st_crs(shp_data))) {
          shp_data <- sf::st_transform(shp_data, 4326)  # 如果已有 CRS,重新投影
        } else {
          sf::st_crs(shp_data) <- 4326  # 如果没有 CRS,先赋值再投影
          shp_data <- sf::st_transform(shp_data, 4326)
        }
        return(shp_data)
      } else {
        shiny::showNotification("No Shapefile found in the ZIP file.", type = "error")
        return(NULL)
      }
    } else {
      # 如果上传的文件既不是 .shp 也不是 .zip,显示错误通知
      shiny::showNotification("Unsupported file type. Please upload .zip file.", type = "error")
      return(NULL)
    }
  })
  # 更新因变量选项(独立模块)
  observeEvent(mgwr_shapefile_data(), {
    req(mgwr_shapefile_data())
    var_names <- names(mgwr_shapefile_data()) 
    # 按首字母排序(不区分大小写)
    sorted_vars <- if (length(var_names) > 0) {
      var_names[order(tolower(var_names))]
    } else {
      character(0)
    }
    # 设置默认因变量(示例:选第一个变量)
    default_dependent <- if (length(sorted_vars) >= 1) sorted_vars[1] else character(0)
    
    # 更新因变量下拉框
    updateSelectInput(session,"mgwr_dependentVar",choices = sorted_vars,selected = default_dependent)
  })
  # 更新自变量选项(监听因变量和数据变化)
  observeEvent(list(mgwr_shapefile_data(), input$mgwr_dependentVar), {
    req(mgwr_shapefile_data())
    
    # 获取所有变量名并排序
    all_vars <- names(mgwr_shapefile_data())
    sorted_all_vars <- all_vars[order(tolower(all_vars))]
    
    # 安全获取当前因变量(处理未选择的情况)
    dependent_var <- if (is.null(input$mgwr_dependentVar)) {
      character(0)
    } else {
      input$mgwr_dependentVar
    }
    
    # 排除因变量(若存在)
    independent_vars <- if (dependent_var %in% sorted_all_vars) {
      setdiff(sorted_all_vars, dependent_var)
    } else {
      sorted_all_vars  # 因变量无效时显示全部变量
    }
    
    # 设置默认自变量(选前两个有效变量)
    selected_independent <- if (length(independent_vars) >= 2) {
      independent_vars[1:2]
    } else if (length(independent_vars) == 1) {
      independent_vars[1]
    } else {
      character(0)
    }
    
    # 更新自变量下拉框
    updateSelectInput(session,"mgwr_independentVars", choices = independent_vars, selected = selected_independent)
  })
  # 输出因变量选择控件
  output$mgwr_dependentVar <- renderUI({
    selectInput("mgwr_dependentVar", "Choose dependentVar", choices = NULL)
  })
  # 输出自变量选择控件
  output$mgwr_independentVars <- renderUI({
    selectInput("mgwr_independentVars", "Choose independentVars", choices = NULL, multiple = TRUE, selected = NULL)
  })
  # 带宽值
  bwValues <- reactiveValues(sliders = NULL)
  # 带宽响应函数
  observe({
      if (is.null(input$mgwr_shapefile)) { return(NULL) }  # 确保 shapefile 存在
      shp_data <- mgwr_shapefile_data()  # 读取 shapefile 数据
      req(shp_data)
      req(input$mgwr_independentVars)

      # 处理无效几何
      shp_data <- sf::st_make_valid(shp_data)

      # 计算最大距离
      if (sf::st_is_longlat(shp_data)) {
          max_distance <- as.integer(max(sf::st_distance(shp_data)))  # 计算球面距离
      } else {
          coords <- sf::st_coordinates(shp_data)
          max_distance <- as.integer(max(dist(coords)))  # 计算欧式距离
      }

      # 监听 adaptive 变化
      mgwr_adaptive <- as.logical(input$mgwr_adaptive)

      # 设定最大带宽
      max_bw <- ifelse(mgwr_adaptive, nrow(shp_data), max_distance)
      if (!is.finite(max_bw) || is.na(max_bw)) {
          max_bw <- 1000  # 兜底处理,防止 NaN 出现
      }

      # 变量列表:Intercept + 用户选择的变量
      Vars <- c("Intercept", input$mgwr_independentVars)

      # 生成滑块
      if(input$mgwr_adaptive)
      {
        bwValues$sliders <- lapply(Vars, function(var) {
          sliderInput(
              inputId = paste0("bw_", var),
              label = paste0("Bandwidth for ", var,"(Number of points)"),
              min = 0,
              max = max_bw,  # 这里响应 shapefile 和 adaptive
              value = 20
          )
        })
      }else{
        bwValues$sliders <- lapply(Vars, function(var) {
          sliderInput(
              inputId = paste0("bw_", var),
              label = paste0("Bandwidth for ", var,"(meter)"),
              min = 0,
              max = max_bw,  # 这里响应 shapefile 和 adaptive
              value = 20
          )
        })
      }
     
  })
  # 带宽输出控件
  output$mgwr_bandwidth <- renderUI({
    req(bwValues$sliders)
    if(!input$mgwr_best_bandwidth)
    {
      do.call(tagList, bwValues$sliders)
    }
  })
  #--------------------------# MGWR Analysis #-------------------------#
  # mgwr模型结果
  mgwr_result <- eventReactive(input$mgwr_execute, {
    req(input$mgwr_dependentVar, input$mgwr_independentVars)
    req(input$mgwr_shapefile)
    req(input$mgwr_kernel, input$mgwr_adaptive)

    # 显示自定义模态窗口
    showModal(modalDialog(
      title = "Processing MGWR Analysis...",
      tags$div(
        style = "text-align: center;",
        shinyWidgets::progressBar(
          id = "mgwr_progress",
          value = 0,
          display_pct = TRUE,
          status = "info",
          striped = TRUE
        )
      ),
      footer = tagList(
        modalButton("Cancel")  # 添加关闭按钮
      ),
      easyClose = FALSE
    ))
    # 更新进度条20%
    shinyWidgets::updateProgressBar(session = session, id = "mgwr_progress", value = 20)

    # 获取实际的数据和坐标
    mgwr_shp_data <- mgwr_shapefile_data()
    if (inherits(mgwr_shp_data, "sf")) {mgwr_shp_data <- sf::st_make_valid(mgwr_shp_data)}
    spdf <- as(mgwr_shp_data,"Spatial")

    # 更新进度条40%
    shinyWidgets::updateProgressBar(session = session, id = "mgwr_progress", value = 40)

    # 获取用户选定的变量
    mgwr_dependentVar <- as.character(input$mgwr_dependentVar)
    mgwr_independentVars <- as.character(input$mgwr_independentVars)

    # 1. 检查变量存在性
    all_vars <- c(mgwr_dependentVar, mgwr_independentVars)
    missing_vars <- all_vars[!all_vars %in% names(mgwr_shp_data)]
    if (length(missing_vars) > 0) {
      shiny::showNotification(
        paste("Missing variables:", paste(missing_vars, collapse = ", ")),
        type = "error",
        duration = NULL
      )
      shiny::removeModal()
      return()
    }
    # 2. 检查数值类型
    check_numeric <- function(var_name) {
      var_data <- mgwr_shp_data[[var_name]]
      if (!is.numeric(var_data)) {
        return(var_name)
      }
      return(NULL)
    }
    # 检查所有变量
    non_numeric_vars <- unlist(lapply(all_vars, check_numeric))
    if (length(non_numeric_vars) > 0) {
      shiny::showNotification(
        paste("Non-numeric variables:", paste(non_numeric_vars, collapse = ", ")),
        type = "error",
        duration = NULL
      )
      shiny::removeModal()
      return()
    }
        # 初始化 dp.locat
    dp.locat <- NULL
    # 提取坐标点
    tryCatch({
      geometry_type <- unique(sf::st_geometry_type(mgwr_shp_data))  # 获取所有几何类型
      if ("MULTIPOLYGON" %in% geometry_type || "POLYGON" %in% geometry_type) {
        centroids <- sf::st_centroid(sf::st_geometry(mgwr_shp_data))  # 仅计算几何的质心
        coords <- sf::st_coordinates(centroids)
        dp.locat <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
      } else if ("POINT" %in% geometry_type || "MULTIPOINT" %in% geometry_type) {
        coords <- sf::st_coordinates(mgwr_shp_data)
        dp.locat <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
      } else if ("LINESTRING" %in% geometry_type) {
        # 如果是线状几何,可以取中点(或其他逻辑)
        midpoints <- sf::st_line_sample(sf::st_geometry(mgwr_shp_data), n = 1)
        coords <- sf::st_coordinates(midpoints)
        dp.locat <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
      } else {
        shiny::showNotification(paste("Unsupported geometry type(s):", paste(geometry_type, collapse = ", ")), type = "error")
        shiny::removeModal()
        return()
      }
    }, error = function(e) {
      shiny::showNotification(paste("Error extracting coordinates:", e$message), type = "error", duration = NULL)
      shiny::removeModal()
      return(NULL)
    })
    # 检查 dp.locat 是否为空
    if (is.null(dp.locat)) {
      shiny::showNotification("Coordinates could not be extracted from the shapefile.", type = "error", duration = NULL)
      return()
    }
    dp.locat <- na.omit(dp.locat)
    # 检查并生成 FID 列
    if (!"FID" %in% names(mgwr_shp_data)) {
      dp.n <- nrow(dp.locat)
      mgwr_shp_data$FID <- seq_len(dp.n)
    }

    # 获取选项
    mgwr_kernel <- as.character(input$mgwr_kernel)
    mgwr_adaptive <- as.logical(input$mgwr_adaptive)
    # 构造公式
    formula <- as.formula(paste(mgwr_dependentVar, "~", paste(mgwr_independentVars, collapse = "+")))

    # 更新进度条60%
    shinyWidgets::updateProgressBar(session = session, id = "mgwr_progress", value = 60)

    # 执行MGWR模型
    if(input$mgwr_best_bandwidth){
      model <- GWmodel::gwr.multiscale(formula, data = spdf, kernel = mgwr_kernel, adaptive = mgwr_adaptive, bw.seled = FALSE)
      # 添加最优带宽到模型对象
      model$optimal_bws <- model$GW.arguments$bws
    }else{
      # 获取带宽值,确保它们是数值类型
      Vars <- c("Intercept", input$mgwr_independentVars)
      bw <- sapply(Vars, function(x) {
        as.numeric(input[[paste0("bw_", x)]])
      })
      model <- GWmodel::gwr.multiscale(formula, data = spdf, kernel = mgwr_kernel, adaptive = mgwr_adaptive, bws0 = bw, bw.seled = TRUE)
    }

    # 模型成功后检查FID列
    if (!is.null(model)) {
      # 获取空间数据框架
      sdf <- model$SDF
      # 检查是否存在FID列
      if (!"FID" %in% names(sdf)) {
        # 获取数据点数
        dp.n <- nrow(sdf@data)
        # sdf@data$FID <- seq_len(dp.n)
        sdf$FID <- seq_len(dp.n)
        # 更新模型结果
        model$SDF <- sdf
      }
    }

    # 更新进度条100%
    shinyWidgets::updateProgressBar(session = session, id = "mgwr_progress", value = 100)
    Sys.sleep(1)
    shiny::removeModal()

    return(model)  # 返回
  })
  # 更新图层选择控件
  observeEvent(mgwr_result(), {
    req(mgwr_result())

    result <- mgwr_result()
    sdf <- result$SDF

    # 触发重新渲染 UI 控件
    output$mgwr_map_layer <- renderUI({
        # 提取有效图层名称
        layer_names <- names(sdf)
        layer_names <- layer_names[!grepl("_SE$", layer_names)]
        layer_names <- layer_names[!grepl("y", layer_names)]
        layer_names <- layer_names[!grepl("yhat", layer_names)]
        layer_names <- layer_names[!grepl("_TV$", layer_names)]
        layer_names <- layer_names[!grepl("FID", layer_names)]


        # 创建下拉框
        selectInput(
            inputId = "mgwr_map_layer",
            label = "Select Map Layer to Display",
            choices = layer_names,
            selected = layer_names[1]  # 默认选择第一个图层
        )
    })

    # 动态更新变量选择选项
    output$mgwr_columns <- renderUI({
      # 筛选列名,排除不需要的列
      names <- names(sdf)
      names <- names[!grepl("_SE$", names)]
      names <- names[!grepl("y", names)]
      names <- names[!grepl("yhat", names)]
      names <- names[!grepl("_TV$", names)]
      names <- names[!grepl("FID", names)]

      # 更新选择框的选项
      selectInput(
        inputId = "mgwr_columns",
        label = "Choose Variables",
        choices = names,
        multiple = TRUE,
        selected = names[1]  # 默认不选中任何选项
      )
    })

    # **自动触发地图更新,确保默认图层直接显示**
    observe({
      req(input$mgwr_map_layer)  #
      shinyjs::delay(1000, shinyjs::click("mgwr_map_layer_execute"))  
    })
  })
  #--------------------------# Table View #-------------------------#
  # 输出模型结果
  output$mgwr_summaryTable <- DT::renderDataTable({
    result <- mgwr_result()
    sdf <- as.data.frame(result$SDF)
    DT::datatable(
      sdf,
      extensions = 'Buttons',  # 关键要素1:声明使用扩展
      options = list(
        pageLength = 10,        # 默认每页显示10条
        lengthMenu = c(10, 15, 20, 25),  # 每页显示条目数选项
        searching = TRUE,       # 启用搜索框
        scrollX = TRUE,          # 启用水平滚动
        dom = 'Blfrtip',          # 控制界面元素布局
        buttons = list(       # 关键要素3:按钮嵌套配置
          list(extend = 'copy', text = 'Copy'),
          list(extend = 'csv', text = 'Export CSV'),
          list(extend = 'excel', text = 'Export Excel')
        )
      ),
      rownames = FALSE,         # 不显示行号
      class = "'display nowrap hover" 
    )
  })
  #--------------------------# Summary Information #-------------------------#
  # 输出模型结果
  output$mgwr_modelDiagnostics <- renderPrint({
    mgwr_result()
  })
  #--------------------------# Web Map #-------------------------#
  # 地图可视化
  output$mgwr_mapPlot <- leaflet::renderLeaflet({
    map <- leaflet::leaflet() %>%
          leaflet::clearMarkers() %>%
          leaflet::addProviderTiles("CartoDB.Positron")

    # 返回最终的map对象
    map
  })
  # 更新地图上的图层
  observeEvent(input$mgwr_map_layer_execute, {
    req(mgwr_result())
    req(input$mgwr_map_layer)
    req(mgwr_shapefile_data())
    req(input$mgwr_independentVars)
    req(input$mgwr_color_palette)

    shinyjs::hide("mgwr_Significant_point_div")
    shinyjs::hide("mgwr_Significant_line_div")

    # 每更新一次图层清除音频文件
    prefixes_to_remove <- c("^mgwr_map_audio_",
                            "^mgwr_map_audio_composite_",
                            "^mgwr_map_waveform_",
                            "mgwr_map_sound_video.mp4")
    for (prefix in prefixes_to_remove) {
      files_to_remove <- list.files(temp_dir, pattern = prefix, full.names = TRUE)
      www_dir <- file.path(code_dir, "www")
      file_to_remove <- list.files(www_dir, pattern = prefix, full.names = TRUE)
      if (length(files_to_remove) > 0) {
        file.remove(files_to_remove)
      } else if (length(file_to_remove) > 0) {
        file.remove(file_to_remove)}
    }

    # 获取底图map对象
    map <- leaflet::leafletProxy("mgwr_mapPlot", session)
    if(!is.null(map)){}
    else{print("map is null")}
    # 获取result和sdf
    result <- mgwr_result()
    if(!is.null(result$SDF)){sdf <- result$SDF}else{print("result sdf is null")}

    # 获取sdf的坐标
    coords_sdf <- sp::coordinates(sdf)
    # 获取自变量independentVars
    independentvars <- as.character(input$mgwr_independentVars)
    # 获取图层名称layer_to_plot
    layer_to_plot <- input$mgwr_map_layer
    # 获取shp数据
    shp_data <- mgwr_shapefile_data()

    library(leaflet.extras)
    library(RColorBrewer)

    # 计算p值
    result_enp <- result$GW.diagnostic$enp
    n<-nrow(sdf)
    rdf<-n-result_enp
    # 获取所有包含 "_TV" 的列名
    t_columns <- grep("_TV$", names(sdf), value = TRUE)
    for (col in t_columns){
      # 创建一个新的列名用于存储 p 值
      p_value_col <- gsub("_TV", "_p_value", col)
      sdf[[p_value_col]]<-2*pt(abs(sdf[[col]]), df=rdf, lower.tail = F)
    }

    # 输出图层选择控件
    if (inherits(shp_data, "sf")) {shp_data <- sf::st_make_valid(shp_data)}
    # 根据几何类型进行不同专题图显示
    if (sf::st_geometry_type(shp_data)[1] == "MULTIPOLYGON" || sf::st_geometry_type(shp_data)[1] == "POLYGON"){
        # 根据用户选择动态设置颜色映射
        selected_palette <- input$mgwr_color_palette
        if (selected_palette == "viridis") {
          color_palette <- leaflet::colorNumeric(palette = "viridis", domain = c(
            min(sdf[[layer_to_plot]], na.rm = TRUE),
            max(sdf[[layer_to_plot]], na.rm = TRUE)
          ))
        } else {
          gradient <- color_gradients[[selected_palette]]
          color_palette <- leaflet::colorNumeric(
            palette = c(gradient["low"], gradient["high"]),
            domain = c(min(sdf[[layer_to_plot]], na.rm = TRUE),
                      max(sdf[[layer_to_plot]], na.rm = TRUE))
          )
        }
        if (layer_to_plot == "Intercept" || layer_to_plot %in% independentvars) {
          shinyjs::hide("mgwr_Significant_line_div")
          shinyjs::hide("mgwr_Significant_point_div")
          # 添加多边形填充图&图例
          if (sf::st_geometry_type(shp_data)[1] == "MULTIPOLYGON" || sf::st_geometry_type(shp_data)[1] == "POLYGON"){
            map %>%
                leaflet::clearGroup("Polygons") %>%
                leaflet::clearGroup("Circles") %>%
                leaflet::removeControl("Polygons") %>%
                leaflet::removeControl("Circles") %>%
                leaflet::clearGroup("Significant") %>%
                leaflet::removeControl("Significant") %>%
                # setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 9) %>%
                leaflet::addPolygons(
                  data = shp_data,
                  fillColor = ~color_palette(sdf[[layer_to_plot]]),
                  color = "black",
                  fillOpacity = 0.4,
                  weight = 0.3,
                  opacity = 0.5,
                  # popup = paste("FID : ",sdf[["FID"]], "<br>", layer_to_plot, " : ",  sdf[[layer_to_plot]]),
                  group = "Polygons",
                  layerId = sdf[["FID"]]
                )%>%
                leaflet::addLegend(
                  position = "bottomright",
                  pal = color_palette,                                # 图例位置
                  values = sdf[[layer_to_plot]],
                  title = layer_to_plot,                              # 图例标题
                  labFormat = labelFormat(prefix = "", suffix = ""),  # 可选:标签格式
                  opacity = 1,                                         # 图例的不透明度
                  layerId = "Polygons"
                )%>%
                # 动态计算地图边界
                leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                          lat1 = min(coords_sdf[, 2]),
                          lng2 = max(coords_sdf[, 1]),
                          lat2 = max(coords_sdf[, 2]))
          }

          # Significance 图
          if(input$mgwr_significance_show){
            shinyjs::show("mgwr_Significant_line_div")

            # 根据 p 值创建 dashArray 向量
            dash_array_vec <- function(p_value) {
              ifelse(p_value < 0.05, "10,1", "5, 10")
            }

            # 创建一个新的列名用于存储 p 值
            p_value_col <- paste0(layer_to_plot, "_p_value")
            # 添加多边形填充图&图例
            if (sf::st_geometry_type(shp_data)[1] == "MULTIPOLYGON" || sf::st_geometry_type(shp_data)[1] == "POLYGON"){
              map %>%
                  leaflet::clearGroup("Significant") %>%
                  leaflet::removeControl("Significant") %>%
                  # setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 9) %>%
                  leaflet::addPolygons(
                    data = shp_data,
                    color = "black",
                    weight = 1,
                    dashArray = dash_array_vec(sdf[[p_value_col]]), # 粗线条为实线,细线条为虚线
                    fillOpacity = 0,
                    opacity = 1,
                    # popup = paste(layer_to_plot, " : ",  sdf[[layer_to_plot]]),
                    group = "Significant"
                  )%>%
                  # 动态计算地图边界
                  leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                            lat1 = min(coords_sdf[, 2]),
                            lng2 = max(coords_sdf[, 1]),
                            lat2 = max(coords_sdf[, 2]))
            }
          }else{
              shinyjs::hide("mgwr_Significant_line_div")
          }
        } else if (layer_to_plot == "residual" || layer_to_plot == "CV_Score" ||
                    layer_to_plot == "Stud_residual" || layer_to_plot == "Local_R2") {
          shinyjs::hide("mgwr_Significant_point_div")
          shinyjs::hide("mgwr_Significant_line_div")
          # 添加多边形填充图&图例
          if (sf::st_geometry_type(shp_data)[1] == "MULTIPOLYGON" || sf::st_geometry_type(shp_data)[1] == "POLYGON"){
            map %>%
                leaflet::clearGroup("Polygons") %>%
                leaflet::clearGroup("Circles") %>%
                leaflet::removeControl("Polygons") %>%
                leaflet::removeControl("Circles") %>%
                leaflet::clearGroup("Significant") %>%
                leaflet::removeControl("Significant") %>%
                # setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 9) %>%
                leaflet::addPolygons(
                  data = shp_data,
                  fillColor = ~color_palette(sdf[[layer_to_plot]]),
                  color = "black",
                  fillOpacity = 0.4,
                  weight = 0.3,
                  opacity = 0.5,
                  # popup = paste("FID : ",sdf[["FID"]], "<br>", layer_to_plot,  " : ", sdf[[layer_to_plot]]),
                  group = "Polygons",
                  layerId = sdf[["FID"]]
                )%>%
                leaflet::addLegend(
                  position = "bottomright",
                  pal = color_palette,                                # 图例位置
                  values = sdf[[layer_to_plot]],
                  title = layer_to_plot,                              # 图例标题
                  labFormat = labelFormat(prefix = "", suffix = ""),  # 可选:标签格式
                  opacity = 1,                                         # 图例的不透明度
                  layerId = "Polygons"
                )%>%
                # 动态计算地图边界
                leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                          lat1 = min(coords_sdf[, 2]),
                          lng2 = max(coords_sdf[, 1]),
                          lat2 = max(coords_sdf[, 2]))
          }
        }
    } else if (sf::st_geometry_type(shp_data)[1] == "POINT" || sf::st_geometry_type(shp_data)[1] == " MULTIPOINT"){
        # 根据用户选择动态设置颜色映射
        domain <- c(min(sdf[[layer_to_plot]], na.rm = TRUE),
        max(sdf[[layer_to_plot]], na.rm = TRUE))
        selected_palette <- input$mgwr_color_palette
        if (selected_palette == "viridis") {
          color_palette <- leaflet::colorNumeric(palette = "viridis", domain = domain)
        } else {
          colors <- color_gradients[[selected_palette]]
          color_palette <- leaflet::colorNumeric(palette = colors, domain = domain)
        }
        # 设置半径大小
        point_radius <- 50
        if (layer_to_plot == "Intercept" || layer_to_plot %in% independentvars) {
          shinyjs::hide("mgwr_Significant_line_div")
          shinyjs::hide("mgwr_Significant_point_div")
          # 添加气泡图&图例
          map %>%
              leaflet::clearGroup("Polygons") %>%
              leaflet::clearGroup("Circles") %>%
              leaflet::removeControl("Polygons") %>%
              leaflet::removeControl("Circles") %>%
              leaflet::clearGroup("Significant") %>%
              leaflet::removeControl("Significant") %>%
              # setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 11) %>%
              leaflet::addCircles(
                        data = sdf,
                        weight = 3,
                        radius = point_radius,
                        color = ~color_palette(sdf[[layer_to_plot]]),
                        fillOpacity = 1,
                        lng = coords_sdf[, 1],
                        lat = coords_sdf[, 2],
                        # popup = paste("FID : ",sdf[["FID"]], "<br>", layer_to_plot, " : ",  sdf[[layer_to_plot]]),
                        opacity = 1,
                        group = "Circles",
                        layerId = sdf[["FID"]])%>%
              leaflet::addLegend(
                position = "bottomright",
                pal = color_palette,                                # 图例位置
                values = sdf[[layer_to_plot]],
                title = layer_to_plot,                              # 图例标题
                labFormat = labelFormat(prefix = "", suffix = ""),  # 可选:标签格式
                opacity = 1,                                         # 图例的不透明度
                layerId = "Circles"
              )%>%
              # 动态计算地图边界
              leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                        lat1 = min(coords_sdf[, 2]),
                        lng2 = max(coords_sdf[, 1]),
                        lat2 = max(coords_sdf[, 2]))

          # Significance 图
          if(input$mgwr_significance_show){
            shinyjs::show("mgwr_Significant_point_div")
            # 创建一个新的列名用于存储 p 值
            p_value_col <- paste0(layer_to_plot, "_p_value")

            # 假设sdf是一个SpatialPointsDataFrame,coords_sdf是其坐标矩阵
            sdf_df <- as.data.frame(sdf@data)
            # 将坐标添加到数据框中
            sdf_df$lng <- coords_sdf[, 1]
            sdf_df$lat <- coords_sdf[, 2]
            # 过滤数据
            p_values <- sdf_df %>% dplyr::pull(!!dplyr::sym(p_value_col))
            sdf_significant <- sdf_df[p_values < 0.05 & !is.na(p_values), ]
            sdf_not_significant <- sdf_df[p_values >= 0.05 & !is.na(p_values), ]
            # 为两组数据创建不同的图标
            icon_not_significant <-  leaflet::makeIcon(
                                      iconUrl = "https://s2.loli.net/2024/10/29/sih4RPljFwvVT5S.png",
                                      iconWidth = 15, iconHeight = 15
                                      # iconAnchorX = 0, iconAnchorY = 94
                                    )

            # 添加气泡图&图例
            map %>%
                leaflet::clearGroup("Significant") %>%
                leaflet::removeControl("Significant") %>%
                # setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 11) %>%
                leaflet::addMarkers(data = sdf_not_significant,
                                  lng = ~lng, lat = ~lat,
                                  icon = icon_not_significant,
                                  group = "Significant")%>%
                # 动态计算地图边界
                leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                          lat1 = min(coords_sdf[, 2]),
                          lng2 = max(coords_sdf[, 1]),
                          lat2 = max(coords_sdf[, 2]))
          }
        } else if (layer_to_plot == "residual" || layer_to_plot == "CV_Score" ||
                  layer_to_plot == "Stud_residual" || layer_to_plot == "Local_R2") {
          shinyjs::hide("mgwr_Significant_point_div")
          shinyjs::hide("mgwr_Significant_line_div")
          # 添加气泡图&图例
          map %>%
              leaflet::clearGroup("Polygons") %>%
              leaflet::clearGroup("Circles") %>%
              leaflet::removeControl("Polygons") %>%
              leaflet::removeControl("Circles") %>%
              leaflet::clearGroup("Significant") %>%
              leaflet::removeControl("Significant") %>%
              # setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 11) %>%
              leaflet::addCircles(
                        data = sdf,
                        weight = 3,
                        radius = point_radius,
                        color = ~color_palette(sdf[[layer_to_plot]]),
                        fillOpacity = 1,
                        lng = coords_sdf[, 1],
                        lat = coords_sdf[, 2],
                        # popup = paste("FID : ",sdf[["FID"]], "<br>", layer_to_plot, " : ",  sdf[[layer_to_plot]]),
                        opacity = 1,
                        group = "Circles",
                        layerId = sdf[["FID"]])%>%
              leaflet::addLegend(
                position = "bottomright",
                pal = color_palette,                                # 图例位置
                values = sdf[[layer_to_plot]],
                title = layer_to_plot,                              # 图例标题
                labFormat = labelFormat(prefix = "", suffix = ""),  # 可选:标签格式
                opacity = 1,                                         # 图例的不透明度
                layerId = "Circles"
              )%>%
              # 动态计算地图边界
              leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                        lat1 = min(coords_sdf[, 2]),
                        lng2 = max(coords_sdf[, 1]),
                        lat2 = max(coords_sdf[, 2]))
        }
    }

  })
  # 清除显著性图层
  observeEvent(input$mgwr_significance_show,{
    # 获取底图map对象
    map <- leaflet::leafletProxy("mgwr_mapPlot", session)
    if(!is.null(map)){}
    else{print("map is null")}
    if(!input$mgwr_significance_show){
      shinyjs::hide("mgwr_Significant_line_div")
      shinyjs::hide("mgwr_Significant_point_div")
      # 清除所有图层
      map %>%
          leaflet::clearGroup("Significant") %>%
          leaflet::removeControl("Significant")
    }
    map
  })
  # 用 reactiveValues 存储用户点击的点和生成的短音频文件路径
  mgwr_rv <- reactiveValues(
    clicked_points = list(),  # 用于存放点击的经纬度(仅连线模式时使用)
    audio_files = character() # 存放每个点击生成的短音频文件路径
  )
  # 点击地图产生音频
  observeEvent(input$mgwr_mapPlot_click, {
    req(mgwr_result())
    click <- input$mgwr_mapPlot_click
    shp_data <- mgwr_shapefile_data() %>% 
        sf::st_as_sf() %>% 
        sf::st_make_valid() %>% 
        sf::st_set_crs(4326)
    geom_type <- sf::st_geometry_type(shp_data) %>% 
      as.character() %>% 
      unique() %>% 
      dplyr::first()

    # 选择模式:点击模式直接生成短音频
    if(input$mgwr_audio_mode == "click"){
      # 获取必要数据
      mgwr_sdf <- mgwr_result()$SDF
      layer_to_plot <- input$mgwr_map_layer
      
      # 这里假设已将 mgwr_sdf 转换为 sf 对象,或直接使用 spatial 包的 nearest feature 方法
      mgwr_sdf_sf <- sf::st_as_sf(mgwr_sdf)
      click_point <- sf::st_sfc(sf::st_point(c(click$lng, click$lat)), crs = 4326)
      selected_id <- sf::st_nearest_feature(click_point, mgwr_sdf_sf)
      selected_row <- mgwr_sdf[selected_id, ]
      
      # 生成 popup 内容
      popup_content <- paste("FID :", selected_row$FID, "<br>", 
                            layer_to_plot, ":", round(selected_row[[layer_to_plot]], 4))
      if(geom_type %in% c("POLYGON", "MULTIPOLYGON")) {
        # 找到包含点击点的 Polygon
        selected_feature <- shp_data[sf::st_contains(shp_data, click_point, sparse = FALSE), ]
        if (nrow(selected_feature) == 0) {
          shiny::showNotification("No polygon data found", type = "error")
          return(NULL)
        }

        leaflet::leafletProxy("mgwr_mapPlot") %>%
          leaflet::clearPopups() %>%
          # addPopups(lng = click$lng, lat = click$lat, popup = popup_content) %>%
          leaflet::clearGroup("highlighted_features") %>%  # 清除之前的高亮
          leaflet::addPolygons(
            data = selected_feature,
            group = "highlighted_features",
            fillColor = "yellow",
            color = "#FF0000",
            fillOpacity = 0,
            weight = 2,
            highlightOptions = leaflet::highlightOptions(
              weight = 5,
              color = "#FF0000",
              bringToFront = TRUE
              )
            )%>%
          leaflet::addMarkers(  
                  lng = click$lng, 
                  lat = click$lat, 
                  popup = popup_content,
                  group = "highlighted_features",
                  icon = musicIcon # 使用自定义图标
              )
      }else if(geom_type %in% c("POINT", "MULTIPOINT")) {
        # 找到最近的点
        selected_id <- sf::st_nearest_feature(click_point, shp_data)
        selected_feature <- shp_data[selected_id, ]
        if (nrow(selected_feature) == 0) {
          shiny::showNotification("No point data found", type = "error")
          return(NULL)
        }
        leaflet::leafletProxy("mgwr_mapPlot") %>%
          leaflet::clearPopups() %>%
          leaflet::clearGroup("highlighted_features") %>%  # 清除之前的高亮
          # addPopups(lng = click$lng, lat = click$lat, popup = popup_content) %>%
          leaflet::addCircleMarkers(
            data = selected_feature,
            group = "highlighted_features",
            color = "red",
            fillOpacity = 0,
            radius = 8,
            stroke = TRUE,
            weight = 2
          )%>%
          leaflet::addMarkers(  
                  lng = click$lng, 
                  lat = click$lat, 
                  popup = popup_content,
                  group = "highlighted_features",
                  icon = musicIcon # 使用自定义图标
              )
      } else {
        shiny::showNotification("The current geometry type is not supported", type = "error")
        return(NULL)
      }
      
      
      # 生成短音频文件
      coeff <- ifelse(is.na(selected_row[[layer_to_plot]]), 0, selected_row[[layer_to_plot]])
      short_filename <- paste0("www/mgwr_map_audio_", as.integer(Sys.time()), "_", sample(1:1000, 1), ".wav")
      generate_audio(value = coeff,
                    filename = short_filename,
                    x_min = min(mgwr_sdf[[layer_to_plot]], na.rm = TRUE),
                    x_max = max(mgwr_sdf[[layer_to_plot]], na.rm = TRUE))
      
      # 更新 audio 控件,播放短音频
      shinyjs::runjs(sprintf("document.getElementById('mgwr_map_audio').src='%s'; document.getElementById('mgwr_map_audio').play();", short_filename))
      
    } else if(input$mgwr_audio_mode == "line") {
      # 连线模式:记录点击点,不直接生成音频
      mgwr_rv$clicked_points <- append(mgwr_rv$clicked_points, list(c(click$lng, click$lat)))
      # 在地图上添加标记以便确认顺序
      leaflet::leafletProxy("mgwr_mapPlot") %>%
        leaflet::addMarkers(
                  lng = click$lng, 
                  lat = click$lat,
                  popup = paste("point", length(mgwr_rv$clicked_points)),
                  group = "point",
                  icon = musicIcon
                  )
    }
  })
  # 处理连线模式下的确认按钮:生成长音频
  observeEvent(input$mgwr_confirm_audio, {
    req(length(mgwr_rv$clicked_points) > 0)
    # 仅在连线模式时触发检查
    if (input$mgwr_audio_mode == "line") {
      # 检查点击点数量是否足够
      if (length(mgwr_rv$clicked_points) < 2) {
        shiny::showNotification("Choose at least two points", type = "error")
        return() # 不满足条件时提前退出
      }
    }

    mgwr_buffer_length <- as.numeric(input$mgwr_buffer_length)
    layer_to_plot <- input$mgwr_map_layer

    # 显示自定义模态窗口
    showModal(modalDialog(
      title = "Processing mgwr Audio...",
      tags$div(
        style = "text-align: center;",
        shinyWidgets::progressBar(
          id = "mgwr_audio_progress",
          value = 0,
          display_pct = TRUE,
          status = "info",
          striped = TRUE
        )
      ),
      footer = tagList(
        modalButton("Cancel")  # 添加关闭按钮
      ),
      easyClose = FALSE
    ))

    # 1. 获取用户点击的坐标并绘制连线
    clicked_matrix <- do.call(rbind, mgwr_rv$clicked_points)
    leaflet::leafletProxy("mgwr_mapPlot") %>%
      leaflet::addPolylines(
                  lng = clicked_matrix[,1], 
                  lat = clicked_matrix[,2],
                  color = "red",
                  weight = mgwr_buffer_length,
                  opacity = 0.8,
                  group = "point_line") %>%
      leaflet::clearGroup("highlighted_features")  # 清除之前的高亮
    
    # 2. 创建 sf 线对象并确保坐标系统一
    clicked_line <- sf::st_linestring(clicked_matrix) %>%
      sf::st_sfc(crs = 4326) %>%
      sf::st_make_valid()
    line_buffer <- sf::st_buffer(clicked_line, dist = mgwr_buffer_length)  # 可调整缓冲区大小
  
    # 3. 处理空间数据
    mgwr_sdf <- mgwr_result()$SDF
    shp_data <- mgwr_shapefile_data() %>% 
      sf::st_as_sf() %>% 
      sf::st_make_valid() %>% 
      sf::st_set_crs(4326)

    # 更新进度条
    shinyWidgets::updateProgressBar(session = session, id = "mgwr_audio_progress", value = 10)

    # 4. 转换Spatial对象为sf
    mgwr_sdf_sf <- sf::st_as_sf(mgwr_sdf) 
    mgwr_sdf_sf <- mgwr_sdf_sf %>%
      dplyr::rename_with(~paste0("mgwr_", .), .cols = -geometry)  
    
    # 将mgwr计算结果合并到原始shp属性(假设行顺序一致)
    if(nrow(shp_data) == nrow(mgwr_sdf_sf)) {
      shp_data_with_mgwr <- shp_data %>%
        dplyr::bind_cols(sf::st_drop_geometry(mgwr_sdf_sf)) %>%  # 使用正确的属性数据
        sf::st_sf()
    } else {
      shiny::showNotification("The number of data rows is inconsistent", type = "error")
      shiny::removeModal()
      return()
    }

    layer_name <- paste0("mgwr_",layer_to_plot)

    geom_type <- sf::st_geometry_type(shp_data_with_mgwr) %>% 
      as.character() %>% 
      unique() %>% 
      dplyr::first()

    if(geom_type %in% c("POLYGON", "MULTIPOLYGON")) {
      # 精确相交检测
      intersects <- tryCatch({
        sf::st_filter(shp_data_with_mgwr, line_buffer)
      }, error = function(e) {
        shiny::showNotification("Fail in intersects", type = "error")
        shiny::removeModal()
        return(NULL)
      })
      
      intersect_features <- intersects
      # **绘制调试图,检查是否有交集**
      plot(sf::st_geometry(shp_data_with_mgwr), col = "blue", border = "black", main = "空间要素 vs. 线")
      plot(sf::st_geometry(line_buffer), col = "red", lwd = mgwr_buffer_length, add = TRUE)

      leaflet::leafletProxy("mgwr_mapPlot") %>%
        leaflet::addPolygons(
          data = intersects,
          group = "highlighted_features",
          color = "#FF0000",
          fillOpacity = 0,
          weight = 2,
          highlightOptions = leaflet::highlightOptions(
            weight = 5,
            color = "#FF0000",
            bringToFront = TRUE
            )
          )
    }else if(geom_type %in% c("POINT", "MULTIPOINT")) {
      # 点处理逻辑
      # intersect_idx  <- st_intersects(mgwr_sdf_sf, line_buffer, sparse = FALSE)
      # intersect_features <- mgwr_sdf_sf[which(intersect_idx, arr.ind = TRUE), ]
      intersects <- tryCatch({
        sf::st_filter(shp_data_with_mgwr, line_buffer)
      }, error = function(e) {
        shiny::showNotification("Fail in intersects", type = "error")
        shiny::removeModal()
        return(NULL)
      })
      intersect_features <- intersects

      if(nrow(intersect_features) == 0) {
        shiny::showNotification("The line does not pass through any features", type = "warning")
        shiny::removeModal()
        return()
      }
      # **绘制调试图,检查是否有交集**
      plot(sf::st_geometry(shp_data_with_mgwr), col = "blue", border = "black", main = "空间要素 vs. 线")
      plot(sf::st_geometry(line_buffer), col = "red", lwd = mgwr_buffer_length, add = TRUE)

      leaflet::leafletProxy("mgwr_mapPlot") %>%
        leaflet::addCircleMarkers(
          data = intersect_features,
          group = "highlighted_features",
          color = "red",
          fillOpacity = 0,
          radius = 8,
          stroke = TRUE,
          weight = 2
        )

    } else {
      shiny::showNotification("The current geometry type is not supported", type = "error")
      shiny::removeModal()
      return()
    }
    
    # 更新进度条
    shinyWidgets::updateProgressBar(session = session, id = "mgwr_audio_progress", value = 20)

    # 6. 沿路径排序(确保交点顺序与点击点一致)
    if (nrow(intersect_features) > 0) {
      start_point <- sf::st_sfc(sf::st_point(clicked_matrix[1, ]), crs = 4326)
      intersect_features <- intersect_features %>%
        dplyr::mutate(
          dist_to_start = as.numeric(sf::st_distance(geometry, start_point))
        ) %>%
        dplyr::arrange(dist_to_start)
    }

    # 7. 生成音频
    mgwr_rv$audio_files <- character()  # 重置音频存储

    # 更新进度条
    shinyWidgets::updateProgressBar(session = session, id = "mgwr_audio_progress", value = 30 )

    # 8. 遍历要素,生成音频
    for (i in seq_len(nrow(intersect_features))) {
      feature <- intersect_features[i, ]
      coeff <- ifelse(is.na(feature[[layer_name]]), 0, feature[[layer_name]])
      short_filename <- paste0("www/mgwr_map_audio_", i, ".wav")

      generate_audio(
        value = coeff,
        filename = short_filename,
        x_min = min(shp_data_with_mgwr[[layer_name]], na.rm = TRUE),
        x_max = max(shp_data_with_mgwr[[layer_name]], na.rm = TRUE)
      )

      mgwr_rv$audio_files <- c(mgwr_rv$audio_files, short_filename)

      # 更新进度条
      shinyWidgets::updateProgressBar(session = session, id = "mgwr_audio_progress", value = 30 + (i/nrow(intersect_features)) * 30)
    }
    
    # 9. 合成所有短音频文件为一个长音频
    composite_filename <- paste0("www/mgwr_map_audio_composite.wav")
    concatenate_audio(mgwr_rv$audio_files, composite_filename)
    # 确保 FFmpeg 使用合成后的音频
    sound_road <- composite_filename  # 这里修正

    # 提取系数数据
    ranges <- seq_len(nrow(intersect_features))  # 用于 X 轴
    coeff_values <- intersect_features[[layer_name]]  # 提取回归系数
    ranges_length <- length(ranges)
    # 生成初始曲线图
    waveform_plot <- ggplot2::ggplot(data.frame(x = ranges, y = coeff_values), ggplot2::aes(x = x, y = y)) +
      ggplot2::geom_point(color = "blue", size = 2) +  
      ggplot2::geom_smooth(method = "loess", color = "blue", span = 0.5, se = FALSE) +  
      ggplot2::theme_minimal() +
      ggplot2::theme(
        panel.background = ggplot2::element_rect(fill = "white", color = NA),  
        plot.background = ggplot2::element_rect(fill = "white", color = NA)  
      ) +
      ggplot2::ggtitle("Coefficient Variation Over Features") +
      ggplot2::xlab("Feature Index") + 
      ggplot2::ylab("Coefficient Value")

    # 逐帧生成视频
    waveform_images <- c()
    for (i in seq_along(ranges)) {
      frame_plot <- waveform_plot +
        ggplot2::geom_vline(xintercept = ranges[i], color = "red", linetype = "dashed", size = 1) +
        ggplot2::ggtitle(paste("Feature:", i, "  Coefficient:", round(coeff_values[i], 4)))

      frame_image <- file.path(temp_dir, paste0("mgwr_map_waveform_", i, ".png"))
      ggplot2::ggsave(frame_image, frame_plot, width = 6, height = 4, dpi = 150)
      waveform_images <- c(waveform_images, frame_image)

      shinyWidgets::updateProgressBar(session, "mgwr_video_progress", value = 60 + (i / ranges_length) * 30)
    }

    # 计算音频时长
    audio_info <- tuneR::readWave(composite_filename)
    total_frames <- length(waveform_images)
    total_audio_duration <- length(audio_info@left) / audio_info@samp.rate
    # 计算帧率,使得视频时长与音频一致
    frame_rate <- total_frames / total_audio_duration
    # 确保 framerate 合理(避免异常)
    if (frame_rate < 1) frame_rate <- 1
    if (frame_rate > 30) frame_rate <- 30  # 限制在 1-30 fps,防止帧率过大或过小
    # 生成视频
    waveform_video <- file.path(temp_dir, "mgwr_map_waveform_video.mp4")
    av::av_encode_video(
      input = waveform_images,
      output = waveform_video,
      framerate = frame_rate,
      codec = "libx264",
      vfilter = "scale=720:720,format=yuv420p",
      audio = NULL,
      verbose = TRUE
    )

    # 合成音视频
    sound_video <- file.path("www", "mgwr_map_sound_video.mp4")
    # sound_road <- mgwr_rv$audio_files  # 获取之前生成的音频
    ffmpeg_command <- paste(
      "ffmpeg",
      "-y", 
      "-i", shQuote(waveform_video),
      "-i", shQuote(sound_road),
      "-ac 2 -c:v libx264 -c:a aac -b:a 192k -strict experimental",
      shQuote(sound_video)
    )
    ffmpeg_result <- tryCatch({
      system(ffmpeg_command, intern = FALSE)
    }, error = function(e) {
      cat("FFmpeg command failed:", e$message, "\n")
      NA
    })
    if (!is.na(ffmpeg_result) && ffmpeg_result != 0) {
      shiny::showNotification("The command execution of FFmpeg failed.Please check (1) whether ffmpeg is installed (2) whether the system path is configured.", type = "error")
      shiny::removeModal()
      return()
    }
    cat(format(Sys.time(), "%Y-%m-%d %H:%M:%S"), " 合成波形图和音频完成,文件保存为: ", sound_video, "\n")

    # 在地图左下角显示视频
    leaflet::leafletProxy("mgwr_mapPlot") %>%
      leaflet::removeControl(layerId ="map_video_control") %>%
      leaflet::addControl(
        HTML(sprintf(
          '<video id="mgwr_video_player" width="300" autoplay controls>
            <source src="%s" type="video/mp4">
            Your browser does not support the video tag.
          </video>
          <audio id="mgwr_audio" src="%s"></audio>
          <script>
            document.getElementById("mgwr_video_player").onplay = function() {
              var audio = document.getElementById("mgwr_audio");
              if (audio) {
                audio.play();
              }
            };
            document.getElementById("mgwr_video_player").onpause = function() {
              var audio = document.getElementById("mgwr_audio");
              if (audio) {
                audio.pause();
              }
            };
            document.getElementById("mgwr_video_player").ontimeupdate = function() {
              var audio = document.getElementById("mgwr_audio");
              if (audio) {
                audio.currentTime = this.currentTime;
              }
            };
          </script>', sound_video, composite_filename
        )),
        position = "bottomleft",
        layerId ="map_video_control"
      )

    # 设置音频源
    # shinyjs::runjs(sprintf("document.getElementById('mgwr_map_audio').src='%s';", composite_filename))

    # 重置记录,便于下次操作
    mgwr_rv$clicked_points <- list()
    mgwr_rv$audio_files <- character()

    shinyWidgets::updateProgressBar(session = session, id = "mgwr_audio_progress", value = 100)
    Sys.sleep(1)
    shiny::removeModal()
  })
  # 处理连线的图标
  observeEvent(input$mgwr_confirm_audio_clear, {
    # 重置记录,便于下次操作
    mgwr_rv$clicked_points <- list()
    mgwr_rv$audio_files <- character()

    leaflet::leafletProxy("mgwr_mapPlot") %>%
      leaflet::removeControl(layerId ="map_video_control") %>%
      leaflet::clearGroup("point") %>%
      leaflet::clearGroup("point_line") %>%
      leaflet::clearGroup("highlighted_features")  # 清除高亮
  })
  #--------------------------# Multiple Visualizations #-------------------------#
  # # 动态生成图片
  # observe({
  #   req(input$mgwr_columns)  # 确保用户已经选择了变量
  #   req(input$mgwr_color_palette)  # 确保用户已经选择了色阶
  #   req(mgwr_result())
  #   req(input$mgwr_independentVars)

  #   shp_data <- mgwr_shapefile_data()
  #   if (!inherits(shp_data, "sf")) {shp_data <- sf::st_as_sf(shp_data)}

  #   # 获取带宽对应名称
  #   Vars <- c("Intercept", input$mgwr_independentVars)

  #   # 获取 mgwr 结果
  #   mgwr_result <- mgwr_result()
  #   sdf <- mgwr_result$SDF
  #   print(sdf)

  #   # 获取带宽逻辑修改
  #   if(input$mgwr_best_bandwidth) {
  #     # 从模型结果中获取自动选择的带宽
  #     optimal_bws <- mgwr_result()$optimal_bws
  #     # 创建带宽命名向量
  #     bw_vector <- setNames(optimal_bws, Vars)
  #   }

  #   # 遍历用户选择的列并动态生成图片
  #   selected_columns <- input$mgwr_columns
  #   for (col in selected_columns) {
  #     local({
  #       column_name <- col  # 需要使用 local() 避免循环中的闭包问题
  #       # 动态创建 UI 输出控件
  #       output[[paste0("plot_", column_name)]] <- renderPlot({
  #         req(column_name %in% names(sdf))

  #         # 修改带宽获取逻辑
  #         if(input$mgwr_best_bandwidth) {
  #           # 从自动选择的带宽向量中获取对应值
  #           bw <- round(as.numeric(bw_vector[column_name]),4)
  #         } else {
  #           # 保持原有手动获取逻辑
  #           bw <- sapply(column_name, function(x) {
  #             as.numeric(input[[paste0("bw_", x)]])
  #           })
  #         } 

  #         # 创建专题图
  #         target_data <- shp_data
  #         target_data$value <- sdf[[column_name]]  # 将数据加入到 sf 对象中,以便于绘制专题图

  #         # 判断数据类型,动态选择绘图样式
  #         geom_type <- unique(sf::st_geometry_type(target_data))

  #         # 根据选择的色阶动态设置颜色
  #         gradient <- color_gradients[[input$mgwr_color_palette]]

  #         if ("POINT" %in% geom_type || "MULTIPOINT" %in% geom_type) {  # 点数据:使用 color 映射
  #             if (input$mgwr_color_palette == "viridis") {
  #               ggplot2::ggplot(target_data) +
  #                 ggplot2::geom_sf(aes(color = value)) +
  #                 ggplot2::scale_color_viridis_c() +  # 使用 Viridis 色阶
  #                 ggplot2::labs(
  #                   title = if (column_name %in% Vars) {
  #                     paste0(column_name, "  (Bandwidth: ", bw, ")")
  #                   } else {
  #                     column_name
  #                   }
  #                 ) +
  #                 ggplot2::theme(
  #                   panel.background = ggplot2::element_rect(fill = "white", color = NA),
  #                   panel.grid = ggplot2::element_blank(),
  #                   plot.background = ggplot2::element_rect(fill = "white", color = NA)
  #                 )
  #             } else {
  #               ggplot2::ggplot(target_data) +
  #                 ggplot2::geom_sf(aes(color = value)) +
  #                 ggplot2::scale_color_gradient(low = gradient["low"], high = gradient["high"]) +
  #                 ggplot2::labs(
  #                   title = if (column_name %in% Vars) {
  #                     paste0(column_name, "  (Bandwidth: ", bw, ")")
  #                   } else {
  #                     column_name
  #                   }
  #                 ) +
  #                 ggplot2::theme(
  #                   panel.background = ggplot2::element_rect(fill = "white", color = NA),
  #                   panel.grid = ggplot2::element_blank(),
  #                   plot.background = ggplot2::element_rect(fill = "white", color = NA)
  #                 )
  #             }
  #         } else if ("POLYGON" %in% geom_type || "MULTIPOLYGON" %in% geom_type) {  # 面数据:使用 fill 映射
  #           if (input$mgwr_color_palette == "viridis") {
  #             ggplot2::ggplot(target_data) +
  #               ggplot2::geom_sf(aes(fill = value)) +
  #               ggplot2::scale_fill_viridis_c() +  # 使用 Viridis 色阶
  #               ggplot2::labs(
  #                   title = if (column_name %in% Vars) {
  #                     paste0(column_name, "  (Bandwidth: ", bw, ")")
  #                   } else {
  #                     column_name
  #                   }
  #                 ) +
  #               ggplot2::theme(
  #                 panel.background = ggplot2::element_rect(fill = "white", color = NA),
  #                 panel.grid = ggplot2::element_blank(),
  #                 plot.background = ggplot2::element_rect(fill = "white", color = NA)
  #               )
  #           } else {
  #             ggplot2::ggplot(target_data) +
  #               ggplot2::geom_sf(aes(fill = value)) +
  #               ggplot2::scale_fill_gradient(low = gradient["low"], high = gradient["high"]) +
  #               ggplot2::labs(
  #                   title = if (column_name %in% Vars) {
  #                     paste0(column_name, "  (Bandwidth: ", bw, ")")
  #                   } else {
  #                     column_name
  #                   }
  #                 ) +
  #               ggplot2::theme(
  #                 panel.background = ggplot2::element_rect(fill = "white", color = NA),
  #                 panel.grid = ggplot2::element_blank(),
  #                 plot.background = ggplot2::element_rect(fill = "white", color = NA)
  #               )
  #           }
  #         } else {
  #           # 如果既不是点也不是面,抛出警告
  #           stop("Unsupported geometry type for plotting.")
  #         }

  #       })
  #     })
  #   }
  # })
  # # 动态显示图片
  # output$mgwr_Plot <- renderUI({
  #   req(input$mgwr_columns)  # 确保用户已经选择了变量

  #   # 每行的图片数量
  #   images_per_row <- 3  # 每行最多显示 3 张图片
  #   plot_outputs <- lapply(seq_along(input$mgwr_columns), function(i) {
  #     column_name <- input$mgwr_columns[i]

  #     # 使用 column 动态调整宽度,每行最多显示 images_per_row 张图片
  #     column(
  #       width = 12 / images_per_row,  # 动态设置列宽(12 栅格系统)
  #       plotOutput(outputId = paste0("plot_", column_name), height = "400px", width = "100%")
  #     )
  #   })

  #   # 将图片控件布局在 fluidRow 中
  #   do.call(fluidRow, plot_outputs)
  # })
  observe({
    req(input$mgwr_color_palette)  # 确保用户已经选择了色阶
    req(mgwr_result())
    req(input$mgwr_independentVars)

    # 获取带宽对应名称
    Vars <- c("Intercept", input$mgwr_independentVars)
    # 获取 mgwr 结果
    mgwr_result <- mgwr_result()
    sdf <- mgwr_result$SDF
    print(sdf)

    shp_data <- mgwr_shapefile_data()
    if (!inherits(shp_data, "sf")) {shp_data <- sf::st_as_sf(shp_data)}

    # 获取 mgwr 结果
    mgwr_result <- mgwr_result()
    sdf <- mgwr_result$SDF
    req(input$mgwr_columns)  # 确保用户已经选择了变量

    # 获取带宽逻辑修改
    if(input$mgwr_best_bandwidth) {
      # 从模型结果中获取自动选择的带宽
      optimal_bws <- mgwr_result()$optimal_bws
      # 创建带宽命名向量
      bw_vector <- setNames(optimal_bws, Vars)
    }

    # 设置高分辨率参数
    dpi <- 100  # 设置DPI值
    width_px <- 1500  # 设置宽度像素
    height_px <- 1500 # 设置高度像素

    # 遍历用户选择的列并动态生成图片
    selected_columns <- input$mgwr_columns
    for (col in selected_columns) {
      local({
        column_name <- col  # 需要使用 local() 避免循环中的闭包问题
        # 动态创建 UI 输出控件
        output[[paste0("plot_", column_name)]] <- renderPlot({

          # 确保列存在于数据框中
          req(column_name %in% names(sdf))
          # 修改带宽获取逻辑
          if(input$mgwr_best_bandwidth) {
            # 从自动选择的带宽向量中获取对应值
            bw <- round(as.numeric(bw_vector[column_name]),4)
          } else {
            # 保持原有手动获取逻辑
            bw <- sapply(column_name, function(x) {
              as.numeric(input[[paste0("bw_", x)]])
            })
          } 

          # 创建专题图
          target_data <- shp_data
          target_data$value <- sdf[[column_name]]  # 将数据加入到 sf 对象中,以便于绘制专题图

          # 判断数据类型,动态选择绘图样式
          geom_type <- unique(sf::st_geometry_type(target_data))
          # 根据选择的色阶动态设置颜色
          gradient <- color_gradients[[input$mgwr_color_palette]]

          if ("POINT" %in% geom_type || "MULTIPOINT" %in% geom_type) {  # 点数据:使用 color 映射
              if (input$mgwr_color_palette == "viridis") {
                ggplot2::ggplot(target_data) +
                  ggplot2::geom_sf(ggplot2::aes(color = value)) +
                  ggplot2::scale_color_viridis_c() +  # 使用 Viridis 色阶
                  ggplot2::labs(
                    title = if (column_name %in% Vars) {
                      paste0(column_name, "  (Bandwidth: ", bw, ")")
                    } else {
                      column_name
                    }
                  ) +
                  ggspatial::annotation_north_arrow(
                    location = "tl",  # 左上角
                    which_north = "true",
                    pad_x = ggplot2::unit(0.3, "cm"),
                    pad_y = ggplot2::unit(0.3, "cm"),
                    style = ggspatial::north_arrow_fancy_orienteering()
                  ) +
                  ggspatial::annotation_scale(
                    location = "bl",  # 左下角
                    width_hint = 0.3
                  ) +
                  ggplot2::theme(
                    panel.background = ggplot2::element_rect(fill = "white", color = NA),
                    panel.grid = ggplot2::element_blank(),
                    plot.background = ggplot2::element_rect(fill = "white", color = NA)
                  )
              } else {
                ggplot2::ggplot(target_data) +
                  ggplot2::geom_sf(ggplot2::aes(color = value)) +
                  ggplot2::scale_color_gradient(low = gradient["low"], high = gradient["high"]) +
                  ggplot2::labs(
                    title = if (column_name %in% Vars) {
                      paste0(column_name, "  (Bandwidth: ", bw, ")")
                    } else {
                      column_name
                    }
                  ) +
                  ggspatial::annotation_north_arrow(
                    location = "tl",  # 左上角
                    which_north = "true",
                    pad_x = ggplot2::unit(0.3, "cm"),
                    pad_y = ggplot2::unit(0.3, "cm"),
                    style = ggspatial::north_arrow_fancy_orienteering()
                  ) +
                  ggspatial::annotation_scale(
                    location = "bl",  # 左下角
                    width_hint = 0.3
                  ) +
                  ggplot2::theme(
                    panel.background = ggplot2::element_rect(fill = "white", color = NA),
                    panel.grid = ggplot2::element_blank(),
                    plot.background = ggplot2::element_rect(fill = "white", color = NA)
                  )
              }
          } else if ("POLYGON" %in% geom_type || "MULTIPOLYGON" %in% geom_type) {  # 面数据:使用 fill 映射
            if (input$mgwr_color_palette == "viridis") {
              ggplot2::ggplot(target_data) +
                ggplot2::geom_sf(ggplot2::aes(fill = value)) +
                ggplot2::scale_fill_viridis_c() +  # 使用 Viridis 色阶
                ggplot2::labs(
                    title = if (column_name %in% Vars) {
                      paste0(column_name, "  (Bandwidth: ", bw, ")")
                    } else {
                      column_name
                    }
                  ) +
                ggspatial::annotation_north_arrow(
                    location = "tl",  # 左上角
                    which_north = "true",
                    pad_x = ggplot2::unit(0.3, "cm"),
                    pad_y = ggplot2::unit(0.3, "cm"),
                    style = ggspatial::north_arrow_fancy_orienteering()
                  ) +
                  ggspatial::annotation_scale(
                    location = "bl",  # 左下角
                    width_hint = 0.3
                  ) +
                ggplot2::theme(
                  panel.background = ggplot2::element_rect(fill = "white", color = NA),
                  panel.grid = ggplot2::element_blank(),
                  plot.background = ggplot2::element_rect(fill = "white", color = NA)
                )
            } else {
              ggplot2::ggplot(target_data) +
                ggplot2::geom_sf(ggplot2::aes(fill = value)) +
                ggplot2::scale_fill_gradient(low = gradient["low"], high = gradient["high"]) +
                ggplot2::labs(
                    title = if (column_name %in% Vars) {
                      paste0(column_name, "  (Bandwidth: ", bw, ")")
                    } else {
                      column_name
                    }
                  ) +
                ggspatial::annotation_north_arrow(
                    location = "tl",  # 左上角
                    which_north = "true",
                    pad_x = ggplot2::unit(0.3, "cm"),
                    pad_y = ggplot2::unit(0.3, "cm"),
                    style = ggspatial::north_arrow_fancy_orienteering()
                  ) +
                  ggspatial::annotation_scale(
                    location = "bl",  # 左下角
                    width_hint = 0.3
                  ) +
                ggplot2::theme(
                  panel.background = ggplot2::element_rect(fill = "white", color = NA),
                  panel.grid = ggplot2::element_blank(),
                  plot.background = ggplot2::element_rect(fill = "white", color = NA)
                )
            }
          } else {
            # 如果既不是点也不是面,抛出警告
            shiny::showNotification("Unsupported geometry type for plotting.", type = "error")
            shiny::removeModal()
            return()
          }        
        })
      })
    }

  })
  # 动态显示图片
  output$mgwr_Plot <- renderUI({
    req(input$mgwr_columns)  # 确保用户已经选择了变量

    # 每行的图片数量
    images_per_row <- min(2, length(input$mgwr_columns))  # 每行最多显示 2 张图片

    plot_outputs <- lapply(seq_along(input$mgwr_columns), function(i) {
      column_name <- input$mgwr_columns[i]

      # 使用 column 动态调整宽度,每行最多显示 images_per_row 张图片
      column(
        width = 12 / images_per_row,  # 动态设置列宽(12 栅格系统)
        plotOutput(outputId = paste0("plot_", column_name), height = "500px", width = "100%")
      )
    })

    # 将图片控件布局在 fluidRow 中
    do.call(fluidRow, plot_outputs)
  })

  #--------------------------# GWPCA #-------------------------#
  # 按钮跳转
  observeEvent(input$gwpca_execute, {
    updateTabsetPanel(session, "gwpca_tabs", selected = "Summary Information")
  })
  # shp读取函数
  gwpca_shapefile_data <- reactive({
    req(input$gwpca_shapefile)  # 确保用户已上传文件

    # 获取上传文件的路径和扩展名
    file_path <- input$gwpca_shapefile$datapath
    file_ext <- tolower(tools::file_ext(input$gwpca_shapefile$name))  # 获取文件扩展名,转为小写
    file_name <- tools::file_path_sans_ext(input$gwpca_shapefile$name)

    # 设置 GDAL 配置选项
    Sys.setenv(SHAPE_RESTORE_SHX = "YES")

    if (file_ext == "zip") {
      # 如果是 ZIP 文件,解压缩并查找 .shp 文件
      temp_dir <- tempdir()
      unzip(file_path, exdir = temp_dir)
      shp_files <- list.files(temp_dir, pattern = paste0("^", file_name, "\\.shp$"), full.names = TRUE)
      dbf_files <- list.files(temp_dir, pattern = paste0("^", file_name, "\\.dbf$"), full.names = TRUE)

      if (length(shp_files) > 0 && length(dbf_files) > 0) {
        shp_file <- shp_files[1]
        dbf_file <- sub("\\.shp$", ".dbf", shp_file)
        shp_data <- tryCatch(
          {
            sf::st_read(shp_file, options = "ENCODING=GBK")
          },
          error = function(e) {
            shiny::showNotification("Error reading Shapefile from ZIP. Please check the file content.", type = "error")
            return(NULL)
          }
        )
        # 处理 CRS(避免警告)
        if (!is.null(sf::st_crs(shp_data))) {
          shp_data <- sf::st_transform(shp_data, 4326)  # 如果已有 CRS,重新投影
        } else {
          sf::st_crs(shp_data) <- 4326  # 如果没有 CRS,先赋值再投影
          shp_data <- sf::st_transform(shp_data, 4326)
        }
        return(shp_data)
      } else {
        shiny::showNotification("No Shapefile found in the ZIP file.", type = "error")
        return(NULL)
      }
    } else {
      # 如果上传的文件既不是 .shp 也不是 .zip,显示错误通知
      shiny::showNotification("Unsupported file type. Please upload .zip file.", type = "error")
      return(NULL)
    }
  })
  # cp_shp读取函数
  gwpca_cp_shapefile_data <- reactive({
    req(input$gwpca_cp_shapefile)  # 确保用户已上传文件

    # 获取上传文件的路径和扩展名
    file_path <- input$gwpca_cp_shapefile$datapath
    file_ext <- tolower(tools::file_ext(input$gwpca_cp_shapefile$name))  # 获取文件扩展名,转为小写
    file_name <- tools::file_path_sans_ext(input$gwpca_cp_shapefile$name)

    # 设置 GDAL 配置选项
    Sys.setenv(SHAPE_RESTORE_SHX = "YES")

    temp_dir <- tempdir()

    if (file_ext == "zip") {
      # 如果是 ZIP 文件,解压缩并查找 .shp 文件
      unzip(file_path, exdir = temp_dir)
      shp_files <- list.files(temp_dir, pattern = paste0("^", file_name, "\\.shp$"), full.names = TRUE)
      dbf_files <- list.files(temp_dir, pattern = paste0("^", file_name, "\\.dbf$"), full.names = TRUE)

      if (length(shp_files) > 0 && length(dbf_files) > 0) {
        shp_file <- shp_files[1]
        dbf_file <- sub("\\.shp$", ".dbf", shp_file)
        shp_data <- tryCatch({
            sf::st_read(shp_file, options = "ENCODING=GBK")
          },error = function(e) {
            shiny::showNotification("Error reading Shapefile from ZIP. Please check the file content.", type = "error")
            return(NULL)
          }
        )
        # 处理 CRS(避免警告)
        if (!is.null(sf::st_crs(shp_data))) {
          shp_data <- sf::st_transform(shp_data, 4326)  # 如果已有 CRS,重新投影
        } else {
          sf::st_crs(shp_data) <- 4326  # 如果没有 CRS,先赋值再投影
          shp_data <- sf::st_transform(shp_data, 4326)
        }
        return(shp_data)
      } else {
        shiny::showNotification("No Shapefile found in the ZIP file.", type = "error")
        return(NULL)
      }
    } else {
      # 如果上传的文件既不是 .shp 也不是 .zip,显示错误通知
      shiny::showNotification("Unsupported file type. Please upload .zip file.", type = "error")
      return(NULL)
    }
  })
  # gwpca结果对象
  gwpca_bw_data <- reactiveValues(
    bw = NULL,          
  )
  # 更新自变量&图层选项
  observeEvent(gwpca_shapefile_data(), {
    # 获取变量名并按首字母排序(不区分大小写)
    var_names <- names(gwpca_shapefile_data())
    
    # 检查变量名是否为空
    if (length(var_names) == 0) {
      sorted_vars <- character(0)  # 空字符向量
    } else {
      # 不区分大小写排序
      sorted_vars <- var_names[order(tolower(var_names))]
    }

    # 确保默认选中项有效(至少有两个变量时才选中前两个)
    selected_vars <- if (length(sorted_vars) >= 2) {
      sorted_vars[1:2]
    } else {
      sorted_vars  # 不足两个时选中所有可用变量
    }

    updateSelectInput(session, "gwpca_vars", choices = sorted_vars, selected = selected_vars)
  })
  # 输出自变量选择控件
  output$gwpca_vars <- renderUI({
    selectInput("gwpca_vars", "Choose at least two variables", choices = NULL, multiple = TRUE, selected = NULL)
  })
  # 更新带宽滑块
  observe({
      req(input$gwpca_shapefile)  # 确保文件已上传
      shp_data <- gwpca_shapefile_data()  # 读取 shapefile 数据
      shp_data <- sf::st_make_valid(shp_data)      # 确保数据有效
      req(shp_data)

      # 计算最大距离(米),确保单位正确
      if (sf::st_is_longlat(shp_data)) {
          max_distance <- as.integer(max(sf::st_distance(shp_data)))  # 计算球面距离
      } else {
          coords <- sf::st_coordinates(shp_data)
          max_distance <- as.integer(max(dist(coords)))  # 计算欧式距离
      }

      # 读取是否为自适应带宽
      gwpca_adaptive <- as.logical(input$gwpca_adaptive)

      # 设置带宽的最大值和单位
      if (gwpca_adaptive) {
          max_bw <- nrow(shp_data)  # 以点的个数为单位
          label <- "Bandwidth (Number of points)"
      } else {
          max_bw <- max_distance  # 以最大距离为单位
          label <- "Bandwidth (meter)"
      }

      # 更新滑块
      updateSliderInput(session, "gwpca_bandwidth",
                        max = max_bw,
                        label = label)
  })
    # 带宽滑块显示隐藏
  observeEvent(input$gwpca_best_bandwidth, {
    if (input$gwpca_best_bandwidth) {
      shinyjs::hide("gwpca_bandwidth_div")  # 隐藏带宽滑块
    } else {
      shinyjs::show("gwpca_bandwidth_div")  # 显示带宽滑块
    }
  })
  #--------------------------# GWPCA Analysis #--------------------------#
  # 运行GWPCA分析
  gwpca_result <- eventReactive(input$gwpca_execute, {
    # 保证参数存在
    req(input$gwpca_shapefile)
    req(input$gwpca_vars)
    req(input$gwpca_kernel, input$gwpca_adaptive)
    req(input$gwpca_k)

    # 显示自定义模态窗口
    showModal(modalDialog(
      title = "Processing GWPCA Analysis...",
      tags$div(
        style = "text-align: center;",
        shinyWidgets::progressBar(
          id = "gwpca_progress",
          value = 0,
          display_pct = TRUE,
          status = "info",
          striped = TRUE
        )
      ),
      footer = tagList(
        modalButton("Cancel")  # 添加关闭按钮
      ),
      easyClose = FALSE
    ))

    # 更新进度条10%
    shinyWidgets::updateProgressBar(session = session, id = "gwpca_progress", value = 10)

    # 获取实际的数据和坐标
    gwpca_shp_data <- gwpca_shapefile_data()    
    # 获取用户选定的变量
    gwpca_vars <- as.character(input$gwpca_vars)
    # 获取选项
    gwpca_kernel <- as.character(input$gwpca_kernel)
    gwpca_adaptive <- as.logical(input$gwpca_adaptive)
    gwpca_k <- as.integer(input$gwpca_k)

    # 检查数据是否为 sf 对象
    if (!inherits(gwpca_shp_data, "sf")) {
      shiny::showNotification("Unsupported data types. Input is not an sf object.", type = "error", duration = NULL)
      return()
    }
    # 修复可能的无效几何数据
    gwpca_shp_data <- sf::st_make_valid(gwpca_shp_data)
    spdf <- as(gwpca_shp_data, "Spatial")

    # 更新进度条30%
    shinyWidgets::updateProgressBar(session = session, id = "gwpca_progress", value = 30)

    # 初始化 dp.locat
    dp.locat <- NULL
    # 提取坐标点
    tryCatch({
      geometry_type <- unique(sf::st_geometry_type(gwpca_shp_data))  # 获取所有几何类型
      if ("MULTIPOLYGON" %in% geometry_type || "POLYGON" %in% geometry_type) {
        centroids <- sf::st_centroid(sf::st_geometry(gwpca_shp_data))  # 仅计算几何的质心
        coords <- sf::st_coordinates(centroids)
        dp.locat <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
      } else if ("POINT" %in% geometry_type || "MULTIPOINT" %in% geometry_type) {
        coords <- sf::st_coordinates(gwpca_shp_data)
        dp.locat <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
      } else if ("LINESTRING" %in% geometry_type) {
        # 如果是线状几何,可以取中点(或其他逻辑)
        midpoints <- sf::st_line_sample(sf::st_geometry(gwpca_shp_data), n = 1)
        coords <- sf::st_coordinates(midpoints)
        dp.locat <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
      } else {
        shiny::showNotification(paste("Unsupported geometry type(s):", paste(geometry_type, collapse = ", ")), type = "error")
        shiny::removeModal()
        return()
      }
    }, error = function(e) {
      shiny::showNotification(paste("Error extracting coordinates:", e$message), type = "error", duration = NULL)
      shiny::removeModal()
      return(NULL)
    })
    # 检查 dp.locat 是否为空
    if (is.null(dp.locat)) {
      shiny::showNotification("Coordinates could not be extracted from the shapefile.", type = "error", duration = NULL)
      return()
    }
    dp.locat <- na.omit(dp.locat)

    # 转换为 SpatialPointsDataFrame
    spdf <- tryCatch({
      sp::SpatialPointsDataFrame(
        coords = dp.locat,
        data = as.data.frame(gwpca_shp_data),  # 转换 sf 对象为 data.frame
        proj4string = sp::CRS("+proj=longlat +datum=WGS84")
      )
    }, error = function(e) {
      shiny::showNotification(paste("Error creating SpatialPointsDataFrame:", e$message), type = "error", duration = NULL)
      shiny::removeModal()
      return(NULL)
    })

    # 检查并生成 FID 列
    if (!"FID" %in% names(gwpca_shp_data)) {
      dp.n <- nrow(dp.locat)
      gwpca_shp_data$FID <- seq_len(dp.n)
    }

    # 选择带宽
    # if (input$gwpca_best_bandwidth) {
    #   bandwidth <- GWmodel::bw.gwpca(data = spdf, vars = gwpca_vars ,k = gwpca_k) # 软件计算带宽
    # } else {
    #   bandwidth <- as.numeric(input$gwpca_bandwidth) # 用户输入带宽
    # }
    bandwidth <- as.numeric(input$gwpca_bandwidth) # 用户输入带宽
    gwpca_bw_data$bw <- bandwidth

    # 更新进度条到 50%
    shinyWidgets::updateProgressBar(session = session, id = "gwpca_progress", value = 50)

    # gwpca_cp_shp_data <- NULL
    # sp_cp_locat <- NULL
    # cp_locat <- NULL
    # # 选择regression_points
    # if(!is.null(input$gwpca_cp_shapefile)){
    #   gwpca_cp_shp_data <- gwpca_cp_shapefile_data()
    #   # 修复无效几何数据
    #   if (!is.null(gwpca_cp_shp_data)) {
    #     gwpca_cp_shp_data <- sf::st_make_valid(gwpca_cp_shp_data)
    #   } else {
    #     shiny::showNotification("Invalid shapefile data.", type = "error", duration = NULL)
    #     shiny::removeModal()
    #     return(NULL)
    #   }
      
    #   # 提取坐标点
    #   tryCatch({
    #     geometry_type <- unique(sf::st_geometry_type(gwpca_cp_shp_data))  # 获取所有几何类型
    #     if ("MULTIPOLYGON" %in% geometry_type || "POLYGON" %in% geometry_type) {
    #       centroids <- sf::st_centroid(sf::st_geometry(gwpca_cp_shp_data))  # 仅计算几何的质心
    #       coords <- sf::st_coordinates(centroids)
    #       cp_locat <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
    #     } else if ("POINT" %in% geometry_type || "MULTIPOINT" %in% geometry_type) {
    #       coords <- sf::st_coordinates(gwpca_cp_shp_data)
    #       cp_locat <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
    #     } else if ("LINESTRING" %in% geometry_type) {
    #       # 如果是线状几何,可以取中点(或其他逻辑)
    #       midpoints <- sf::st_line_sample(sf::st_geometry(gwpca_cp_shp_data), n = 1)
    #       coords <- sf::st_coordinates(midpoints)
    #       cp_locat <- data.frame(longitude = coords[, 1], latitude = coords[, 2])
    #     } else {
    #       stop(paste("Unsupported geometry type(s):", paste(geometry_type, collapse = ", ")))
    #     }
    #   }, error = function(e) {
    #     shiny::showNotification(paste("Error extracting coordinates:", e$message), type = "error", duration = NULL)
    #     shiny::removeModal()
    #     return(NULL)
    #   })
    # }
    # cp_locat <- na.omit(cp_locat)
    # # 检查 cp_locat 是否包含两列
    # if (ncol(cp_locat) != 2) {
    #   stop("cp_locat must contain exactly two columns (longitude and latitude).")
    # }
    # # 确保列名正确(经度和纬度)
    # colnames(cp_locat) <- c("longitude", "latitude")
    # # 处理 cp_locat 为 NULL 的情况
    # if (!is.null(cp_locat) && nrow(cp_locat) > 0) {
    #   sp_cp_locat <- SpatialPoints(cp_locat, proj4string = CRS("+proj=longlat +datum=WGS84"))
    # } else {
    #   shiny::showNotification("Coordinate extraction failed, cp_locat is NULL or empty.", type = "error", duration = NULL)
    #   shiny::removeModal()
    #   return(NULL)
    # }

    # 更新进度条到 60%
    shinyWidgets::updateProgressBar(session = session, id = "gwpca_progress", value = 60)
    # 执行GWPCA模型
    model <- tryCatch({
              GWmodel::gwpca(data = spdf, vars = gwpca_vars, k = gwpca_k, bw = bandwidth, kernel = gwpca_kernel, adaptive = gwpca_adaptive)
            }, error = function(e) {
              shiny::showNotification(paste("Error running GWPCA:", e$message), type = "error", duration = NULL)
              shiny::removeModal()
              return(NULL)
            })

    # 模型成功后检查FID列
    if (!is.null(model)) {
      # 获取空间数据框架
      sdf <- model$SDF
      # 检查是否存在FID列
      if (!"FID" %in% names(sdf)) {
        # 获取数据点数
        dp.n <- nrow(sdf@data)
        # sdf@data$FID <- seq_len(dp.n)
        sdf$FID <- seq_len(dp.n)
        # 更新模型结果
        model$SDF <- sdf
      }
    }
    # if(is.null(input$gwpca_cp_shapefile)){
    #   # 执行GWPCA模型
    #   model <- tryCatch({
    #             GWmodel::gwpca(data = spdf, vars = gwpca_vars, k = gwpca_k, bw = bandwidth, kernel = gwpca_kernel, adaptive = gwpca_adaptive)
    #           }, error = function(e) {
    #             shiny::showNotification(paste("Error running GWPCA:", e$message), type = "error", duration = NULL)
    #             shiny::removeModal()
    #             return(NULL)
    #           })
    # }else{
    #   model <- tryCatch({
    #             GWmodel::gwpca(data = spdf, elocat = sp_cp_locat ,vars = gwpca_vars, k = gwpca_k, bw = bandwidth, kernel = gwpca_kernel, adaptive = gwpca_adaptive)
    #           }, error = function(e) {
    #             shiny::showNotification(paste("Error running GWPCA with elocat:", e$message), type = "error", duration = NULL)
    #             shiny::removeModal()
    #             return(NULL)
    #           })
    # }

    # 函数执行完成后,关闭等待窗口
    # 更新进度条到 100%
    shinyWidgets::updateProgressBar(session = session, id = "gwpca_progress", value = 100)
    Sys.sleep(1)  # 等待一秒,确保用户看到完成的进度条
    shiny::removeModal()

    return(model)
  })
  # 更新图层选择控件
  observeEvent(gwpca_result(), {
    req(gwpca_result())

    # 获取 GWPCA 结果
    gwpca_result <- gwpca_result()
    sdf <- gwpca_result$SDF

    # 触发重新渲染 UI 控件
    output$gwpca_map_layer <- renderUI({
        # 提取图层名称
        layer_names <- names(sdf)
        layer_names <- layer_names[!grepl("FID", layer_names)]

        # 创建下拉框
        selectInput(
            inputId = "gwpca_map_layer",
            label = "Select Map Layer to Display",
            choices = layer_names,
            selected = layer_names[1]  # 默认选择第一个图层
        )
    })

    # 动态更新变量选择选项
    output$gwpca_columns <- renderUI({
      # 筛选列名
      names <- names(sdf)
      names <- names[!grepl("FID", names)]

      # 更新选择框的选项
      selectInput(
        inputId = "gwpca_columns",
        label = "Choose Variables",
        choices = names,
        multiple = TRUE,
        selected = names[1]  # 默认不选中任何选项
      )
    })

    # **自动触发地图更新,确保默认图层直接显示**
    observe({
      req(input$gwpca_map_layer)  #
      shinyjs::delay(1000, shinyjs::click("gwpca_map_layer_execute"))  
    })
  })
  #--------------------------# Table View #-------------------------#
  # 显示汇总表
  output$gwpca_summaryTable <- DT::renderDataTable({
    result <- gwpca_result()
    sdf <- as.data.frame(result$SDF)
    DT::datatable(
      sdf,
      extensions = 'Buttons',  # 关键要素1:声明使用扩展
      options = list(
        pageLength = 10,        # 默认每页显示10条
        lengthMenu = c(10, 15, 20, 25),  # 每页显示条目数选项
        searching = TRUE,       # 启用搜索框
        scrollX = TRUE,          # 启用水平滚动
        dom = 'Blfrtip',          # 控制界面元素布局
        buttons = list(       # 关键要素3:按钮嵌套配置
          list(extend = 'copy', text = 'Copy'),
          list(extend = 'csv', text = 'Export CSV'),
          list(extend = 'excel', text = 'Export Excel')
        )
      ),
      rownames = FALSE,         # 不显示行号
      class = "'display nowrap hover" 
    )
  })
  #--------------------------# Summary Information #-------------------------#
  # 显示模型诊断信息
  output$gwpca_modelDiagnostics <- renderPrint({
    gwpca_result()
  })
  #--------------------------# Web Map #-------------------------#
  # 地图可视化
  output$gwpca_mapPlot <- leaflet::renderLeaflet({
    map <- leaflet::leaflet() %>%
          leaflet::clearMarkers() %>%
          leaflet::addProviderTiles("CartoDB.Positron")

    # 返回最终的map对象
    map
  })
  # 更新地图上的图层
  observeEvent(input$gwpca_map_layer_execute, {
    req(gwpca_result())
    req(input$gwpca_map_layer)
    req(gwpca_shapefile_data())
    req(input$gwpca_color_palette)

    # 每更新一次图层清除音频文件
    prefixes_to_remove <- c("^gwpca_map_audio_",
                            "^gwpca_map_audio_composite_",
                            "^gwpca_map_waveform_",
                            "gwpca_map_sound_video.mp4")
    for (prefix in prefixes_to_remove) {
      files_to_remove <- list.files(temp_dir, pattern = prefix, full.names = TRUE)
      www_dir <- file.path(code_dir, "www")
      file_to_remove <- list.files(www_dir, pattern = prefix, full.names = TRUE)
      if (length(files_to_remove) > 0) {
        file.remove(files_to_remove)
      } else if (length(file_to_remove) > 0) {
        file.remove(file_to_remove)}
    }

    # 获取底图map对象
    map <- leaflet::leafletProxy("gwpca_mapPlot", session)
    if(!is.null(map)){}
    else{print("map is null")}
    # 获取result和sdf
    result <- gwpca_result()
    if(!is.null(result$SDF)){sdf <- result$SDF}else{print("result sdf is null")}

    # 获取sdf的坐标
    coords_sdf <- sp::coordinates(sdf)
    # 获取图层名称layer_to_plot
    layer_to_plot <- input$gwpca_map_layer
    # 获取shp数据
    shp_data <- gwpca_shapefile_data()

    library(leaflet.extras)
    library(RColorBrewer)

    # 输出图层选择控件
    if (inherits(shp_data, "sf")) {shp_data <- sf::st_make_valid(shp_data)}
    # 根据几何类型进行不同专题图显示
    if (sf::st_geometry_type(shp_data)[1] == "MULTIPOLYGON" || sf::st_geometry_type(shp_data)[1] == "POLYGON"){
      if(layer_to_plot == "win_var_PC1"){
        # 获取变量名称的唯一值
        unique_vars <- unique(sdf[[layer_to_plot]])
        
        # 动态设置颜色映射
        selected_palette <- input$gwpca_color_palette
        if (selected_palette == "viridis") {
          # 使用 viridis 调色板,颜色分配基于变量名称的索引
          color_mapping <- leaflet::colorFactor(palette = "viridis", domain = unique_vars)
        } else {
          # 使用自定义渐变调色板,动态生成颜色分配
          gradient <- color_gradients[[selected_palette]]
          
          # 为每个变量分配颜色,按渐变色从低到高
          gradient_palette <- colorRampPalette(c(gradient["low"], gradient["high"]))
          color_mapping <- leaflet::colorFactor(
            palette = gradient_palette(length(unique_vars)),  # 为每个唯一变量生成颜色
            domain = unique_vars
          )
        }

        map %>%
          leaflet::clearGroup("Polygons") %>%
          leaflet::clearGroup("Circles") %>%
          leaflet::removeControl("Polygons") %>%
          leaflet::removeControl("Circles") %>%
          leaflet::clearGroup("Significant") %>%
          leaflet::removeControl("Significant") %>%
          leaflet::addPolygons(
            data = shp_data,
            fillColor = ~color_mapping(sdf[[layer_to_plot]]),  # 根据变量名称映射颜色
            color = "black",
            fillOpacity = 0.7,  # 设置填充透明度
            weight = 0.3,
            opacity = 0.7,
            # popup = paste("FID : ",sdf[["FID"]], "<br>", layer_to_plot, " : ", sdf[[layer_to_plot]]),  # 显示变量名称
            group = "Polygons",
            layerId = sdf[["FID"]]
          ) %>%
          leaflet::addLegend(
            position = "bottomright",
            pal = color_mapping,  # 使用动态颜色映射
            values = unique_vars,  # 显示每个变量名称
            title = layer_to_plot,  # 设置图例标题为图层名称
            opacity = 1,
            layerId = "Polygons"
          ) %>%
          # 动态计算地图边界
          leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                    lat1 = min(coords_sdf[, 2]),
                    lng2 = max(coords_sdf[, 1]),
                    lat2 = max(coords_sdf[, 2]))
      } else{
        # 根据用户选择动态设置颜色映射
        selected_palette <- input$gwpca_color_palette
        if (selected_palette == "viridis") {
          color_palette <- leaflet::colorNumeric(palette = "viridis", domain = c(
            min(sdf[[layer_to_plot]], na.rm = TRUE),
            max(sdf[[layer_to_plot]], na.rm = TRUE)
          ))
        } else {
          gradient <- color_gradients[[selected_palette]]
          color_palette <- leaflet::colorNumeric(
            palette = c(gradient["low"], gradient["high"]),
            domain = c(min(sdf[[layer_to_plot]], na.rm = TRUE),
                      max(sdf[[layer_to_plot]], na.rm = TRUE))
          )
        }
        map %>%
                leaflet::clearGroup("Polygons") %>%
                leaflet::clearGroup("Circles") %>%
                leaflet::removeControl("Polygons") %>%
                leaflet::removeControl("Circles") %>%
                leaflet::clearGroup("Significant") %>%
                leaflet::removeControl("Significant") %>%
                # setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 9) %>%
                leaflet::addPolygons(
                  data = shp_data,
                  fillColor = ~color_palette(sdf[[layer_to_plot]]),
                  color = "black",
                  fillOpacity = 0.4,
                  weight = 0.3,
                  opacity = 0.5,
                  # popup = paste("FID : ",sdf[["FID"]], "<br>", layer_to_plot, " : ",  sdf[[layer_to_plot]]),
                  group = "Polygons",
                  layerId = sdf[["FID"]]
                )%>%
                leaflet::addLegend(
                  position = "bottomright",
                  pal = color_palette,                                # 图例位置
                  values = sdf[[layer_to_plot]],
                  title = layer_to_plot,                              # 图例标题
                  labFormat = labelFormat(prefix = "", suffix = ""),  # 可选:标签格式
                  opacity = 1,                                         # 图例的不透明度
                  layerId = "Polygons"
                )%>%
                # 动态计算地图边界
                leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                          lat1 = min(coords_sdf[, 2]),
                          lng2 = max(coords_sdf[, 1]),
                          lat2 = max(coords_sdf[, 2]))
      }
    } else if (sf::st_geometry_type(shp_data)[1] == "POINT" || sf::st_geometry_type(shp_data)[1] == "MULTIPOINT"){
      if(layer_to_plot == "win_var_PC1"){
        # 获取变量名称的唯一值
        unique_vars <- unique(sdf[[layer_to_plot]])

        # 动态设置颜色映射
        selected_palette <- input$gwpca_color_palette
        if (selected_palette == "viridis") {
          # 使用 viridis 调色板
          color_palette <- leaflet::colorFactor(palette = "viridis", domain = unique_vars)
        } else {
          # 使用自定义渐变调色板
          gradient <- color_gradients[[selected_palette]]
          
          # 为每个变量分配颜色
          gradient_palette <- colorRampPalette(c(gradient["low"], gradient["high"]))
          color_palette <- leaflet::colorFactor(
            palette = gradient_palette(length(unique_vars)),  # 为每个唯一变量生成颜色
            domain = unique_vars
          )
        }

        # 绘制点图,使用变量颜色映射
        point_radius <- 50
        map %>%
          leaflet::clearGroup("Polygons") %>%
          leaflet::clearGroup("Circles") %>%
          leaflet::removeControl("Polygons") %>%
          leaflet::removeControl("Circles") %>%
          leaflet::clearGroup("Significant") %>%
          leaflet::removeControl("Significant") %>%
          leaflet::addCircles(
            data = sdf,
            weight = 3,
            radius = point_radius,
            color = ~color_palette(sdf[[layer_to_plot]]),  # 按变量分配颜色
            fillOpacity = 1,
            lng = coords_sdf[, 1],
            lat = coords_sdf[, 2],
            # popup = paste("FID : ",sdf[["FID"]], "<br>", layer_to_plot, " : ", sdf[[layer_to_plot]]),
            opacity = 1,
            group = "Circles",
            layerId = sdf[["FID"]]
          ) %>%
          leaflet::addLegend(
            position = "bottomright",
            pal = color_palette,                                # 图例
            values = unique_vars,
            title = layer_to_plot,                              # 图例标题
            labFormat = labelFormat(prefix = "", suffix = ""),  # 可选:标签格式
            opacity = 1,
            layerId = "Circles"
          ) %>%
          leaflet::fitBounds(
            lng1 = min(coords_sdf[, 1]),
            lat1 = min(coords_sdf[, 2]),
            lng2 = max(coords_sdf[, 1]),
            lat2 = max(coords_sdf[, 2])
          )
      } else {
        # 根据用户选择动态设置颜色映射
        domain <- c(min(sdf[[layer_to_plot]], na.rm = TRUE),
                    max(sdf[[layer_to_plot]], na.rm = TRUE))
        selected_palette <- input$gwpca_color_palette
        if (selected_palette == "viridis") {
          color_palette <- leaflet::colorNumeric(palette = "viridis", domain = domain)
        } else {
          colors <- color_gradients[[selected_palette]]
          color_palette <- leaflet::colorNumeric(palette = colors, domain = domain)
        }
        # 设置半径大小
        point_radius <- 50
        map %>%
              leaflet::clearGroup("Polygons") %>%
              leaflet::clearGroup("Circles") %>%
              leaflet::removeControl("Polygons") %>%
              leaflet::removeControl("Circles") %>%
              leaflet::clearGroup("Significant") %>%
              leaflet::removeControl("Significant") %>%
              # setView(lng = mean(coords_sdf[, 1]), lat = mean(coords_sdf[, 2]), zoom = 11) %>%
              leaflet::addCircles(
                        data = sdf,
                        weight = 3,
                        radius = point_radius,
                        color = ~color_palette(sdf[[layer_to_plot]]),
                        fillOpacity = 1,
                        lng = coords_sdf[, 1],
                        lat = coords_sdf[, 2],
                        # popup = paste("FID : ",sdf[["FID"]], "<br>", layer_to_plot, " : ",  sdf[[layer_to_plot]]),
                        opacity = 1,
                        group = "Circles",
                        layerId = sdf[["FID"]])%>%
              leaflet::addLegend(
                position = "bottomright",
                pal = color_palette,                                # 图例位置
                values = sdf[[layer_to_plot]],
                title = layer_to_plot,                              # 图例标题
                labFormat = labelFormat(prefix = "", suffix = ""),  # 可选:标签格式
                opacity = 1,                                         # 图例的不透明度
                layerId = "Circles"
              )%>%
              # 动态计算地图边界
              leaflet::fitBounds(lng1 = min(coords_sdf[, 1]),
                        lat1 = min(coords_sdf[, 2]),
                        lng2 = max(coords_sdf[, 1]),
                        lat2 = max(coords_sdf[, 2]))
      }
    }

  })
  # 更新gwpca_k最大值
  observeEvent(input$gwpca_vars, {
    max_length <- length(input$gwpca_vars)
    updateNumericInput(session, "gwpca_k", max = max_length)
  })
  # 用 reactiveValues 存储用户点击的点和生成的短音频文件路径
  gwpca_rv <- reactiveValues(
    clicked_points = list(),  # 用于存放点击的经纬度(仅连线模式时使用)
    audio_files = character() # 存放每个点击生成的短音频文件路径
  )
  # 点击地图产生音频
  observeEvent(input$gwpca_mapPlot_click, {
    req(gwpca_result())
    click <- input$gwpca_mapPlot_click
    shp_data <- gwpca_shapefile_data() %>% 
        sf::st_as_sf() %>% 
        sf::st_make_valid() %>% 
        sf::st_set_crs(4326)
    geom_type <- sf::st_geometry_type(shp_data) %>% 
      as.character() %>% 
      unique() %>% 
      dplyr::first()

    # 选择模式:点击模式直接生成短音频
    if(input$gwpca_audio_mode == "click"){
      # 获取必要数据
      gwpca_sdf <- gwpca_result()$SDF
      layer_to_plot <- input$gwpca_map_layer
      
      # 这里假设已将 gwpca_sdf 转换为 sf 对象,或直接使用 spatial 包的 nearest feature 方法
      gwpca_sdf_sf <- sf::st_as_sf(gwpca_sdf)
      click_point <- sf::st_sfc(sf::st_point(c(click$lng, click$lat)), crs = 4326)
      selected_id <- sf::st_nearest_feature(click_point, gwpca_sdf_sf)
      selected_row <- gwpca_sdf[selected_id, ]
      
      # 生成 popup 内容
      popup_content <- paste("FID :", selected_row$FID, "<br>", 
                            layer_to_plot, ":", round(selected_row[[layer_to_plot]], 4))
      if(geom_type %in% c("POLYGON", "MULTIPOLYGON")) {
        # 找到包含点击点的 Polygon
        selected_feature <- shp_data[sf::st_contains(shp_data, click_point, sparse = FALSE), ]
        if (nrow(selected_feature) == 0) {
          shiny::showNotification("No polygon data found", type = "error")
          return(NULL)
        }

        leaflet::leafletProxy("gwpca_mapPlot") %>%
          leaflet::clearPopups() %>%
          # addPopups(lng = click$lng, lat = click$lat, popup = popup_content) %>%
          leaflet::clearGroup("highlighted_features") %>%  # 清除之前的高亮
          leaflet::addPolygons(
            data = selected_feature,
            group = "highlighted_features",
            fillColor = "yellow",
            color = "#FF0000",
            fillOpacity = 0,
            weight = 2,
            highlightOptions = leaflet::highlightOptions(
              weight = 5,
              color = "#FF0000",
              bringToFront = TRUE
              )
            )%>%
          leaflet::addMarkers(  
                  lng = click$lng, 
                  lat = click$lat, 
                  popup = popup_content,
                  group = "highlighted_features",
                  icon = musicIcon # 使用自定义图标
              )
      }else if(geom_type %in% c("POINT", "MULTIPOINT")) {
        # 找到最近的点
        selected_id <- sf::st_nearest_feature(click_point, shp_data)
        selected_feature <- shp_data[selected_id, ]
        if (nrow(selected_feature) == 0) {
          shiny::showNotification("No point data found", type = "error")
          return(NULL)
        }
        leaflet::leafletProxy("gwpca_mapPlot") %>%
          leaflet::clearPopups() %>%
          leaflet::clearGroup("highlighted_features") %>%  # 清除之前的高亮
          # addPopups(lng = click$lng, lat = click$lat, popup = popup_content) %>%
          leaflet::addCircleMarkers(
            data = selected_feature,
            group = "highlighted_features",
            color = "red",
            fillOpacity = 0,
            radius = 8,
            stroke = TRUE,
            weight = 2
          )%>%
          leaflet::addMarkers(  
                  lng = click$lng, 
                  lat = click$lat, 
                  popup = popup_content,
                  group = "highlighted_features",
                  icon = musicIcon # 使用自定义图标
              )
      } else {
        shiny::showNotification("The current geometry type is not supported", type = "error")
        return(NULL)
      }
      
      
      # 生成短音频文件
      coeff <- ifelse(is.na(selected_row[[layer_to_plot]]), 0, selected_row[[layer_to_plot]])
      short_filename <- paste0("www/gwpca_map_audio_", as.integer(Sys.time()), "_", sample(1:1000, 1), ".wav")
      generate_audio(value = coeff,
                    filename = short_filename,
                    x_min = min(gwpca_sdf[[layer_to_plot]], na.rm = TRUE),
                    x_max = max(gwpca_sdf[[layer_to_plot]], na.rm = TRUE))
      
      # 更新 audio 控件,播放短音频
      shinyjs::runjs(sprintf("document.getElementById('gwpca_map_audio').src='%s'; document.getElementById('gwpca_map_audio').play();", short_filename))
      
    } else if(input$gwpca_audio_mode == "line") {
      # 连线模式:记录点击点,不直接生成音频
      gwpca_rv$clicked_points <- append(gwpca_rv$clicked_points, list(c(click$lng, click$lat)))
      # 在地图上添加标记以便确认顺序
      leaflet::leafletProxy("gwpca_mapPlot") %>%
        leaflet::addMarkers(
                  lng = click$lng, 
                  lat = click$lat,
                  popup = paste("point", length(gwpca_rv$clicked_points)),
                  group = "point",
                  icon = musicIcon
                  )
    }
  })
  # 处理连线模式下的确认按钮:生成长音频
  observeEvent(input$gwpca_confirm_audio, {
    req(length(gwpca_rv$clicked_points) > 0)
    # 仅在连线模式时触发检查
    if (input$gwpca_audio_mode == "line") {
      # 检查点击点数量是否足够
      if (length(gwpca_rv$clicked_points) < 2) {
        shiny::showNotification("Choose at least two points", type = "error")
        return() # 不满足条件时提前退出
      }
    }

    gwpca_buffer_length <- as.numeric(input$gwpca_buffer_length)
    layer_to_plot <- input$gwpca_map_layer

    # 显示自定义模态窗口
    showModal(modalDialog(
      title = "Processing gwpca Audio...",
      tags$div(
        style = "text-align: center;",
        shinyWidgets::progressBar(
          id = "gwpca_audio_progress",
          value = 0,
          display_pct = TRUE,
          status = "info",
          striped = TRUE
        )
      ),
      footer = tagList(
        modalButton("Cancel")  # 添加关闭按钮
      ),
      easyClose = FALSE
    ))

    # 1. 获取用户点击的坐标并绘制连线
    clicked_matrix <- do.call(rbind, gwpca_rv$clicked_points)
    leaflet::leafletProxy("gwpca_mapPlot") %>%
      leaflet::addPolylines(
                  lng = clicked_matrix[,1], 
                  lat = clicked_matrix[,2],
                  color = "red",
                  weight = gwpca_buffer_length,
                  opacity = 0.8,
                  group = "point_line") %>%
      leaflet::clearGroup("highlighted_features")  # 清除之前的高亮
    
    # 2. 创建 sf 线对象并确保坐标系统一
    clicked_line <- sf::st_linestring(clicked_matrix) %>%
      sf::st_sfc(crs = 4326) %>%
      sf::st_make_valid()
    line_buffer <- sf::st_buffer(clicked_line, dist = gwpca_buffer_length)  # 可调整缓冲区大小
  
    # 3. 处理空间数据
    gwpca_sdf <- gwpca_result()$SDF
    shp_data <- gwpca_shapefile_data() %>% 
      sf::st_as_sf() %>% 
      sf::st_make_valid() %>% 
      sf::st_set_crs(4326)

    # 更新进度条
    shinyWidgets::updateProgressBar(session = session, id = "gwpca_audio_progress", value = 10)

    # 4. 转换Spatial对象为sf
    gwpca_sdf_sf <- sf::st_as_sf(gwpca_sdf) 
    
    # 将gwpca计算结果合并到原始shp属性(假设行顺序一致)
    if(nrow(shp_data) == nrow(gwpca_sdf_sf)) {
      shp_data_with_gwpca <- shp_data %>%
        dplyr::bind_cols(sf::st_drop_geometry(gwpca_sdf_sf)) %>%  # 使用正确的属性数据
        sf::st_sf()
    } else {
      shiny::showNotification("The number of data rows is inconsistent", type = "error")
      shiny::removeModal()
      return()
    }

    geom_type <- sf::st_geometry_type(shp_data_with_gwpca) %>% 
      as.character() %>% 
      unique() %>% 
      dplyr::first()

    if(geom_type %in% c("POLYGON", "MULTIPOLYGON")) {
      # 精确相交检测
      intersects <- tryCatch({
        sf::st_filter(shp_data_with_gwpca, line_buffer)
      }, error = function(e) {
        shiny::showNotification("Fail in intersects", type = "error")
        shiny::removeModal()
        return(NULL)
      })
      
      intersect_features <- intersects
      # **绘制调试图,检查是否有交集**
      plot(sf::st_geometry(shp_data_with_gwpca), col = "blue", border = "black", main = "空间要素 vs. 线")
      plot(sf::st_geometry(line_buffer), col = "red", lwd = gwpca_buffer_length, add = TRUE)

      leaflet::leafletProxy("gwpca_mapPlot") %>%
        leaflet::addPolygons(
          data = intersects,
          group = "highlighted_features",
          color = "#FF0000",
          fillOpacity = 0,
          weight = 2,
          highlightOptions = leaflet::highlightOptions(
            weight = 5,
            color = "#FF0000",
            bringToFront = TRUE
            )
          )
    }else if(geom_type %in% c("POINT", "MULTIPOINT")) {
      # 点处理逻辑
      # intersect_idx  <- st_intersects(gwpca_sdf_sf, line_buffer, sparse = FALSE)
      # intersect_features <- gwpca_sdf_sf[which(intersect_idx, arr.ind = TRUE), ]
      intersects <- tryCatch({
        sf::st_filter(shp_data_with_gwpca, line_buffer)
      }, error = function(e) {
        shiny::showNotification("Fail in intersects", type = "error")
        shiny::removeModal()
        return(NULL)
      })
      intersect_features <- intersects

      if(nrow(intersect_features) == 0) {
        shiny::showNotification("The line does not pass through any features", type = "warning")
        shiny::removeModal()
        return()
      }
      # **绘制调试图,检查是否有交集**
      plot(sf::st_geometry(gwpca_sdf_sf), col = "blue", border = "black", main = "空间要素 vs. 线")
      plot(sf::st_geometry(line_buffer), col = "red", lwd = gwpca_buffer_length, add = TRUE)

      leaflet::leafletProxy("gwpca_mapPlot") %>%
        leaflet::addCircleMarkers(
          data = intersect_features,
          group = "highlighted_features",
          color = "red",
          fillOpacity = 0,
          radius = 8,
          stroke = TRUE,
          weight = 2
        )

    } else {
      shiny::showNotification("The current geometry type is not supported", type = "error")
      shiny::removeModal()
      return()
    }
    
    # 更新进度条
    shinyWidgets::updateProgressBar(session = session, id = "gwpca_audio_progress", value = 20)

    # 6. 沿路径排序(确保交点顺序与点击点一致)
    if (nrow(intersect_features) > 0) {
      start_point <- sf::st_sfc(sf::st_point(clicked_matrix[1, ]), crs = 4326)
      intersect_features <- intersect_features %>%
        dplyr::mutate(
          dist_to_start = as.numeric(sf::st_distance(geometry, start_point))
        ) %>%
        dplyr::arrange(dist_to_start)
    }

    # 7. 生成音频
    gwpca_rv$audio_files <- character()  # 重置音频存储

    # 更新进度条
    shinyWidgets::updateProgressBar(session = session, id = "gwpca_audio_progress", value = 30 )

    # 8. 遍历要素,生成音频
    for (i in seq_len(nrow(intersect_features))) {
      feature <- intersect_features[i, ]
      coeff <- ifelse(is.na(feature[[layer_to_plot]]), 0, feature[[layer_to_plot]])
      short_filename <- paste0("www/gwpca_map_audio_", i, ".wav")

      generate_audio(
        value = coeff,
        filename = short_filename,
        x_min = min(gwpca_sdf[[layer_to_plot]], na.rm = TRUE),
        x_max = max(gwpca_sdf[[layer_to_plot]], na.rm = TRUE)
      )

      gwpca_rv$audio_files <- c(gwpca_rv$audio_files, short_filename)

      # 更新进度条
      shinyWidgets::updateProgressBar(session = session, id = "gwpca_audio_progress", value = 30 + (i/nrow(intersect_features)) * 30)
    }
    
    # 9. 合成所有短音频文件为一个长音频
    composite_filename <- paste0("www/gwpca_map_audio_composite.wav")
    concatenate_audio(gwpca_rv$audio_files, composite_filename)
    # 确保 FFmpeg 使用合成后的音频
    sound_road <- composite_filename  # 这里修正

    # 提取系数数据
    ranges <- seq_len(nrow(intersect_features))  # 用于 X 轴
    coeff_values <- intersect_features[[layer_to_plot]]  # 提取回归系数
    ranges_length <- length(ranges)
    # 生成初始曲线图
    waveform_plot <- ggplot2::ggplot(data.frame(x = ranges, y = coeff_values), ggplot2::aes(x = x, y = y)) +
      ggplot2::geom_point(color = "blue", size = 2) +  
      ggplot2::geom_smooth(method = "loess", color = "blue", span = 0.5, se = FALSE) +  
      ggplot2::theme_minimal() +
      ggplot2::theme(
        panel.background = ggplot2::element_rect(fill = "white", color = NA),  
        plot.background = ggplot2::element_rect(fill = "white", color = NA)  
      ) +
      ggplot2::ggtitle("Coefficient Variation Over Features") +
      ggplot2::xlab("Feature Index") + 
      ggplot2::ylab("Coefficient Value")

    # 逐帧生成视频
    waveform_images <- c()
    for (i in seq_along(ranges)) {
      frame_plot <- waveform_plot +
        ggplot2::geom_vline(xintercept = ranges[i], color = "red", linetype = "dashed", size = 1) +
        ggplot2::ggtitle(paste("Feature:", i, "  Coefficient:", round(coeff_values[i], 4)))

      frame_image <- file.path(temp_dir, paste0("gwpca_map_waveform_", i, ".png"))
      ggplot2::ggsave(frame_image, frame_plot, width = 6, height = 4, dpi = 150)
      waveform_images <- c(waveform_images, frame_image)

      shinyWidgets::updateProgressBar(session, "gwpca_video_progress", value = 60 + (i / ranges_length) * 30)
    }

    # 计算音频时长
    audio_info <- tuneR::readWave(composite_filename)
    total_frames <- length(waveform_images)
    total_audio_duration <- length(audio_info@left) / audio_info@samp.rate
    # 计算帧率,使得视频时长与音频一致
    frame_rate <- total_frames / total_audio_duration
    # 确保 framerate 合理(避免异常)
    if (frame_rate < 1) frame_rate <- 1
    if (frame_rate > 30) frame_rate <- 30  # 限制在 1-30 fps,防止帧率过大或过小
    # 生成视频
    waveform_video <- file.path(temp_dir, "gwpca_map_waveform_video.mp4")
    av::av_encode_video(
      input = waveform_images,
      output = waveform_video,
      framerate = frame_rate,
      codec = "libx264",
      vfilter = "scale=720:720,format=yuv420p",
      audio = NULL,
      verbose = TRUE
    )

    # 合成音视频
    sound_video <- file.path("www", "gwpca_map_sound_video.mp4")
    # sound_road <- gwpca_rv$audio_files  # 获取之前生成的音频
    ffmpeg_command <- paste(
      "ffmpeg",
      "-y", 
      "-i", shQuote(waveform_video),
      "-i", shQuote(sound_road),
      "-ac 2 -c:v libx264 -c:a aac -b:a 192k -strict experimental",
      shQuote(sound_video)
    )
    ffmpeg_result <- tryCatch({
      system(ffmpeg_command, intern = FALSE)
    }, error = function(e) {
      cat("FFmpeg command failed:", e$message, "\n")
      NA
    })
    if (!is.na(ffmpeg_result) && ffmpeg_result != 0) {
      shiny::showNotification("The command execution of FFmpeg failed.Please check (1) whether ffmpeg is installed (2) whether the system path is configured.", type = "error")
      shiny::removeModal()
      return()
    }
    cat(format(Sys.time(), "%Y-%m-%d %H:%M:%S"), " 合成波形图和音频完成,文件保存为: ", sound_video, "\n")

    # 在地图左下角显示视频
    leaflet::leafletProxy("gwpca_mapPlot") %>%
      leaflet::removeControl(layerId ="map_video_control") %>%
      leaflet::addControl(
                HTML(sprintf(
          '<video id="gwpca_video_player" width="300" autoplay controls>
            <source src="%s" type="video/mp4">
            Your browser does not support the video tag.
          </video>
          <audio id="gwpca_audio" src="%s"></audio>
          <script>
            document.getElementById("gwpca_video_player").onplay = function() {
              var audio = document.getElementById("gwpca_audio");
              if (audio) {
                audio.play();
              }
            };
            document.getElementById("gwpca_video_player").onpause = function() {
              var audio = document.getElementById("gwpca_audio");
              if (audio) {
                audio.pause();
              }
            };
            document.getElementById("gwpca_video_player").ontimeupdate = function() {
              var audio = document.getElementById("gwpca_audio");
              if (audio) {
                audio.currentTime = this.currentTime;
              }
            };
          </script>', sound_video, composite_filename
        )),
        position = "bottomleft",
        layerId ="map_video_control"
      )

    # 设置音频源
    # shinyjs::runjs(sprintf("document.getElementById('gwpca_map_audio').src='%s';", composite_filename))

    # 重置记录,便于下次操作
    gwpca_rv$clicked_points <- list()
    gwpca_rv$audio_files <- character()

    shinyWidgets::updateProgressBar(session = session, id = "gwpca_audio_progress", value = 100)
    Sys.sleep(1)
    shiny::removeModal()
  })
  # 处理连线的图标
  observeEvent(input$gwpca_confirm_audio_clear, {
    # 重置记录,便于下次操作
    gwpca_rv$clicked_points <- list()
    gwpca_rv$audio_files <- character()

    leaflet::leafletProxy("gwpca_mapPlot") %>%
      leaflet::removeControl(layerId ="map_video_control") %>%
      leaflet::clearGroup("point") %>%
      leaflet::clearGroup("point_line") %>%
      leaflet::clearGroup("highlighted_features")  # 清除高亮
  })
  #--------------------------# Multiple Visualizations #-------------------------#
  # 动态生成图片
  observe({
    req(input$gwpca_columns)  # 确保用户已经选择了变量
    req(input$gwpca_bandwidth)
    req(input$gwpca_color_palette)  # 确保用户已经选择了色阶

    # 显示等待窗口
    showModal(modalDialog(
      title = "Please wait",
      "The GWPCA result is drawing...",
      easyClose = FALSE
    ))

    bw <- round(as.numeric(gwpca_bw_data$bw), 3)

    shp_data <- gwpca_shapefile_data()
    if (!inherits(shp_data, "sf")) {shp_data <- sf::st_as_sf(shp_data)}

    # 获取 gwpca 结果
    gwpca_result <- gwpca_result()
    sdf <- gwpca_result$SDF

    # 设置高分辨率参数
    dpi <- 100  # 设置DPI值
    width_px <- 1500  # 设置宽度像素
    height_px <- 1500 # 设置高度像素

    # 遍历用户选择的列并动态生成图片
    selected_columns <- input$gwpca_columns
    for (col in selected_columns) {
      local({
        column_name <- col  # 需要使用 local() 避免循环中的闭包问题
        # 动态创建 UI 输出控件
        output[[paste0("plot_", column_name)]] <- renderPlot({

          # 确保列存在于数据框中
          req(column_name %in% names(sdf))

          # 创建专题图
          target_data <- shp_data
          target_data$value <- sdf[[column_name]]  # 将数据加入到 sf 对象中,以便于绘制专题图

          # 判断数据类型,动态选择绘图样式
          geom_type <- unique(sf::st_geometry_type(target_data))
          # 根据选择的色阶动态设置颜色
          gradient <- color_gradients[[input$gwpca_color_palette]]

          if ("POINT" %in% geom_type || "MULTIPOINT" %in% geom_type) {  # 点数据:使用 color 映射
              if (input$gwpca_color_palette == "viridis") {
                ggplot2::ggplot(target_data) +
                  ggplot2::geom_sf(ggplot2::aes(color = value)) +
                  ggplot2::scale_color_viridis_c() +  # 使用 Viridis 色阶
                  ggplot2::labs(title = paste0(column_name, "  (Bandwidth: ", bw, ")")) +
                  ggspatial::annotation_north_arrow(
                    location = "tl",  # 左上角
                    which_north = "true",
                    pad_x = ggplot2::unit(0.3, "cm"),
                    pad_y = ggplot2::unit(0.3, "cm"),
                    style = ggspatial::north_arrow_fancy_orienteering()
                  ) +
                  ggspatial::annotation_scale(
                    location = "bl",  # 左下角
                    width_hint = 0.3
                  ) +
                  ggplot2::theme(
                    panel.background = ggplot2::element_rect(fill = "white", color = NA),
                    panel.grid = ggplot2::element_blank(),
                    plot.background = ggplot2::element_rect(fill = "white", color = NA)
                  )
              } else {
                ggplot2::ggplot(target_data) +
                  ggplot2::geom_sf(ggplot2::aes(color = value)) +
                  ggplot2::scale_color_gradient(low = gradient["low"], high = gradient["high"]) +
                  ggplot2::labs(title = paste0(column_name, "  (Bandwidth: ", bw, ")")) +
                  ggspatial::annotation_north_arrow(
                    location = "tl",  # 左上角
                    which_north = "true",
                    pad_x = ggplot2::unit(0.3, "cm"),
                    pad_y = ggplot2::unit(0.3, "cm"),
                    style = ggspatial::north_arrow_fancy_orienteering()
                  ) +
                  ggspatial::annotation_scale(
                    location = "bl",  # 左下角
                    width_hint = 0.3
                  ) +
                  ggplot2::theme(
                    panel.background = ggplot2::element_rect(fill = "white", color = NA),
                    panel.grid = ggplot2::element_blank(),
                    plot.background = ggplot2::element_rect(fill = "white", color = NA)
                  )
              }
          } else if ("POLYGON" %in% geom_type || "MULTIPOLYGON" %in% geom_type) {  # 面数据:使用 fill 映射
            if (input$gwpca_color_palette == "viridis") {
              ggplot2::ggplot(target_data) +
                ggplot2::geom_sf(ggplot2::aes(fill = value)) +
                ggplot2::scale_fill_viridis_c() +  # 使用 Viridis 色阶
                ggplot2::labs(title = paste0(column_name, "  (Bandwidth: ", bw, ")")) +
                ggspatial::annotation_north_arrow(
                    location = "tl",  # 左上角
                    which_north = "true",
                    pad_x = ggplot2::unit(0.3, "cm"),
                    pad_y = ggplot2::unit(0.3, "cm"),
                    style = ggspatial::north_arrow_fancy_orienteering()
                  ) +
                  ggspatial::annotation_scale(
                    location = "bl",  # 左下角
                    width_hint = 0.3
                  ) +
                ggplot2::theme(
                  panel.background = ggplot2::element_rect(fill = "white", color = NA),
                  panel.grid = ggplot2::element_blank(),
                  plot.background = ggplot2::element_rect(fill = "white", color = NA)
                )
            } else {
              ggplot2::ggplot(target_data) +
                ggplot2::geom_sf(ggplot2::aes(fill = value)) +
                ggplot2::scale_fill_gradient(low = gradient["low"], high = gradient["high"]) +
                ggplot2::labs(title = paste0(column_name, "  (Bandwidth: ", bw, ")")) +
                ggspatial::annotation_north_arrow(
                    location = "tl",  # 左上角
                    which_north = "true",
                    pad_x = ggplot2::unit(0.3, "cm"),
                    pad_y = ggplot2::unit(0.3, "cm"),
                    style = ggspatial::north_arrow_fancy_orienteering()
                  ) +
                  ggspatial::annotation_scale(
                    location = "bl",  # 左下角
                    width_hint = 0.3
                  ) +
                ggplot2::theme(
                  panel.background = ggplot2::element_rect(fill = "white", color = NA),
                  panel.grid = ggplot2::element_blank(),
                  plot.background = ggplot2::element_rect(fill = "white", color = NA)
                )
            }
          } else {
            # 如果既不是点也不是面,抛出警告
            shiny::showNotification("Unsupported geometry type for plotting.", type = "error")
            shiny::removeModal()
            return()
          }        
        })
      })
    }

    # # 动态图片生成完成后关闭等待窗口
    shiny::removeModal()
  })
  # 动态显示图片
  output$gwpca_Plot <- renderUI({
    req(input$gwpca_columns)  # 确保用户已经选择了变量

    # 每行的图片数量
    images_per_row <- min(2, length(input$gwpca_columns))  # 每行最多显示 2 张图片

    plot_outputs <- lapply(seq_along(input$gwpca_columns), function(i) {
      column_name <- input$gwpca_columns[i]

      # 使用 column 动态调整宽度,每行最多显示 images_per_row 张图片
      column(
        width = 12 / images_per_row,  # 动态设置列宽(12 栅格系统)
        plotOutput(outputId = paste0("plot_", column_name), height = "500px", width = "100%")
      )
    })

    # 将图片控件布局在 fluidRow 中
    do.call(fluidRow, plot_outputs)
  })
  #--------------------------# Glygh Plot #-------------------------#
  output$gwpca_glyph_plot <- renderPlot({
    gwpca_result <- gwpca_result()
    sdf    <- gwpca_result$SDF
    coords <- sp::coordinates(sdf)

    # 基本校验
    if (nrow(gwpca_result$loadings) != nrow(coords)) {
      showNotification("载荷与坐标行数不匹配", type = "error")
      return()
    }
    if (any(apply(gwpca_result$loadings, 1, max) == 0)) {
      showNotification("存在全零载荷行", type = "error")
      return()
    }

    if(isTRUE(input$gwpca_glyph_add)){
      # 取 k 个主成分
      k <- dim(gwpca_result$loadings)[3]
      for (pc in 1:k) {
        ld_mat <- gwpca_result$loadings[, , pc]
        GWmodel::gwpca.glyph.plot(
          ld            = ld_mat,
          loc           = coords,
          r1            = 50,
          add           = (pc != 1),        # 第一张图 recreate,后续叠加
          alpha         = 1,              # 可调整透明度
          sep.contrasts = as.logical(input$gwpca_glyph_sep) 
        )
      }
    }else{
      # 取第1个主成分的局部载荷矩阵(维度:n_locations × n_variables)
      local_loadings_pc1 <- gwpca_result$loadings[, , 1]
      GWmodel::gwpca.glyph.plot(
          ld = local_loadings_pc1,  
          loc = coords,
          sep.contrasts =  as.logical(input$gwpca_glyph_sep),
          r1 = 50,
          add = FALSE,
          alpha = 1
      )
    }
  })


  #--------------------------# 停止服务器 #--------------------------#
  # 在应用退出时停止服务器
  onStop(function() {
    stop_servr(file_server)
  })

}


# 运行Shiny应用
# shinyApp(ui = ui, server = server)

Try the GWmodelVis package in your browser

Any scripts or data that you put into this service are public.

GWmodelVis documentation built on June 8, 2025, 1:13 p.m.