tidy up lat lng timezone api for calls classify and push certainty
Dependencies
- [2]
YE6BZJUKtidy up lat lng timezone api for calls clip cmd - [3]
2P27XV3Dfixed cyclo over 30 - [4]
KZKLAINJrun out of space on nest, cleaned out - [5]
54GPBNIXadded +_ for tui to select segments with no calltype - [6]
XO5DF6WRI tiedied up readme - [7]
KLUEQ6X5cyclo 21+ - [8]
NS4TDPLNcyclomatic complexity - [9]
GPQSOVBPcyclo complexity over 25
Change contents
- edit in tools/calls_clip.go at line 10
"strconv" - replacement in tools/calls_clip.go at line 75
lat, lng, timezone, err = parseLocation(input.Location)lat, lng, timezone, err = utils.ParseLocation(input.Location) - edit in tools/calls_clip.go at line 124
}// 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
"strconv" - replacement in cmd/calls_push_certainty.go at line 21
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")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
fmt.Fprintf(os.Stderr, " skraak calls push-certainty --folder ./data --species Kiwi --night --lat -45.5 --lng 167.4\n")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
timezone string - replacement in cmd/calls_push_certainty.go at line 40
lat float64lng float64latSet boollngSet boollocation 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 = truecase "--lng":f.lng = requireFloat(arg, args, &i)f.lngSet = truecase "--timezone":f.timezone = requireValue(arg, args, &i)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
// requireFloat parses the next argument as a float64, or exits with an errorfunc 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")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
var lat, lng float64var timezone stringif f.location != "" {var err errorlat, 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
Lat: f.lat,Lng: f.lng,Timezone: f.timezone,Lat: lat,Lng: lng,Timezone: timezone, - replacement in cmd/calls_classify.go at line 40
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")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
timezone string - replacement in cmd/calls_classify.go at line 77
lat float64lng float64latSet boollngSet boollocation 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 += 2case "--timezone":a.timezone = a.requireValue(args, i, "--timezone")i += 2case "--lat":a.lat = a.requireFloat(args, i, "--lat")a.latSet = true - replacement in cmd/calls_classify.go at line 111
case "--lng":a.lng = a.requireFloat(args, i, "--lng")a.lngSet = truecase "--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
if (a.night || a.day) && (!a.latSet || !a.lngSet) {fmt.Fprintf(os.Stderr, "Error: --night/--day requires both --lat and --lng\n\n")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
// Parse location into lat/lng/timezonevar lat, lng float64var timezone stringif a.location != "" {var err errorlat, 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
Lat: a.lat,Lng: a.lng,Timezone: a.timezone,Lat: lat,Lng: lng,Timezone: timezone, - replacement in README.md at line 250
./skraak calls push-certainty --folder ./data --species Kiwi --night --lat -45.5 --lng 167.4./skraak calls push-certainty --folder ./data --species Kiwi --night --location "-45.5,167.4" - replacement in CHANGELOG.md at line 5
## [2026-05-05] `calls clip`: replace --lat/--lng/--timezone with --location; remove --wav-only## [2026-05-05] Replace --lat/--lng/--timezone with --location across CLI; remove --wav-only - replacement in CHANGELOG.md at line 7
**Breaking CLI change.** Two flag changes to `skraak calls clip`:**Breaking CLI change.** The `--lat`, `--lng`, `--timezone` flags are replacedby a single `--location "lat,lng[,timezone]"` flag in three commands: - replacement in CHANGELOG.md at line 10
1. **`--lat`, `--lng`, `--timezone` → `--location`** — The three GPS/timezoneflags 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).- `skraak calls clip`- `skraak calls classify`- `skraak calls push-certainty` - replacement in CHANGELOG.md at line 14
Before:```bashskraak calls clip --folder ./data --output ./clips --prefix kiwi \--species Kiwi --night --lat -40.85 --lng 172.81 --timezone Pacific/Auckland```After:```bashskraak calls clip --folder ./data --output ./clips --prefix kiwi \--species Kiwi --night --location "-40.85,172.81,Pacific/Auckland"```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
2. **`--wav-only` removed** — This flag skipped spectrogram PNG generation.Removed as unnecessary; the tool's primary purpose is generating trainingdata (PNG+WAV pairs).Before:```bashskraak calls clip --folder ./data --output ./clips --prefix kiwi \--species Kiwi --night --lat -40.85 --lng 172.81 --timezone Pacific/Auckland```After:```bashskraak 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 flagskipped spectrogram PNG generation; removed as unnecessary since the tool'sprimary purpose is generating training data (PNG+WAV pairs). - edit in CHANGELOG.md at line 32
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
- `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- `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`