All files / core/systems/utm system.ts

100% Statements 18/18
78.57% Branches 11/14
100% Functions 6/6
100% Lines 18/18

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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83                14x 14x   14x         1058x 732x 732x           326x 326x                   14x   14x 11030x   677x 677x                             14x   14x 10353x   325x                       14x                  
import { Option, pipe } from 'effect'
 
import { makeParser } from '../parser'
import { pairReading } from '../reading'
import type { Formatter, Grid, Parser } from '../types'
import { gzdGrid } from './gzd'
import { LAT_BANDS, fromUps, fromUtm, toUps, toUtm } from './utm'
 
const UTM_SOUTH_LIMIT = -80
const UTM_NORTH_LIMIT = 84
 
export const formatter: Formatter = {
  id: 'UTM',
  label: 'UTM / UPS',
  maybeGridId: Option.some('Gzd'),
  format: point => {
    if (point.lat >= UTM_SOUTH_LIMIT && point.lat <= UTM_NORTH_LIMIT) {
      const utm = toUtm(point)
      return pairReading(
        `${utm.zone}${utm.band} ${utm.easting.toFixed(0)}E`,
        `${utm.northing.toFixed(0)}N`,
        ' ',
      )
    }
    const ups = toUps(point)
    return pairReading(
      `UPS ${ups.hemisphere} ${ups.easting.toFixed(0)}E`,
      `${ups.northing.toFixed(0)}N`,
      ' ',
    )
  },
}
 
// "31U 448251 5411932", "31U 448251mE 5411932mN". The letter is the MGRS latitude band.
const UTM_PATTERN =
  /^(\d{1,2})\s*([C-HJ-NP-X])\s+(\d+(?:\.\d+)?)\s*M?E?\s+(\d+(?:\.\d+)?)\s*M?N?$/
 
export const utmParser: Parser = makeParser('UTM', text =>
  pipe(
    Option.fromNullishOr(UTM_PATTERN.exec(text.toUpperCase())),
    Option.filter(([, , band]) => LAT_BANDS.includes(band ?? '')),
    Option.map(([, zone, band, easting, northing]) => ({
      kind: 'UTM',
      point: fromUtm(
        Number(zone),
        (band ?? '') >= 'N' ? 'N' : 'S',
        Number(easting),
        Number(northing),
      ),
      maybeBounds: Option.none(),
    })),
  ),
)
 
// "UPS N 2000000 2000000", or an MGRS polar letter: "Z 2000000 2000000".
const UPS_PATTERN =
  /^(?:UPS\s*)?([NSABYZ])\s+(\d+(?:\.\d+)?)\s*M?E?\s+(\d+(?:\.\d+)?)\s*M?N?$/
 
export const upsParser: Parser = makeParser('UPS', text =>
  Option.map(
    Option.fromNullishOr(UPS_PATTERN.exec(text.toUpperCase())),
    ([, letter, easting, northing]) => ({
      kind: 'UPS',
      point: fromUps(
        'NYZ'.includes(letter ?? '') ? 'N' : 'S',
        Number(easting),
        Number(northing),
      ),
      maybeBounds: Option.none(),
    }),
  ),
)
 
export const grid: Grid = {
  id: 'Gzd',
  label: 'UTM zones',
  description: 'UTM zones and MGRS grid zone designations',
  colors: { onDark: [245, 245, 245, 140], onLight: [34, 34, 38, 150] },
  isEnabledByDefault: true,
  build: gzdGrid,
  maybeCueOutline: Option.none(),
}