Fahrtenliste zeigt Orte aus dem Zwischenspeicher (2026.9.1.23)
Was die Einzelfahrt einmal aufgeloest hat, zeigt die Liste umsonst mit:
ortAusCache() liest nur den Zwischenspeicher und fragt nie das Netz. Ein Abruf
je Zeile waere genau der Vorratsabruf, um dessen Unterlassung Nominatim bittet.
In der Liste steht nur der ORT, nicht die Anschrift (Wunsch des Eigentuemers):
eine Zeile traegt Datum, Art, Strecke und Verbrauch, zwei volle Anschriften
passen dort nicht. Auf der Einzelfahrt bleibt die vollstaendige Anschrift.
Der Ortsname wird beim Aufloesen separat gemerkt ("ort:"-Schluessel), nicht aus
der Anschrift geschnitten - faellt Nominatim auf display_name zurueck, stuende
hinter dem letzten Komma das LAND.
Der Rueckfall war trotzdem noetig, und das Ausliefern hat es gezeigt: die Liste
stand weiter voller Anschriften, weil der Ortsname nur bei einem frischen Abruf
entsteht und die Adressen laengst im Speicher lagen. stadtAusAnschrift()
schneidet ihn deshalb aus einer gespeicherten Anschrift - aber nur, wenn hinter
dem letzten Komma eine Postleitzahl steht. Damit greift es bei unserem Format
"<Strasse>, <PLZ> <Ort>" und niemals bei display_name.
VERIFIZIERT: 7 Faelle gegen den Schnitt (mit/ohne Strasse, benannter Platz,
ohne PLZ, display_name-Form, null), Panel als Modul, tsc --noEmit und
vite build sauber, 165/165 Tests gruen. Im Browser: "Eichstaett -> Adelschlag"
in der Liste, volle Anschrift auf der Einzelfahrt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
# AGENTS.md — Project state, review findings, open items, and working rules
|
||||
|
||||
**Last updated: 2026-09-01** (Einzelfahrt: Orte aus den Koordinaten, Kleingedrucktes weg, „Min.",
|
||||
`2026.9.1.20`, Abschnitt BJ. Davor: Strecken gestaffelt, Abschnitt BI. Davor: Audit ueber Panel, App und Backend, Abschnitt BH. Davor: Setup mit gruener Bestaetigung und Erwartungswert je
|
||||
`2026.9.1.23`, Abschnitt BJ - dazu Orte in der Fahrtenliste ohne Netzabruf.
|
||||
Davor: Strecken gestaffelt, Abschnitt BI. Davor: Audit ueber Panel, App und Backend, Abschnitt BH. Davor: Setup mit gruener Bestaetigung und Erwartungswert je
|
||||
Listenposition, Abschnitt BG. Davor: Rueckblick ohne Nullfahrten, Streckenwahl gemeinsam in `verlauf.py`,
|
||||
`2026.9.1.14`, Abschnitt BF. Davor: Fahrtstrecke auf 100 m: CAN als Anker, GNSS als Nachkommastelle,
|
||||
`2026.9.1.10`, Abschnitt BE. Davor: Nachlauf-Abzug nur mit zugeordnetem `TRIP_SENSOR`, `2026.9.1.9`;
|
||||
@@ -7070,6 +7071,32 @@ Verifiziert: Panel als Modul geparst, `tsc --noEmit` und `vite build` sauber, 16
|
||||
im Browser nachgesehen — Adressen, `Dauer 13 Min.`, und die Liste zeigt die Staffelung aus
|
||||
Abschnitt BI (`6,9 km`, `21,0 km`, `104 km`, `478 km`).
|
||||
|
||||
### Nachtrag (2026.9.1.21–.23): die Liste zeigt Orte, ohne einen einzigen Abruf
|
||||
|
||||
**Was schon im Speicher liegt, zeigt die Liste umsonst mit.** `ortAusCache()` liest nur den
|
||||
Zwischenspeicher und fragt nie das Netz. Ein Abruf je Zeile wäre genau der Vorratsabruf, um dessen
|
||||
Unterlassung Nominatim bittet — bei zweihundert Fahrten vierhundert Anfragen für einen Blick auf
|
||||
die Liste.
|
||||
|
||||
**In der Liste nur der Ort, nicht die Anschrift** (Wunsch des Eigentümers): eine Zeile trägt Datum,
|
||||
Art, Strecke und Verbrauch; „Schottenau, 85072 Eichstätt → Am Anger 7a, 85111 Adelschlag" ist länger
|
||||
als der ganze Rest. Auf der Einzelfahrt bleibt die vollständige Anschrift.
|
||||
|
||||
Der Ortsname wird beim Auflösen **separat gemerkt** (`ort:`-Schlüssel), nicht nachträglich aus der
|
||||
Anschrift geschnitten — fällt Nominatim auf `display_name` zurück, stünde hinter dem letzten Komma
|
||||
das **Land**, und in der Liste stünde „Deutschland → Deutschland".
|
||||
|
||||
**Der Rückfall war trotzdem nötig, und das Ausliefern hat es gezeigt:** die Liste stand weiter voller
|
||||
Anschriften. Grund — der Ortsname entsteht nur bei einem *frischen* Abruf, und die Adressen lagen
|
||||
längst im Speicher, also kehrte die Auflösung sofort zurück und schrieb nichts. `stadtAusAnschrift()`
|
||||
schneidet den Ort deshalb aus einer bereits gespeicherten Anschrift, **aber nur wenn hinter dem
|
||||
letzten Komma eine Postleitzahl steht**. Damit greift es bei unserem eigenen Format
|
||||
`„<Straße>, <PLZ> <Ort>"` und niemals bei `display_name`.
|
||||
|
||||
Geprüft: 7 Fälle gegen den Schnitt (mit/ohne Straße, benannter Platz, ohne PLZ, `display_name`-Form,
|
||||
`null`). Im Browser nachgesehen: `„Eichstätt → Adelschlag"` in der Liste, volle Anschrift auf der
|
||||
Einzelfahrt.
|
||||
|
||||
## Working conventions (observed — keep them)
|
||||
|
||||
- German is the project language: identifiers, comments, commits, UI texts. Exceptions:
|
||||
|
||||
@@ -77,6 +77,67 @@ function cacheSchreiben(suche: string, treffer: Koordinate | null): void {
|
||||
* Koordinatenpaar. Auf drei Nachkommastellen gerundet (~110 m), damit ein
|
||||
* paar Meter Abweichung keinen neuen Abruf auslösen.
|
||||
*/
|
||||
/** Ein bereits aufgeloester Ort aus dem Zwischenspeicher - OHNE Netzabruf.
|
||||
*
|
||||
* Die Einzelfahrt loest Koordinaten bei Bedarf auf und legt das Ergebnis im
|
||||
* selben Fach ab wie die Standortansicht. Was einmal dort steht, kann die
|
||||
* Liste umsonst mitbenutzen: kein zusaetzlicher Abruf, keine Wartezeit, kein
|
||||
* Flackern beim Zeichnen.
|
||||
*
|
||||
* Bewusst NUR aus dem Speicher. Ein Abruf je Zeile waere genau der
|
||||
* Vorratsabruf, um dessen Unterlassung Nominatim in seinen
|
||||
* Nutzungsbedingungen bittet - bei zweihundert Fahrten waeren das
|
||||
* vierhundert Anfragen fuer einen Blick auf die Liste.
|
||||
*
|
||||
* Schluessel wie bei der Aufloesung: drei Nachkommastellen (~110 m). */
|
||||
/** Der Ortsname allein, fuer die Fahrtenliste.
|
||||
*
|
||||
* In der Liste steht eine Fahrt in einer Zeile neben Datum, Art, Strecke und
|
||||
* Verbrauch. Zwei volle Anschriften passen dort nicht - "Schottenau, 85072
|
||||
* Eichstaett -> Am Anger 7a, 85111 Adelschlag" ist laenger als der Rest der
|
||||
* Zeile zusammen. Auf der Einzelfahrt bleibt die vollstaendige Anschrift.
|
||||
*
|
||||
* Der Ort wird beim Aufloesen SEPARAT gemerkt (Schluessel "ort:"), nicht
|
||||
* nachtraeglich aus der Anschrift geschnitten. Das Zerlegen der eigenen
|
||||
* Ausgabe waere fast immer richtig und in einem Fall falsch: faellt Nominatim
|
||||
* auf display_name zurueck, steht hinter dem letzten Komma das LAND. Dann
|
||||
* stuende in der Liste "Deutschland -> Deutschland". */
|
||||
/** Der Ortsname aus einer bereits gespeicherten Anschrift.
|
||||
*
|
||||
* Rueckfall fuer alles, was VOR dem Merken des Ortsnamens aufgeloest wurde:
|
||||
* dort liegt nur die volle Anschrift, und ein neuer Netzabruf, nur um den Ort
|
||||
* zu erfahren, waere Verschwendung.
|
||||
*
|
||||
* Geschnitten wird ausschliesslich, wenn hinter dem letzten Komma eine
|
||||
* Postleitzahl steht - unser eigenes Format ist "<Strasse>, <PLZ> <Ort>".
|
||||
* Faellt Nominatim auf display_name zurueck, fehlt die PLZ und hinter dem
|
||||
* letzten Komma stuende das LAND; dann lieber die volle Anschrift zeigen als
|
||||
* "Deutschland". */
|
||||
export function stadtAusAnschrift(anschrift: string | null | undefined): string | null {
|
||||
if (!anschrift) return null
|
||||
const teil = String(anschrift).split(", ").pop()?.trim() ?? ""
|
||||
const treffer = teil.match(/^\d{4,5}\s+(.+)$/)
|
||||
return treffer && treffer[1] ? treffer[1].trim() : null
|
||||
}
|
||||
|
||||
export function stadtAusCache(
|
||||
lat: number | null | undefined,
|
||||
lon: number | null | undefined,
|
||||
): string | null {
|
||||
if (lat === null || lat === undefined || lon === null || lon === undefined) return null
|
||||
const wert = cacheLesen()[`ort:${lat.toFixed(3)},${lon.toFixed(3)}`]
|
||||
return typeof wert === "string" && wert ? wert : null
|
||||
}
|
||||
|
||||
export function ortAusCache(
|
||||
lat: number | null | undefined,
|
||||
lon: number | null | undefined,
|
||||
): string | null {
|
||||
if (lat === null || lat === undefined || lon === null || lon === undefined) return null
|
||||
const wert = cacheLesen()[`rev:${lat.toFixed(3)},${lon.toFixed(3)}`]
|
||||
return typeof wert === "string" ? wert : null
|
||||
}
|
||||
|
||||
export async function koordinatenAufloesen(lat: number, lon: number): Promise<string | null> {
|
||||
const schluessel = `rev:${lat.toFixed(3)},${lon.toFixed(3)}`
|
||||
const cache = cacheLesen()
|
||||
@@ -96,7 +157,8 @@ export async function koordinatenAufloesen(lat: number, lon: number): Promise<st
|
||||
}
|
||||
const a = daten.address ?? {}
|
||||
const strasse = [a["road"], a["house_number"]].filter(Boolean).join(" ")
|
||||
const ort = [a["postcode"], a["city"] ?? a["town"] ?? a["village"]].filter(Boolean).join(" ")
|
||||
const stadt = (a["city"] ?? a["town"] ?? a["village"] ?? "").trim()
|
||||
const ort = [a["postcode"], stadt].filter(Boolean).join(" ")
|
||||
/* Steht das Auto auf einem benannten Platz — Parkplatz, Parkhaus,
|
||||
Rastplatz, Ladesäule —, sagt dessen Name mehr als die nächstgelegene
|
||||
Hausnummer: "Parkhaus Westentor" statt "Rathausstraße 4". Nominatim
|
||||
@@ -111,6 +173,8 @@ export async function koordinatenAufloesen(lat: number, lon: number): Promise<st
|
||||
// Nur echte Treffer merken: eine gedrosselte Antwort ist kein "hier gibt
|
||||
// es keine Adresse", die soll beim nächsten Mal erneut versucht werden.
|
||||
if (adresse) adresseMerken(schluessel, adresse)
|
||||
// Der reine Ortsname zusätzlich, für die Fahrtenliste — siehe stadtAusCache().
|
||||
if (stadt) adresseMerken(`ort:${lat.toFixed(3)},${lon.toFixed(3)}`, stadt)
|
||||
return adresse
|
||||
} catch {
|
||||
return null
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Accordion, ActionButton, Feld, Tile } from "@audi-dash/ui"
|
||||
|
||||
import { useDaten } from "../daten/DatenKontext"
|
||||
import { nachJahrUndMonat } from "../daten/statistik"
|
||||
import { ortAusCache, stadtAusAnschrift, stadtAusCache } from "../daten/geokodierung"
|
||||
import { datum, de, streckeText, summe } from "../format"
|
||||
import type { SeitenName } from "../navigation"
|
||||
import { Blattzeile, Leerzustand } from "./bausteine"
|
||||
@@ -20,8 +21,31 @@ import { Zeilenmenue } from "./Zeilenmenue"
|
||||
"Fahrt ohne Ortsangabe". Trug bisher in der Liste hier gar nichts bei
|
||||
(nur auf der Einzelfahrt-Seite sichtbar) - im Panel ist das die
|
||||
führende Information jeder Fahrtzeile. */
|
||||
function strecke(fahrt: { start_address?: string | null; end_address?: string | null }): string | null {
|
||||
const a = fahrt.start_address, b = fahrt.end_address
|
||||
function strecke(fahrt: {
|
||||
start_address?: string | null
|
||||
end_address?: string | null
|
||||
start_lat?: number | null
|
||||
start_lon?: number | null
|
||||
end_lat?: number | null
|
||||
end_lon?: number | null
|
||||
}): string | null {
|
||||
// Was die Einzelfahrt schon einmal aufgeloest hat, zeigt die Liste umsonst
|
||||
// mit - siehe ortAusCache(), kein zusaetzlicher Abruf.
|
||||
// Ortsname bevorzugt; ohne ihn die volle Anschrift, damit eine vor dieser
|
||||
// Aenderung aufgeloeste Fahrt nicht ploetzlich wieder ortlos dasteht.
|
||||
const ortsname = (
|
||||
adresse: string | null | undefined,
|
||||
lat: number | null | undefined,
|
||||
lon: number | null | undefined,
|
||||
): string | null => {
|
||||
if (adresse) return adresse
|
||||
const gemerkt = stadtAusCache(lat, lon)
|
||||
if (gemerkt) return gemerkt
|
||||
const voll = ortAusCache(lat, lon)
|
||||
return stadtAusAnschrift(voll) ?? voll
|
||||
}
|
||||
const a = ortsname(fahrt.start_address, fahrt.start_lat, fahrt.start_lon)
|
||||
const b = ortsname(fahrt.end_address, fahrt.end_lat, fahrt.end_lon)
|
||||
if (!a && !b) return "Fahrt ohne Ortsangabe"
|
||||
if (!b) return a ?? null
|
||||
if (!a) return b
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":"2026.9.1.20","sha256":"20cc80f881d429303da2558b3171aabe414851aacbe2fee9253f7026c376043b","bytes":266021,"gebaut":"2026-09-01T17:35:23Z"}
|
||||
{"version":"2026.9.1.23","sha256":"c3e1376f4e314b2994bc6341c423b92d251142e0abd2abe4752e1f5f1a578d13","bytes":266223,"gebaut":"2026-09-01T18:35:21Z"}
|
||||
Binary file not shown.
@@ -754,6 +754,7 @@ let USER_POS_FEHLER = null;
|
||||
let USER_WATCH = null; // watchPosition-Kennung, solange die Standortseite offen ist
|
||||
let STANDORT_ADRESSE = null; // zuletzt aufgelöste Adresse (Cache über Koordinaten-Rundung)
|
||||
let STANDORT_ADRESSE_KEY = null;
|
||||
let STANDORT_STADT = ""; // reiner Ortsname zum selben Punkt
|
||||
let STANDORTMENU_OFFEN = false;
|
||||
let SMAP = null, SMAP_TILE = null, SMAP_STIL = null; // Vollbild-Karte (eigenständig von MAP/TMAP); SMAP_STIL: null/"strasse" oder "satellit"
|
||||
let TMAP = null, TMAP_TILE = null; // Vorschau-Karte der Übersicht-Kachel
|
||||
@@ -1016,7 +1017,8 @@ async function standortAdresseAufloesen(lat, lon) {
|
||||
const daten = await res.json();
|
||||
const a = daten.address || {};
|
||||
const strasse = [a.road, a.house_number].filter(Boolean).join(" ");
|
||||
const ort = [a.postcode, a.city || a.town || a.village].filter(Boolean).join(" ");
|
||||
STANDORT_STADT = (a.city || a.town || a.village || "").trim();
|
||||
const ort = [a.postcode, STANDORT_STADT].filter(Boolean).join(" ");
|
||||
/* Steht das Auto auf einem benannten Platz - Parkplatz, Parkhaus,
|
||||
Rastplatz -, sagt dessen Name mehr als die naechstgelegene Hausnummer.
|
||||
Nominatim liefert ihn im Feld name; die Pruefung auf die Art der
|
||||
@@ -1038,6 +1040,9 @@ async function standortAdresseAufloesen(lat, lon) {
|
||||
// Nur echte Treffer merken - eine gedrosselte Antwort ist kein "hier gibt es
|
||||
// keine Adresse" und soll beim naechsten Mal erneut versucht werden.
|
||||
if (STANDORT_ADRESSE) geoCacheSchreiben(cacheKey, STANDORT_ADRESSE);
|
||||
// Der reine Ortsname zusaetzlich, fuer die Fahrtenliste - siehe
|
||||
// stadtAusCache().
|
||||
if (STANDORT_STADT) geoCacheSchreiben("ort:" + key, STANDORT_STADT);
|
||||
return STANDORT_ADRESSE;
|
||||
}
|
||||
function standortAdresseLaden() {
|
||||
@@ -3720,8 +3725,73 @@ function istNachtZeit(ts) {
|
||||
}
|
||||
/* D3: Die Strecke führt die Zeile, das Datum steht darunter. Umbruch nur
|
||||
zwischen den Orten, nie am Pfeil. */
|
||||
/* Ein bereits aufgeloester Ort aus dem Zwischenspeicher - OHNE Netzabruf.
|
||||
|
||||
Die Einzelfahrt loest Koordinaten bei Bedarf auf und legt das Ergebnis im
|
||||
selben Fach ab wie die Standortansicht. Was einmal dort steht, kann die
|
||||
Liste umsonst mitbenutzen: kein zusaetzlicher Abruf, keine Wartezeit, kein
|
||||
Flackern beim Zeichnen.
|
||||
|
||||
Bewusst NUR aus dem Speicher. Ein Abruf je Zeile waere genau der
|
||||
Vorratsabruf, um dessen Unterlassung Nominatim in seinen
|
||||
Nutzungsbedingungen bittet - bei zweihundert Fahrten waeren das
|
||||
vierhundert Anfragen fuer einen Blick auf die Liste.
|
||||
|
||||
Schluessel wie bei der Aufloesung: drei Nachkommastellen (~110 m). */
|
||||
function ortAusCache(lat, lon) {
|
||||
if (lat == null || lon == null) return null;
|
||||
const wert = geoCacheLesen()["rev:" + lat.toFixed(3) + "," + lon.toFixed(3)];
|
||||
return typeof wert === "string" ? wert : null;
|
||||
}
|
||||
|
||||
/* Der Ortsname allein, fuer die Fahrtenliste.
|
||||
|
||||
In der Liste steht eine Fahrt in einer Zeile neben Datum, Art, Strecke und
|
||||
Verbrauch. Zwei volle Anschriften passen dort nicht - "Schottenau, 85072
|
||||
Eichstaett -> Am Anger 7a, 85111 Adelschlag" ist laenger als der Rest der
|
||||
Zeile zusammen. Auf der Einzelfahrt bleibt die vollstaendige Anschrift.
|
||||
|
||||
Der Ort wird beim Aufloesen SEPARAT gemerkt (Schluessel "ort:"), nicht
|
||||
nachtraeglich aus der Anschrift geschnitten. Das Zerlegen der eigenen
|
||||
Ausgabe waere fast immer richtig und in einem Fall falsch: faellt Nominatim
|
||||
auf display_name zurueck, steht hinter dem letzten Komma das LAND. Dann
|
||||
stuende in der Liste "Deutschland -> Deutschland". */
|
||||
/* Der Ortsname aus einer bereits gespeicherten Anschrift.
|
||||
|
||||
Rueckfall fuer alles, was VOR dem Merken des Ortsnamens aufgeloest wurde:
|
||||
dort liegt nur die volle Anschrift, und ein neuer Netzabruf, nur um den Ort
|
||||
zu erfahren, waere Verschwendung.
|
||||
|
||||
Geschnitten wird ausschliesslich, wenn hinter dem letzten Komma eine
|
||||
Postleitzahl steht - unser eigenes Format ist "<Strasse>, <PLZ> <Ort>".
|
||||
Faellt Nominatim auf display_name zurueck, fehlt die PLZ und hinter dem
|
||||
letzten Komma stuende das LAND; dann lieber die volle Anschrift zeigen als
|
||||
"Deutschland". */
|
||||
function stadtAusAnschrift(anschrift) {
|
||||
if (!anschrift) return null;
|
||||
const teil = String(anschrift).split(", ").pop().trim();
|
||||
const treffer = teil.match(/^\d{4,5}\s+(.+)$/);
|
||||
return treffer ? treffer[1].trim() : null;
|
||||
}
|
||||
|
||||
function stadtAusCache(lat, lon) {
|
||||
if (lat == null || lon == null) return null;
|
||||
const wert = geoCacheLesen()["ort:" + lat.toFixed(3) + "," + lon.toFixed(3)];
|
||||
return typeof wert === "string" && wert ? wert : null;
|
||||
}
|
||||
|
||||
function strecke(t) {
|
||||
const a = t.start_address, b = t.end_address;
|
||||
// Ortsname bevorzugt; ohne ihn die volle Anschrift, damit eine vor dieser
|
||||
// Aenderung aufgeloeste Fahrt nicht ploetzlich wieder ortlos dasteht.
|
||||
const ortsname = (adresse, lat, lon) => {
|
||||
if (adresse) return adresse;
|
||||
const gemerkt = stadtAusCache(lat, lon);
|
||||
if (gemerkt) return gemerkt;
|
||||
const voll = ortAusCache(lat, lon);
|
||||
return stadtAusAnschrift(voll) || voll;
|
||||
};
|
||||
const a = ortsname(t.start_address, t.start_lat, t.start_lon);
|
||||
const b = ortsname(t.end_address, t.end_lat, t.end_lon);
|
||||
if (!a && !b) return "Fahrt ohne Ortsangabe";
|
||||
if (!b) return esc(a);
|
||||
if (!a) return esc(b);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "audi_dashboard",
|
||||
"name": "Audi Dashboard",
|
||||
"version": "2026.9.1.20",
|
||||
"version": "2026.9.1.23",
|
||||
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
|
||||
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
|
||||
"codeowners": ["@paul"],
|
||||
|
||||
Reference in New Issue
Block a user