generate_tracks <- function(x) {
  console_log("generate_tracks.r", glue("generate_tracks()"))
  track_points <- x$unfiltered_obs_data %>%
    filter(ONOFFTX != "OFF") %>%
    # select(Latitude, Longitude, timestamp, Date, Type, ONOFFTX, TripID,
    #        Transect, Record, Species, Count, PrimaryBehavior,
    #        Comments, Beaufort, NumberObs, LObs, Lcond, RObs, Rcond,
    #        TransType, TransWidth) %>%
    arrange(TripID, Transect, Date, timestamp) %>%
    mutate(
      seconds = as.numeric(as.duration(timestamp - lag(timestamp))),
      seconds = if_else(is.na(seconds), 0, seconds),
      rleid = as.integer(
        seconds > as.numeric(input$max_point_interval_seconds)
      ),
      rleid = cumsum(rleid)
    ) %>%
    mutate(
      next_lat = lead(Latitude),
      next_lon = lead(Longitude),
      prev_lat = lag(Latitude),
      prev_lon = lag(Longitude),
      jump = (abs(next_lat - Latitude) > 0.2 & abs(prev_lat - Latitude) > 0.2) |
        (abs(next_lon - Longitude) > 0.2 & abs(prev_lon - Longitude) > 0.2) &
          rleid == lead(rleid)
    ) %>%
    filter(!jump | !is.na(Species)) %>%
    mutate(
      Latitude = if_else(jump & !is.na(next_lat), next_lat, Latitude),
      Longitude = if_else(jump & !is.na(next_lon), next_lon, Longitude)
    ) %>%
    select(-c(next_lat, next_lon, prev_lat, prev_lon, jump))

  raw_track_points <- track_points %>%
    filter(is.na(Species))

  track_points <- track_points %>%
    st_as_sf(
      coords = c("Longitude", "Latitude"),
      crs = 4326
    )

  x$raw_track_points <- raw_track_points
  x$track_points <- track_points

  observation_points <- track_points %>%
    filter(!is.na(Species))

  x$observation_points <- observation_points

  track_lines <- track_points %>%
    filter(!is.na(Transect)) %>%
    arrange(timestamp) %>%
    group_by(TripID, Transect, Date, rleid) %>%
    summarize(
      do_union = FALSE,
      start_time = min(timestamp),
      end_time = max(timestamp),
      points = n(),
      .groups = "drop"
    ) %>%
    filter(points > 1) %>%
    st_cast("LINESTRING") %>%
    st_transform(x$crs) %>%
    st_segmentize(dfMaxLength = 50) %>%
    group_by(TripID, Transect, Date) %>%
    # select(-do_union) %>%
    summarize(
      do_union = FALSE,
      start_time = min(start_time),
      end_time = max(end_time),
      .groups = "drop"
    ) %>%
    st_cast("MULTILINESTRING") %>%
    st_transform(x$crs) %>%
    mutate(length_km = map_dbl(geometry, ~ st_length(.x)) / 1000) %>%
    st_transform(4326)

  x$track_lines <- track_lines

  segmentized_points <- x$track_lines %>%
    st_cast("POINT", warn = FALSE) %>%
    st_join(track_points, join = st_nearest_feature) %>%
    rename(
      TripID = TripID.y,
      Transect = Transect.y,
      Date = Date.y
    ) %>%
    select(
      -c(
        start_time,
        end_time,
        length_km,
        TripID.x,
        Transect.x,
        Date.x
      )
    )

  x$segmentized_points <- segmentized_points

  track_centroids <- track_points %>%
    st_transform(x$crs) %>%
    group_by(TripID, Transect, Date) %>%
    summarize(.groups = "drop") %>%
    mutate(
      x = map_dbl(geometry, ~ st_centroid(.x)[[1]]),
      y = map_dbl(geometry, ~ st_centroid(.x)[[2]])
    ) %>%
    st_drop_geometry() %>%
    st_as_sf(
      coords = c("x", "y"),
      crs = x$crs
    ) %>%
    st_transform(4326) %>%
    mutate(
      Latitude = map_dbl(geometry, ~ st_centroid(.x)[[2]]),
      Longitude = map_dbl(geometry, ~ st_centroid(.x)[[1]])
    )

  x$track_centroids <- track_centroids

  valid_transects <- x$unfiltered_obs_data %>%
    filter(ONOFFTX != "OFF") %>%
    group_by(TripID, Transect, Date) %>%
    summarize(.groups = "drop")

  x$valid_transects <- valid_transects

  # A transect with no surviving track line (e.g. all its segments had a single
  # point and were dropped by filter(points > 1), or geometry ops collapsed it)
  # will be dropped by the inner_join in generate_transects().
  log_dropped_keys(
    "generate_tracks.r",
    "no track line built for transect",
    valid_transects,
    track_lines,
    c("TripID", "Transect", "Date")
  )

  x
}
