tidy up lat lng timezone api for calls classify and push certainty

quietlight
May 4, 2026, 8:17 PM
DD3LCTLZFDIPVXXSG7RDINZCQ7NGKVG3X52OFVGVZIISD5VPF35QC

Dependencies

  • [2] YE6BZJUK tidy up lat lng timezone api for calls clip cmd
  • [3] 2P27XV3D fixed cyclo over 30
  • [4] KZKLAINJ run out of space on nest, cleaned out
  • [5] 54GPBNIX added +_ for tui to select segments with no calltype
  • [6] XO5DF6WR I tiedied up readme
  • [7] KLUEQ6X5 cyclo 21+
  • [8] NS4TDPLN cyclomatic complexity
  • [9] GPQSOVBP cyclo complexity over 25

Change contents

  • edit in tools/calls_clip.go at line 10
    [3.574177][2.87:98]()
    "strconv"
  • replacement in tools/calls_clip.go at line 75
    [2.334][2.334:392]()
    lat, lng, timezone, err = parseLocation(input.Location)
    [2.334]
    [2.392]
    lat, lng, timezone, err = utils.ParseLocation(input.Location)
  • edit in tools/calls_clip.go at line 124
    [3.9215][2.703:1531]()
    }
    // parseLocation parses a "lat,lng[,timezone]" string into its components.
    // Timezone is optional and defaults to "" (UTC).
    func parseLocation(location string) (lat, lng float64, timezone string, err error) {
    parts := strings.Split(location, ",")
    if len(parts) < 2 || len(parts) > 3 {
    return 0, 0, "", fmt.Errorf("--location must be \"lat,lng\" or \"lat,lng,timezone\" (got %d parts)", len(parts))
    }
    lat, err = strconv.ParseFloat(strings.TrimSpace(parts[0]), 64)
    if err != nil {
    return 0, 0, "", fmt.Errorf("--location: invalid latitude: %s", parts[0])
    }
    lng, err = strconv.ParseFloat(strings.TrimSpace(parts[1]), 64)
    if err != nil {
    return 0, 0, "", fmt.Errorf("--location: invalid longitude: %s", parts[1])
    }
    if len(parts) == 3 {
    timezone = strings.TrimSpace(parts[2])
    }
    return lat, lng, timezone, nil
  • edit in cmd/calls_push_certainty.go at line 7
    [3.1104034][3.1104034:1104045]()
    "strconv"
  • replacement in cmd/calls_push_certainty.go at line 21
    [3.1104856][3.1104856:1105407]()
    fmt.Fprintf(os.Stderr, " --night Only act on solar-night recordings (requires --lat and --lng)\n")
    fmt.Fprintf(os.Stderr, " --day Only act on solar-day recordings (requires --lat and --lng)\n")
    fmt.Fprintf(os.Stderr, " --lat <float> Latitude in decimal degrees (required with --night or --day)\n")
    fmt.Fprintf(os.Stderr, " --lng <float> Longitude in decimal degrees (required with --night or --day)\n")
    fmt.Fprintf(os.Stderr, " --timezone <zone> IANA timezone ID (e.g. Pacific/Auckland)\n")
    [3.1104856]
    [3.1105407]
    fmt.Fprintf(os.Stderr, " --night Only act on solar-night recordings (requires --location)\n")
    fmt.Fprintf(os.Stderr, " --day Only act on solar-day recordings (requires --location)\n")
    fmt.Fprintf(os.Stderr, " --location <lat,lng[,tz]> GPS coordinates and optional IANA timezone\n")
    fmt.Fprintf(os.Stderr, " e.g. --location \"-36.85,174.76\" or --location \"-36.85,174.76,Pacific/Auckland\"\n")
    fmt.Fprintf(os.Stderr, " Required with --night or --day. Timezone defaults to UTC.\n")
    fmt.Fprintf(os.Stderr, " Not needed for AudioMoth data (UTC from WAV comment).\n")
  • replacement in cmd/calls_push_certainty.go at line 29
    [3.1105538][3.1105538:1105660]()
    fmt.Fprintf(os.Stderr, " skraak calls push-certainty --folder ./data --species Kiwi --night --lat -45.5 --lng 167.4\n")
    [3.1105538]
    [3.1105660]
    fmt.Fprintf(os.Stderr, " skraak calls push-certainty --folder ./data --species Kiwi --night --location \"-45.5,167.4\"\n")
  • edit in cmd/calls_push_certainty.go at line 38
    [3.24554][3.24554:24571]()
    timezone string
  • replacement in cmd/calls_push_certainty.go at line 40
    [3.24601][3.24601:24667]()
    lat float64
    lng float64
    latSet bool
    lngSet bool
    [3.24601]
    [3.24667]
    location string
  • replacement in cmd/calls_push_certainty.go at line 64
    [3.1106971][3.1106971:1106987](),[3.1106987][3.25026:25084](),[3.25084][3.1107281:1107297](),[3.1107281][3.1107281:1107297](),[3.1107297][3.25085:25143](),[3.25143][3.1107591:1107612](),[3.1107591][3.1107591:1107612](),[3.1107612][3.25144:25188]()
    case "--lat":
    f.lat = requireFloat(arg, args, &i)
    f.latSet = true
    case "--lng":
    f.lng = requireFloat(arg, args, &i)
    f.lngSet = true
    case "--timezone":
    f.timezone = requireValue(arg, args, &i)
    [3.1106970]
    [3.1107759]
    case "--location":
    if f.location != "" {
    fmt.Fprintf(os.Stderr, "Error: --location can only be specified once\n")
    os.Exit(1)
    }
    f.location = requireValue(arg, args, &i)
  • edit in cmd/calls_push_certainty.go at line 93
    [3.25486][3.25486:25808]()
    // requireFloat parses the next argument as a float64, or exits with an error
    func requireFloat(flag string, args []string, i *int) float64 {
    s := requireValue(flag, args, i)
    v, err := strconv.ParseFloat(s, 64)
    if err != nil {
    fmt.Fprintf(os.Stderr, "Error: %s must be a number\n", flag)
    os.Exit(1)
    }
    return v
    }
  • replacement in cmd/calls_push_certainty.go at line 105
    [3.1108251][3.26000:26053](),[3.26053][3.1108296:1108379](),[3.1108296][3.1108296:1108379]()
    if (f.night || f.day) && (!f.latSet || !f.lngSet) {
    fmt.Fprintf(os.Stderr, "Error: --night/--day requires both --lat and --lng\n\n")
    [3.1108251]
    [3.1108379]
    if (f.night || f.day) && f.location == "" {
    fmt.Fprintf(os.Stderr, "Error: --night/--day requires --location\n\n")
  • edit in cmd/calls_push_certainty.go at line 138
    [3.1108868]
    [3.1108868]
    var lat, lng float64
    var timezone string
    if f.location != "" {
    var err error
    lat, lng, timezone, err = utils.ParseLocation(f.location)
    if err != nil {
    fmt.Fprintf(os.Stderr, "Error: %v\n", err)
    os.Exit(1)
    }
    }
  • replacement in cmd/calls_push_certainty.go at line 157
    [3.26696][3.26696:26758]()
    Lat: f.lat,
    Lng: f.lng,
    Timezone: f.timezone,
    [3.26696]
    [3.1109103]
    Lat: lat,
    Lng: lng,
    Timezone: timezone,
  • replacement in cmd/calls_classify.go at line 40
    [3.1143399][3.1143399:1144090]()
    fmt.Fprintf(os.Stderr, " --night Only review solar-night recordings (requires --lat and --lng)\n")
    fmt.Fprintf(os.Stderr, " --day Only review solar-day recordings (requires --lat and --lng)\n")
    fmt.Fprintf(os.Stderr, " --lat <float> Latitude in decimal degrees (required with --night or --day)\n")
    fmt.Fprintf(os.Stderr, " --lng <float> Longitude in decimal degrees (required with --night or --day)\n")
    fmt.Fprintf(os.Stderr, " --timezone <zone> IANA timezone ID (e.g. Pacific/Auckland). Required for non-AudioMoth\n")
    fmt.Fprintf(os.Stderr, " recorders whose filenames embed local time (e.g. DOC AR4).\n")
    [3.1143399]
    [3.1144090]
    fmt.Fprintf(os.Stderr, " --night Only review solar-night recordings (requires --location)\n")
    fmt.Fprintf(os.Stderr, " --day Only review solar-day recordings (requires --location)\n")
    fmt.Fprintf(os.Stderr, " --location <lat,lng[,tz]> GPS coordinates and optional IANA timezone\n")
    fmt.Fprintf(os.Stderr, " e.g. --location \"-36.85,174.76\" or --location \"-36.85,174.76,Pacific/Auckland\"\n")
    fmt.Fprintf(os.Stderr, " Required with --night or --day. Timezone defaults to UTC.\n")
    fmt.Fprintf(os.Stderr, " Not needed for AudioMoth data (UTC from WAV comment).\n")
  • edit in cmd/calls_classify.go at line 73
    [3.8074][3.8074:8092]()
    timezone string
  • replacement in cmd/calls_classify.go at line 77
    [3.8154][3.8154:8224]()
    lat float64
    lng float64
    latSet bool
    lngSet bool
    [3.8154]
    [3.8224]
    location string
  • edit in cmd/calls_classify.go at line 110
    [3.8880][3.1147592:1147602](),[3.1147592][3.1147592:1147602](),[3.1147603][3.1147603:1147624](),[3.1147624][3.8881:9024]()
    i += 2
    case "--timezone":
    a.timezone = a.requireValue(args, i, "--timezone")
    i += 2
    case "--lat":
    a.lat = a.requireFloat(args, i, "--lat")
    a.latSet = true
  • replacement in cmd/calls_classify.go at line 111
    [3.9034][3.9034:9113]()
    case "--lng":
    a.lng = a.requireFloat(args, i, "--lng")
    a.lngSet = true
    [3.9034]
    [3.1147760]
    case "--location":
    a.location = a.requireUniqueValue(args, i, "--location", a.location)
  • edit in cmd/calls_classify.go at line 166
    [3.1149225][3.10202:10407](),[3.10407][3.1149341:1149358](),[3.1149341][3.1149341:1149358](),[3.1149358][3.10408:10471](),[3.10471][3.1149532:1149548](),[3.1149532][3.1149532:1149548](),[3.1149548][3.10472:10484](),[3.10484][3.1149548:1149549](),[3.1149548][3.1149548:1149549]()
    // requireFloat parses the next arg as float64.
    func (a classifyArgs) requireFloat(args []string, i int, flag string) float64 {
    val := a.requireValue(args, i, flag)
    v, err := strconv.ParseFloat(val, 64)
    if err != nil {
    fmt.Fprintf(os.Stderr, "Error: %s must be a number\n", flag)
    os.Exit(1)
    }
    return v
    }
  • replacement in cmd/calls_classify.go at line 182
    [3.11010][3.11010:11146]()
    if (a.night || a.day) && (!a.latSet || !a.lngSet) {
    fmt.Fprintf(os.Stderr, "Error: --night/--day requires both --lat and --lng\n\n")
    [3.11010]
    [3.11146]
    if (a.night || a.day) && a.location == "" {
    fmt.Fprintf(os.Stderr, "Error: --night/--day requires --location\n\n")
  • edit in cmd/calls_classify.go at line 270
    [3.1151628]
    [3.1151628]
    // Parse location into lat/lng/timezone
    var lat, lng float64
    var timezone string
    if a.location != "" {
    var err error
    lat, lng, timezone, err = utils.ParseLocation(a.location)
    if err != nil {
    fmt.Fprintf(os.Stderr, "Error: %v\n", err)
    os.Exit(1)
    }
    }
  • replacement in cmd/calls_classify.go at line 301
    [3.12572][3.12572:12661]()
    Lat: a.lat,
    Lng: a.lng,
    Timezone: a.timezone,
    [3.12572]
    [3.1152351]
    Lat: lat,
    Lng: lng,
    Timezone: timezone,
  • replacement in README.md at line 250
    [3.1188592][3.1188592:1188685]()
    ./skraak calls push-certainty --folder ./data --species Kiwi --night --lat -45.5 --lng 167.4
    [3.1188592]
    [3.1188685]
    ./skraak calls push-certainty --folder ./data --species Kiwi --night --location "-45.5,167.4"
  • replacement in CHANGELOG.md at line 5
    [2.3274][2.3274:3370]()
    ## [2026-05-05] `calls clip`: replace --lat/--lng/--timezone with --location; remove --wav-only
    [2.3274]
    [2.3370]
    ## [2026-05-05] Replace --lat/--lng/--timezone with --location across CLI; remove --wav-only
  • replacement in CHANGELOG.md at line 7
    [2.3371][2.3371:3437]()
    **Breaking CLI change.** Two flag changes to `skraak calls clip`:
    [2.3371]
    [2.3437]
    **Breaking CLI change.** The `--lat`, `--lng`, `--timezone` flags are replaced
    by a single `--location "lat,lng[,timezone]"` flag in three commands:
  • replacement in CHANGELOG.md at line 10
    [2.3438][2.3438:3742]()
    1. **`--lat`, `--lng`, `--timezone` → `--location`** — The three GPS/timezone
    flags are replaced by a single `--location "lat,lng[,timezone]"` flag.
    Timezone is optional (defaults to UTC; not needed for AudioMoth).
    This makes invalid states unrepresentable (you can't pass lat without lng).
    [2.3438]
    [2.3742]
    - `skraak calls clip`
    - `skraak calls classify`
    - `skraak calls push-certainty`
  • replacement in CHANGELOG.md at line 14
    [2.3743][2.3743:4094]()
    Before:
    ```bash
    skraak calls clip --folder ./data --output ./clips --prefix kiwi \
    --species Kiwi --night --lat -40.85 --lng 172.81 --timezone Pacific/Auckland
    ```
    After:
    ```bash
    skraak calls clip --folder ./data --output ./clips --prefix kiwi \
    --species Kiwi --night --location "-40.85,172.81,Pacific/Auckland"
    ```
    [2.3743]
    [3.27627]
    Timezone is optional (defaults to UTC; not needed for AudioMoth).
    This makes invalid states unrepresentable (you can't pass lat without lng).
  • replacement in CHANGELOG.md at line 17
    [3.27628][2.4095:4275]()
    2. **`--wav-only` removed** — This flag skipped spectrogram PNG generation.
    Removed as unnecessary; the tool's primary purpose is generating training
    data (PNG+WAV pairs).
    [3.27628]
    [2.4275]
    Before:
    ```bash
    skraak calls clip --folder ./data --output ./clips --prefix kiwi \
    --species Kiwi --night --lat -40.85 --lng 172.81 --timezone Pacific/Auckland
    ```
    After:
    ```bash
    skraak calls clip --folder ./data --output ./clips --prefix kiwi \
    --species Kiwi --night --location "-40.85,172.81,Pacific/Auckland"
    ```
    Additionally, `--wav-only` was removed from `skraak calls clip`. This flag
    skipped spectrogram PNG generation; removed as unnecessary since the tool's
    primary purpose is generating training data (PNG+WAV pairs).
  • edit in CHANGELOG.md at line 32
    [2.4276]
    [2.4276]
    A shared `utils.ParseLocation` helper was added to avoid duplicating the
    `lat,lng[,tz]` parse logic across commands.
  • replacement in CHANGELOG.md at line 36
    [2.4295][2.4295:4515]()
    - `cmd/calls_clip.go` — flag parsing, validation, usage text
    - `tools/calls_clip.go` — `CallsClipInput` struct, `parseLocation` helper,
    removed `wavOnly` from function signatures, unwrapped spectrogram conditional
    [2.4295]
    [2.4515]
    - `utils/location.go` — new `ParseLocation` helper
    - `cmd/calls_clip.go` — `--location` flag, removed `--wav-only`
    - `cmd/calls_classify.go` — `--location` flag
    - `cmd/calls_push_certainty.go` — `--location` flag
    - `tools/calls_clip.go` — `CallsClipInput` struct, removed `wavOnly`/`parseLocation`