Offline-Karten Runde 4: Minimal-Speicher-Modell (Modell Rene)
Funkloch-Gedaechtnis = Quelle der Wahrheit, Kacheln = ableitbarer Cache: - Ephemeres Vorausladen: Prefetch-Kacheln werden bei Aufzeichnungsende geloescht, wenn die Runde kein Funkloch hatte (nur neue Keys) - Netz-Probe bei Aufzeichnung (~2min, 6s-Timeout): erkennt Funkloecher auch in bereits gespeicherten Gebieten (dort kein Remote-Miss als Signal) - clear() behaelt Zonen (filled=false) -> naechster Online-Start laedt Funkloch-Gebiete automatisch neu, auch nach 'Alles loeschen' - Start-Check mit Position: nur Zonen im 50-km-Umkreis, naechste zuerst, Coverage-Verify (faengt Eviction ab); Pfote Segment 5 = alle Zonen gefuellt - Coverage-Layer zweifarbig: Funkloch orange, manuell blau + Modal-Legende - Stub-Tests Runde 4 (Prune/Probe/clear/Naehe/Verify/Faerbung) bestanden Bump v1230
This commit is contained in:
parent
763108fa7c
commit
53bc27faa3
10 changed files with 221 additions and 74 deletions
|
|
@ -25,9 +25,11 @@ window.OfflineIndicator = (() => {
|
|||
function _offlineTilesMode() {
|
||||
try { return !!(window.BY && BY.offlineTiles()); } catch (e) { return false; }
|
||||
}
|
||||
// Ist eine Offline-Region (Vektorkacheln) in IndexedDB gespeichert? (ohne MapOffline zu laden)
|
||||
// WICHTIG: dasselbe Schema/Version wie map-offline.js anlegen — sonst legt ein versionsloses open()
|
||||
// die DB leer an und MapOffline kann seine Stores nicht mehr erstellen.
|
||||
// Offline-Ready (Pfote Segment 5) — ohne MapOffline/GL-Stack zu laden.
|
||||
// Semantik (Modell René 2026-06-08): Gibt es bekannte FUNKLOCH-Zonen, zählt deren
|
||||
// Füllstand (alle gefüllt = grün); ohne bekannte Zonen wie bisher: irgendein Gebiet da.
|
||||
// WICHTIG: dasselbe Schema/Version wie map-offline.js anlegen — sonst legt ein versionsloses
|
||||
// open() die DB leer an und MapOffline kann seine Stores nicht mehr erstellen.
|
||||
function _offlineRegionStored() {
|
||||
return new Promise(res => {
|
||||
try {
|
||||
|
|
@ -39,10 +41,18 @@ window.OfflineIndicator = (() => {
|
|||
};
|
||||
r.onsuccess = () => {
|
||||
const db = r.result;
|
||||
if (!db.objectStoreNames.contains('tiles')) { db.close(); return res(false); }
|
||||
const cnt = db.transaction('tiles', 'readonly').objectStore('tiles').count();
|
||||
cnt.onsuccess = () => { res(cnt.result > 0); db.close(); };
|
||||
cnt.onerror = () => { res(false); db.close(); };
|
||||
if (!db.objectStoreNames.contains('tiles') || !db.objectStoreNames.contains('meta')) {
|
||||
db.close(); return res(false);
|
||||
}
|
||||
const mz = db.transaction('meta', 'readonly').objectStore('meta').get('deadzones');
|
||||
mz.onsuccess = () => {
|
||||
const zones = mz.result || [];
|
||||
if (zones.length) { res(zones.every(z => z.filled)); db.close(); return; }
|
||||
const cnt = db.transaction('tiles', 'readonly').objectStore('tiles').count();
|
||||
cnt.onsuccess = () => { res(cnt.result > 0); db.close(); };
|
||||
cnt.onerror = () => { res(false); db.close(); };
|
||||
};
|
||||
mz.onerror = () => { res(false); db.close(); };
|
||||
};
|
||||
r.onerror = () => res(false);
|
||||
} catch (e) { res(false); }
|
||||
|
|
@ -224,9 +234,10 @@ window.OfflineIndicator = (() => {
|
|||
} catch (e) { console.warn('Offline-Region-Download fehlgeschlagen:', e); }
|
||||
}
|
||||
|
||||
// Gibt es offene (ungefüllte) Funkloch-Zonen? — direkt aus IndexedDB, OHNE den
|
||||
// GL-Stack zu laden (gleiches Schema/Version wie map-offline.js, s. Warnung oben).
|
||||
function _openDeadZonesStored() {
|
||||
// Gibt es überhaupt Funkloch-Zonen? — direkt aus IndexedDB, OHNE den GL-Stack zu
|
||||
// laden. Auch GEFÜLLTE zählen: der Start-Check verifiziert deren Kacheln (nach
|
||||
// „Alles löschen"/Eviction werden sie automatisch neu geladen, Modell René 2026-06-08).
|
||||
function _anyDeadZonesStored() {
|
||||
return new Promise(res => {
|
||||
try {
|
||||
const r = indexedDB.open('by-offline-tiles', 1);
|
||||
|
|
@ -239,7 +250,7 @@ window.OfflineIndicator = (() => {
|
|||
const db = r.result;
|
||||
if (!db.objectStoreNames.contains('meta')) { db.close(); return res(false); }
|
||||
const rq = db.transaction('meta', 'readonly').objectStore('meta').get('deadzones');
|
||||
rq.onsuccess = () => { res((rq.result || []).some(z => !z.filled)); db.close(); };
|
||||
rq.onsuccess = () => { res((rq.result || []).length > 0); db.close(); };
|
||||
rq.onerror = () => { res(false); db.close(); };
|
||||
};
|
||||
r.onerror = () => res(false);
|
||||
|
|
@ -247,8 +258,29 @@ window.OfflineIndicator = (() => {
|
|||
});
|
||||
}
|
||||
|
||||
// Funkloch-Zonen automatisch füllen, sobald Netz da ist — das Gerät lernt selbst,
|
||||
// wo Offline-Karten nötig sind (dort wo Netz ist, braucht es keine).
|
||||
// Letzte bekannte Position: GPS nur wenn Permission schon erteilt (kein Popup),
|
||||
// sonst localStorage-Stand (gesetzt von wetter.js u.a.).
|
||||
async function _lastKnownPos() {
|
||||
try {
|
||||
if (navigator.permissions && navigator.geolocation) {
|
||||
const perm = await navigator.permissions.query({ name: 'geolocation' });
|
||||
if (perm.state === 'granted') {
|
||||
const pos = await new Promise(res =>
|
||||
navigator.geolocation.getCurrentPosition(p => res(p), () => res(null), { timeout: 5000 }));
|
||||
if (pos) return { lat: pos.coords.latitude, lon: pos.coords.longitude };
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
try {
|
||||
const raw = localStorage.getItem(LS_LAST_POS);
|
||||
if (raw) { const p = JSON.parse(raw); if (p?.lat != null) return { lat: p.lat, lon: p.lon }; }
|
||||
} catch {}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Funkloch-Zonen automatisch füllen/verifizieren, sobald Netz da ist — das Gerät lernt
|
||||
// selbst, wo Offline-Karten nötig sind. Mit Position werden nur Zonen im Umkreis
|
||||
// (50 km) geladen → Speicher bleibt minimal, ferne Zonen kommen, wenn man dort ist.
|
||||
let _autoFillTimer = null;
|
||||
function _scheduleAutoFill(delayMs) {
|
||||
if (!_offlineTilesMode()) return;
|
||||
|
|
@ -256,9 +288,10 @@ window.OfflineIndicator = (() => {
|
|||
_autoFillTimer = setTimeout(async () => {
|
||||
if (!navigator.onLine) return;
|
||||
try {
|
||||
if (!(await _openDeadZonesStored())) return; // nichts zu tun → GL-Stack nicht laden
|
||||
if (!(await _anyDeadZonesStored())) return; // nichts zu tun → GL-Stack nicht laden
|
||||
const pos = await _lastKnownPos();
|
||||
await UI.loadMapLibreUI();
|
||||
const n = await window.MapOffline?.autoFillDeadZones?.();
|
||||
const n = await window.MapOffline?.autoFillDeadZones?.(pos ? { lat: pos.lat, lon: pos.lon } : {});
|
||||
if (n) {
|
||||
UI.toast?.info(`${n} Funkloch-${n === 1 ? 'Gebiet' : 'Gebiete'} automatisch offline gespeichert.`);
|
||||
refresh();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue