All files / core/systems/graticule graticule.ts

100% Statements 17/17
66.66% Branches 4/6
100% Functions 2/2
100% Lines 14/14

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37                    22x 22x   203x 22x 22x 22x 22x 22x 309x 309x           22x 81x 81x           22x    
/** Latitude and longitude graticule with adaptive spacing. */
import {
  type Box,
  type GridGeometry,
  type GridLabel,
  type GridPath,
  densify,
} from '../../geo/geometry'
 
export function graticule(view: Box): GridGeometry {
  const choices = [30, 10, 5, 2, 1, 0.5, 0.25, 0.1, 0.05, 0.01]
  const span = Math.max(view[2] - view[0], view[3] - view[1])
  // Choices run largest first, so take the last (smallest) spacing that keeps about two dozen lines in view.
  const spacing = choices.findLast(c => span / c <= 24) ?? 30
  const paths: GridPath[] = []
  const labels: GridLabel[] = []
  const [w, s, e, n] = view
  const decimals = spacing >= 1 ? 0 : spacing >= 0.1 ? 1 : 2
  for (let lon = Math.ceil(w / spacing) * spacing; lon <= e; lon += spacing) {
    paths.push({ path: densify([lon, s], [lon, n], (n - s) / 32), width: 0.75 })
    labels.push({
      position: [lon, s + (n - s) * 0.03],
      text: `${lon.toFixed(decimals)}°`,
      size: 10,
    })
  }
  for (let lat = Math.ceil(s / spacing) * spacing; lat <= n; lat += spacing) {
    paths.push({ path: densify([w, lat], [e, lat], (e - w) / 32), width: 0.75 })
    labels.push({
      position: [w + (e - w) * 0.03, lat],
      text: `${lat.toFixed(decimals)}°`,
      size: 10,
    })
  }
  return { paths, labels }
}