feat(train): the instance's own map tiles, drawn by Train's style (ADR 0030, D50)

Protomaps' basemap cut for Europe and served by pmtiles beside Train's server
(compose.maps.yaml), drawn by Train's own style, light and dark, which the
server serves. Only people signed in get tiles: map.source hands a key that
Caddy has the server check before each tile; nothing logs a tile.

Also fixes .dockerignore, which left apps/train/ out of the build context, so
Train's server's and BRouter's images couldn't have been built.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-29 16:04:05 +01:00
commit 190c3872f4
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
5 changed files with 3726 additions and 97 deletions

View file

@ -92,6 +92,7 @@
"workoutSpeedChart": "Geschwindigkeit",
"workoutElevationChart": "Höhe",
"workoutMapSource": "Karte von {name}: sieht die Adresse deines Geräts und den gezeigten Ausschnitt, nicht deine Strecke.",
"workoutMapOwn": "Karte von deiner Instanz, {name}, aus OpenStreetMap: sie speichert nichts vom gezeigten Ausschnitt.",
"workoutMapSourceAbout": "Was {name} speichert",
"workoutMapUnavailable": "Die Karte kann gerade nicht geladen werden.",
"workoutNotFound": "Dieses Training gibt es nicht mehr.",

View file

@ -116,6 +116,8 @@
"workoutElevationChart": "Elevation",
"workoutMapSource": "Map by {name}: it sees your device's address and the area shown, not your track.",
"@workoutMapSource": {"description": "Under a workout's map: what the map's source sees.", "placeholders": {"name": {"type": "String", "example": "OpenFreeMap"}}},
"workoutMapOwn": "Map by your instance, {name}, from OpenStreetMap: it keeps nothing of the area shown.",
"@workoutMapOwn": {"description": "Under a workout's map drawn from the instance's own tiles (D50): what it sees.", "placeholders": {"name": {"type": "String", "example": "train.halkyon.eu"}}},
"workoutMapSourceAbout": "What {name} keeps",
"@workoutMapSourceAbout": {"description": "A link to the map source's page about what it keeps.", "placeholders": {"name": {"type": "String", "example": "OpenFreeMap"}}},
"workoutMapUnavailable": "The map can't be loaded now.",

View file

@ -610,6 +610,12 @@ abstract class AppLocalizations {
/// **'Map by {name}: it sees your device\'s address and the area shown, not your track.'**
String workoutMapSource(String name);
/// Under a workout's map drawn from the instance's own tiles (D50): what it sees.
///
/// In en, this message translates to:
/// **'Map by your instance, {name}, from OpenStreetMap: it keeps nothing of the area shown.'**
String workoutMapOwn(String name);
/// A link to the map source's page about what it keeps.
///
/// In en, this message translates to:

View file

@ -329,6 +329,11 @@ class AppLocalizationsDe extends AppLocalizations {
return 'Karte von $name: sieht die Adresse deines Geräts und den gezeigten Ausschnitt, nicht deine Strecke.';
}
@override
String workoutMapOwn(String name) {
return 'Karte von deiner Instanz, $name, aus OpenStreetMap: sie speichert nichts vom gezeigten Ausschnitt.';
}
@override
String workoutMapSourceAbout(String name) {
return 'Was $name speichert';

View file

@ -327,6 +327,11 @@ class AppLocalizationsEn extends AppLocalizations {
return 'Map by $name: it sees your device\'s address and the area shown, not your track.';
}
@override
String workoutMapOwn(String name) {
return 'Map by your instance, $name, from OpenStreetMap: it keeps nothing of the area shown.';
}
@override
String workoutMapSourceAbout(String name) {
return 'What $name keeps';