Add region picker for OpenSky default bounding box

Replace the free-text bounding-box field in the Web App OpenSky
settings with a grouped preset picker (World / Continents / Countries)
plus a Custom option that keeps the manual lamin,lomin,lamax,lomax
entry. Saved values that match a preset render as the friendly region
name, including in the locked/inherited display.

Change the API Server default bounding box to Europe to match the new
"Europe" preset.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
tajniak81
2026-07-13 23:20:08 +02:00
co-authored by Claude Opus 4.8
parent 6a007fd25e
commit c5e34e32be
6 changed files with 102 additions and 28 deletions
+75 -3
View File
@@ -206,6 +206,68 @@ const OS_SCOPE_OPTS = [
{ value: 'org', label: 'Organization', icon: 'users' },
]
// Predefined bounding boxes (lamin,lomin,lamax,lomax). The picker offers these
// plus a "Custom…" option that reveals the free-text field for manual entry.
// Europe is the default (kept in sync with the API Server's defaultBBox).
const OS_BBOX_GROUPS = [
{ label: 'World', options: [
{ value: '-90,-180,90,180', label: 'World' },
] },
{ label: 'Continents', options: [
{ value: '34,-25,72,45', label: 'Europe' },
{ value: '-35,-18,38,52', label: 'Africa' },
{ value: '5,25,82,180', label: 'Asia' },
{ value: '7,-168,72,-52', label: 'North America' },
{ value: '-56,-82,13,-34', label: 'South America' },
{ value: '-48,110,-10,180', label: 'Oceania' },
] },
{ label: 'Countries', options: [
{ value: '49,14.1,54.9,24.2', label: 'Poland' },
{ value: '50.5,3.2,53.7,7.3', label: 'Netherlands' },
{ value: '47.2,5.8,55.1,15.1', label: 'Germany' },
{ value: '41.3,-5.2,51.1,9.6', label: 'France' },
{ value: '49.9,-8.7,59,1.8', label: 'United Kingdom' },
{ value: '35.9,-9.6,43.8,3.4', label: 'Spain' },
{ value: '36.6,6.6,47.1,18.6', label: 'Italy' },
{ value: '24,-125,49.5,-66.5', label: 'United States' },
] },
]
const OS_BBOX_FLAT = OS_BBOX_GROUPS.flatMap((g) => g.options)
// Normalise a bbox string to "n1,n2,n3,n4" so preset matching ignores spacing
// and trailing-zero differences; returns '' when it isn't four valid numbers.
function osNormBbox(s) {
const parts = String(s || '').split(',').map((x) => x.trim())
if (parts.length !== 4) return ''
const nums = parts.map(Number)
if (nums.some((n) => Number.isNaN(n))) return ''
return nums.join(',')
}
// Friendly label for a bbox value when it matches a preset (else '').
function osBboxLabel(v) {
const norm = osNormBbox(v)
const m = norm && OS_BBOX_FLAT.find((o) => osNormBbox(o.value) === norm)
return m ? m.label : ''
}
// Manual-entry toggle: sticky once the user picks "Custom…", and implied when
// the current value doesn't match any preset.
const osBboxCustom = ref(false)
const osBboxPreset = computed({
get() {
if (osBboxCustom.value) return '__custom__'
const norm = osNormBbox(osForm.bbox)
const match = norm && OS_BBOX_FLAT.find((o) => osNormBbox(o.value) === norm)
return match ? match.value : '__custom__'
},
set(v) {
if (v === '__custom__') { osBboxCustom.value = true; return }
osBboxCustom.value = false
osForm.bbox = v
},
})
const osBboxManual = computed(() => osBboxPreset.value === '__custom__')
// Superadmin edits the global layer in the API panel — read-only in the Web App.
const osReadOnly = computed(() => os.isSuperadmin)
// The scope key currently in view (superadmin always sees the single read-only view).
@@ -233,6 +295,8 @@ function fillOsForm() {
osForm.clientSecret = osField('clientSecret').own || ''
osForm.plan = osField('plan').own || ''
osForm.bbox = osField('bbox').own || ''
// Show the preset dropdown; the getter falls back to Custom for non-preset values.
osBboxCustom.value = false
}
function applyOpenSkyView(body) {
@@ -1433,14 +1497,22 @@ onBeforeUnmount(() => {
</Row>
<!-- bounding box -->
<Row title="Default bounding box" desc="lamin,lomin,lamax,lomax — used for live queries and the health probe." keywords="bounding box bbox area">
<Row title="Default bounding box" desc="Pick a region, or choose Custom to enter lamin,lomin,lamax,lomax by hand — used for live queries and the health probe." keywords="bounding box bbox area region country continent world europe custom coordinates">
<template v-if="osLocked('bbox')">
<span class="inline-flex items-center gap-2 font-mono text-sm text-ink">
{{ osField('bbox').effective || '' }}
{{ osBboxLabel(osField('bbox').effective) || osField('bbox').effective || '' }}
<span v-if="osSourceLabel('bbox')" class="inline-flex items-center gap-1 rounded-full bg-surface-2 px-2 py-0.5 text-[10px] font-semibold text-ink-secondary"><Icon name="lock" :size="10" />{{ osSourceLabel('bbox') }}</span>
</span>
</template>
<input v-else v-model="osForm.bbox" class="field w-64 font-mono" placeholder="50.5,3.2,53.7,7.3" />
<div v-else class="flex flex-col items-end gap-2">
<select v-model="osBboxPreset" class="field w-64">
<optgroup v-for="g in OS_BBOX_GROUPS" :key="g.label" :label="g.label">
<option v-for="o in g.options" :key="o.value" :value="o.value">{{ o.label }}</option>
</optgroup>
<option value="__custom__">Custom…</option>
</select>
<input v-if="osBboxManual" v-model="osForm.bbox" class="field w-64 font-mono" placeholder="50.5,3.2,53.7,7.3" />
</div>
</Row>
<!-- client id -->