# Overview – what the app does There is a **Hilfe** menu with "Nach Update suchen" (self-update via `git`, see [setup.md](setup.md#updating)) and "Version…". The app is a single window: a **DRK-red header bar**, two tabs, and a **colour-coded status bar** at the bottom (grey = neutral, green = success, orange = warning, red = error; transient messages fade back to neutral after ~8 s). The window title shows the total entry count, and the window remembers its size and position between sessions (`data/window.json`). The UI font is enlarged for readability (`UI_FONT_SIZE`). ## Tab 1 – "Neuer Eintrag" (new entry) Workflow: build up a **queue** of entries, let them geocode in the background, then save the whole batch at once. 1. **Entry form** – date (a **calendar picker**, `ttkbootstrap.DateEntry`, pre-filled with today; typed input is accepted in `JJJJ-MM-TT` or `TT.MM.JJJJ` and **validated** – an invalid date is rejected with a dialog), city (autocomplete combobox), postal code (optional). Pressing `Return` in any field, or the **+ Hinzufügen** button, adds the row to the queue. The PLZ field only accepts digits. 2. **Queue table** – shows date, city, PLZ, and a live coordinate column that updates from `⏳ wird gesucht…` to either `lat / lon` or `⚠ nicht gefunden` as the background geocoder works through the queue. - Select a row and press Delete, use the **"Auswahl entfernen"** button, or click the `✕` cell. - Right-click a row for *Entfernen* / *Koordinaten erneut suchen*. 3. **Action bar** - **Alle speichern** – appends every queued row to `orte.csv`. If any row looks like a duplicate (same date + city + PLZ), a **modal** lists them and asks whether to save anyway. - **Warteschlange leeren** – discards the queue (asks for confirmation). - **Karte öffnen** – regenerates `karte.html` and opens it in the browser. ## Tab 2 – "Einträge verwalten" (manage entries) A table view of everything in `orte.csv`. - **Columns:** Datum, Ort, PLZ, and **Karte** – a status column showing `✓` when the row has coordinates or a red `fehlt` when it doesn't (the whole row is red too). The raw lat/lon numbers live in the edit dialog, not this table. - **Search box** – live filter across date, city, and PLZ (substring match). - **Sortable columns** – click a header to sort; clicking again reverses. The active column shows a ▲/▼ arrow. Default sort is by date, newest first. Sorting by **Karte** groups the rows without coordinates together. - **"Nur ohne Koordinaten"** toggle – filters to entries that have no coordinates yet. The hint line always shows the total count, plus how many are missing coordinates. - **Edit** – double-click a row (or *Bearbeiten*) opens a modal dialog to change date (calendar picker, validated) / city / PLZ. Two ways to fix coordinates: a "Koordinaten automatisch neu suchen" toggle re-runs geocoding after saving, or the **Breitengrad / Längengrad** fields let you type them in by hand (both empty = no map marker). - **Right-click → Koordinaten suchen** – runs geocoding for that one row (handy for rows that failed the first time). - **Delete** – *Löschen* removes the selected row(s) after a confirmation dialog. This is **irreversible**, but a timestamped copy of the file is written to `data/backups/` before every change (see [data-model.md](data-model.md#backups)). - **Karte öffnen** – same as on Tab 1. ## The map (`karte.html`) Generated by `generate_map()`: - Base layer: **Esri "World Light Gray"** (`MAP_TILES` + a labels overlay) – a light, muted basemap that needs no API key and no `Referer`. Initial view centred on `[49.0, 9.0]`, zoom 8; auto-fits to the markers when there is more than one. - One **`CircleMarker`** per unique `(city, lat, lon)` group. Radius scales linearly with the visit count for that location (`5 + count / max_count * 20` px), colour is DRK red (`#CC0000`). - Popup shows the city and the visit count; tooltip shows the city. - Rows with missing/blank coordinates are silently excluded. - If there are no usable coordinates at all, a dialog says so (and the status bar shows a warning). > The generated HTML pulls Leaflet, jQuery and Bootstrap from CDNs, and the map > tiles from `server.arcgisonline.com`, so the map needs an internet connection > to render even though the data is local. To change the look, edit `MAP_TILES` / > `MAP_TILES_LABELS` near the top of `app.py` — the comment there lists other > no-key Esri layers (Street, Topo). `tile.openstreetmap.org` was tried first > but its tile-usage policy **403s** pages opened from `file://` (no `Referer`); > `CartoDB positron` needs an API key. ## Geocoding behaviour `geocode_city(city, postal_code)` tries a series of queries in order and returns the first hit: 1. `" , Germany"` (only if a PLZ was given) 2. `", Baden-Württemberg, Germany"` 3. `", Hessen, Germany"` 4. `", Germany"` The regional bias is the `GEOCODE_REGIONS` constant (`Baden-Württemberg`, `Hessen`) near the top of `app.py`. Results are rounded to five decimal places. Nominatim's usage policy (max 1 req/s, identifying `user_agent`) is respected by a manual `time.sleep(1)` in the worker loop. If the geocoding service is unreachable (no internet), entries still save – without coordinates – and after a few consecutive failures a one-time dialog explains that the coordinates can be added later via *Bearbeiten*. The worker thread logs failures to `data/app.log` and keeps running.