Add location-aware automatic default bounding box

The OpenSky "Default bounding box" now follows where flying happens.
A new "Automatic" picker mode (the default) resolves the live-map area
from a location cascade — drone telemetry → phone GPS → browser
geolocation → the user's Region country → Europe — instead of a fixed
box. Manual presets and Custom coordinates still work.

- Web App: new shared countries.js dataset (all countries + bbox,
  offline point→country); the bbox picker gains all European countries
  and an Automatic option (client pref prefs.autoBbox); the Region
  setting expands from 6 locale entries to all countries; the live map
  resolves the cascade each poll and sends it as ?bbox=.
- API Server: the states endpoint accepts and validates a ?bbox=
  override (validBBox); the Web App BFF forwards the query; the hub
  relays new phoneLatitude/phoneLongitude telemetry to the Web App.
- Fly App: reports the phone's own GPS (geolocator) alongside
  telemetry, used as the "your location" fallback.
- API panel: the OpenSky bbox picker lists all European countries.

Builds verified across web, panel, both Go modules and the Fly App
APK. Region list, Automatic default and the cascade ?bbox= override
verified in the browser.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
tajniak81
2026-07-14 00:07:12 +02:00
co-authored by Claude Opus 4.8
parent 150758b0bf
commit 94f6876024
20 changed files with 637 additions and 71 deletions
+34 -1
View File
@@ -7,9 +7,34 @@ import (
"io"
"net/http"
"net/url"
"strconv"
"strings"
)
// validBBox reports whether s is a well-formed "lamin,lomin,lamax,lomax" bounding
// box: four numbers, latitudes in [-90,90], longitudes in [-180,180], and min < max
// on each axis. Used to vet the Live map's client-supplied ?bbox= override before
// it reaches OpenSky.
func validBBox(s string) bool {
parts := strings.Split(s, ",")
if len(parts) != 4 {
return false
}
n := make([]float64, 4)
for i, p := range parts {
v, err := strconv.ParseFloat(strings.TrimSpace(p), 64)
if err != nil {
return false
}
n[i] = v
}
laMin, loMin, laMax, loMax := n[0], n[1], n[2], n[3]
if laMin < -90 || laMax > 90 || loMin < -180 || loMax > 180 {
return false
}
return laMin < laMax && loMin < loMax
}
// Integrations exposes the OpenSky plugin's settings to end users under a
// three-layer cascade (superadmin/global → organization → user). Each of the
// four settings resolves independently, top wins, and a blank field falls
@@ -586,11 +611,19 @@ func (s *Server) handleOpenSkyStates(w http.ResponseWriter, r *http.Request) {
return
}
// The Live map may request a specific area (auto cascade: drone → device →
// region). Honour a well-formed ?bbox= override; otherwise use the resolved
// config bbox. Malformed input is ignored rather than erroring.
bbox := res.eff.Bbox
if q := strings.TrimSpace(r.URL.Query().Get("bbox")); q != "" && validBBox(q) {
bbox = q
}
cfg := map[string]string{
"clientId": res.eff.ClientID,
"clientSecret": res.eff.ClientSecret,
"plan": res.eff.Plan,
"bbox": res.eff.Bbox,
"bbox": bbox,
"allowAnonymous": boolStr(res.allowAnon),
}
raw, err := s.plugins.InvokeWith(r.Context(), openSkyPlugin, cfg, "states.bbox", nil)