tests/testthat/test-intersect.R

test_that("geodesic_intersect finds intersection of two geodesics", {
  # Two geodesics crossing near origin
  # Geodesic X: starts at (0, 0), azimuth 45 degrees (NE)
  # Geodesic Y: starts at (1, 0), azimuth 315 degrees (NW)
  result <- geodesic_intersect(c(0, 0), 45, c(1, 0), 315)

  expect_s3_class(result, "data.frame")
  expect_named(result, c("x", "y", "coincidence", "lat", "lon"))
  expect_equal(nrow(result), 1)

  # Both displacements should be positive (intersection ahead on both geodesics)
  expect_gt(result$x, 0)
  expect_gt(result$y, 0)

  # Coincidence should be 0 (normal intersection)
  expect_equal(result$coincidence, 0)

  # Intersection should be roughly between the two starting points
  expect_gt(result$lon, 0)
  expect_lt(result$lon, 1)
  expect_gt(result$lat, 0)
})

test_that("geodesic_intersect handles parallel geodesics",
 {
  # Two parallel geodesics (same azimuth)
  result <- geodesic_intersect(c(0, 0), 90, c(0, 1), 90)

  # For parallel non-coincident geodesics, the result depends on 
  # how GeographicLib handles this case
  expect_s3_class(result, "data.frame")
})

test_that("geodesic_intersect is vectorized", {
  # Multiple intersection problems
  result <- geodesic_intersect(
    cbind(c(0, 0, 0), c(0, 0, 0)),
    c(30, 45, 60),
    cbind(c(1, 1, 1), c(0, 0, 0)),
    c(330, 315, 300)
  )

  expect_equal(nrow(result), 3)
  expect_true(all(result$x > 0))
  expect_true(all(result$y > 0))
})

test_that("geodesic_intersect handles NA inputs", {
  result <- geodesic_intersect(c(NA, 0), 45, c(1, 0), 315)

  expect_true(is.na(result$x))
  expect_true(is.na(result$y))
  expect_true(is.na(result$lat))
  expect_true(is.na(result$lon))
})

test_that("geodesic_intersect_segment finds segment intersection", {
  # Two crossing segments
  # Segment X: from (0, -1) to (0, 1) - roughly N-S line at lon 0
  # Segment Y: from (-1, 0) to (1, 0) - roughly W-E line at lat 0
  result <- geodesic_intersect_segment(
    c(0, -1), c(0, 1),
    c(-1, 0), c(1, 0)
  )

  expect_s3_class(result, "data.frame")
  expect_named(result, c("x", "y", "segmode", "coincidence", "lat", "lon"))

  # segmode = 0 means intersection within both segments
  expect_equal(result$segmode, 0)

  # Intersection should be near origin
  expect_lt(abs(result$lat), 1)
  expect_lt(abs(result$lon), 1)
})

test_that("geodesic_intersect_segment detects non-intersecting segments", {
  # Two segments that don't intersect
  # Segment X: from (0, 0) to (0, 1)
  # Segment Y: from (10, 0) to (10, 1)
  result <- geodesic_intersect_segment(
    c(0, 0), c(0, 1),
    c(10, 0), c(10, 1)
  )

  # segmode != 0 means intersection outside segments
  expect_false(result$segmode == 0)
})

test_that("geodesic_intersect_segment is vectorized", {
  # Multiple segment intersection problems
  result <- geodesic_intersect_segment(
    cbind(c(0, 0), c(-1, -1)),
    cbind(c(0, 0), c(1, 1)),
    cbind(c(-1, -1), c(0, 0)),
    cbind(c(1, 1), c(0, 0))
  )

  expect_equal(nrow(result), 2)
})

test_that("geodesic_intersect_next finds next intersection", {
  # Two geodesics crossing at origin
  result <- geodesic_intersect_next(c(0, 0), 45, 315)

  expect_s3_class(result, "data.frame")
  expect_named(result, c("x", "y", "coincidence", "lat", "lon"))

  # The "next" intersection should be some distance away
  # (not at the starting point)
  expect_true(abs(result$x) > 1000 || abs(result$y) > 1000)
})

