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:
parent
a848b71bd4
commit
190c3872f4
5 changed files with 3726 additions and 97 deletions
|
|
@ -84,6 +84,7 @@ void main() {
|
|||
RecordingDevice? recording,
|
||||
WorkoutRepository? workouts,
|
||||
bool mapReachable = true,
|
||||
MapSource? mapSource,
|
||||
HealthConnect? health,
|
||||
ProfileRepository? profile,
|
||||
DayRepository? days,
|
||||
|
|
@ -208,7 +209,9 @@ void main() {
|
|||
health: health,
|
||||
loop: loop,
|
||||
maps: MapSourceRepository(
|
||||
_FakeMapSourceGateway(mapReachable ? openFreeMap : null),
|
||||
_FakeMapSourceGateway(
|
||||
mapReachable ? mapSource ?? openFreeMap : null,
|
||||
),
|
||||
),
|
||||
// No tiles: widget tests reach no network.
|
||||
mapTiles: (source) => const SizedBox.shrink(),
|
||||
|
|
@ -2604,6 +2607,35 @@ void main() {
|
|||
expect(find.text('Pace'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets("drawn from the instance's own tiles, the page says the "
|
||||
'instance keeps nothing of the area shown (D50)', (tester) async {
|
||||
await saveRun(DateTime(2026, 10, 27, 7, 30));
|
||||
await start(
|
||||
tester,
|
||||
usedBefore: true,
|
||||
workouts: workouts,
|
||||
mapSource: MapSource(
|
||||
name: 'train.example.org',
|
||||
styleUrl: 'https://train.example.org/map/style/light.json',
|
||||
darkStyleUrl: 'https://train.example.org/map/style/dark.json',
|
||||
attribution: '© OpenStreetMap contributors · Protomaps',
|
||||
tileKey: 'm1.20000.mac',
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Morning run'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
find.text(
|
||||
'Map by your instance, train.example.org, from OpenStreetMap: it '
|
||||
'keeps nothing of the area shown.',
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.textContaining('sees your device'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets("a workout with a strap shows the heart's average and highest "
|
||||
'rate, by split too, and its chart', (tester) async {
|
||||
await saveRun(DateTime(2026, 10, 27, 7, 30), strap: true);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:halkyon_train_client/halkyon_train_client.dart' show MapSource;
|
||||
import 'package:halkyon_train_flutter/src/feature/map/data/map_style.dart';
|
||||
import 'package:vector_tile_renderer/vector_tile_renderer.dart';
|
||||
|
||||
/// Train's style for the instance's own tiles (ADR 0030, D50), as its
|
||||
/// server makes it (the fixtures its tests keep), read by the renderer
|
||||
/// without a warning; and which style and key a source's requests take.
|
||||
void main() {
|
||||
for (final name in ['train_style_light_de.json', 'train_style_dark.json']) {
|
||||
test(
|
||||
'$name: vector_tile_renderer reads each layer, warning of nothing',
|
||||
() {
|
||||
final style = jsonDecode(
|
||||
File('test/fixtures/map/$name').readAsStringSync(),
|
||||
) as Map<String, dynamic>;
|
||||
final warnings = <String>[];
|
||||
final theme = ThemeReader(logger: _Warnings(warnings)).read(style);
|
||||
|
||||
expect(warnings, isEmpty);
|
||||
expect(theme.layers, hasLength((style['layers'] as List).length));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
final own = MapSource(
|
||||
name: 'train.example.org',
|
||||
styleUrl: 'https://train.example.org/map/style/light.json',
|
||||
darkStyleUrl: 'https://train.example.org/map/style/dark.json',
|
||||
attribution: '© OpenStreetMap contributors · Protomaps',
|
||||
tileKey: 'm1.20000.mac',
|
||||
);
|
||||
final provider = MapSource(
|
||||
name: 'MapTiler',
|
||||
styleUrl: 'https://api.maptiler.com/maps/outdoor-v2/style.json?key=k',
|
||||
attribution: '© MapTiler',
|
||||
);
|
||||
|
||||
test("the instance's own: dark in a dark theme, in the app's language, "
|
||||
'its tiles asked for with its key', () {
|
||||
expect(
|
||||
styleUrlOf(own, dark: true, language: 'de'),
|
||||
'https://train.example.org/map/style/dark.json?lang=de',
|
||||
);
|
||||
expect(styleUrlOf(own), own.styleUrl);
|
||||
expect(tileHeadersOf(own), {'authorization': 'Bearer m1.20000.mac'});
|
||||
});
|
||||
|
||||
test(
|
||||
"a provider's: its one style as given, and no key of the instance's",
|
||||
() {
|
||||
expect(
|
||||
styleUrlOf(provider, dark: true, language: 'de'),
|
||||
provider.styleUrl,
|
||||
);
|
||||
expect(tileHeadersOf(provider), isNull);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class _Warnings(final List<String> _warnings) implements Logger {
|
||||
@override
|
||||
void log(MessageFunction message) {}
|
||||
|
||||
@override
|
||||
void warn(MessageFunction message) => _warnings.add(message());
|
||||
}
|
||||
|
|
@ -217,6 +217,49 @@ void main() {
|
|||
},
|
||||
);
|
||||
|
||||
test("the instance's own tiles are kept with its key; another source's "
|
||||
'with none (D50)', () async {
|
||||
final own = MapSource(
|
||||
name: 'tiles.example',
|
||||
styleUrl: 'https://tiles.example/map/style/light.json',
|
||||
darkStyleUrl: 'https://tiles.example/map/style/dark.json',
|
||||
attribution: '© OpenStreetMap',
|
||||
tileKey: 'm1.20000.mac',
|
||||
);
|
||||
await maps().keep('route-1', north, own);
|
||||
expect(server.keys, isNotEmpty);
|
||||
expect(server.keys.toSet(), {'Bearer m1.20000.mac'});
|
||||
|
||||
server.keys.clear();
|
||||
await maps().keep('route-2', east, source);
|
||||
expect(server.keys.toSet(), {null});
|
||||
});
|
||||
|
||||
test("each style is kept apart: light and dark, in the app's language "
|
||||
"for the instance's own (D50)", () async {
|
||||
final own = MapSource(
|
||||
name: 'tiles.example',
|
||||
styleUrl: 'https://tiles.example/map/style/light.json',
|
||||
darkStyleUrl: 'https://tiles.example/map/style/dark.json',
|
||||
attribution: '© OpenStreetMap',
|
||||
tileKey: 'm1.20000.mac',
|
||||
);
|
||||
await maps().style(own, language: 'de');
|
||||
await maps().style(own, dark: true, language: 'de');
|
||||
expect(server.styles, [
|
||||
'https://tiles.example/map/style/light.json?lang=de',
|
||||
'https://tiles.example/map/style/dark.json?lang=de',
|
||||
]);
|
||||
|
||||
server.offline = true;
|
||||
expect((await maps().style(own, language: 'de'))!.json['name'], 'Liberty');
|
||||
expect(
|
||||
(await maps().style(own, dark: true, language: 'de'))!.json['name'],
|
||||
'Dark',
|
||||
);
|
||||
expect(await maps().style(own, language: 'en'), isNull);
|
||||
});
|
||||
|
||||
test('the style read once draws offline, with its sprites; another '
|
||||
"source's does not", () async {
|
||||
final online = await maps().style(source);
|
||||
|
|
@ -249,6 +292,12 @@ void main() {
|
|||
/// tiles asked for listed.
|
||||
class _Tiles() {
|
||||
final tiles = <String>[];
|
||||
|
||||
/// The `Authorization` header each tile came with, null for none.
|
||||
final keys = <String?>[];
|
||||
|
||||
/// The styles asked for, with their query.
|
||||
final styles = <String>[];
|
||||
bool failing = false;
|
||||
bool empty = false;
|
||||
bool offline = false;
|
||||
|
|
@ -259,19 +308,24 @@ class _Tiles() {
|
|||
}
|
||||
final path = request.url.path;
|
||||
return switch (path) {
|
||||
'/styles/liberty' => http.Response(
|
||||
jsonEncode({
|
||||
'version': 8,
|
||||
'name': 'Liberty',
|
||||
'sources': {
|
||||
'planet': {'type': 'vector', 'url': '/planet'},
|
||||
'shade': {'type': 'raster-dem', 'url': '/dem'},
|
||||
},
|
||||
'sprite': 'https://tiles.example/sprites/liberty',
|
||||
'layers': <Object?>[],
|
||||
}),
|
||||
200,
|
||||
),
|
||||
'/styles/liberty' ||
|
||||
'/map/style/light.json' ||
|
||||
'/map/style/dark.json' => () {
|
||||
styles.add(request.url.toString());
|
||||
return http.Response(
|
||||
jsonEncode({
|
||||
'version': 8,
|
||||
'name': path.contains('dark') ? 'Dark' : 'Liberty',
|
||||
'sources': {
|
||||
'planet': {'type': 'vector', 'url': '/planet'},
|
||||
'shade': {'type': 'raster-dem', 'url': '/dem'},
|
||||
},
|
||||
'sprite': 'https://tiles.example/sprites/liberty',
|
||||
'layers': <Object?>[],
|
||||
}),
|
||||
200,
|
||||
);
|
||||
}(),
|
||||
'/planet' => http.Response(
|
||||
jsonEncode({
|
||||
'tiles': ['https://tiles.example/planet/{z}/{x}/{y}.pbf'],
|
||||
|
|
@ -292,6 +346,7 @@ class _Tiles() {
|
|||
),
|
||||
_ when path.startsWith('/planet/') => () {
|
||||
tiles.add(path.substring(1).replaceAll('.pbf', ''));
|
||||
keys.add(request.headers['authorization']);
|
||||
if (failing) {
|
||||
return http.Response('', 503);
|
||||
}
|
||||
|
|
|
|||
958
apps/train/halkyon_train_flutter/test/fixtures/map/train_style_dark.json
vendored
Normal file
958
apps/train/halkyon_train_flutter/test/fixtures/map/train_style_dark.json
vendored
Normal file
|
|
@ -0,0 +1,958 @@
|
|||
{
|
||||
"version": 8,
|
||||
"name": "Halkyon Train, dark",
|
||||
"sources": {
|
||||
"protomaps": {
|
||||
"type": "vector",
|
||||
"tiles": [
|
||||
"https://train.example.org/tiles/europe/{z}/{x}/{y}.mvt"
|
||||
],
|
||||
"minzoom": 0,
|
||||
"maxzoom": 15,
|
||||
"attribution": "<a href=\"https://www.openstreetmap.org/copyright\">© OpenStreetMap contributors</a> · <a href=\"https://protomaps.com\">Protomaps</a>"
|
||||
}
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"id": "background",
|
||||
"type": "background",
|
||||
"paint": {
|
||||
"background-color": "#1b3042"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "earth",
|
||||
"type": "fill",
|
||||
"source": "protomaps",
|
||||
"source-layer": "earth",
|
||||
"paint": {
|
||||
"fill-color": "#1d2024"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "landcover",
|
||||
"type": "fill",
|
||||
"source": "protomaps",
|
||||
"source-layer": "landcover",
|
||||
"paint": {
|
||||
"fill-color": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"forest",
|
||||
"scrub"
|
||||
],
|
||||
"#22302a",
|
||||
[
|
||||
"grassland",
|
||||
"farmland"
|
||||
],
|
||||
"#222a24",
|
||||
"glacier",
|
||||
"#2b3036",
|
||||
"barren",
|
||||
"#2e2c25",
|
||||
"#212428"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "landuse-wood",
|
||||
"type": "fill",
|
||||
"source": "protomaps",
|
||||
"source-layer": "landuse",
|
||||
"filter": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"forest",
|
||||
"wood",
|
||||
"orchard",
|
||||
"scrub"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
"paint": {
|
||||
"fill-color": "#22302a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "landuse-park",
|
||||
"type": "fill",
|
||||
"source": "protomaps",
|
||||
"source-layer": "landuse",
|
||||
"filter": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"park",
|
||||
"garden",
|
||||
"grass",
|
||||
"meadow",
|
||||
"recreation_ground",
|
||||
"playground",
|
||||
"dog_park",
|
||||
"golf_course",
|
||||
"nature_reserve",
|
||||
"national_park",
|
||||
"protected_area",
|
||||
"cemetery",
|
||||
"camp_site"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
"paint": {
|
||||
"fill-color": "#233127"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "landuse-pitch",
|
||||
"type": "fill",
|
||||
"source": "protomaps",
|
||||
"source-layer": "landuse",
|
||||
"filter": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"pitch",
|
||||
"stadium"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
"paint": {
|
||||
"fill-color": "#253427"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "landuse-sand",
|
||||
"type": "fill",
|
||||
"source": "protomaps",
|
||||
"source-layer": "landuse",
|
||||
"filter": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"beach",
|
||||
"sand",
|
||||
"bare_rock"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
"paint": {
|
||||
"fill-color": "#2e2c25"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "landuse-wetland",
|
||||
"type": "fill",
|
||||
"source": "protomaps",
|
||||
"source-layer": "landuse",
|
||||
"filter": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"wetland"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
"paint": {
|
||||
"fill-color": "#20302d"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "landuse-built",
|
||||
"type": "fill",
|
||||
"source": "protomaps",
|
||||
"source-layer": "landuse",
|
||||
"filter": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"industrial",
|
||||
"commercial",
|
||||
"railway",
|
||||
"military",
|
||||
"aerodrome",
|
||||
"hospital",
|
||||
"school",
|
||||
"college",
|
||||
"university",
|
||||
"kindergarten"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
"paint": {
|
||||
"fill-color": "#25272b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "landuse-runway",
|
||||
"type": "fill",
|
||||
"source": "protomaps",
|
||||
"source-layer": "landuse",
|
||||
"filter": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"runway",
|
||||
"taxiway"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
"paint": {
|
||||
"fill-color": "#2c2e32"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "water",
|
||||
"type": "fill",
|
||||
"source": "protomaps",
|
||||
"source-layer": "water",
|
||||
"filter": [
|
||||
"==",
|
||||
[
|
||||
"geometry-type"
|
||||
],
|
||||
"Polygon"
|
||||
],
|
||||
"paint": {
|
||||
"fill-color": "#1b3042"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "water-lines",
|
||||
"type": "line",
|
||||
"source": "protomaps",
|
||||
"source-layer": "water",
|
||||
"filter": [
|
||||
"==",
|
||||
[
|
||||
"geometry-type"
|
||||
],
|
||||
"LineString"
|
||||
],
|
||||
"minzoom": 9,
|
||||
"layout": {
|
||||
"line-cap": "round",
|
||||
"line-join": "round"
|
||||
},
|
||||
"paint": {
|
||||
"line-color": "#1b3042",
|
||||
"line-width": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind_detail"
|
||||
],
|
||||
[
|
||||
"river",
|
||||
"canal"
|
||||
],
|
||||
[
|
||||
"interpolate",
|
||||
[
|
||||
"exponential",
|
||||
1.6
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
12,
|
||||
1.5,
|
||||
18,
|
||||
12
|
||||
],
|
||||
[
|
||||
"interpolate",
|
||||
[
|
||||
"exponential",
|
||||
1.6
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
12,
|
||||
0.5,
|
||||
18,
|
||||
3
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "buildings",
|
||||
"type": "fill",
|
||||
"source": "protomaps",
|
||||
"source-layer": "buildings",
|
||||
"filter": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"building",
|
||||
"building_part"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
"minzoom": 14,
|
||||
"paint": {
|
||||
"fill-color": "#2a2c30"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "boundaries",
|
||||
"type": "line",
|
||||
"source": "protomaps",
|
||||
"source-layer": "boundaries",
|
||||
"filter": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"country",
|
||||
"region"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
"paint": {
|
||||
"line-color": "#7d7290",
|
||||
"line-width": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
"country",
|
||||
1.5,
|
||||
0.8
|
||||
],
|
||||
"line-dasharray": [
|
||||
3,
|
||||
2
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "rail",
|
||||
"type": "line",
|
||||
"source": "protomaps",
|
||||
"source-layer": "roads",
|
||||
"filter": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"rail"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
"minzoom": 10,
|
||||
"paint": {
|
||||
"line-color": "#55575b",
|
||||
"line-width": [
|
||||
"interpolate",
|
||||
[
|
||||
"exponential",
|
||||
1.6
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
12,
|
||||
0.8,
|
||||
18,
|
||||
3
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "roads-major-casing",
|
||||
"type": "line",
|
||||
"source": "protomaps",
|
||||
"source-layer": "roads",
|
||||
"filter": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"highway",
|
||||
"major_road"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
"minzoom": 7,
|
||||
"layout": {
|
||||
"line-cap": "round",
|
||||
"line-join": "round"
|
||||
},
|
||||
"paint": {
|
||||
"line-color": "#15171a",
|
||||
"line-width": [
|
||||
"interpolate",
|
||||
[
|
||||
"exponential",
|
||||
1.6
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
12,
|
||||
3,
|
||||
18,
|
||||
22
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "roads-minor-casing",
|
||||
"type": "line",
|
||||
"source": "protomaps",
|
||||
"source-layer": "roads",
|
||||
"filter": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"minor_road"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
"minzoom": 13,
|
||||
"layout": {
|
||||
"line-cap": "round",
|
||||
"line-join": "round"
|
||||
},
|
||||
"paint": {
|
||||
"line-color": "#15171a",
|
||||
"line-width": [
|
||||
"interpolate",
|
||||
[
|
||||
"exponential",
|
||||
1.6
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
12,
|
||||
1.5,
|
||||
18,
|
||||
14
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "roads-minor",
|
||||
"type": "line",
|
||||
"source": "protomaps",
|
||||
"source-layer": "roads",
|
||||
"filter": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"minor_road"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
"minzoom": 11,
|
||||
"layout": {
|
||||
"line-cap": "round",
|
||||
"line-join": "round"
|
||||
},
|
||||
"paint": {
|
||||
"line-color": "#3a3d42",
|
||||
"line-width": [
|
||||
"interpolate",
|
||||
[
|
||||
"exponential",
|
||||
1.6
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
12,
|
||||
0.8,
|
||||
18,
|
||||
11
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "roads-major",
|
||||
"type": "line",
|
||||
"source": "protomaps",
|
||||
"source-layer": "roads",
|
||||
"filter": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"highway",
|
||||
"major_road"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
"minzoom": 5,
|
||||
"layout": {
|
||||
"line-cap": "round",
|
||||
"line-join": "round"
|
||||
},
|
||||
"paint": {
|
||||
"line-color": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
"highway",
|
||||
"#6a573a",
|
||||
"#4b4639"
|
||||
],
|
||||
"line-width": [
|
||||
"interpolate",
|
||||
[
|
||||
"exponential",
|
||||
1.6
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
12,
|
||||
2,
|
||||
18,
|
||||
18
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "tracks",
|
||||
"type": "line",
|
||||
"source": "protomaps",
|
||||
"source-layer": "roads",
|
||||
"filter": [
|
||||
"all",
|
||||
[
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"path",
|
||||
"minor_road"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
[
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind_detail"
|
||||
],
|
||||
[
|
||||
"track"
|
||||
],
|
||||
true,
|
||||
false
|
||||
]
|
||||
],
|
||||
"minzoom": 12,
|
||||
"layout": {
|
||||
"line-cap": "round",
|
||||
"line-join": "round"
|
||||
},
|
||||
"paint": {
|
||||
"line-color": "#b88a5a",
|
||||
"line-width": [
|
||||
"interpolate",
|
||||
[
|
||||
"exponential",
|
||||
1.6
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
12,
|
||||
1,
|
||||
18,
|
||||
4
|
||||
],
|
||||
"line-dasharray": [
|
||||
4,
|
||||
1.5
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "footpaths",
|
||||
"type": "line",
|
||||
"source": "protomaps",
|
||||
"source-layer": "roads",
|
||||
"filter": [
|
||||
"all",
|
||||
[
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"path"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
[
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind_detail"
|
||||
],
|
||||
[
|
||||
"footway",
|
||||
"path",
|
||||
"pedestrian",
|
||||
"steps",
|
||||
"bridleway"
|
||||
],
|
||||
true,
|
||||
false
|
||||
]
|
||||
],
|
||||
"minzoom": 13,
|
||||
"paint": {
|
||||
"line-color": "#d07a5c",
|
||||
"line-width": [
|
||||
"interpolate",
|
||||
[
|
||||
"exponential",
|
||||
1.6
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
12,
|
||||
1,
|
||||
18,
|
||||
3.5
|
||||
],
|
||||
"line-dasharray": [
|
||||
1.5,
|
||||
1.5
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "cycleways",
|
||||
"type": "line",
|
||||
"source": "protomaps",
|
||||
"source-layer": "roads",
|
||||
"filter": [
|
||||
"all",
|
||||
[
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"path"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
[
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind_detail"
|
||||
],
|
||||
[
|
||||
"cycleway"
|
||||
],
|
||||
true,
|
||||
false
|
||||
]
|
||||
],
|
||||
"minzoom": 12,
|
||||
"layout": {
|
||||
"line-cap": "round",
|
||||
"line-join": "round"
|
||||
},
|
||||
"paint": {
|
||||
"line-color": "#5fb5ae",
|
||||
"line-width": [
|
||||
"interpolate",
|
||||
[
|
||||
"exponential",
|
||||
1.6
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
12,
|
||||
1,
|
||||
18,
|
||||
4
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "water-names",
|
||||
"type": "symbol",
|
||||
"source": "protomaps",
|
||||
"source-layer": "water",
|
||||
"filter": [
|
||||
"has",
|
||||
"name"
|
||||
],
|
||||
"minzoom": 12,
|
||||
"layout": {
|
||||
"symbol-placement": "line",
|
||||
"text-field": [
|
||||
"get",
|
||||
"name"
|
||||
],
|
||||
"text-size": 12
|
||||
},
|
||||
"paint": {
|
||||
"text-color": "#8fb2cb",
|
||||
"text-halo-color": "#1d2024",
|
||||
"text-halo-width": 1.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "road-names",
|
||||
"type": "symbol",
|
||||
"source": "protomaps",
|
||||
"source-layer": "roads",
|
||||
"filter": [
|
||||
"all",
|
||||
[
|
||||
"has",
|
||||
"name"
|
||||
],
|
||||
[
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"highway",
|
||||
"major_road",
|
||||
"minor_road",
|
||||
"path"
|
||||
],
|
||||
true,
|
||||
false
|
||||
]
|
||||
],
|
||||
"minzoom": 14,
|
||||
"layout": {
|
||||
"symbol-placement": "line",
|
||||
"text-field": [
|
||||
"get",
|
||||
"name"
|
||||
],
|
||||
"text-size": 12
|
||||
},
|
||||
"paint": {
|
||||
"text-color": "#e2ded8",
|
||||
"text-halo-color": "#1d2024",
|
||||
"text-halo-width": 1.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "places-local",
|
||||
"type": "symbol",
|
||||
"source": "protomaps",
|
||||
"source-layer": "places",
|
||||
"filter": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"neighbourhood",
|
||||
"macrohood"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
"minzoom": 13,
|
||||
"layout": {
|
||||
"text-field": [
|
||||
"get",
|
||||
"name"
|
||||
],
|
||||
"text-size": 12,
|
||||
"text-transform": "uppercase",
|
||||
"text-letter-spacing": 0.08
|
||||
},
|
||||
"paint": {
|
||||
"text-color": "#a29b92",
|
||||
"text-halo-color": "#1d2024",
|
||||
"text-halo-width": 1.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "places-locality",
|
||||
"type": "symbol",
|
||||
"source": "protomaps",
|
||||
"source-layer": "places",
|
||||
"filter": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"locality"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
"layout": {
|
||||
"text-field": [
|
||||
"get",
|
||||
"name"
|
||||
],
|
||||
"text-size": [
|
||||
"interpolate",
|
||||
[
|
||||
"linear"
|
||||
],
|
||||
[
|
||||
"get",
|
||||
"population_rank"
|
||||
],
|
||||
1,
|
||||
11,
|
||||
15,
|
||||
18
|
||||
]
|
||||
},
|
||||
"paint": {
|
||||
"text-color": "#e2ded8",
|
||||
"text-halo-color": "#1d2024",
|
||||
"text-halo-width": 1.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "places-region",
|
||||
"type": "symbol",
|
||||
"source": "protomaps",
|
||||
"source-layer": "places",
|
||||
"filter": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"region"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
"layout": {
|
||||
"text-field": [
|
||||
"get",
|
||||
"name"
|
||||
],
|
||||
"text-size": 12,
|
||||
"text-transform": "uppercase"
|
||||
},
|
||||
"paint": {
|
||||
"text-color": "#a29b92",
|
||||
"text-halo-color": "#1d2024",
|
||||
"text-halo-width": 1.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "places-country",
|
||||
"type": "symbol",
|
||||
"source": "protomaps",
|
||||
"source-layer": "places",
|
||||
"filter": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"kind"
|
||||
],
|
||||
[
|
||||
"country"
|
||||
],
|
||||
true,
|
||||
false
|
||||
],
|
||||
"layout": {
|
||||
"text-field": [
|
||||
"get",
|
||||
"name"
|
||||
],
|
||||
"text-size": 15
|
||||
},
|
||||
"paint": {
|
||||
"text-color": "#e2ded8",
|
||||
"text-halo-color": "#1d2024",
|
||||
"text-halo-width": 1.5
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
1000
apps/train/halkyon_train_flutter/test/fixtures/map/train_style_light_de.json
vendored
Normal file
1000
apps/train/halkyon_train_flutter/test/fixtures/map/train_style_light_de.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue