chore: release v0.27.12

This commit is contained in:
2026-09-05 19:07:21 +02:00
parent f93e1c5898
commit be889681a9
40 changed files with 4971 additions and 453 deletions
+63
View File
@@ -11,6 +11,7 @@ package geo
import (
"math"
"sort"
"strings"
)
@@ -118,3 +119,65 @@ func NeighbourGrids(lat, lon float64, ring int) []string {
}
return out
}
// GridsWithin returns every Maidenhead square whose centre lies within km of
// (lat, lon), nearest first, at most max of them.
//
// NeighbourGrids answers "the ring around here", which is the right shape for a
// few hundred kilometres and the wrong one past that: a ring is a square, so
// asking for 2000 km through it means 1369 squares, most of them further away
// than the ones it left out. This measures instead, and the count then follows
// the AREA asked for rather than the corner of a box.
//
// Nearest first because the caller has to be able to trim: these become one
// broker subscription each, and when there are more than can be afforded, the
// squares to keep are the close ones.
func GridsWithin(lat, lon, km float64, max int) []string {
if km <= 0 || max <= 0 {
return nil
}
// One square is 1° of latitude and 2° of longitude. Sweep a box big enough
// to hold the circle — a degree of latitude is ~111 km everywhere, and a
// degree of longitude never MORE than that, so this cannot cut the circle.
steps := int(km/111.0) + 1
type cand struct {
grid string
d float64
}
seen := map[string]bool{}
out := []cand{}
for dLat := -steps; dLat <= steps; dLat++ {
for dLon := -2 * steps; dLon <= 2*steps; dLon++ {
la := lat + float64(dLat)
lo := lon + float64(dLon)*2
if la > 90 || la < -90 {
continue
}
g := LatLonToGrid(la, lo)
if seen[g] {
continue
}
// Measured to the square's own centre, not to the sample point, so
// two samples landing in one square agree about how far it is.
cLat, cLon, ok := GridToLatLon(g)
if !ok {
continue
}
d := HaversineKm(lat, lon, cLat, cLon)
if d > km {
continue
}
seen[g] = true
out = append(out, cand{g, d})
}
}
sort.Slice(out, func(i, j int) bool { return out[i].d < out[j].d })
if len(out) > max {
out = out[:max]
}
grids := make([]string, len(out))
for i, c := range out {
grids[i] = c.grid
}
return grids
}