bb01e9a06c
## Summary - **ADS-B & AIS:** OpenSky and AISStream OSINT feeds upsert into the CoT store; tactical tracks still arrive via adsbcot/aiscot on `:8089`. Map clients subscribe via `GET /api/cot/stream` (SSE) with viewport bbox filtering and Air / Surface / Team layer toggles. - **ALPR (Flock/OSM):** Toggleable license-plate reader layer sourced from OpenStreetMap, with SQLite cache, Overpass fallback, tiled viewport fetching, and clustered markers with direction cones. - **Map performance:** Ring-based tile selection (fixes zoom-out crash), immutable tile cache, incremental marker sync, split cluster load/query, and padded SSE bbox to reduce reconnect churn. ## Docs - `docs/tracking.md` — ADS-B/AIS accuracy tiers, freshness, self-hosted receivers, optional OSINT API keys - `docs/map-and-cameras.md` — ALPR layer and map behavior updates --------- Co-authored-by: Madison Grubb <madison@elastiflow.com> Reviewed-on: #36
58 lines
1.8 KiB
Vue
58 lines
1.8 KiB
Vue
<template>
|
|
<div class="flex h-full w-full flex-col md:flex-row">
|
|
<div class="relative min-h-0 flex-1">
|
|
<ClientOnly>
|
|
<KestrelMap
|
|
:devices="devices ?? []"
|
|
:pois="pois ?? []"
|
|
:live-sessions="liveSessions ?? []"
|
|
:cot-entities="cotEntities ?? []"
|
|
:cot-layers="cotLayers"
|
|
:alpr-markers="showAlpr ? (alprMarkers ?? []) : []"
|
|
:show-alpr="showAlpr"
|
|
:can-edit-pois="canEditPois"
|
|
@select="selectedCamera = $event"
|
|
@select-live="onSelectLive($event)"
|
|
@refresh-pois="refreshPois"
|
|
@bounds-change="onMapBoundsChange"
|
|
@toggle-alpr="toggleAlpr"
|
|
@toggle-cot-layer="toggleLayer"
|
|
/>
|
|
<template #fallback>
|
|
<div
|
|
class="h-full min-h-[300px] bg-kestrel-bg"
|
|
aria-hidden="true"
|
|
/>
|
|
</template>
|
|
</ClientOnly>
|
|
</div>
|
|
<CameraViewer
|
|
v-if="selectedCamera"
|
|
:camera="selectedCamera"
|
|
@close="selectedCamera = null"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
const { devices, liveSessions } = useCameras()
|
|
const { data: pois, refresh: refreshPois } = usePois()
|
|
const { canEditPois } = useUser()
|
|
const { showAlpr, toggleAlpr, alprMarkers, onBoundsChange: onAlprBoundsChange } = useAlprCameras()
|
|
const { layers: cotLayers, layerQuery, toggleLayer } = useCotLayers()
|
|
const mapBounds = ref(null)
|
|
const { cotEntities } = useCotStream(mapBounds, layerQuery)
|
|
const selectedCamera = ref(null)
|
|
|
|
function onMapBoundsChange(bounds) {
|
|
mapBounds.value = bounds
|
|
onAlprBoundsChange(bounds)
|
|
}
|
|
|
|
function onSelectLive(session) {
|
|
selectedCamera.value = (liveSessions.value ?? []).find(s => s.id === session?.id) ?? session
|
|
}
|
|
|
|
useAutoCloseLiveSession(selectedCamera, liveSessions)
|
|
</script>
|