test_that("geodesic_intersect_next is vectorized", {
  result <- geodesic_intersect_next(
    cbind(c(0, 0), c(0, 0)),
    c(45, 30),
    c(315, 330)
  )

  expect_equal(nrow(result), 2)
})

test_that("geodesic_intersect_all finds multiple intersections", {
  # Two geodesics with multiple intersections within search radius
  result <- geodesic_intersect_all(c(0, 0), 45, c(1, 0), 315, maxdist = 1e7)

  expect_s3_class(result, "data.frame")
  # Should find at least the closest intersection
  expect_gte(nrow(result), 1)
})

test_that("geodesic_intersect_all returns list for multiple inputs", {
  result <- geodesic_intersect_all(
    cbind(c(0, 0), c(0, 0)),
    c(45, 30),
    cbind(c(1, 1), c(0, 0)),
    c(315, 330),
    maxdist = 1e6
  )

  expect_type(result, "list")
  expect_length(result, 2)
  expect_s3_class(result[[1]], "data.frame")
  expect_s3_class(result[[2]], "data.frame")
})

test_that("geodesic_intersect_all respects maxdist", {
  # Small search radius
  result_small <- geodesic_intersect_all(c(0, 0), 45, c(1, 0), 315, maxdist = 1e5)
  # Large search radius
  result_large <- geodesic_intersect_all(c(0, 0), 45, c(1, 0), 315, maxdist = 1e8)

  # Larger radius should find at least as many intersections
  expect_gte(nrow(result_large), nrow(result_small))
})

test_that("geodesic_intersect accepts different input formats", {
  # Vector input
  r1 <- geodesic_intersect(c(0, 0), 45, c(1, 0), 315)

  # Matrix input
  r2 <- geodesic_intersect(matrix(c(0, 0), ncol = 2), 45,
                            matrix(c(1, 0), ncol = 2), 315)

  # List input
  r3 <- geodesic_intersect(list(lon = 0, lat = 0), 45,
                            list(lon = 1, lat = 0), 315)

  expect_equal(r1$lat, r2$lat, tolerance = 1e-10)
  expect_equal(r1$lat, r3$lat, tolerance = 1e-10)
  expect_equal(r1$lon, r2$lon, tolerance = 1e-10)
  expect_equal(r1$lon, r3$lon, tolerance = 1e-10)
})

test_that("geodesic_intersect handles antipodal intersection", {
  # Two geodesics from same point with different azimuths
  # They will intersect at the antipode
  result <- geodesic_intersect(c(0, 0), 0, c(0, 0), 90)

  expect_s3_class(result, "data.frame")
  # At origin, both displacements should be 0
  expect_lt(abs(result$x), 1)
  expect_lt(abs(result$y), 1)
})

test_that("geodesic_intersect_segment handles NA inputs", {
  result <- geodesic_intersect_segment(
    c(NA, 0), c(0, 1),
    c(-1, 0), c(1, 0)
  )

  expect_true(is.na(result$x))
  expect_true(is.na(result$segmode))
})

test_that("geodesic_intersect_next handles NA inputs", {
  result <- geodesic_intersect_next(c(NA, 0), 45, 315)

  expect_true(is.na(result$x))
  expect_true(is.na(result$y))
})

test_that("intersection lat/lon is consistent with displacement", {
  # Verify that the returned lat/lon matches moving along geodesic X by x meters
  result <- geodesic_intersect(c(0, 0), 45, c(1, 0), 315)

  # Use geodesic_direct to verify
  check <- geodesic_direct_fast(c(0, 0), 45, result$x)

  expect_equal(result$lat, check$lat2, tolerance = 1e-8)
  expect_equal(result$lon, check$lon2, tolerance = 1e-8)
})

Try the geographiclib package in your browser

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

geographiclib documentation built on March 4, 2026, 9:07 a.m.