@versatiles/style 5.2.6 → 5.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/dist/index.d.ts +274 -14
  2. package/dist/index.js +3650 -11
  3. package/dist/index.js.map +1 -1
  4. package/package.json +8 -8
  5. package/src/color/abstract.ts +83 -0
  6. package/src/color/hsl.test.ts +182 -0
  7. package/src/color/hsl.ts +122 -0
  8. package/src/color/hsv.test.ts +174 -0
  9. package/src/color/hsv.ts +100 -0
  10. package/src/color/index.test.ts +119 -0
  11. package/src/color/index.ts +38 -0
  12. package/src/color/random.test.ts +35 -0
  13. package/src/color/random.ts +165 -0
  14. package/src/color/rgb.test.ts +227 -0
  15. package/src/color/rgb.ts +248 -0
  16. package/src/color/utils.test.ts +86 -0
  17. package/src/color/utils.ts +13 -0
  18. package/src/guess_style/guess_style.test.ts +134 -0
  19. package/src/guess_style/guess_style.ts +166 -0
  20. package/{dist/guess_style/index.d.ts → src/guess_style/index.ts} +1 -0
  21. package/src/index.test.ts +77 -0
  22. package/src/index.ts +18 -0
  23. package/src/lib/utils.test.ts +197 -0
  24. package/src/lib/utils.ts +134 -0
  25. package/{dist/shortbread/index.d.ts → src/shortbread/index.ts} +1 -0
  26. package/src/shortbread/layers.test.ts +36 -0
  27. package/src/shortbread/layers.ts +564 -0
  28. package/src/shortbread/properties.test.ts +44 -0
  29. package/src/shortbread/properties.ts +142 -0
  30. package/src/shortbread/template.test.ts +43 -0
  31. package/src/shortbread/template.ts +343 -0
  32. package/src/style_builder/decorator.test.ts +67 -0
  33. package/src/style_builder/decorator.ts +135 -0
  34. package/src/style_builder/recolor.test.ts +306 -0
  35. package/src/style_builder/recolor.ts +110 -0
  36. package/src/style_builder/style_builder.test.ts +103 -0
  37. package/src/style_builder/style_builder.ts +134 -0
  38. package/src/style_builder/types.ts +141 -0
  39. package/src/styles/LICENSE.md +41 -0
  40. package/src/styles/colorful.ts +1041 -0
  41. package/src/styles/eclipse.ts +11 -0
  42. package/{dist/styles/empty.d.ts → src/styles/empty.ts} +7 -3
  43. package/src/styles/graybeard.ts +11 -0
  44. package/src/styles/index.ts +33 -0
  45. package/src/styles/neutrino.ts +429 -0
  46. package/{dist/types/index.d.ts → src/types/index.ts} +1 -0
  47. package/{dist/types/maplibre.d.ts → src/types/maplibre.ts} +3 -0
  48. package/src/types/tilejson.test.ts +94 -0
  49. package/src/types/tilejson.ts +125 -0
  50. package/src/types/vector_layer.test.ts +64 -0
  51. package/src/types/vector_layer.ts +69 -0
  52. package/dist/color/abstract.d.ts +0 -34
  53. package/dist/color/abstract.js +0 -53
  54. package/dist/color/abstract.js.map +0 -1
  55. package/dist/color/hsl.d.ts +0 -23
  56. package/dist/color/hsl.js +0 -98
  57. package/dist/color/hsl.js.map +0 -1
  58. package/dist/color/hsv.d.ts +0 -20
  59. package/dist/color/hsv.js +0 -100
  60. package/dist/color/hsv.js.map +0 -1
  61. package/dist/color/index.d.ts +0 -6
  62. package/dist/color/index.js +0 -29
  63. package/dist/color/index.js.map +0 -1
  64. package/dist/color/random.d.ts +0 -9
  65. package/dist/color/random.js +0 -134
  66. package/dist/color/random.js.map +0 -1
  67. package/dist/color/rgb.d.ts +0 -28
  68. package/dist/color/rgb.js +0 -195
  69. package/dist/color/rgb.js.map +0 -1
  70. package/dist/color/utils.d.ts +0 -3
  71. package/dist/color/utils.js +0 -10
  72. package/dist/color/utils.js.map +0 -1
  73. package/dist/guess_style/guess_style.d.ts +0 -8
  74. package/dist/guess_style/guess_style.js +0 -147
  75. package/dist/guess_style/guess_style.js.map +0 -1
  76. package/dist/guess_style/index.js +0 -2
  77. package/dist/guess_style/index.js.map +0 -1
  78. package/dist/lib/utils.d.ts +0 -6
  79. package/dist/lib/utils.js +0 -126
  80. package/dist/lib/utils.js.map +0 -1
  81. package/dist/shortbread/index.js +0 -3
  82. package/dist/shortbread/index.js.map +0 -1
  83. package/dist/shortbread/layers.d.ts +0 -5
  84. package/dist/shortbread/layers.js +0 -521
  85. package/dist/shortbread/layers.js.map +0 -1
  86. package/dist/shortbread/properties.d.ts +0 -7
  87. package/dist/shortbread/properties.js +0 -125
  88. package/dist/shortbread/properties.js.map +0 -1
  89. package/dist/shortbread/template.d.ts +0 -4
  90. package/dist/shortbread/template.js +0 -339
  91. package/dist/shortbread/template.js.map +0 -1
  92. package/dist/style_builder/decorator.d.ts +0 -4
  93. package/dist/style_builder/decorator.js +0 -127
  94. package/dist/style_builder/decorator.js.map +0 -1
  95. package/dist/style_builder/recolor.d.ts +0 -22
  96. package/dist/style_builder/recolor.js +0 -89
  97. package/dist/style_builder/recolor.js.map +0 -1
  98. package/dist/style_builder/style_builder.d.ts +0 -15
  99. package/dist/style_builder/style_builder.js +0 -106
  100. package/dist/style_builder/style_builder.js.map +0 -1
  101. package/dist/style_builder/types.d.ts +0 -122
  102. package/dist/style_builder/types.js +0 -3
  103. package/dist/style_builder/types.js.map +0 -1
  104. package/dist/styles/colorful.d.ts +0 -11
  105. package/dist/styles/colorful.js +0 -956
  106. package/dist/styles/colorful.js.map +0 -1
  107. package/dist/styles/eclipse.d.ts +0 -5
  108. package/dist/styles/eclipse.js +0 -9
  109. package/dist/styles/eclipse.js.map +0 -1
  110. package/dist/styles/empty.js +0 -8
  111. package/dist/styles/empty.js.map +0 -1
  112. package/dist/styles/graybeard.d.ts +0 -5
  113. package/dist/styles/graybeard.js +0 -9
  114. package/dist/styles/graybeard.js.map +0 -1
  115. package/dist/styles/index.d.ts +0 -11
  116. package/dist/styles/index.js +0 -20
  117. package/dist/styles/index.js.map +0 -1
  118. package/dist/styles/neutrino.d.ts +0 -11
  119. package/dist/styles/neutrino.js +0 -401
  120. package/dist/styles/neutrino.js.map +0 -1
  121. package/dist/types/index.js +0 -3
  122. package/dist/types/index.js.map +0 -1
  123. package/dist/types/maplibre.js +0 -2
  124. package/dist/types/maplibre.js.map +0 -1
  125. package/dist/types/tilejson.d.ts +0 -32
  126. package/dist/types/tilejson.js +0 -87
  127. package/dist/types/tilejson.js.map +0 -1
  128. package/dist/types/vector_layer.d.ts +0 -14
  129. package/dist/types/vector_layer.js +0 -51
  130. package/dist/types/vector_layer.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,11 +1,3650 @@
1
- export { colorful, eclipse, graybeard, neutrino } from './styles/index.js';
2
- import { colorful, eclipse, graybeard, neutrino } from './styles/index.js';
3
- export const styles = {
4
- colorful,
5
- eclipse,
6
- graybeard,
7
- neutrino,
8
- };
9
- export { guessStyle } from './guess_style/index.js';
10
- export { Color } from './color/index.js';
11
- //# sourceMappingURL=index.js.map
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.VersaTilesStyle = {}));
5
+ })(this, (function (exports) { 'use strict';
6
+
7
+ class Color {
8
+ static parse;
9
+ static HSL;
10
+ static HSV;
11
+ static RGB;
12
+ static random;
13
+ asHex() {
14
+ return this.toRGB().asHex();
15
+ }
16
+ toHSL() {
17
+ return this.asHSL();
18
+ }
19
+ toHSV() {
20
+ return this.asHSV();
21
+ }
22
+ toRGB() {
23
+ return this.asRGB();
24
+ }
25
+ invertLuminosity() {
26
+ return this.toHSL().invertLuminosity();
27
+ }
28
+ rotateHue(offset) {
29
+ return this.toHSL().rotateHue(offset);
30
+ }
31
+ saturate(ratio) {
32
+ return this.toHSL().saturate(ratio);
33
+ }
34
+ gamma(value) {
35
+ return this.toRGB().gamma(value);
36
+ }
37
+ invert() {
38
+ return this.toRGB().invert();
39
+ }
40
+ contrast(value) {
41
+ return this.toRGB().contrast(value);
42
+ }
43
+ brightness(value) {
44
+ return this.toRGB().brightness(value);
45
+ }
46
+ lighten(value) {
47
+ return this.toRGB().lighten(value);
48
+ }
49
+ darken(value) {
50
+ return this.toRGB().darken(value);
51
+ }
52
+ tint(value, tintColor) {
53
+ return this.toRGB().tint(value, tintColor);
54
+ }
55
+ setHue(value) {
56
+ return this.toHSV().setHue(value);
57
+ }
58
+ }
59
+
60
+ function clamp(num, min, max) {
61
+ return Math.min(Math.max(min, num), max);
62
+ }
63
+ function mod(num, max) {
64
+ return ((num % max) + max) % max;
65
+ }
66
+ function formatFloat(num, precision) {
67
+ return num.toFixed(precision).replace(/0+$/, '').replace(/\.$/, '');
68
+ }
69
+
70
+ class RGB extends Color {
71
+ r = 0; // between 0 and 255
72
+ g = 0; // between 0 and 255
73
+ b = 0; // between 0 and 255
74
+ a = 1; // between 0 and 1
75
+ constructor(r, g, b, a = 1) {
76
+ super();
77
+ this.r = clamp(r, 0, 255);
78
+ this.g = clamp(g, 0, 255);
79
+ this.b = clamp(b, 0, 255);
80
+ this.a = clamp(a, 0, 1);
81
+ }
82
+ clone() {
83
+ return new RGB(this.r, this.g, this.b, this.a);
84
+ }
85
+ asArray() {
86
+ return [this.r, this.g, this.b, this.a];
87
+ }
88
+ round() {
89
+ return new RGB(Math.round(this.r), Math.round(this.g), Math.round(this.b), Math.round(this.a * 1000) / 1000);
90
+ }
91
+ asString() {
92
+ if (this.a === 1) {
93
+ return `rgb(${this.r.toFixed(0)},${this.g.toFixed(0)},${this.b.toFixed(0)})`;
94
+ }
95
+ else {
96
+ return `rgba(${this.r.toFixed(0)},${this.g.toFixed(0)},${this.b.toFixed(0)},${formatFloat(this.a, 3)})`;
97
+ }
98
+ }
99
+ asHex() {
100
+ const r = Math.round(this.r).toString(16).padStart(2, '0');
101
+ const g = Math.round(this.g).toString(16).padStart(2, '0');
102
+ const b = Math.round(this.b).toString(16).padStart(2, '0');
103
+ if (this.a === 1) {
104
+ return `#${r}${g}${b}`.toUpperCase();
105
+ }
106
+ else {
107
+ const a = Math.round(this.a * 255).toString(16).padStart(2, '0');
108
+ return `#${r}${g}${b}${a}`.toUpperCase();
109
+ }
110
+ }
111
+ asHSL() {
112
+ const r = this.r / 255;
113
+ const g = this.g / 255;
114
+ const b = this.b / 255;
115
+ const min = Math.min(r, g, b);
116
+ const max = Math.max(r, g, b);
117
+ const delta = max - min;
118
+ let h = 0;
119
+ let s = 0;
120
+ if (max === min)
121
+ h = 0;
122
+ else if (r === max)
123
+ h = (g - b) / delta;
124
+ else if (g === max)
125
+ h = 2 + (b - r) / delta;
126
+ else if (b === max)
127
+ h = 4 + (r - g) / delta;
128
+ h = Math.min(h * 60, 360);
129
+ if (h < 0)
130
+ h += 360;
131
+ const l = (min + max) / 2;
132
+ if (max === min)
133
+ s = 0;
134
+ else if (l <= 0.5)
135
+ s = delta / (max + min);
136
+ else
137
+ s = delta / (2 - max - min);
138
+ return new HSL(h, s * 100, l * 100, this.a);
139
+ }
140
+ ;
141
+ asHSV() {
142
+ const r = this.r / 255;
143
+ const g = this.g / 255;
144
+ const b = this.b / 255;
145
+ const v = Math.max(r, g, b);
146
+ const diff = v - Math.min(r, g, b);
147
+ let h = 0;
148
+ let s = 0;
149
+ if (diff !== 0) {
150
+ function diffc(c) {
151
+ return (v - c) / 6 / diff + 1 / 2;
152
+ }
153
+ s = diff / v;
154
+ const rdif = diffc(r);
155
+ const gdif = diffc(g);
156
+ const bdif = diffc(b);
157
+ if (r === v)
158
+ h = bdif - gdif;
159
+ else if (g === v)
160
+ h = (1 / 3) + rdif - bdif;
161
+ else if (b === v)
162
+ h = (2 / 3) + gdif - rdif;
163
+ if (h < 0)
164
+ h += 1;
165
+ else if (h > 1)
166
+ h -= 1;
167
+ }
168
+ return new HSV(h * 360, s * 100, v * 100, this.a);
169
+ }
170
+ asRGB() {
171
+ return this.clone();
172
+ }
173
+ toRGB() {
174
+ return this;
175
+ }
176
+ static parse(input) {
177
+ if (input instanceof Color)
178
+ return input.asRGB();
179
+ input = input.toLowerCase().replaceAll(/[^0-9a-z.#,()]/g, '');
180
+ let match;
181
+ match = input.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/);
182
+ if (match) {
183
+ const r = parseInt(match[1], 16);
184
+ const g = parseInt(match[2], 16);
185
+ const b = parseInt(match[3], 16);
186
+ const a = match[4] ? parseInt(match[4], 16) / 255 : 1;
187
+ return new RGB(r, g, b, a);
188
+ }
189
+ match = input.match(/^#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?$/);
190
+ if (match) {
191
+ const r = parseInt(match[1], 16) * 17;
192
+ const g = parseInt(match[2], 16) * 17;
193
+ const b = parseInt(match[3], 16) * 17;
194
+ const a = match[4] ? parseInt(match[4], 16) / 15 : 1;
195
+ return new RGB(r, g, b, a);
196
+ }
197
+ input = input.trim().toLowerCase().replaceAll(' ', '');
198
+ match = input.match(/^rgb\((\d+),(\d+),(\d+)\)$/);
199
+ if (match) {
200
+ const r = parseInt(match[1]);
201
+ const g = parseInt(match[2]);
202
+ const b = parseInt(match[3]);
203
+ return new RGB(r, g, b);
204
+ }
205
+ match = input.match(/^rgba\((\d+),(\d+),(\d+),([.\d]+)\)$/);
206
+ if (match) {
207
+ const r = parseInt(match[1]);
208
+ const g = parseInt(match[2]);
209
+ const b = parseInt(match[3]);
210
+ const a = parseFloat(match[4]);
211
+ return new RGB(r, g, b, a);
212
+ }
213
+ throw new Error(`Invalid RGB color string: "${input}"`);
214
+ }
215
+ gamma(value) {
216
+ if (value < 1e-3)
217
+ value = 1e-3;
218
+ if (value > 1e3)
219
+ value = 1e3;
220
+ return new RGB(Math.pow(this.r / 255, value) * 255, Math.pow(this.g / 255, value) * 255, Math.pow(this.b / 255, value) * 255, this.a);
221
+ }
222
+ invert() {
223
+ return new RGB(255 - this.r, 255 - this.g, 255 - this.b, this.a);
224
+ }
225
+ contrast(value) {
226
+ if (value < 0)
227
+ value = 0;
228
+ if (value > 1e6)
229
+ value = 1e6;
230
+ return new RGB(clamp((this.r - 127.5) * value + 127.5, 0, 255), clamp((this.g - 127.5) * value + 127.5, 0, 255), clamp((this.b - 127.5) * value + 127.5, 0, 255), this.a);
231
+ }
232
+ brightness(value) {
233
+ if (value < -1)
234
+ value = -1;
235
+ if (value > 1)
236
+ value = 1;
237
+ const a = 1 - Math.abs(value);
238
+ const b = (value < 0) ? 0 : 255 * value;
239
+ return new RGB(this.r * a + b, this.g * a + b, this.b * a + b, this.a);
240
+ }
241
+ tint(value, tintColor) {
242
+ if (value < 0)
243
+ value = 0;
244
+ if (value > 1)
245
+ value = 1;
246
+ const rgbNew = this.setHue(tintColor.toHSV().h).toRGB();
247
+ return new RGB(this.r * (1 - value) + value * rgbNew.r, this.g * (1 - value) + value * rgbNew.g, this.b * (1 - value) + value * rgbNew.b, this.a);
248
+ }
249
+ lighten(ratio) {
250
+ return new RGB(clamp(255 - (255 - this.r) * (1 - ratio), 0, 255), clamp(255 - (255 - this.g) * (1 - ratio), 0, 255), clamp(255 - (255 - this.b) * (1 - ratio), 0, 255), this.a);
251
+ }
252
+ darken(ratio) {
253
+ return new RGB(clamp(this.r * (1 - ratio), 0, 255), clamp(this.g * (1 - ratio), 0, 255), clamp(this.b * (1 - ratio), 0, 255), this.a);
254
+ }
255
+ fade(value) {
256
+ return new RGB(this.r, this.g, this.b, this.a * (1 - value));
257
+ }
258
+ }
259
+
260
+ class HSV extends Color {
261
+ h = 0; // between 0 and 360
262
+ s = 0; // between 0 and 100
263
+ v = 0; // between 0 and 100
264
+ a = 1; // between 0 and 1
265
+ constructor(h, s, v, a = 1) {
266
+ super();
267
+ this.h = mod(h, 360);
268
+ this.s = clamp(s, 0, 100);
269
+ this.v = clamp(v, 0, 100);
270
+ this.a = clamp(a, 0, 1);
271
+ }
272
+ asArray() {
273
+ return [this.h, this.s, this.v, this.a];
274
+ }
275
+ round() {
276
+ return new HSV(Math.round(this.h), Math.round(this.s), Math.round(this.v), Math.round(this.a * 1000) / 1000);
277
+ }
278
+ asString() {
279
+ return this.asHSL().asString();
280
+ }
281
+ clone() {
282
+ return new HSV(this.h, this.s, this.v, this.a);
283
+ }
284
+ asHSL() {
285
+ const s = this.s / 100;
286
+ const v = this.v / 100;
287
+ const k = (2 - s) * v;
288
+ const q = k < 1 ? k : 2 - k;
289
+ return new HSL(this.h, q == 0 ? 0 : 100 * s * v / q, 100 * k / 2, this.a);
290
+ }
291
+ asHSV() {
292
+ return this.clone();
293
+ }
294
+ toHSV() {
295
+ return this;
296
+ }
297
+ asRGB() {
298
+ const h = this.h / 360; // Normalize h to range [0, 1]
299
+ const s = this.s / 100; // Normalize s to range [0, 1]
300
+ const v = this.v / 100; // Normalize v to range [0, 1]
301
+ let r = 0, g = 0, b = 0;
302
+ if (s === 0) {
303
+ // Achromatic (grey)
304
+ r = g = b = v;
305
+ }
306
+ else {
307
+ const i = Math.floor(h * 6); // Determine the sector of the color wheel
308
+ const f = h * 6 - i; // Fractional part of h * 6
309
+ const p = v * (1 - s);
310
+ const q = v * (1 - s * f);
311
+ const t = v * (1 - s * (1 - f));
312
+ switch (i % 6) {
313
+ case 0:
314
+ r = v;
315
+ g = t;
316
+ b = p;
317
+ break;
318
+ case 1:
319
+ r = q;
320
+ g = v;
321
+ b = p;
322
+ break;
323
+ case 2:
324
+ r = p;
325
+ g = v;
326
+ b = t;
327
+ break;
328
+ case 3:
329
+ r = p;
330
+ g = q;
331
+ b = v;
332
+ break;
333
+ case 4:
334
+ r = t;
335
+ g = p;
336
+ b = v;
337
+ break;
338
+ case 5:
339
+ r = v;
340
+ g = p;
341
+ b = q;
342
+ break;
343
+ }
344
+ }
345
+ // Convert to RGB in the 0-255 range and return
346
+ return new RGB(r * 255, g * 255, b * 255, this.a);
347
+ }
348
+ fade(value) {
349
+ return new HSV(this.h, this.s, this.v, this.a * (1 - value));
350
+ }
351
+ setHue(value) {
352
+ return new HSV(value, this.s, this.v, this.a);
353
+ }
354
+ }
355
+
356
+ class HSL extends Color {
357
+ h = 0; // between 0 and 360
358
+ s = 0; // between 0 and 100
359
+ l = 0; // between 0 and 100
360
+ a = 1; // between 0 and 1
361
+ constructor(h, s, l, a = 1) {
362
+ super();
363
+ this.h = mod(h, 360);
364
+ this.s = clamp(s, 0, 100);
365
+ this.l = clamp(l, 0, 100);
366
+ this.a = clamp(a, 0, 1);
367
+ }
368
+ asArray() {
369
+ return [this.h, this.s, this.l, this.a];
370
+ }
371
+ round() {
372
+ return new HSL(Math.round(this.h), Math.round(this.s), Math.round(this.l), Math.round(this.a * 1000) / 1000);
373
+ }
374
+ clone() {
375
+ return new HSL(this.h, this.s, this.l, this.a);
376
+ }
377
+ asString() {
378
+ if (this.a === 1) {
379
+ return `hsl(${this.h.toFixed(0)},${this.s.toFixed(0)}%,${this.l.toFixed(0)}%)`;
380
+ }
381
+ else {
382
+ return `hsla(${this.h.toFixed(0)},${this.s.toFixed(0)}%,${this.l.toFixed(0)}%,${formatFloat(this.a, 3)})`;
383
+ }
384
+ }
385
+ asHSL() {
386
+ return this.clone();
387
+ }
388
+ toHSL() {
389
+ return this;
390
+ }
391
+ asHSV() {
392
+ const s = this.s / 100, l = this.l / 100;
393
+ const v = l + s * Math.min(l, 1 - l);
394
+ const sv = v === 0 ? 0 : 2 * (1 - l / v);
395
+ return new HSV(this.h, sv * 100, v * 100, this.a);
396
+ }
397
+ asRGB() {
398
+ const h = this.h / 360;
399
+ const s = this.s / 100;
400
+ const l = this.l / 100;
401
+ // Achromatic (grey)
402
+ if (s === 0)
403
+ return new RGB(l * 255, l * 255, l * 255, this.a);
404
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
405
+ const p = 2 * l - q;
406
+ const hueToRgb = (t) => {
407
+ if (t < 0)
408
+ t += 1;
409
+ if (t > 1)
410
+ t -= 1;
411
+ if (t < 1 / 6)
412
+ return p + (q - p) * 6 * t;
413
+ if (t < 1 / 2)
414
+ return q;
415
+ if (t < 2 / 3)
416
+ return p + (q - p) * (2 / 3 - t) * 6;
417
+ return p;
418
+ };
419
+ // Convert to RGB in the 0-255 range and return
420
+ return new RGB(255 * hueToRgb(h + 1 / 3), 255 * hueToRgb(h), 255 * hueToRgb(h - 1 / 3), this.a);
421
+ }
422
+ static parse(input) {
423
+ if (input instanceof Color)
424
+ return input.asHSL();
425
+ input = input.replace(/\s+/g, '').toLowerCase();
426
+ let match = input.match(/^hsl\((?<h>[-+0-9.]+)(?:deg)?,(?<s>[-+0-9.]+)%,(?<l>[-+0-9.]+)%\)$/);
427
+ if (match) {
428
+ return new HSL(parseFloat(match.groups.h), parseFloat(match.groups.s), parseFloat(match.groups.l));
429
+ }
430
+ match = input.match(/^hsla\((?<h>[-+0-9.]+)(?:deg)?,(?<s>[-+0-9.]+)%,(?<l>[-+0-9.]+)%,(?<a>[-+0-9.]+)\)$/);
431
+ if (match) {
432
+ return new HSL(parseFloat(match.groups.h), parseFloat(match.groups.s), parseFloat(match.groups.l), parseFloat(match.groups.a));
433
+ }
434
+ throw new Error(`Invalid HSL color string: "${input}"`);
435
+ }
436
+ invertLuminosity() {
437
+ return new HSL(this.h, this.s, 100 - this.l, this.a);
438
+ }
439
+ rotateHue(offset) {
440
+ return new HSL(mod(this.h + offset, 360), this.s, this.l, this.a);
441
+ }
442
+ saturate(ratio) {
443
+ return new HSL(this.h, clamp(this.s * (1 + ratio), 0, 100), this.l, this.a);
444
+ }
445
+ fade(value) {
446
+ return new HSL(this.h, this.s, this.l, this.a * (1 - value));
447
+ }
448
+ }
449
+
450
+ let colorDictionary = new Map();
451
+ function randomColor(options) {
452
+ if (colorDictionary.size === 0)
453
+ colorDictionary = initColorDictionary();
454
+ options ??= {};
455
+ let seed = inputToSeed(options.seed);
456
+ const H = pickHue(options);
457
+ const S = pickSaturation(H, options);
458
+ const V = pickBrightness(H, S, options);
459
+ return new HSV(H, S, V, options.opacity ?? 1);
460
+ function pickHue(options) {
461
+ return mod(randomWithin(getHueRange(options.hue)), 360);
462
+ function getHueRange(hue) {
463
+ if (typeof hue === 'number') {
464
+ hue = mod(hue, 360);
465
+ return [hue, hue];
466
+ }
467
+ if (typeof hue === 'string') {
468
+ const color = colorDictionary.get(hue);
469
+ if (color?.hueRange)
470
+ return color.hueRange;
471
+ }
472
+ return [0, 360];
473
+ }
474
+ }
475
+ function pickSaturation(hue, options) {
476
+ if (options.hue === 'monochrome')
477
+ return 0;
478
+ if (options.luminosity === 'random')
479
+ return randomWithin([0, 100]);
480
+ let [sMin, sMax] = getColorInfo(hue).saturationRange;
481
+ if (options.saturation === 'strong')
482
+ return sMax;
483
+ switch (options.luminosity) {
484
+ case 'bright':
485
+ sMin = 55;
486
+ break;
487
+ case 'dark':
488
+ sMin = sMax - 10;
489
+ break;
490
+ case 'light':
491
+ sMax = 55;
492
+ break;
493
+ }
494
+ return randomWithin([sMin, sMax]);
495
+ }
496
+ function pickBrightness(h, s, options) {
497
+ let bMin = getMinimumBrightness(h, s), bMax = 100;
498
+ if (typeof options.luminosity === 'number') {
499
+ bMin = options.luminosity;
500
+ bMax = options.luminosity;
501
+ }
502
+ else {
503
+ switch (options.luminosity) {
504
+ case 'dark':
505
+ bMax = Math.min(100, bMin + 20);
506
+ break;
507
+ case 'light':
508
+ bMin = (bMax + bMin) / 2;
509
+ break;
510
+ case 'random':
511
+ bMin = 0;
512
+ bMax = 100;
513
+ break;
514
+ }
515
+ }
516
+ return randomWithin([bMin, bMax]);
517
+ function getMinimumBrightness(h, s) {
518
+ const { lowerBounds } = getColorInfo(h);
519
+ for (let i = 0; i < lowerBounds.length - 1; i++) {
520
+ const [s1, v1] = lowerBounds[i];
521
+ const [s2, v2] = lowerBounds[i + 1];
522
+ if (s >= s1 && s <= s2) {
523
+ const m = (v2 - v1) / (s2 - s1), b = v1 - m * s1;
524
+ return m * s + b;
525
+ }
526
+ }
527
+ return 0;
528
+ }
529
+ }
530
+ function randomWithin(range) {
531
+ //Seeded random algorithm from http://indiegamr.com/generate-repeatable-random-numbers-in-js/
532
+ seed = (seed * 9301 + 49297) % 233280;
533
+ return Math.floor(range[0] + seed / 233280.0 * (range[1] - range[0]));
534
+ }
535
+ }
536
+ function inputToSeed(input) {
537
+ if (input == null)
538
+ return 0;
539
+ if (typeof input === 'number')
540
+ return input;
541
+ let i = 0;
542
+ for (let p = 0; p < input.length; p++)
543
+ i = (i * 0x101 + input.charCodeAt(p)) % 0x100000000;
544
+ return i;
545
+ }
546
+ function initColorDictionary() {
547
+ const dict = new Map();
548
+ const defineColor = (name, hueRange, lowerBounds) => {
549
+ const [greyest] = lowerBounds;
550
+ const colorful = lowerBounds[lowerBounds.length - 1];
551
+ dict.set(name, {
552
+ hueRange,
553
+ lowerBounds,
554
+ saturationRange: [greyest[0], colorful[0]],
555
+ brightnessRange: [colorful[1], greyest[1]],
556
+ });
557
+ };
558
+ defineColor('monochrome', null, [[0, 0], [100, 0]]);
559
+ defineColor('red', [-26, 18], [[20, 100], [30, 92], [40, 89], [50, 85], [60, 78], [70, 70], [80, 60], [90, 55], [100, 50]]);
560
+ defineColor('orange', [18, 46], [[20, 100], [30, 93], [40, 88], [50, 86], [60, 85], [70, 70], [100, 70]]);
561
+ defineColor('yellow', [46, 62], [[25, 100], [40, 94], [50, 89], [60, 86], [70, 84], [80, 82], [90, 80], [100, 75]]);
562
+ defineColor('green', [62, 178], [[30, 100], [40, 90], [50, 85], [60, 81], [70, 74], [80, 64], [90, 50], [100, 40]]);
563
+ defineColor('blue', [178, 257], [[20, 100], [30, 86], [40, 80], [50, 74], [60, 60], [70, 52], [80, 44], [90, 39], [100, 35]]);
564
+ defineColor('purple', [257, 282], [[20, 100], [30, 87], [40, 79], [50, 70], [60, 65], [70, 59], [80, 52], [90, 45], [100, 42]]);
565
+ defineColor('pink', [282, 334], [[20, 100], [30, 90], [40, 86], [60, 84], [80, 80], [90, 75], [100, 73]]);
566
+ return dict;
567
+ }
568
+ function getColorInfo(hue) {
569
+ hue = mod(hue, 360);
570
+ if (hue >= 334)
571
+ hue -= 360;
572
+ for (const color of colorDictionary.values()) {
573
+ if (color.hueRange && hue >= color.hueRange[0] && hue <= color.hueRange[1]) {
574
+ return color;
575
+ }
576
+ }
577
+ throw Error('Color hue value not found');
578
+ }
579
+
580
+ Color.parse = function (input) {
581
+ if (input instanceof Color)
582
+ return input;
583
+ input = input.trim().toLowerCase();
584
+ if (input.startsWith('#'))
585
+ return RGB.parse(input);
586
+ const prefix = input.replace(/\d.*/, '').trim().toLowerCase();
587
+ switch (prefix) {
588
+ case 'rgb(':
589
+ case 'rgba(':
590
+ return RGB.parse(input);
591
+ case 'hsl(':
592
+ case 'hsla(':
593
+ return HSL.parse(input);
594
+ default:
595
+ throw Error('Unknown color format: ' + input);
596
+ }
597
+ };
598
+ Color.HSL = HSL;
599
+ Color.HSV = HSV;
600
+ Color.RGB = RGB;
601
+ Color.random = randomColor;
602
+
603
+ const maxzoom = 14;
604
+ function getShortbreadTemplate() {
605
+ return {
606
+ version: 8,
607
+ name: 'versatiles',
608
+ metadata: {
609
+ license: 'https://creativecommons.org/publicdomain/zero/1.0/',
610
+ },
611
+ glyphs: 'https://tiles.versatiles.org/assets/glyphs/{fontstack}/{range}.pbf',
612
+ sprite: [{ id: 'basics', url: 'https://tiles.versatiles.org/assets/sprites/basics/sprites' }],
613
+ sources: {
614
+ 'versatiles-shortbread': {
615
+ attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
616
+ tiles: [
617
+ 'https://tiles.versatiles.org/tiles/osm/{z}/{x}/{y}',
618
+ ],
619
+ type: 'vector',
620
+ scheme: 'xyz',
621
+ bounds: [
622
+ -180,
623
+ -85.0511287798066,
624
+ 180,
625
+ 85.0511287798066,
626
+ ],
627
+ minzoom: 0,
628
+ maxzoom
629
+ },
630
+ },
631
+ layers: [],
632
+ };
633
+ }
634
+
635
+ function getShortbreadLayers(option) {
636
+ const { language } = option;
637
+ const nameField = language ? '{name_' + language + '}' : '{name}';
638
+ return [
639
+ // background
640
+ { id: 'background', type: 'background' },
641
+ // ocean
642
+ { id: 'water-ocean', type: 'fill', 'source-layer': 'ocean' },
643
+ // land
644
+ {
645
+ id: 'land-glacier',
646
+ type: 'fill', 'source-layer': 'water_polygons',
647
+ filter: ['all', ['==', 'kind', 'glacier']],
648
+ },
649
+ ...[
650
+ { id: 'commercial', kinds: ['commercial', 'retail'] },
651
+ { id: 'industrial', kinds: ['industrial', 'quarry', 'railway'] },
652
+ { id: 'residential', kinds: ['garages', 'residential'] },
653
+ { id: 'agriculture', kinds: ['brownfield', 'farmland', 'farmyard', 'greenfield', 'greenhouse_horticulture', 'orchard', 'plant_nursery', 'vineyard'] },
654
+ { id: 'waste', kinds: ['landfill'] },
655
+ { id: 'park', kinds: ['park', 'village_green', 'recreation_ground'] },
656
+ { id: 'garden', kinds: ['allotments', 'garden'] },
657
+ { id: 'burial', kinds: ['cemetery', 'grave_yard'] },
658
+ { id: 'leisure', kinds: ['miniature_golf', 'playground', 'golf_course'] },
659
+ { id: 'rock', kinds: ['bare_rock', 'scree', 'shingle'] },
660
+ { id: 'forest', kinds: ['forest'] },
661
+ { id: 'grass', kinds: ['grass', 'grassland', 'meadow', 'wet_meadow'] },
662
+ { id: 'vegetation', kinds: ['heath', 'scrub'] },
663
+ { id: 'sand', kinds: ['beach', 'sand'] },
664
+ { id: 'wetland', kinds: ['bog', 'marsh', 'string_bog', 'swamp'] },
665
+ ].map(({ id, kinds }) => ({
666
+ id: 'land-' + id,
667
+ type: 'fill',
668
+ 'source-layer': 'land',
669
+ filter: ['all',
670
+ ['in', 'kind', ...kinds],
671
+ ],
672
+ })),
673
+ // water-lines
674
+ ...['river', 'canal', 'stream', 'ditch'].map((t) => ({
675
+ id: 'water-' + t,
676
+ type: 'line',
677
+ 'source-layer': 'water_lines',
678
+ filter: ['all',
679
+ ['in', 'kind', t],
680
+ ['!=', 'tunnel', true],
681
+ ['!=', 'bridge', true],
682
+ ],
683
+ })),
684
+ // water polygons
685
+ {
686
+ id: 'water-area',
687
+ type: 'fill', 'source-layer': 'water_polygons',
688
+ filter: ['==', 'kind', 'water'],
689
+ },
690
+ {
691
+ id: 'water-area-river',
692
+ type: 'fill', 'source-layer': 'water_polygons',
693
+ filter: ['==', 'kind', 'river'],
694
+ },
695
+ {
696
+ id: 'water-area-small',
697
+ type: 'fill', 'source-layer': 'water_polygons',
698
+ filter: ['in', 'kind', 'reservoir', 'basin', 'dock'],
699
+ },
700
+ // dam
701
+ { id: 'water-dam-area', type: 'fill', 'source-layer': 'dam_polygons', filter: ['==', 'kind', 'dam'] },
702
+ { id: 'water-dam', type: 'line', 'source-layer': 'dam_lines', filter: ['==', 'kind', 'dam'] },
703
+ // pier
704
+ { id: 'water-pier-area', type: 'fill', 'source-layer': 'pier_polygons', filter: ['in', 'kind', 'pier', 'breakwater', 'groyne'] },
705
+ { id: 'water-pier', type: 'line', 'source-layer': 'pier_lines', filter: ['in', 'kind', 'pier', 'breakwater', 'groyne'] },
706
+ // site
707
+ ...['danger_area', 'sports_center', 'university', 'college', 'school', 'hospital', 'prison', 'parking', 'bicycle_parking', 'construction'].map((t) => ({
708
+ id: 'site-' + t.replace(/_/g, ''),
709
+ type: 'fill',
710
+ 'source-layer': 'sites',
711
+ filter: ['in', 'kind', t],
712
+ })),
713
+ // airport
714
+ {
715
+ id: 'airport-area',
716
+ type: 'fill', 'source-layer': 'street_polygons', filter: ['in', 'kind', 'runway', 'taxiway'],
717
+ },
718
+ {
719
+ id: 'airport-taxiway:outline',
720
+ type: 'line', 'source-layer': 'streets', filter: ['==', 'kind', 'taxiway'],
721
+ },
722
+ {
723
+ id: 'airport-runway:outline',
724
+ type: 'line', 'source-layer': 'streets', filter: ['==', 'kind', 'runway'],
725
+ },
726
+ {
727
+ id: 'airport-taxiway',
728
+ type: 'line', 'source-layer': 'streets', filter: ['==', 'kind', 'taxiway'],
729
+ },
730
+ {
731
+ id: 'airport-runway',
732
+ type: 'line', 'source-layer': 'streets', filter: ['==', 'kind', 'runway'],
733
+ },
734
+ // building
735
+ {
736
+ id: 'building:outline',
737
+ type: 'fill', 'source-layer': 'buildings',
738
+ },
739
+ {
740
+ id: 'building',
741
+ type: 'fill', 'source-layer': 'buildings',
742
+ },
743
+ // tunnel-, street-, bridges-bridge
744
+ ...['tunnel', 'street', 'bridge'].flatMap((c) => {
745
+ let filter;
746
+ let prefix;
747
+ let suffixes = [];
748
+ const results = [];
749
+ switch (c) {
750
+ case 'tunnel':
751
+ filter = [['==', 'tunnel', true]];
752
+ prefix = 'tunnel-';
753
+ suffixes = [':outline', ''];
754
+ break;
755
+ case 'street':
756
+ filter = [['!=', 'bridge', true], ['!=', 'tunnel', true]];
757
+ prefix = '';
758
+ suffixes = [':outline', ''];
759
+ break;
760
+ case 'bridge':
761
+ filter = [['==', 'bridge', true]];
762
+ prefix = 'bridge-';
763
+ suffixes = [':bridge', ':outline', ''];
764
+ break;
765
+ }
766
+ // in osm data streets on bridges are often not tagged as such
767
+ // to be able to have multiple levels of bridges cross over each
768
+ // other in the right order without using a secondary property.
769
+ // this results in bridge-polygons being rendered above streets.
770
+ // therefore bridge polygons are *under* surface streets here.
771
+ // this workaround is also wrong, but everyone is using it since
772
+ // it's simpler than removing all these tagging hacks from osm.
773
+ // bridges, above tunnel, below street
774
+ if (c === 'street')
775
+ results.push({
776
+ id: 'bridge',
777
+ type: 'fill',
778
+ 'source-layer': 'bridges',
779
+ });
780
+ suffixes.forEach(suffix => {
781
+ // pedestrian zone — no outline
782
+ if (suffix === ':outline')
783
+ results.push({
784
+ id: prefix + 'street-pedestrian-zone',
785
+ type: 'fill',
786
+ 'source-layer': 'street_polygons',
787
+ filter: ['all',
788
+ ...filter,
789
+ ['==', 'kind', 'pedestrian'],
790
+ ],
791
+ });
792
+ // non-car streets
793
+ ['footway', 'steps', 'path', 'cycleway'].forEach(t => {
794
+ results.push({
795
+ id: prefix + 'way-' + t.replace(/_/g, '') + suffix,
796
+ type: 'line',
797
+ 'source-layer': 'streets',
798
+ filter: ['all',
799
+ ...filter,
800
+ ['in', 'kind', t],
801
+ ],
802
+ });
803
+ });
804
+ // no links
805
+ const noDrivewayExpression = ['!=', 'service', 'driveway'];
806
+ ['track', 'pedestrian', 'service', 'living_street', 'residential', 'unclassified'].forEach(t => {
807
+ results.push({
808
+ id: prefix + 'street-' + t.replace(/_/g, '') + suffix,
809
+ type: 'line',
810
+ 'source-layer': 'streets',
811
+ filter: ['all',
812
+ ['==', 'kind', t],
813
+ ...filter,
814
+ ...(t === 'service') ? [noDrivewayExpression] : [], // ignore driveways
815
+ ],
816
+ });
817
+ });
818
+ // no links, bicycle=designated
819
+ if (suffix === '')
820
+ ['track', 'pedestrian', 'service', 'living_street', 'residential', 'unclassified'].forEach(t => {
821
+ results.push({
822
+ id: prefix + 'street-' + t.replace(/_/g, '') + '-bicycle',
823
+ type: 'line',
824
+ 'source-layer': 'streets',
825
+ filter: ['all',
826
+ ['==', 'kind', t],
827
+ ['==', 'bicycle', 'designated'],
828
+ ...filter,
829
+ ...(t === 'service') ? [noDrivewayExpression] : [], // ignore driveways
830
+ ],
831
+ });
832
+ });
833
+ // links
834
+ ['tertiary', 'secondary', 'primary', 'trunk', 'motorway'].forEach(t => {
835
+ results.push({
836
+ id: prefix + 'street-' + t.replace(/_/g, '') + '-link' + suffix,
837
+ type: 'line',
838
+ 'source-layer': 'streets',
839
+ filter: ['all',
840
+ ...filter,
841
+ ['in', 'kind', t],
842
+ ['==', 'link', true],
843
+ ],
844
+ });
845
+ });
846
+ // main
847
+ ['tertiary', 'secondary', 'primary', 'trunk', 'motorway'].forEach(t => {
848
+ results.push({
849
+ id: prefix + 'street-' + t.replace(/_/g, '') + suffix,
850
+ type: 'line',
851
+ 'source-layer': 'streets',
852
+ filter: ['all',
853
+ ...filter,
854
+ ['in', 'kind', t],
855
+ ['!=', 'link', true],
856
+ ],
857
+ });
858
+ });
859
+ });
860
+ // separate outline for trains
861
+ [':outline', ''].forEach(suffix => {
862
+ // transport
863
+ ['rail', 'light_rail', 'subway', 'narrow_gauge', 'tram', 'funicular', 'monorail', 'bus_guideway', 'busway'].reverse().forEach((t) => {
864
+ results.push({
865
+ id: prefix + 'transport-' + t.replace(/_/g, '') + suffix,
866
+ type: 'line',
867
+ 'source-layer': 'streets',
868
+ filter: ['all',
869
+ ['in', 'kind', t],
870
+ ['!has', 'service'],
871
+ ...filter,
872
+ ],
873
+ });
874
+ });
875
+ if (c === 'street') {
876
+ // aerialway, no bridges, above street evel
877
+ ['cable_car', 'gondola', 'goods', 'chair_lift', 'drag_lift', 't-bar', 'j-bar', 'platter', 'rope-tow'].reverse().forEach((t) => {
878
+ results.push({
879
+ id: 'aerialway-' + t.replace(/[_-]+/g, '') + suffix,
880
+ type: 'line',
881
+ 'source-layer': 'aerialways',
882
+ filter: ['all',
883
+ ...filter,
884
+ ['in', 'kind', t],
885
+ ],
886
+ });
887
+ });
888
+ // ferry — only on street level
889
+ results.push({
890
+ id: 'transport-ferry' + suffix,
891
+ type: 'line',
892
+ 'source-layer': 'ferries',
893
+ });
894
+ }
895
+ });
896
+ return results;
897
+ }),
898
+ // poi, one layer per type
899
+ ...['amenity', 'leisure', 'tourism', 'shop', 'man_made', 'historic', 'emergency', 'highway', 'office'].map((key) => ({
900
+ id: 'poi-' + key,
901
+ type: 'symbol',
902
+ 'source-layer': 'pois',
903
+ filter: ['to-boolean', ['get', key]],
904
+ })),
905
+ // boundary
906
+ ...[':outline', ''].flatMap((suffix) => [
907
+ {
908
+ id: 'boundary-country' + suffix,
909
+ type: 'line',
910
+ 'source-layer': 'boundaries',
911
+ filter: ['all',
912
+ ['==', 'admin_level', 2],
913
+ ['!=', 'maritime', true],
914
+ ['!=', 'disputed', true],
915
+ ['!=', 'coastline', true],
916
+ ],
917
+ },
918
+ {
919
+ id: 'boundary-country-disputed' + suffix,
920
+ type: 'line',
921
+ 'source-layer': 'boundaries',
922
+ filter: ['all',
923
+ ['==', 'admin_level', 2],
924
+ ['==', 'disputed', true],
925
+ ['!=', 'maritime', true],
926
+ ['!=', 'coastline', true],
927
+ ],
928
+ },
929
+ {
930
+ id: 'boundary-country-maritime' + suffix,
931
+ type: 'line',
932
+ 'source-layer': 'boundaries',
933
+ filter: ['all',
934
+ ['==', 'admin_level', 2],
935
+ ['==', 'maritime', true],
936
+ ['!=', 'disputed', true],
937
+ ['!=', 'coastline', true],
938
+ ],
939
+ },
940
+ {
941
+ id: 'boundary-state' + suffix,
942
+ type: 'line',
943
+ 'source-layer': 'boundaries',
944
+ filter: ['all',
945
+ ['==', 'admin_level', 4],
946
+ ['!=', 'maritime', true],
947
+ ['!=', 'disputed', true],
948
+ ['!=', 'coastline', true],
949
+ ],
950
+ },
951
+ ]),
952
+ // label-address
953
+ {
954
+ id: 'label-address-housenumber',
955
+ type: 'symbol',
956
+ 'source-layer': 'addresses',
957
+ filter: ['has', 'housenumber'],
958
+ layout: { 'text-field': '{housenumber}' },
959
+ },
960
+ // label-motorway
961
+ {
962
+ id: 'label-motorway-exit',
963
+ type: 'symbol',
964
+ 'source-layer': 'street_labels_points', // docs say `streets_labels_points`, but layer is actually called `street_labels_points`
965
+ filter: ['==', 'kind', 'motorway_junction'],
966
+ layout: { 'text-field': '{ref}' },
967
+ // FIXME shield
968
+ },
969
+ {
970
+ id: 'label-motorway-shield',
971
+ type: 'symbol',
972
+ 'source-layer': 'street_labels',
973
+ filter: ['==', 'kind', 'motorway'],
974
+ layout: { 'text-field': '{ref}' },
975
+ // FIXME shield
976
+ },
977
+ // label-street
978
+ ...['pedestrian', 'living_street', 'residential', 'unclassified', 'tertiary', 'secondary', 'primary', 'trunk'].map((t) => ({
979
+ id: 'label-street-' + t.replace(/_/g, ''),
980
+ type: 'symbol',
981
+ 'source-layer': 'street_labels',
982
+ filter: ['==', 'kind', t],
983
+ layout: { 'text-field': nameField },
984
+ })),
985
+ // label-place of small places
986
+ ...[/*'locality', 'island', 'farm', 'dwelling',*/ 'neighbourhood', 'quarter', 'suburb', 'hamlet', 'village', 'town'].map((id) => ({
987
+ id: 'label-place-' + id.replace(/_/g, ''),
988
+ type: 'symbol',
989
+ 'source-layer': 'place_labels',
990
+ filter: ['==', 'kind', id],
991
+ layout: { 'text-field': nameField },
992
+ })),
993
+ // label-boundary
994
+ {
995
+ id: 'label-boundary-state',
996
+ type: 'symbol',
997
+ 'source-layer': 'boundary_labels',
998
+ filter: ['in', 'admin_level', 4, '4'],
999
+ layout: { 'text-field': nameField },
1000
+ },
1001
+ // label-place-* of large places
1002
+ ...['city', 'state_capital', 'capital'].map((id) => ({
1003
+ id: 'label-place-' + id.replace(/_/g, ''),
1004
+ type: 'symbol',
1005
+ 'source-layer': 'place_labels',
1006
+ filter: ['==', 'kind', id],
1007
+ layout: { 'text-field': nameField },
1008
+ })),
1009
+ {
1010
+ id: 'label-boundary-country-small',
1011
+ type: 'symbol',
1012
+ 'source-layer': 'boundary_labels',
1013
+ filter: ['all',
1014
+ ['in', 'admin_level', 2, '2'],
1015
+ ['<=', 'way_area', 10000000],
1016
+ ],
1017
+ layout: { 'text-field': nameField },
1018
+ },
1019
+ {
1020
+ id: 'label-boundary-country-medium',
1021
+ type: 'symbol',
1022
+ 'source-layer': 'boundary_labels',
1023
+ filter: ['all',
1024
+ ['in', 'admin_level', 2, '2'],
1025
+ ['<', 'way_area', 90000000],
1026
+ ['>', 'way_area', 10000000],
1027
+ ],
1028
+ layout: { 'text-field': nameField },
1029
+ },
1030
+ {
1031
+ id: 'label-boundary-country-large',
1032
+ type: 'symbol',
1033
+ 'source-layer': 'boundary_labels',
1034
+ filter: ['all',
1035
+ ['in', 'admin_level', 2, '2'],
1036
+ ['>=', 'way_area', 90000000],
1037
+ ],
1038
+ layout: { 'text-field': nameField },
1039
+ },
1040
+ // marking
1041
+ {
1042
+ id: 'marking-oneway', // streets → oneway
1043
+ type: 'symbol',
1044
+ 'source-layer': 'streets',
1045
+ filter: ['all',
1046
+ ['==', 'oneway', true],
1047
+ ['in', 'kind', 'trunk', 'primary', 'secondary', 'tertiary', 'unclassified', 'residential', 'living_street'],
1048
+ ],
1049
+ layout: {
1050
+ 'symbol-placement': 'line',
1051
+ 'symbol-spacing': 175,
1052
+ 'icon-rotate': 90,
1053
+ 'icon-rotation-alignment': 'map',
1054
+ 'icon-padding': 5,
1055
+ 'symbol-avoid-edges': true,
1056
+ },
1057
+ },
1058
+ {
1059
+ id: 'marking-oneway-reverse', // oneway_reverse
1060
+ type: 'symbol',
1061
+ 'source-layer': 'streets',
1062
+ filter: ['all',
1063
+ ['==', 'oneway_reverse', true],
1064
+ ['in', 'kind', 'trunk', 'primary', 'secondary', 'tertiary', 'unclassified', 'residential', 'living_street'],
1065
+ ],
1066
+ layout: {
1067
+ 'symbol-placement': 'line',
1068
+ 'symbol-spacing': 75,
1069
+ 'icon-rotate': -90,
1070
+ 'icon-rotation-alignment': 'map',
1071
+ 'icon-padding': 5,
1072
+ 'symbol-avoid-edges': true,
1073
+ },
1074
+ },
1075
+ {
1076
+ id: 'marking-bicycle', // bicycle=designated or kind=cycleway
1077
+ type: 'symbol',
1078
+ 'source-layer': 'streets',
1079
+ filter: ['all',
1080
+ ['==', 'bicycle', 'designated'],
1081
+ ['==', 'kind', 'cycleway'],
1082
+ ],
1083
+ layout: {
1084
+ 'symbol-placement': 'line',
1085
+ 'symbol-spacing': 50,
1086
+ },
1087
+ },
1088
+ // symbol
1089
+ {
1090
+ id: 'symbol-transit-bus',
1091
+ type: 'symbol',
1092
+ 'source-layer': 'public_transport',
1093
+ filter: ['==', 'kind', 'bus_stop'],
1094
+ layout: { 'text-field': nameField },
1095
+ },
1096
+ {
1097
+ id: 'symbol-transit-tram',
1098
+ type: 'symbol',
1099
+ 'source-layer': 'public_transport',
1100
+ filter: ['==', 'kind', 'tram_stop'],
1101
+ layout: { 'text-field': nameField },
1102
+ },
1103
+ {
1104
+ id: 'symbol-transit-subway',
1105
+ type: 'symbol',
1106
+ 'source-layer': 'public_transport',
1107
+ filter: ['all',
1108
+ ['in', 'kind', 'station', 'halt'],
1109
+ ['==', 'station', 'subway'],
1110
+ ],
1111
+ layout: { 'text-field': nameField },
1112
+ },
1113
+ {
1114
+ id: 'symbol-transit-lightrail',
1115
+ type: 'symbol',
1116
+ 'source-layer': 'public_transport',
1117
+ filter: ['all',
1118
+ ['in', 'kind', 'station', 'halt'],
1119
+ ['==', 'station', 'light_rail'],
1120
+ ],
1121
+ layout: { 'text-field': nameField },
1122
+ },
1123
+ {
1124
+ id: 'symbol-transit-station',
1125
+ type: 'symbol',
1126
+ 'source-layer': 'public_transport',
1127
+ filter: ['all',
1128
+ ['in', 'kind', 'station', 'halt'],
1129
+ ['!in', 'station', 'light_rail', 'subway'],
1130
+ ],
1131
+ layout: { 'text-field': nameField },
1132
+ },
1133
+ {
1134
+ id: 'symbol-transit-airfield',
1135
+ type: 'symbol',
1136
+ 'source-layer': 'public_transport',
1137
+ filter: ['all',
1138
+ ['==', 'kind', 'aerodrome'],
1139
+ ['!has', 'iata'],
1140
+ ],
1141
+ layout: { 'text-field': nameField },
1142
+ },
1143
+ {
1144
+ id: 'symbol-transit-airport',
1145
+ type: 'symbol',
1146
+ 'source-layer': 'public_transport',
1147
+ filter: ['all',
1148
+ ['==', 'kind', 'aerodrome'],
1149
+ ['has', 'iata'],
1150
+ ],
1151
+ layout: { 'text-field': nameField },
1152
+ },
1153
+ ];
1154
+ }
1155
+
1156
+ /**
1157
+ * @param {string | RegExp} a
1158
+ * @param {string | RegExp} b
1159
+ * @param {string} str
1160
+ */
1161
+ function balanced (a, b, str) {
1162
+ if (a instanceof RegExp) a = maybeMatch(a, str);
1163
+ if (b instanceof RegExp) b = maybeMatch(b, str);
1164
+
1165
+ const r = range(a, b, str);
1166
+
1167
+ return (
1168
+ r && {
1169
+ start: r[0],
1170
+ end: r[1],
1171
+ pre: str.slice(0, r[0]),
1172
+ body: str.slice(r[0] + a.length, r[1]),
1173
+ post: str.slice(r[1] + b.length)
1174
+ }
1175
+ )
1176
+ }
1177
+
1178
+ /**
1179
+ * @param {RegExp} reg
1180
+ * @param {string} str
1181
+ */
1182
+ function maybeMatch (reg, str) {
1183
+ const m = str.match(reg);
1184
+ return m ? m[0] : null
1185
+ }
1186
+
1187
+ /**
1188
+ * @param {string} a
1189
+ * @param {string} b
1190
+ * @param {string} str
1191
+ */
1192
+ function range (a, b, str) {
1193
+ let begs, beg, left, right, result;
1194
+ let ai = str.indexOf(a);
1195
+ let bi = str.indexOf(b, ai + 1);
1196
+ let i = ai;
1197
+
1198
+ if (ai >= 0 && bi > 0) {
1199
+ if (a === b) {
1200
+ return [ai, bi]
1201
+ }
1202
+ begs = [];
1203
+ left = str.length;
1204
+
1205
+ while (i >= 0 && !result) {
1206
+ if (i === ai) {
1207
+ begs.push(i);
1208
+ ai = str.indexOf(a, i + 1);
1209
+ } else if (begs.length === 1) {
1210
+ result = [begs.pop(), bi];
1211
+ } else {
1212
+ beg = begs.pop();
1213
+ if (beg < left) {
1214
+ left = beg;
1215
+ right = bi;
1216
+ }
1217
+
1218
+ bi = str.indexOf(b, i + 1);
1219
+ }
1220
+
1221
+ i = ai < bi && ai >= 0 ? ai : bi;
1222
+ }
1223
+
1224
+ if (begs.length) {
1225
+ result = [left, right];
1226
+ }
1227
+ }
1228
+
1229
+ return result
1230
+ }
1231
+
1232
+ const escSlash = '\0SLASH' + Math.random() + '\0';
1233
+ const escOpen = '\0OPEN' + Math.random() + '\0';
1234
+ const escClose = '\0CLOSE' + Math.random() + '\0';
1235
+ const escComma = '\0COMMA' + Math.random() + '\0';
1236
+ const escPeriod = '\0PERIOD' + Math.random() + '\0';
1237
+ const escSlashPattern = new RegExp(escSlash, 'g');
1238
+ const escOpenPattern = new RegExp(escOpen, 'g');
1239
+ const escClosePattern = new RegExp(escClose, 'g');
1240
+ const escCommaPattern = new RegExp(escComma, 'g');
1241
+ const escPeriodPattern = new RegExp(escPeriod, 'g');
1242
+ const slashPattern = /\\\\/g;
1243
+ const openPattern = /\\{/g;
1244
+ const closePattern = /\\}/g;
1245
+ const commaPattern = /\\,/g;
1246
+ const periodPattern = /\\./g;
1247
+
1248
+ /**
1249
+ * @return {number}
1250
+ */
1251
+ function numeric (str) {
1252
+ return !isNaN(str)
1253
+ ? parseInt(str, 10)
1254
+ : str.charCodeAt(0)
1255
+ }
1256
+
1257
+ /**
1258
+ * @param {string} str
1259
+ */
1260
+ function escapeBraces (str) {
1261
+ return str.replace(slashPattern, escSlash)
1262
+ .replace(openPattern, escOpen)
1263
+ .replace(closePattern, escClose)
1264
+ .replace(commaPattern, escComma)
1265
+ .replace(periodPattern, escPeriod)
1266
+ }
1267
+
1268
+ /**
1269
+ * @param {string} str
1270
+ */
1271
+ function unescapeBraces (str) {
1272
+ return str.replace(escSlashPattern, '\\')
1273
+ .replace(escOpenPattern, '{')
1274
+ .replace(escClosePattern, '}')
1275
+ .replace(escCommaPattern, ',')
1276
+ .replace(escPeriodPattern, '.')
1277
+ }
1278
+
1279
+ /**
1280
+ * Basically just str.split(","), but handling cases
1281
+ * where we have nested braced sections, which should be
1282
+ * treated as individual members, like {a,{b,c},d}
1283
+ * @param {string} str
1284
+ */
1285
+ function parseCommaParts (str) {
1286
+ if (!str) { return [''] }
1287
+
1288
+ const parts = [];
1289
+ const m = balanced('{', '}', str);
1290
+
1291
+ if (!m) { return str.split(',') }
1292
+
1293
+ const { pre, body, post } = m;
1294
+ const p = pre.split(',');
1295
+
1296
+ p[p.length - 1] += '{' + body + '}';
1297
+ const postParts = parseCommaParts(post);
1298
+ if (post.length) {
1299
+ p[p.length - 1] += postParts.shift();
1300
+ p.push.apply(p, postParts);
1301
+ }
1302
+
1303
+ parts.push.apply(parts, p);
1304
+
1305
+ return parts
1306
+ }
1307
+
1308
+ /**
1309
+ * @param {string} str
1310
+ */
1311
+ function expandTop (str) {
1312
+ if (!str) { return [] }
1313
+
1314
+ // I don't know why Bash 4.3 does this, but it does.
1315
+ // Anything starting with {} will have the first two bytes preserved
1316
+ // but *only* at the top level, so {},a}b will not expand to anything,
1317
+ // but a{},b}c will be expanded to [a}c,abc].
1318
+ // One could argue that this is a bug in Bash, but since the goal of
1319
+ // this module is to match Bash's rules, we escape a leading {}
1320
+ if (str.slice(0, 2) === '{}') {
1321
+ str = '\\{\\}' + str.slice(2);
1322
+ }
1323
+
1324
+ return expand(escapeBraces(str), true).map(unescapeBraces)
1325
+ }
1326
+
1327
+ /**
1328
+ * @param {string} str
1329
+ */
1330
+ function embrace (str) {
1331
+ return '{' + str + '}'
1332
+ }
1333
+
1334
+ /**
1335
+ * @param {string} el
1336
+ */
1337
+ function isPadded (el) {
1338
+ return /^-?0\d/.test(el)
1339
+ }
1340
+
1341
+ /**
1342
+ * @param {number} i
1343
+ * @param {number} y
1344
+ */
1345
+ function lte (i, y) {
1346
+ return i <= y
1347
+ }
1348
+
1349
+ /**
1350
+ * @param {number} i
1351
+ * @param {number} y
1352
+ */
1353
+ function gte (i, y) {
1354
+ return i >= y
1355
+ }
1356
+
1357
+ /**
1358
+ * @param {string} str
1359
+ * @param {boolean} [isTop]
1360
+ */
1361
+ function expand (str, isTop) {
1362
+ /** @type {string[]} */
1363
+ const expansions = [];
1364
+
1365
+ const m = balanced('{', '}', str);
1366
+ if (!m) return [str]
1367
+
1368
+ // no need to expand pre, since it is guaranteed to be free of brace-sets
1369
+ const pre = m.pre;
1370
+ const post = m.post.length
1371
+ ? expand(m.post, false)
1372
+ : [''];
1373
+
1374
+ if (/\$$/.test(m.pre)) {
1375
+ for (let k = 0; k < post.length; k++) {
1376
+ const expansion = pre + '{' + m.body + '}' + post[k];
1377
+ expansions.push(expansion);
1378
+ }
1379
+ } else {
1380
+ const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
1381
+ const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
1382
+ const isSequence = isNumericSequence || isAlphaSequence;
1383
+ const isOptions = m.body.indexOf(',') >= 0;
1384
+ if (!isSequence && !isOptions) {
1385
+ // {a},b}
1386
+ if (m.post.match(/,.*\}/)) {
1387
+ str = m.pre + '{' + m.body + escClose + m.post;
1388
+ return expand(str)
1389
+ }
1390
+ return [str]
1391
+ }
1392
+
1393
+ let n;
1394
+ if (isSequence) {
1395
+ n = m.body.split(/\.\./);
1396
+ } else {
1397
+ n = parseCommaParts(m.body);
1398
+ if (n.length === 1) {
1399
+ // x{{a,b}}y ==> x{a}y x{b}y
1400
+ n = expand(n[0], false).map(embrace);
1401
+ if (n.length === 1) {
1402
+ return post.map(function (p) {
1403
+ return m.pre + n[0] + p
1404
+ })
1405
+ }
1406
+ }
1407
+ }
1408
+
1409
+ // at this point, n is the parts, and we know it's not a comma set
1410
+ // with a single entry.
1411
+ let N;
1412
+
1413
+ if (isSequence) {
1414
+ const x = numeric(n[0]);
1415
+ const y = numeric(n[1]);
1416
+ const width = Math.max(n[0].length, n[1].length);
1417
+ let incr = n.length === 3
1418
+ ? Math.abs(numeric(n[2]))
1419
+ : 1;
1420
+ let test = lte;
1421
+ const reverse = y < x;
1422
+ if (reverse) {
1423
+ incr *= -1;
1424
+ test = gte;
1425
+ }
1426
+ const pad = n.some(isPadded);
1427
+
1428
+ N = [];
1429
+
1430
+ for (let i = x; test(i, y); i += incr) {
1431
+ let c;
1432
+ if (isAlphaSequence) {
1433
+ c = String.fromCharCode(i);
1434
+ if (c === '\\') { c = ''; }
1435
+ } else {
1436
+ c = String(i);
1437
+ if (pad) {
1438
+ const need = width - c.length;
1439
+ if (need > 0) {
1440
+ const z = new Array(need + 1).join('0');
1441
+ if (i < 0) { c = '-' + z + c.slice(1); } else { c = z + c; }
1442
+ }
1443
+ }
1444
+ }
1445
+ N.push(c);
1446
+ }
1447
+ } else {
1448
+ N = [];
1449
+
1450
+ for (let j = 0; j < n.length; j++) {
1451
+ N.push.apply(N, expand(n[j], false));
1452
+ }
1453
+ }
1454
+
1455
+ for (let j = 0; j < N.length; j++) {
1456
+ for (let k = 0; k < post.length; k++) {
1457
+ const expansion = pre + N[j] + post[k];
1458
+ if (!isTop || isSequence || expansion) { expansions.push(expansion); }
1459
+ }
1460
+ }
1461
+ }
1462
+
1463
+ return expansions
1464
+ }
1465
+
1466
+ const propertyLookup = new Map();
1467
+ const propertyDefs = [
1468
+ { parent: 'layer', types: 'background,fill,line,symbol', key: 'filter', valueType: 'filter' },
1469
+ { parent: 'layer', types: 'background,fill,line,symbol', key: 'maxzoom', valueType: 'number' },
1470
+ { parent: 'layer', types: 'background,fill,line,symbol', key: 'minzoom', valueType: 'number' },
1471
+ { parent: 'layout', types: 'background,fill,line,symbol', key: 'visibility', valueType: 'enum' },
1472
+ { parent: 'layout', types: 'fill', key: 'fill-sort-key', valueType: 'number' },
1473
+ { parent: 'layout', types: 'line', key: 'line-cap', valueType: 'enum' },
1474
+ { parent: 'layout', types: 'line', key: 'line-join', valueType: 'enum' },
1475
+ { parent: 'layout', types: 'line', key: 'line-miter-limit', valueType: 'number' },
1476
+ { parent: 'layout', types: 'line', key: 'line-round-limit', valueType: 'number' },
1477
+ { parent: 'layout', types: 'line', key: 'line-sort-key', valueType: 'number' },
1478
+ { parent: 'layout', types: 'symbol', key: 'icon-allow-overlap', valueType: 'boolean' },
1479
+ { parent: 'layout', types: 'symbol', key: 'icon-anchor', valueType: 'enum' },
1480
+ { parent: 'layout', types: 'symbol', key: 'icon-ignore-placement', valueType: 'boolean' },
1481
+ { parent: 'layout', types: 'symbol', key: 'icon-image', short: 'image', valueType: 'resolvedImage' },
1482
+ { parent: 'layout', types: 'symbol', key: 'icon-keep-upright', valueType: 'boolean' },
1483
+ { parent: 'layout', types: 'symbol', key: 'icon-offset', valueType: 'array' },
1484
+ { parent: 'layout', types: 'symbol', key: 'icon-optional', valueType: 'boolean' },
1485
+ { parent: 'layout', types: 'symbol', key: 'icon-overlap', valueType: 'enum' },
1486
+ { parent: 'layout', types: 'symbol', key: 'icon-padding', valueType: 'padding' },
1487
+ { parent: 'layout', types: 'symbol', key: 'icon-pitch-alignment', valueType: 'enum' },
1488
+ { parent: 'layout', types: 'symbol', key: 'icon-rotate', valueType: 'number' },
1489
+ { parent: 'layout', types: 'symbol', key: 'icon-rotation-alignment', valueType: 'enum' },
1490
+ { parent: 'layout', types: 'symbol', key: 'icon-size', valueType: 'number' },
1491
+ { parent: 'layout', types: 'symbol', key: 'icon-text-fit-padding', valueType: 'array' },
1492
+ { parent: 'layout', types: 'symbol', key: 'icon-text-fit', valueType: 'enum' },
1493
+ { parent: 'layout', types: 'symbol', key: 'symbol-avoid-edges', valueType: 'boolean' },
1494
+ { parent: 'layout', types: 'symbol', key: 'symbol-placement', valueType: 'enum' },
1495
+ { parent: 'layout', types: 'symbol', key: 'symbol-sort-key', valueType: 'number' },
1496
+ { parent: 'layout', types: 'symbol', key: 'symbol-spacing', valueType: 'number' },
1497
+ { parent: 'layout', types: 'symbol', key: 'symbol-z-order', valueType: 'enum' },
1498
+ { parent: 'layout', types: 'symbol', key: 'text-allow-overlap', valueType: 'boolean' },
1499
+ { parent: 'layout', types: 'symbol', key: 'text-anchor', valueType: 'enum' },
1500
+ { parent: 'layout', types: 'symbol', key: 'text-field', short: 'text', valueType: 'formatted' },
1501
+ { parent: 'layout', types: 'symbol', key: 'text-font', short: 'font', valueType: 'fonts' },
1502
+ { parent: 'layout', types: 'symbol', key: 'text-ignore-placement', valueType: 'boolean' },
1503
+ { parent: 'layout', types: 'symbol', key: 'text-justify', valueType: 'enum' },
1504
+ { parent: 'layout', types: 'symbol', key: 'text-keep-upright', valueType: 'boolean' },
1505
+ { parent: 'layout', types: 'symbol', key: 'text-letter-spacing', valueType: 'number' },
1506
+ { parent: 'layout', types: 'symbol', key: 'text-line-height', valueType: 'number' },
1507
+ { parent: 'layout', types: 'symbol', key: 'text-max-angle', valueType: 'number' },
1508
+ { parent: 'layout', types: 'symbol', key: 'text-max-width', valueType: 'number' },
1509
+ { parent: 'layout', types: 'symbol', key: 'text-offset', valueType: 'array' },
1510
+ { parent: 'layout', types: 'symbol', key: 'text-optional', valueType: 'boolean' },
1511
+ { parent: 'layout', types: 'symbol', key: 'text-overlap', valueType: 'enum' },
1512
+ { parent: 'layout', types: 'symbol', key: 'text-padding', valueType: 'number' },
1513
+ { parent: 'layout', types: 'symbol', key: 'text-pitch-alignment', valueType: 'enum' },
1514
+ { parent: 'layout', types: 'symbol', key: 'text-radial-offset', valueType: 'number' },
1515
+ { parent: 'layout', types: 'symbol', key: 'text-rotate', valueType: 'number' },
1516
+ { parent: 'layout', types: 'symbol', key: 'text-rotation-alignment', valueType: 'enum' },
1517
+ { parent: 'layout', types: 'symbol', key: 'text-size', short: 'size', valueType: 'number' },
1518
+ { parent: 'layout', types: 'symbol', key: 'text-transform', valueType: 'enum' },
1519
+ { parent: 'layout', types: 'symbol', key: 'text-variable-anchor-offset', valueType: 'variableAnchorOffsetCollection' },
1520
+ { parent: 'layout', types: 'symbol', key: 'text-variable-anchor', valueType: 'array' },
1521
+ { parent: 'layout', types: 'symbol', key: 'text-writing-mode', valueType: 'array' },
1522
+ { parent: 'paint', types: 'background', key: 'background-color', short: 'color', valueType: 'color' },
1523
+ { parent: 'paint', types: 'background', key: 'background-opacity', short: 'opacity', valueType: 'number' },
1524
+ { parent: 'paint', types: 'background', key: 'background-pattern', short: 'image', valueType: 'resolvedImage' },
1525
+ { parent: 'paint', types: 'fill', key: 'fill-antialias', valueType: 'boolean' },
1526
+ { parent: 'paint', types: 'fill', key: 'fill-color', short: 'color', valueType: 'color' },
1527
+ { parent: 'paint', types: 'fill', key: 'fill-extrusion-base', valueType: 'number' },
1528
+ { parent: 'paint', types: 'fill', key: 'fill-extrusion-color', valueType: 'color' },
1529
+ { parent: 'paint', types: 'fill', key: 'fill-extrusion-height', valueType: 'number' },
1530
+ { parent: 'paint', types: 'fill', key: 'fill-extrusion-opacity', valueType: 'number' },
1531
+ { parent: 'paint', types: 'fill', key: 'fill-extrusion-pattern', valueType: 'resolvedImage' },
1532
+ { parent: 'paint', types: 'fill', key: 'fill-extrusion-translate-anchor', valueType: 'enum' },
1533
+ { parent: 'paint', types: 'fill', key: 'fill-extrusion-translate', valueType: 'array' },
1534
+ { parent: 'paint', types: 'fill', key: 'fill-extrusion-vertical-gradient', valueType: 'boolean' },
1535
+ { parent: 'paint', types: 'fill', key: 'fill-opacity', short: 'opacity', valueType: 'number' },
1536
+ { parent: 'paint', types: 'fill', key: 'fill-outline-color', valueType: 'color' },
1537
+ { parent: 'paint', types: 'fill', key: 'fill-pattern', short: 'image', valueType: 'resolvedImage' },
1538
+ { parent: 'paint', types: 'fill', key: 'fill-translate-anchor', valueType: 'enum' },
1539
+ { parent: 'paint', types: 'fill', key: 'fill-translate', valueType: 'array' },
1540
+ { parent: 'paint', types: 'line', key: 'line-blur', valueType: 'number' },
1541
+ { parent: 'paint', types: 'line', key: 'line-color', short: 'color', valueType: 'color' },
1542
+ { parent: 'paint', types: 'line', key: 'line-dasharray', valueType: 'array' },
1543
+ { parent: 'paint', types: 'line', key: 'line-gap-width', valueType: 'number' },
1544
+ { parent: 'paint', types: 'line', key: 'line-gradient', valueType: 'color' },
1545
+ { parent: 'paint', types: 'line', key: 'line-offset', valueType: 'number' },
1546
+ { parent: 'paint', types: 'line', key: 'line-opacity', short: 'opacity', valueType: 'number' },
1547
+ { parent: 'paint', types: 'line', key: 'line-pattern', short: 'image', valueType: 'resolvedImage' },
1548
+ { parent: 'paint', types: 'line', key: 'line-translate-anchor', valueType: 'enum' },
1549
+ { parent: 'paint', types: 'line', key: 'line-translate', valueType: 'array' },
1550
+ { parent: 'paint', types: 'line', key: 'line-width', short: 'size', valueType: 'number' },
1551
+ { parent: 'paint', types: 'symbol', key: 'icon-color', short: 'color', valueType: 'color' },
1552
+ { parent: 'paint', types: 'symbol', key: 'icon-halo-blur', valueType: 'number' },
1553
+ { parent: 'paint', types: 'symbol', key: 'icon-halo-color', valueType: 'color' },
1554
+ { parent: 'paint', types: 'symbol', key: 'icon-halo-width', valueType: 'number' },
1555
+ { parent: 'paint', types: 'symbol', key: 'icon-opacity', short: 'opacity', valueType: 'number' },
1556
+ { parent: 'paint', types: 'symbol', key: 'icon-translate-anchor', valueType: 'enum' },
1557
+ { parent: 'paint', types: 'symbol', key: 'icon-translate', valueType: 'array' },
1558
+ { parent: 'paint', types: 'symbol', key: 'text-color', short: 'color', valueType: 'color' },
1559
+ { parent: 'paint', types: 'symbol', key: 'text-halo-blur', valueType: 'number' },
1560
+ { parent: 'paint', types: 'symbol', key: 'text-halo-color', valueType: 'color' },
1561
+ { parent: 'paint', types: 'symbol', key: 'text-halo-width', valueType: 'number' },
1562
+ { parent: 'paint', types: 'symbol', key: 'text-opacity', short: 'opacity', valueType: 'number' },
1563
+ { parent: 'paint', types: 'symbol', key: 'text-translate-anchor', valueType: 'enum' },
1564
+ { parent: 'paint', types: 'symbol', key: 'text-translate', valueType: 'array' },
1565
+ ];
1566
+ propertyDefs.forEach((propertyDef) => {
1567
+ const types = propertyDef.types;
1568
+ types.split(',').forEach((type) => {
1569
+ function add(propertyKey) {
1570
+ const key = type + '/' + propertyKey;
1571
+ const property = {
1572
+ key: propertyDef.key,
1573
+ parent: propertyDef.parent,
1574
+ valueType: propertyDef.valueType,
1575
+ };
1576
+ const propertyList = propertyLookup.get(key);
1577
+ if (propertyList) {
1578
+ propertyList.push(property);
1579
+ }
1580
+ else {
1581
+ propertyLookup.set(key, [property]);
1582
+ }
1583
+ }
1584
+ add(propertyDef.key);
1585
+ if (propertyDef.short != null)
1586
+ add(propertyDef.short);
1587
+ });
1588
+ });
1589
+
1590
+ // Utility function to deep clone an object
1591
+ function deepClone(obj) {
1592
+ const type = typeof obj;
1593
+ if (type !== 'object') {
1594
+ switch (type) {
1595
+ case 'boolean':
1596
+ case 'number':
1597
+ case 'string':
1598
+ case 'undefined':
1599
+ return obj;
1600
+ default: throw new Error(`Not implemented yet: "${type}" case`);
1601
+ }
1602
+ }
1603
+ if (isSimpleObject(obj)) {
1604
+ // @ts-expect-error: Too complicated to handle
1605
+ return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, deepClone(value)]));
1606
+ }
1607
+ if (obj instanceof Array) {
1608
+ // @ts-expect-error: Too complicated to handle
1609
+ return obj.map((e) => deepClone(e));
1610
+ }
1611
+ if (obj instanceof Color) {
1612
+ return obj.clone();
1613
+ }
1614
+ if (obj == null)
1615
+ return obj;
1616
+ console.log('obj', obj);
1617
+ console.log('obj.prototype', Object.getPrototypeOf(obj));
1618
+ throw Error();
1619
+ }
1620
+ function isSimpleObject(item) {
1621
+ if (item === null)
1622
+ return false;
1623
+ if (typeof item !== 'object')
1624
+ return false;
1625
+ if (Array.isArray(item))
1626
+ return false;
1627
+ const prototypeKeyCount = Object.keys(Object.getPrototypeOf(item)).length;
1628
+ if (prototypeKeyCount !== 0)
1629
+ return false;
1630
+ if (item.constructor.name !== 'Object')
1631
+ return false;
1632
+ return true;
1633
+ }
1634
+ function isBasicType(item) {
1635
+ switch (typeof item) {
1636
+ case 'boolean':
1637
+ case 'number':
1638
+ case 'string':
1639
+ case 'undefined':
1640
+ return true;
1641
+ case 'object':
1642
+ return false;
1643
+ default:
1644
+ throw Error('unknown type: ' + typeof item);
1645
+ }
1646
+ }
1647
+ function deepMerge(source0, ...sources) {
1648
+ const target = deepClone(source0);
1649
+ for (const source of sources) {
1650
+ if (typeof source !== 'object')
1651
+ continue;
1652
+ for (const key in source) {
1653
+ if (!(key in source))
1654
+ continue;
1655
+ const sourceValue = source[key];
1656
+ // *********
1657
+ // overwrite
1658
+ // *********
1659
+ switch (typeof sourceValue) {
1660
+ case 'number':
1661
+ case 'string':
1662
+ case 'boolean':
1663
+ target[key] = sourceValue;
1664
+ continue;
1665
+ }
1666
+ if (isBasicType(target[key])) {
1667
+ target[key] = deepClone(sourceValue);
1668
+ continue;
1669
+ }
1670
+ if (sourceValue instanceof Color) {
1671
+ // @ts-expect-error: Too complicated to handle
1672
+ target[key] = sourceValue.clone();
1673
+ continue;
1674
+ }
1675
+ if (isSimpleObject(target[key]) && isSimpleObject(sourceValue)) {
1676
+ target[key] = deepMerge(target[key], sourceValue);
1677
+ continue;
1678
+ }
1679
+ // *********
1680
+ // merge
1681
+ // *********
1682
+ if (isSimpleObject(target[key]) && isSimpleObject(sourceValue)) {
1683
+ target[key] = deepMerge(target[key], sourceValue);
1684
+ continue;
1685
+ }
1686
+ console.log('target[key]:', target[key]);
1687
+ console.log('source[key]:', source[key]);
1688
+ throw Error('unpredicted case');
1689
+ }
1690
+ }
1691
+ return target;
1692
+ }
1693
+ function resolveUrl(base, url) {
1694
+ if (!base)
1695
+ return url;
1696
+ url = new URL(url, base).href;
1697
+ url = url.replace(/%7B/gi, '{');
1698
+ url = url.replace(/%7D/gi, '}');
1699
+ return url;
1700
+ }
1701
+ function basename(url) {
1702
+ if (!url)
1703
+ return '';
1704
+ try {
1705
+ url = new URL(url, 'http://example.org').pathname;
1706
+ }
1707
+ catch (_) {
1708
+ // ignore
1709
+ }
1710
+ url = url.replace(/\/+$/, '');
1711
+ return url.split('/').pop() ?? '';
1712
+ }
1713
+
1714
+ function decorate(layers, rules, recolor) {
1715
+ const layerIds = layers.map(l => l.id);
1716
+ const layerIdSet = new Set(layerIds);
1717
+ // Initialize a new map to hold final styles for layers
1718
+ const layerStyles = new Map();
1719
+ // Iterate through the generated layer style rules
1720
+ Object.entries(rules).forEach(([idDef, layerStyle]) => {
1721
+ if (layerStyle == null)
1722
+ return;
1723
+ // Expand any braces in IDs and filter them through a RegExp if necessary
1724
+ const ids = expandTop(idDef).flatMap(id => {
1725
+ if (!id.includes('*'))
1726
+ return id;
1727
+ const regExpString = id.replace(/[^a-z_:-]/g, c => {
1728
+ if (c === '*')
1729
+ return '[a-z_-]*';
1730
+ throw new Error('unknown char to process. Do not know how to make a RegExp from: ' + JSON.stringify(c));
1731
+ });
1732
+ const regExp = new RegExp(`^${regExpString}$`, 'i');
1733
+ return layerIds.filter(layerId => regExp.test(layerId));
1734
+ });
1735
+ ids.forEach(id => {
1736
+ if (!layerIdSet.has(id))
1737
+ return;
1738
+ layerStyles.set(id, deepMerge(layerStyles.get(id) ?? {}, layerStyle));
1739
+ });
1740
+ });
1741
+ // Deep clone the original layers and apply styles
1742
+ return layers.flatMap(layer => {
1743
+ // Get the id and style of the layer
1744
+ const layerStyle = layerStyles.get(layer.id);
1745
+ // Don't export layers that have no style
1746
+ if (!layerStyle)
1747
+ return [];
1748
+ processStyling(layer, layerStyle);
1749
+ return [layer];
1750
+ });
1751
+ // Function to process each style attribute for the layer
1752
+ function processStyling(layer, styleRule) {
1753
+ for (const [ruleKeyCamelCase, ruleValue] of Object.entries(styleRule)) {
1754
+ if (ruleValue == null)
1755
+ continue;
1756
+ // CamelCase to not-camel-case
1757
+ const ruleKey = ruleKeyCamelCase.replace(/[A-Z]/g, c => '-' + c.toLowerCase());
1758
+ const propertyDefs = propertyLookup.get(layer.type + '/' + ruleKey);
1759
+ if (!propertyDefs)
1760
+ continue;
1761
+ propertyDefs.forEach(propertyDef => {
1762
+ const { key } = propertyDef;
1763
+ let value = ruleValue;
1764
+ switch (propertyDef.valueType) {
1765
+ case 'color':
1766
+ value = processExpression(value, processColor);
1767
+ break;
1768
+ case 'fonts':
1769
+ value = processExpression(value, processFont);
1770
+ break;
1771
+ case 'resolvedImage':
1772
+ case 'formatted':
1773
+ case 'array':
1774
+ case 'boolean':
1775
+ case 'enum':
1776
+ case 'number':
1777
+ value = processExpression(value);
1778
+ break;
1779
+ default: throw new Error(`unknown propertyDef.valueType "${propertyDef.valueType}" for key "${key}"`);
1780
+ }
1781
+ switch (propertyDef.parent) {
1782
+ case 'layer':
1783
+ // @ts-expect-error: too complex to handle
1784
+ layer[key] = value;
1785
+ break;
1786
+ case 'layout':
1787
+ if (!layer.layout)
1788
+ layer.layout = {};
1789
+ // @ts-expect-error: too complex to handle
1790
+ layer.layout[key] = value;
1791
+ break;
1792
+ case 'paint':
1793
+ if (!layer.paint)
1794
+ layer.paint = {};
1795
+ // @ts-expect-error: too complex to handle
1796
+ layer.paint[key] = value;
1797
+ break;
1798
+ default:
1799
+ throw new Error(`unknown parent "${propertyDef.parent}" for key "${key}"`);
1800
+ }
1801
+ });
1802
+ }
1803
+ function processColor(value) {
1804
+ if (typeof value === 'string')
1805
+ value = Color.parse(value);
1806
+ if (value instanceof Color) {
1807
+ const color = recolor.do(value);
1808
+ return color.asString();
1809
+ }
1810
+ throw new Error(`unknown color type "${typeof value}"`);
1811
+ }
1812
+ function processFont(value) {
1813
+ if (typeof value === 'string')
1814
+ return [value];
1815
+ throw new Error(`unknown font type "${typeof value}"`);
1816
+ }
1817
+ function processExpression(value, cbValue) {
1818
+ if (typeof value === 'object') {
1819
+ if (value instanceof Color)
1820
+ return processColor(value);
1821
+ if (!Array.isArray(value)) {
1822
+ return processZoomStops(value, cbValue);
1823
+ }
1824
+ }
1825
+ return cbValue ? cbValue(value) : value;
1826
+ }
1827
+ function processZoomStops(obj, cbValue) {
1828
+ return {
1829
+ stops: Object.entries(obj)
1830
+ .map(([z, v]) => [parseInt(z, 10), cbValue ? cbValue(v) : v])
1831
+ .sort((a, b) => a[0] - b[0]),
1832
+ };
1833
+ }
1834
+ }
1835
+ }
1836
+
1837
+ function getDefaultRecolorFlags() {
1838
+ return {
1839
+ invertBrightness: false,
1840
+ rotate: 0,
1841
+ saturate: 0,
1842
+ gamma: 1,
1843
+ contrast: 1,
1844
+ brightness: 0,
1845
+ tint: 0,
1846
+ tintColor: '#FF0000',
1847
+ };
1848
+ }
1849
+ function isValidRecolorOptions(opt) {
1850
+ if (!opt)
1851
+ return false;
1852
+ if ((opt.invertBrightness != null) && opt.invertBrightness)
1853
+ return true;
1854
+ if ((opt.rotate != null) && (opt.rotate !== 0))
1855
+ return true;
1856
+ if ((opt.saturate != null) && (opt.saturate !== 0))
1857
+ return true;
1858
+ if ((opt.gamma != null) && (opt.gamma !== 1))
1859
+ return true;
1860
+ if ((opt.contrast != null) && (opt.contrast !== 1))
1861
+ return true;
1862
+ if ((opt.brightness != null) && (opt.brightness !== 0))
1863
+ return true;
1864
+ if ((opt.tint != null) && (opt.tint !== 0))
1865
+ return true;
1866
+ if ((opt.tintColor != null) && (opt.tintColor !== '#FF0000'))
1867
+ return true;
1868
+ return false;
1869
+ }
1870
+ class CachedRecolor {
1871
+ skip;
1872
+ opt;
1873
+ cache;
1874
+ constructor(opt) {
1875
+ this.skip = !isValidRecolorOptions(opt);
1876
+ this.cache = new Map();
1877
+ this.opt = opt;
1878
+ }
1879
+ do(color) {
1880
+ if (this.skip)
1881
+ return color;
1882
+ const key = color.asHex();
1883
+ const result = this.cache.get(key);
1884
+ if (result)
1885
+ return result;
1886
+ color = recolor(color, this.opt);
1887
+ this.cache.set(key, color);
1888
+ return color;
1889
+ }
1890
+ }
1891
+ function recolor(color, opt) {
1892
+ if (!isValidRecolorOptions(opt))
1893
+ return color;
1894
+ if (opt.invertBrightness ?? false)
1895
+ color = color.invertLuminosity();
1896
+ if ((opt.rotate !== undefined) && (opt.rotate !== 0))
1897
+ color = color.rotateHue(opt.rotate);
1898
+ if ((opt.saturate !== undefined) && (opt.saturate !== 0))
1899
+ color = color.saturate(opt.saturate);
1900
+ if ((opt.gamma !== undefined) && (opt.gamma !== 1))
1901
+ color = color.gamma(opt.gamma);
1902
+ if ((opt.contrast !== undefined) && (opt.contrast !== 1))
1903
+ color = color.contrast(opt.contrast);
1904
+ if ((opt.brightness !== undefined) && (opt.brightness !== 0))
1905
+ color = color.brightness(opt.brightness);
1906
+ if ((opt.tint !== undefined) && (opt.tintColor !== undefined) && (opt.tint !== 0))
1907
+ color = color.tint(opt.tint, Color.parse(opt.tintColor));
1908
+ return color;
1909
+ }
1910
+
1911
+ // StyleBuilder class definition
1912
+ class StyleBuilder {
1913
+ #sourceName = 'versatiles-shortbread';
1914
+ build(options) {
1915
+ options ??= {};
1916
+ const baseUrl = options.baseUrl ?? globalThis?.document?.location?.origin ?? 'https://tiles.versatiles.org';
1917
+ const glyphs = options.glyphs ?? '/assets/glyphs/{fontstack}/{range}.pbf';
1918
+ const sprite = options.sprite ?? [{ id: 'basics', url: '/assets/sprites/basics/sprites' }];
1919
+ const tiles = options.tiles ?? ['/tiles/osm/{z}/{x}/{y}'];
1920
+ const hideLabels = options.hideLabels ?? false;
1921
+ const language = options.language ?? null;
1922
+ const recolorOptions = options.recolor ?? getDefaultRecolorFlags();
1923
+ const colors = this.getColors(this.defaultColors);
1924
+ if (options.colors) {
1925
+ let key;
1926
+ for (key in options.colors) {
1927
+ const value = options.colors[key];
1928
+ if (value != null)
1929
+ colors[key] = Color.parse(value);
1930
+ }
1931
+ }
1932
+ const fonts = deepClone(this.defaultFonts);
1933
+ if (options.fonts) {
1934
+ let key;
1935
+ for (key in options.fonts) {
1936
+ const fontName = options.fonts[key];
1937
+ if (fontName != null)
1938
+ fonts[key] = fontName;
1939
+ }
1940
+ }
1941
+ // get empty shortbread style
1942
+ const style = getShortbreadTemplate();
1943
+ const styleRuleOptions = {
1944
+ colors,
1945
+ fonts,
1946
+ language,
1947
+ };
1948
+ // get layer style rules from child class
1949
+ const layerStyleRules = this.getStyleRules(styleRuleOptions);
1950
+ // get shortbread layers
1951
+ const layerDefinitions = getShortbreadLayers({ language });
1952
+ let layers = layerDefinitions.map(layer => {
1953
+ switch (layer.type) {
1954
+ case 'background':
1955
+ return layer;
1956
+ case 'fill':
1957
+ case 'line':
1958
+ case 'symbol':
1959
+ return {
1960
+ source: this.#sourceName,
1961
+ ...layer,
1962
+ };
1963
+ }
1964
+ throw Error('unknown layer type');
1965
+ });
1966
+ // apply layer rules
1967
+ layers = decorate(layers, layerStyleRules, new CachedRecolor(recolorOptions));
1968
+ // hide labels, if wanted
1969
+ if (hideLabels)
1970
+ layers = layers.filter(l => l.type !== 'symbol');
1971
+ style.layers = layers;
1972
+ style.name = 'versatiles-' + this.name.toLowerCase();
1973
+ style.glyphs = resolveUrl(baseUrl, glyphs);
1974
+ if (typeof sprite == 'string') {
1975
+ style.sprite = [{ id: basename(sprite), url: resolveUrl(baseUrl, sprite) }];
1976
+ }
1977
+ else {
1978
+ style.sprite = sprite.map(({ id, url }) => ({ id, url: resolveUrl(baseUrl, url) }));
1979
+ }
1980
+ const source = style.sources[this.#sourceName];
1981
+ if ('tiles' in source)
1982
+ source.tiles = tiles.map(url => resolveUrl(baseUrl, url));
1983
+ return style;
1984
+ }
1985
+ getColors(colors) {
1986
+ const entriesString = Object.entries(colors);
1987
+ const result = Object.fromEntries(entriesString.map(([key, value]) => [key, Color.parse(value)]));
1988
+ return result;
1989
+ }
1990
+ getDefaultOptions() {
1991
+ return {
1992
+ baseUrl: '',
1993
+ glyphs: '',
1994
+ sprite: '',
1995
+ tiles: [],
1996
+ hideLabels: false,
1997
+ language: undefined,
1998
+ colors: deepClone(this.defaultColors),
1999
+ fonts: deepClone(this.defaultFonts),
2000
+ recolor: getDefaultRecolorFlags(),
2001
+ };
2002
+ }
2003
+ transformDefaultColors(callback) {
2004
+ const colors = this.getColors(this.defaultColors);
2005
+ let key;
2006
+ for (key in colors) {
2007
+ this.defaultColors[key] = callback(colors[key]);
2008
+ }
2009
+ }
2010
+ }
2011
+
2012
+ class Colorful extends StyleBuilder {
2013
+ name = 'Colorful';
2014
+ defaultFonts = {
2015
+ regular: 'noto_sans_regular',
2016
+ bold: 'noto_sans_bold',
2017
+ };
2018
+ defaultColors = {
2019
+ /** Color for land areas on the map. */
2020
+ land: '#F9F4EE',
2021
+ /** Color for water bodies like lakes and rivers. */
2022
+ water: '#BEDDF3',
2023
+ /** Color for glacier areas, usually shown as white. */
2024
+ glacier: '#FFFFFF',
2025
+ /** Color for wooded or forested areas. */
2026
+ wood: '#66AA44',
2027
+ /** Color for grasslands or open fields. */
2028
+ grass: '#D8E8C8',
2029
+ /** Color for parks and recreational areas. */
2030
+ park: '#D9D9A5',
2031
+ /** Color for streets and roads on the map. */
2032
+ street: '#FFFFFF',
2033
+ /** Background color for streets. */
2034
+ streetbg: '#CFCDCA',
2035
+ /** Color for major highways or motorways. */
2036
+ motorway: '#FFCC88',
2037
+ /** Background color for motorways. */
2038
+ motorwaybg: '#E9AC77',
2039
+ /** Color for trunk roads. */
2040
+ trunk: '#FFEEAA',
2041
+ /** Background color for trunk roads. */
2042
+ trunkbg: '#E9AC77',
2043
+ /** Background color for buildings. */
2044
+ buildingbg: '#DFDBD7',
2045
+ /** Primary color for buildings. */
2046
+ building: '#F2EAE2',
2047
+ /** Color used for boundaries. */
2048
+ boundary: '#A6A6C8',
2049
+ /** Color used for disputed boundaries. */
2050
+ disputed: '#BEBCCF',
2051
+ /** Color used for residential areas. */
2052
+ residential: '#EAE6E133',
2053
+ /** Color used for commercial areas. */
2054
+ commercial: '#F7DEED40',
2055
+ /** Color used for industrial areas. */
2056
+ industrial: '#FFF4C255',
2057
+ /** Color used for footpaths and pedestrian areas. */
2058
+ foot: '#FBEBFF',
2059
+ /** Primary color used for labels. */
2060
+ label: '#333344',
2061
+ /** Color used for label halos. */
2062
+ labelHalo: '#FFFFFFCC',
2063
+ /** Color used for shields on maps. */
2064
+ shield: '#FFFFFF',
2065
+ /** Color used for agriculture areas. */
2066
+ agriculture: '#F0E7D1',
2067
+ /** Color used for railways. */
2068
+ rail: '#B1BBC4',
2069
+ /** Color used for subways and underground systems. */
2070
+ subway: '#A6B8C7',
2071
+ /** Color used for cycle paths. */
2072
+ cycle: '#EFF9FF',
2073
+ /** Color used for waste areas. */
2074
+ waste: '#DBD6BD',
2075
+ /** Color used for burial and cemetery areas. */
2076
+ burial: '#DDDBCA',
2077
+ /** Color used for sand areas like beaches. */
2078
+ sand: '#FAFAED',
2079
+ /** Color used for rocky terrain. */
2080
+ rock: '#E0E4E5',
2081
+ /** Color used for leisure areas like parks and gardens. */
2082
+ leisure: '#E7EDDE',
2083
+ /** Color used for wetland areas like marshes. */
2084
+ wetland: '#D3E6DB',
2085
+ /** Color used for various symbols on the map. */
2086
+ symbol: '#66626A',
2087
+ /** Color indicating danger or warning areas. */
2088
+ danger: '#FF0000',
2089
+ /** Color used for prison areas. */
2090
+ prison: '#FDF2FC',
2091
+ /** Color used for parking areas. */
2092
+ parking: '#EBE8E6',
2093
+ /** Color used for construction sites. */
2094
+ construction: '#A9A9A9',
2095
+ /** Color used for educational facilities. */
2096
+ education: '#FFFF80',
2097
+ /** Color used for hospitals and medical facilities. */
2098
+ hospital: '#FF6666',
2099
+ /** Color used for points of interest. */
2100
+ poi: '#555555',
2101
+ };
2102
+ getStyleRules(options) {
2103
+ const { colors, fonts } = options;
2104
+ return {
2105
+ // background
2106
+ 'background': {
2107
+ color: colors.land,
2108
+ },
2109
+ // boundary
2110
+ 'boundary-{country,state}:outline': {
2111
+ color: colors.land.lighten(0.1),
2112
+ lineBlur: 1,
2113
+ lineCap: 'round',
2114
+ lineJoin: 'round',
2115
+ },
2116
+ 'boundary-{country,state}': {
2117
+ color: colors.boundary,
2118
+ lineCap: 'round',
2119
+ lineJoin: 'round',
2120
+ },
2121
+ 'boundary-country{-disputed,}:outline': {
2122
+ size: { 2: 0, 3: 2, 10: 8 },
2123
+ opacity: 0.75,
2124
+ color: colors.land.lighten(0.05),
2125
+ },
2126
+ 'boundary-country{-disputed,}': {
2127
+ size: { 2: 0, 3: 1, 10: 4 },
2128
+ },
2129
+ 'boundary-country-disputed': {
2130
+ color: colors.disputed,
2131
+ lineDasharray: [2, 1],
2132
+ lineCap: 'square',
2133
+ },
2134
+ 'boundary-state:outline': {
2135
+ size: { 7: 0, 8: 2, 10: 4 },
2136
+ opacity: 0.75,
2137
+ },
2138
+ 'boundary-state': {
2139
+ size: { 7: 0, 8: 1, 10: 2 },
2140
+ },
2141
+ // water
2142
+ 'water-*': {
2143
+ color: colors.water,
2144
+ lineCap: 'round',
2145
+ lineJoin: 'round',
2146
+ },
2147
+ 'water-area': {
2148
+ opacity: { 4: 0, 6: 1 },
2149
+ },
2150
+ 'water-area-*': {
2151
+ opacity: { 4: 0, 6: 1 },
2152
+ },
2153
+ 'water-{pier,dam}-area': {
2154
+ color: colors.land,
2155
+ opacity: { 12: 0, 13: 1 },
2156
+ },
2157
+ 'water-pier': {
2158
+ color: colors.land,
2159
+ },
2160
+ 'water-river': {
2161
+ lineWidth: { 9: 0, 10: 3, 15: 5, 17: 9, 18: 20, 20: 60 },
2162
+ },
2163
+ 'water-canal': {
2164
+ lineWidth: { 9: 0, 10: 2, 15: 4, 17: 8, 18: 17, 20: 50 },
2165
+ },
2166
+ 'water-stream': {
2167
+ lineWidth: { 13: 0, 14: 1, 15: 2, 17: 6, 18: 12, 20: 30 },
2168
+ },
2169
+ 'water-ditch': {
2170
+ lineWidth: { 14: 0, 15: 1, 17: 4, 18: 8, 20: 20 },
2171
+ },
2172
+ // land
2173
+ 'land-*': {
2174
+ color: colors.land,
2175
+ },
2176
+ 'land-glacier': {
2177
+ color: colors.glacier,
2178
+ },
2179
+ 'land-forest': {
2180
+ color: colors.wood,
2181
+ opacity: { 7: 0, 8: 0.1 },
2182
+ },
2183
+ 'land-grass': {
2184
+ color: colors.grass,
2185
+ opacity: { 11: 0, 12: 1 },
2186
+ },
2187
+ 'land-{park,garden,vegetation}': {
2188
+ color: colors.park,
2189
+ opacity: { 11: 0, 12: 1 },
2190
+ },
2191
+ 'land-agriculture': {
2192
+ color: colors.agriculture,
2193
+ opacity: { 10: 0, 11: 1 },
2194
+ },
2195
+ 'land-residential': {
2196
+ color: colors.residential,
2197
+ opacity: { 10: 0, 11: 1 },
2198
+ },
2199
+ 'land-commercial': {
2200
+ color: colors.commercial,
2201
+ opacity: { 10: 0, 11: 1 },
2202
+ },
2203
+ 'land-industrial': {
2204
+ color: colors.industrial,
2205
+ opacity: { 10: 0, 11: 1 },
2206
+ },
2207
+ 'land-waste': {
2208
+ color: colors.waste,
2209
+ opacity: { 10: 0, 11: 1 },
2210
+ },
2211
+ 'land-burial': {
2212
+ color: colors.burial,
2213
+ opacity: { 13: 0, 14: 1 },
2214
+ },
2215
+ 'land-leisure': {
2216
+ color: colors.leisure,
2217
+ },
2218
+ 'land-rock': {
2219
+ color: colors.rock,
2220
+ },
2221
+ 'land-sand': {
2222
+ color: colors.sand,
2223
+ },
2224
+ 'land-wetland': {
2225
+ color: colors.wetland,
2226
+ },
2227
+ // site
2228
+ 'site-dangerarea': {
2229
+ color: colors.danger,
2230
+ fillOutlineColor: colors.danger,
2231
+ opacity: 0.3,
2232
+ image: 'basics:pattern-warning',
2233
+ },
2234
+ 'site-hospital': {
2235
+ color: colors.hospital,
2236
+ opacity: 0.1,
2237
+ },
2238
+ 'site-prison': {
2239
+ color: colors.prison,
2240
+ image: 'basics:pattern-striped',
2241
+ opacity: 0.1,
2242
+ },
2243
+ 'site-construction': {
2244
+ color: colors.construction,
2245
+ image: 'basics:pattern-hatched_thin',
2246
+ opacity: 0.1,
2247
+ },
2248
+ 'site-{university,college,school}': {
2249
+ color: colors.education,
2250
+ opacity: 0.1,
2251
+ },
2252
+ 'site-{bicycleparking,parking}': {
2253
+ color: colors.parking,
2254
+ },
2255
+ // building
2256
+ 'building:outline': {
2257
+ color: colors.buildingbg,
2258
+ opacity: { 14: 0, 15: 1 },
2259
+ },
2260
+ 'building': {
2261
+ color: colors.building,
2262
+ opacity: { 14: 0, 15: 1 },
2263
+ fillTranslate: [-2, -2],
2264
+ },
2265
+ // airport
2266
+ 'airport-area': {
2267
+ color: colors.street,
2268
+ opacity: 0.5,
2269
+ },
2270
+ 'airport-{runway,taxiway}:outline': {
2271
+ color: colors.streetbg,
2272
+ lineJoin: 'round',
2273
+ },
2274
+ 'airport-{runway,taxiway}': {
2275
+ color: colors.street,
2276
+ lineJoin: 'round',
2277
+ },
2278
+ 'airport-runway:outline': {
2279
+ size: { 11: 0, 12: 6, 13: 9, 14: 16, 15: 24, 16: 40, 17: 100, 18: 160, 20: 300 },
2280
+ },
2281
+ 'airport-runway': {
2282
+ size: { 11: 0, 12: 5, 13: 8, 14: 14, 15: 22, 16: 38, 17: 98, 18: 158, 20: 298 },
2283
+ opacity: { 11: 0, 12: 1 },
2284
+ },
2285
+ 'airport-taxiway:outline': {
2286
+ size: { 13: 0, 14: 2, 15: 10, 16: 14, 18: 20, 20: 40 },
2287
+ },
2288
+ 'airport-taxiway': {
2289
+ size: { 13: 0, 14: 1, 15: 8, 16: 12, 18: 18, 20: 36 },
2290
+ opacity: { 13: 0, 14: 1 },
2291
+ },
2292
+ // bridge
2293
+ 'bridge': {
2294
+ color: colors.land.darken(0.02),
2295
+ fillAntialias: true,
2296
+ opacity: 0.8,
2297
+ },
2298
+ // street
2299
+ // colors and joins
2300
+ '{tunnel-,bridge-,}street-*:outline': {
2301
+ color: colors.streetbg,
2302
+ lineJoin: 'round',
2303
+ },
2304
+ '{tunnel-,bridge-,}street-*': {
2305
+ color: colors.street,
2306
+ lineJoin: 'round',
2307
+ },
2308
+ 'tunnel-street-*:outline': {
2309
+ color: colors.street.darken(0.13),
2310
+ },
2311
+ 'tunnel-street-*': {
2312
+ color: colors.street.darken(0.03),
2313
+ },
2314
+ 'bridge-street-*:outline': {
2315
+ color: colors.street.darken(0.15),
2316
+ },
2317
+ // streets and ways, line caps
2318
+ '{tunnel-,}{street,way}-*': {
2319
+ lineCap: 'round',
2320
+ },
2321
+ '{tunnel-,}{street,way}-*:outline': {
2322
+ lineCap: 'round',
2323
+ },
2324
+ 'bridge-{street,way}-*': {
2325
+ lineCap: 'butt',
2326
+ },
2327
+ 'bridge-{street,way}-*:outline': {
2328
+ lineCap: 'butt',
2329
+ },
2330
+ // faux bridges
2331
+ 'bridge-{street,way}-*:bridge': {
2332
+ lineCap: 'butt',
2333
+ lineJoin: 'round',
2334
+ color: colors.land.darken(0.02),
2335
+ fillAntialias: true,
2336
+ opacity: 0.5,
2337
+ },
2338
+ 'bridge-street-motorway:bridge': {
2339
+ size: { '5': 0, '6': 3, '10': 7, '14': 7, '16': 20, '18': 53, '19': 118, '20': 235 }
2340
+ },
2341
+ 'bridge-street-trunk:bridge': {
2342
+ size: { '7': 0, '8': 3, '10': 6, '14': 8, '16': 17, '18': 50, '19': 104, '20': 202 }
2343
+ },
2344
+ 'bridge-street-primary:bridge': {
2345
+ size: { '8': 0, '9': 1, '10': 6, '14': 8, '16': 17, '18': 50, '19': 104, '20': 202 }
2346
+ },
2347
+ 'bridge-street-secondary:bridge': {
2348
+ size: { '11': 3, '14': 7, '16': 11, '18': 42, '19': 95, '20': 193 },
2349
+ opacity: { '11': 0, '12': 1 }
2350
+ },
2351
+ 'bridge-street-motorway-link:bridge': {
2352
+ minzoom: 12,
2353
+ size: { '12': 3, '14': 4, '16': 10, '18': 20, '20': 56 }
2354
+ },
2355
+ 'bridge-street-{trunk,primary,secondary}-link:bridge': {
2356
+ minzoom: 13,
2357
+ size: { '12': 3, '14': 4, '16': 10, '18': 20, '20': 56 }
2358
+ },
2359
+ 'bridge-street-{tertiary,tertiary-link,unclassified,residential,livingstreet,pedestrian}*:bridge': {
2360
+ size: { '12': 3, '14': 4, '16': 8, '18': 36, '19': 90, '20': 179 },
2361
+ opacity: { '12': 0, '13': 1 }
2362
+ },
2363
+ 'bridge-street-{service,track}:bridge': {
2364
+ size: { '14': 3, '16': 6, '18': 25, '19': 67, '20': 134 },
2365
+ opacity: { '14': 0, '15': 1 }
2366
+ },
2367
+ 'bridge-way-*:bridge': {
2368
+ size: { '15': 0, '16': 7, '18': 10, '19': 17, '20': 31 },
2369
+ minzoom: 15
2370
+ },
2371
+ // special color: motorway
2372
+ '{bridge-,}street-motorway{-link,}:outline': {
2373
+ color: colors.motorwaybg,
2374
+ },
2375
+ '{bridge-,}street-motorway{-link,}': {
2376
+ color: colors.motorway,
2377
+ },
2378
+ '{bridge-,}street-{trunk,primary,secondary}{-link,}:outline': {
2379
+ color: colors.trunkbg,
2380
+ },
2381
+ '{bridge-,}street-{trunk,primary,secondary}{-link,}': {
2382
+ color: colors.trunk,
2383
+ },
2384
+ 'tunnel-street-motorway{-link,}:outline': {
2385
+ color: colors.motorwaybg.lighten(0.05),
2386
+ lineDasharray: [1, 0.3],
2387
+ },
2388
+ 'tunnel-street-motorway{-link,}': {
2389
+ color: colors.motorway.lighten(0.1),
2390
+ lineCap: 'butt',
2391
+ },
2392
+ 'tunnel-street-{trunk,primary,secondary}{-link,}:outline': {
2393
+ color: colors.trunkbg.lighten(0.05),
2394
+ lineDasharray: [1, 0.3],
2395
+ },
2396
+ 'tunnel-street-{trunk,primary,secondary}{-link,}': {
2397
+ color: colors.trunk.lighten(0.1),
2398
+ lineCap: 'butt',
2399
+ },
2400
+ // motorway
2401
+ '{bridge-,tunnel-,}street-motorway:outline': {
2402
+ size: { 5: 0, 6: 2, 10: 5, 14: 5, 16: 14, 18: 38, 19: 84, 20: 168 },
2403
+ },
2404
+ '{bridge-,tunnel-,}street-motorway': {
2405
+ size: { 5: 0, 6: 1, 10: 4, 14: 4, 16: 12, 18: 36, 19: 80, 20: 160 },
2406
+ opacity: { 5: 0, 6: 1 },
2407
+ },
2408
+ // trunk
2409
+ '{bridge-,tunnel-,}street-trunk:outline': {
2410
+ size: { 7: 0, 8: 2, 10: 4, 14: 6, 16: 12, 18: 36, 19: 74, 20: 144 },
2411
+ },
2412
+ '{bridge-,tunnel-,}street-trunk': {
2413
+ size: { 7: 0, 8: 1, 10: 3, 14: 5, 16: 10, 18: 34, 19: 70, 20: 140 },
2414
+ opacity: { 7: 0, 8: 1 },
2415
+ },
2416
+ // primary
2417
+ '{bridge-,tunnel-,}street-primary:outline': {
2418
+ size: { 8: 0, 9: 1, 10: 4, 14: 6, 16: 12, 18: 36, 19: 74, 20: 144 },
2419
+ },
2420
+ '{bridge-,tunnel-,}street-primary': {
2421
+ size: { 8: 0, 9: 2, 10: 3, 14: 5, 16: 10, 18: 34, 19: 70, 20: 140 },
2422
+ opacity: { 8: 0, 9: 1 },
2423
+ },
2424
+ // secondary
2425
+ '{bridge-,tunnel-,}street-secondary:outline': {
2426
+ size: { 11: 2, 14: 5, 16: 8, 18: 30, 19: 68, 20: 138 },
2427
+ opacity: { 11: 0, 12: 1 },
2428
+ },
2429
+ '{bridge-,tunnel-,}street-secondary': {
2430
+ size: { 11: 1, 14: 4, 16: 6, 18: 28, 19: 64, 20: 130 },
2431
+ opacity: { 11: 0, 12: 1 },
2432
+ },
2433
+ // links
2434
+ '{bridge-,tunnel-,}street-motorway-link:outline': {
2435
+ minzoom: 12,
2436
+ size: { 12: 2, 14: 3, 16: 7, 18: 14, 20: 40 },
2437
+ // opacity: { 12: 0, 13: 1 }, // no fade-in because those are merged in lower zooms
2438
+ },
2439
+ '{bridge-,tunnel-,}street-motorway-link': {
2440
+ minzoom: 12,
2441
+ size: { 12: 1, 14: 2, 16: 5, 18: 12, 20: 38 },
2442
+ // opacity: { 12: 0, 13: 1 }, // no fade-in because those are merged in lower zooms
2443
+ },
2444
+ '{bridge-,tunnel-,}street-{trunk,primary,secondary}-link:outline': {
2445
+ minzoom: 13,
2446
+ size: { 12: 2, 14: 3, 16: 7, 18: 14, 20: 40 },
2447
+ // opacity: { 13: 0, 14: 1 }, // no fade-in because those are merged in lower zooms
2448
+ },
2449
+ '{bridge-,tunnel-,}street-{trunk,primary,secondary}-link': {
2450
+ minzoom: 13,
2451
+ size: { 12: 1, 14: 2, 16: 5, 18: 12, 20: 38 },
2452
+ // opacity: { 13: 0, 14: 1 }, // no fade-in because those are merged in lower zooms
2453
+ },
2454
+ // minor streets
2455
+ '{bridge-,tunnel-,}street-{tertiary,tertiary-link,unclassified,residential,livingstreet,pedestrian}*:outline': {
2456
+ size: { 12: 2, 14: 3, 16: 6, 18: 26, 19: 64, 20: 128 },
2457
+ opacity: { 12: 0, 13: 1 },
2458
+ },
2459
+ '{bridge-,tunnel-,}street-{tertiary,tertiary-link,unclassified,residential,livingstreet,pedestrian}*': {
2460
+ size: { 12: 1, 14: 2, 16: 5, 18: 24, 19: 60, 20: 120 },
2461
+ opacity: { 12: 0, 13: 1 },
2462
+ },
2463
+ // service and tracks
2464
+ '{bridge-,tunnel-,}street-{service,track}:outline': {
2465
+ size: { 14: 2, 16: 4, 18: 18, 19: 48, 20: 96 },
2466
+ opacity: { 14: 0, 15: 1 },
2467
+ },
2468
+ '{bridge-,tunnel-,}street-{service,track}': {
2469
+ size: { 14: 1, 16: 3, 18: 16, 19: 44, 20: 88 },
2470
+ opacity: { 14: 0, 15: 1 },
2471
+ },
2472
+ // ways
2473
+ '{bridge-,tunnel-,}way-*:outline': {
2474
+ size: { 15: 0, 16: 5, 18: 7, 19: 12, 20: 22 },
2475
+ minzoom: 15,
2476
+ },
2477
+ '{bridge-,tunnel-,}way-*': {
2478
+ size: { 15: 0, 16: 4, 18: 6, 19: 10, 20: 20 },
2479
+ minzoom: 15,
2480
+ },
2481
+ // foot
2482
+ '{bridge-,}way-{footway,path,steps}:outline': {
2483
+ color: colors.foot.darken(0.1),
2484
+ },
2485
+ '{bridge-,}way-{footway,path,steps}': {
2486
+ color: colors.foot.lighten(0.02),
2487
+ },
2488
+ 'tunnel-way-{footway,path,steps}:outline': {
2489
+ color: colors.foot.darken(0.1).saturate(-0.5),
2490
+ },
2491
+ 'tunnel-way-{footway,path,steps}': {
2492
+ color: colors.foot.darken(0.02).saturate(-0.5),
2493
+ lineDasharray: [1, 0.2],
2494
+ },
2495
+ // cycleway
2496
+ '{bridge-,}way-cycleway:outline': {
2497
+ color: colors.cycle.darken(0.1),
2498
+ },
2499
+ '{bridge-,}way-cycleway': {
2500
+ color: colors.cycle,
2501
+ },
2502
+ 'tunnel-way-cycleway:outline': {
2503
+ color: colors.cycle.darken(0.1).saturate(-0.5),
2504
+ },
2505
+ 'tunnel-way-cycleway': {
2506
+ color: colors.cycle.darken(0.02).saturate(-0.5),
2507
+ lineDasharray: [1, 0.2],
2508
+ },
2509
+ // cycle streets overlay
2510
+ '{bridge-,tunnel-,}street-{tertiary,tertiary-link,unclassified,residential,livingstreet,pedestrian}-bicycle': {
2511
+ lineCap: 'butt',
2512
+ color: colors.cycle,
2513
+ },
2514
+ // pedestrian
2515
+ 'street-pedestrian': {
2516
+ size: { 12: 1, 14: 2, 16: 5, 18: 24, 19: 60, 20: 120 },
2517
+ opacity: { 13: 0, 14: 1 },
2518
+ color: colors.foot,
2519
+ },
2520
+ 'street-pedestrian-zone': {
2521
+ color: colors.foot.lighten(0.02).fade(0.75),
2522
+ opacity: { 14: 0, 15: 1 },
2523
+ },
2524
+ // rail, lightrail
2525
+ '{tunnel-,bridge-,}transport-{rail,lightrail}:outline': {
2526
+ color: colors.rail,
2527
+ size: { 8: 1, 13: 1, 15: 3, 16: 4, 18: 8, 19: 11, 20: 14 },
2528
+ },
2529
+ '{tunnel-,bridge-,}transport-{rail,lightrail}': {
2530
+ color: colors.rail.lighten(0.25),
2531
+ size: { 8: 1, 13: 1, 15: 2, 16: 3, 18: 6, 19: 8, 20: 10 },
2532
+ lineDasharray: [2, 2],
2533
+ },
2534
+ // subway
2535
+ '{tunnel-,bridge-,}transport-subway:outline': {
2536
+ color: colors.subway,
2537
+ size: { 11: 0, 12: 1, 15: 3, 16: 3, 18: 6, 19: 8, 20: 10 },
2538
+ },
2539
+ '{tunnel-,bridge-,}transport-subway': {
2540
+ color: colors.subway.lighten(0.25),
2541
+ size: { 11: 0, 12: 1, 15: 2, 16: 2, 18: 5, 19: 6, 20: 8 },
2542
+ lineDasharray: [2, 2],
2543
+ },
2544
+ // monorail
2545
+ '{tunnel-,bridge-,}transport-{tram,narrowgauge,funicular,monorail}:outline': {
2546
+ minzoom: 15,
2547
+ color: colors.rail,
2548
+ size: { 15: 0, 16: 5, 18: 7, 20: 20 },
2549
+ lineDasharray: [0.1, 0.5],
2550
+ },
2551
+ '{tunnel-,bridge-,}transport-{tram,narrowgauge,funicular,monorail}': {
2552
+ minzoom: 13,
2553
+ size: { 13: 0, 16: 1, 17: 2, 18: 3, 20: 5 },
2554
+ color: colors.rail,
2555
+ },
2556
+ // bridge
2557
+ '{bridge-,}transport-rail:outline': {
2558
+ opacity: { 8: 0, 9: 1 },
2559
+ },
2560
+ '{bridge-,}transport-rail': {
2561
+ opacity: { 14: 0, 15: 1 },
2562
+ },
2563
+ '{bridge-,}transport-{lightrail,subway}:outline': {
2564
+ opacity: { 11: 0, 12: 1 },
2565
+ },
2566
+ '{bridge-,}transport-{lightrail,subway}': {
2567
+ opacity: { 14: 0, 15: 1 },
2568
+ },
2569
+ // tunnel
2570
+ 'tunnel-transport-rail:outline': {
2571
+ opacity: { 8: 0, 9: 0.3 },
2572
+ },
2573
+ 'tunnel-transport-rail': {
2574
+ opacity: { 14: 0, 15: 0.3 },
2575
+ },
2576
+ 'tunnel-transport-{lightrail,subway}:outline': {
2577
+ opacity: { 11: 0, 12: 0.5 },
2578
+ },
2579
+ 'tunnel-transport-{lightrail,subway}': {
2580
+ opacity: { 14: 0, 15: 1 },
2581
+ },
2582
+ // ferry
2583
+ 'transport-ferry': {
2584
+ minzoom: 10,
2585
+ color: colors.water.darken(0.1),
2586
+ size: { 10: 1, 13: 2, 14: 3, 16: 4, 17: 6 },
2587
+ opacity: { 10: 0, 11: 1 },
2588
+ lineDasharray: [1, 1],
2589
+ },
2590
+ // labels
2591
+ 'label-boundary-*': {
2592
+ color: colors.label,
2593
+ font: fonts.regular,
2594
+ textTransform: 'uppercase',
2595
+ textHaloColor: colors.labelHalo,
2596
+ textHaloWidth: 2,
2597
+ textHaloBlur: 1,
2598
+ textAnchor: 'top',
2599
+ textOffset: [0, 0.2],
2600
+ textPadding: 0,
2601
+ textOptional: true,
2602
+ },
2603
+ 'label-boundary-country-large': {
2604
+ minzoom: 2,
2605
+ size: { 2: 8, 5: 13 },
2606
+ },
2607
+ 'label-boundary-country-medium': {
2608
+ minzoom: 3,
2609
+ size: { 3: 8, 5: 12 },
2610
+ },
2611
+ 'label-boundary-country-small': {
2612
+ minzoom: 4,
2613
+ size: { 4: 8, 5: 11 },
2614
+ },
2615
+ 'label-boundary-state': {
2616
+ minzoom: 5,
2617
+ color: colors.label.lighten(0.05),
2618
+ size: { 5: 8, 8: 12 },
2619
+ },
2620
+ 'label-place-*': {
2621
+ color: colors.label.rotateHue(-15).saturate(1).darken(0.05),
2622
+ font: fonts.regular,
2623
+ textHaloColor: colors.labelHalo,
2624
+ textHaloWidth: 2,
2625
+ textHaloBlur: 1,
2626
+ },
2627
+ 'label-place-capital': {
2628
+ minzoom: 5,
2629
+ size: { 5: 12, 10: 16 },
2630
+ },
2631
+ 'label-place-statecapital': {
2632
+ minzoom: 6,
2633
+ size: { 6: 11, 10: 15 },
2634
+ },
2635
+ 'label-place-city': {
2636
+ minzoom: 7,
2637
+ size: { 7: 11, 10: 14 },
2638
+ },
2639
+ 'label-place-town': {
2640
+ minzoom: 9,
2641
+ size: { 8: 11, 12: 14 },
2642
+ },
2643
+ 'label-place-village': {
2644
+ minzoom: 11,
2645
+ size: { 9: 11, 12: 14 },
2646
+ },
2647
+ 'label-place-hamlet': {
2648
+ minzoom: 13,
2649
+ size: { 10: 11, 12: 14 },
2650
+ },
2651
+ // all the city things
2652
+ 'label-place-suburb': {
2653
+ minzoom: 11,
2654
+ size: { 11: 11, 13: 14 },
2655
+ textTransform: 'uppercase',
2656
+ color: colors.label.rotateHue(-30).saturate(1).darken(0.05),
2657
+ },
2658
+ 'label-place-quarter': {
2659
+ minzoom: 13,
2660
+ size: { 13: 13 },
2661
+ textTransform: 'uppercase',
2662
+ color: colors.label.rotateHue(-40).saturate(1).darken(0.05),
2663
+ },
2664
+ 'label-place-neighbourhood': {
2665
+ minzoom: 14,
2666
+ size: { 14: 12 },
2667
+ textTransform: 'uppercase',
2668
+ color: colors.label.rotateHue(-50).saturate(1).darken(0.05),
2669
+ },
2670
+ 'label-motorway-shield': {
2671
+ color: colors.shield,
2672
+ font: fonts.bold,
2673
+ textHaloColor: colors.motorway,
2674
+ textHaloWidth: 0.1,
2675
+ textHaloBlur: 1,
2676
+ symbolPlacement: 'line',
2677
+ textAnchor: 'center',
2678
+ minzoom: 14,
2679
+ size: { 14: 10, 18: 12, 20: 16 },
2680
+ },
2681
+ 'label-street-*': {
2682
+ color: colors.label,
2683
+ font: fonts.regular,
2684
+ textHaloColor: colors.labelHalo,
2685
+ textHaloWidth: 2,
2686
+ textHaloBlur: 1,
2687
+ symbolPlacement: 'line',
2688
+ textAnchor: 'center',
2689
+ minzoom: 12,
2690
+ size: { 12: 10, 15: 13 },
2691
+ },
2692
+ 'label-address-housenumber': {
2693
+ font: fonts.regular,
2694
+ textHaloColor: colors.building.lighten(0.05),
2695
+ textHaloWidth: 2,
2696
+ textHaloBlur: 1,
2697
+ symbolPlacement: 'point',
2698
+ textAnchor: 'center',
2699
+ minzoom: 17,
2700
+ size: { 17: 8, 19: 10 },
2701
+ color: colors.building.darken(0.3),
2702
+ },
2703
+ // markings
2704
+ 'marking-oneway{-reverse,}': {
2705
+ minzoom: 16,
2706
+ image: 'basics:marking-arrow',
2707
+ opacity: { 16: 0, 17: 0.7 },
2708
+ font: fonts.regular,
2709
+ },
2710
+ // TODO: bicycle and pedestrian
2711
+ // transit
2712
+ 'symbol-*': {
2713
+ iconSize: 1,
2714
+ symbolPlacement: 'point',
2715
+ iconOpacity: 0.7,
2716
+ iconKeepUpright: true,
2717
+ font: fonts.regular,
2718
+ size: 10,
2719
+ color: colors.symbol,
2720
+ iconAnchor: 'bottom',
2721
+ textAnchor: 'top',
2722
+ textHaloColor: colors.labelHalo,
2723
+ textHaloWidth: 2,
2724
+ textHaloBlur: 1,
2725
+ },
2726
+ 'symbol-transit-airport': {
2727
+ minzoom: 12,
2728
+ image: 'basics:icon-airport',
2729
+ iconSize: { 12: 0.5, 14: 1 },
2730
+ },
2731
+ 'symbol-transit-airfield': {
2732
+ minzoom: 13,
2733
+ image: 'basics:icon-airfield',
2734
+ iconSize: { 13: 0.5, 15: 1 },
2735
+ },
2736
+ 'symbol-transit-station': {
2737
+ minzoom: 13,
2738
+ image: 'basics:icon-rail',
2739
+ iconSize: { 13: 0.5, 15: 1 },
2740
+ },
2741
+ 'symbol-transit-lightrail': {
2742
+ minzoom: 14,
2743
+ image: 'basics:icon-rail_light',
2744
+ iconSize: { 14: 0.5, 16: 1 },
2745
+ },
2746
+ 'symbol-transit-subway': {
2747
+ minzoom: 14,
2748
+ image: 'basics:icon-rail_metro',
2749
+ iconSize: { 14: 0.5, 16: 1 },
2750
+ },
2751
+ 'symbol-transit-tram': {
2752
+ minzoom: 15,
2753
+ image: 'basics:transport-tram',
2754
+ iconSize: { 15: 0.5, 17: 1 },
2755
+ },
2756
+ 'symbol-transit-bus': {
2757
+ minzoom: 16,
2758
+ image: 'basics:icon-bus',
2759
+ iconSize: { 16: 0.5, 18: 1 },
2760
+ },
2761
+ // TODO: localized symbols? depends on shortbread
2762
+ // pois
2763
+ 'poi-*': {
2764
+ minzoom: 16,
2765
+ iconSize: { 16: 0.5, 19: 0.5, 20: 1 },
2766
+ opacity: { 16: 0, 17: 0.4 },
2767
+ symbolPlacement: 'point',
2768
+ iconOptional: true,
2769
+ font: fonts.regular,
2770
+ color: colors.poi,
2771
+ },
2772
+ 'poi-amenity': {
2773
+ image: ['match',
2774
+ ['get', 'amenity'],
2775
+ 'arts_centre', 'basics:icon-art_gallery',
2776
+ 'atm', 'basics:icon-atm',
2777
+ 'bank', 'basics:icon-bank',
2778
+ 'bar', 'basics:icon-bar',
2779
+ 'bench', 'basics:icon-bench',
2780
+ 'bicycle_rental', 'basics:icon-bicycle_share',
2781
+ 'biergarten', 'basics:icon-beergarden',
2782
+ 'cafe', 'basics:icon-cafe',
2783
+ 'car_rental', 'basics:icon-car_rental',
2784
+ 'car_sharing', 'basics:icon-car_rental',
2785
+ 'car_wash', 'basics:icon-car_wash',
2786
+ 'cinema', 'basics:icon-cinema',
2787
+ //'clinic', 'basics:icon-clinic',
2788
+ 'college', 'basics:icon-college',
2789
+ 'community_centre', 'basics:icon-community',
2790
+ //'courthouse', 'basics:icon-courthouse',
2791
+ 'dentist', 'basics:icon-dentist',
2792
+ 'doctors', 'basics:icon-doctor',
2793
+ 'dog_park', 'basics:icon-dog_park',
2794
+ 'drinking_water', 'basics:icon-drinking_water',
2795
+ 'embassy', 'basics:icon-embassy',
2796
+ 'fast_food', 'basics:icon-fast_food',
2797
+ 'fire_station', 'basics:icon-fire_station',
2798
+ //'food_court', 'basics:icon-food_court',
2799
+ 'fountain', 'basics:icon-fountain',
2800
+ 'grave_yard', 'basics:icon-cemetery',
2801
+ 'hospital', 'basics:icon-hospital',
2802
+ 'hunting_stand', 'basics:icon-huntingstand',
2803
+ 'library', 'basics:icon-library',
2804
+ 'marketplace', 'basics:icon-marketplace',
2805
+ 'nightclub', 'basics:icon-nightclub',
2806
+ 'nursing_home', 'basics:icon-nursinghome',
2807
+ 'pharmacy', 'basics:icon-pharmacy',
2808
+ 'place_of_worship', 'basics:icon-place_of_worship',
2809
+ 'playground', 'basics:icon-playground',
2810
+ 'police', 'basics:icon-police',
2811
+ 'post_box', 'basics:icon-postbox',
2812
+ 'post_office', 'basics:icon-post',
2813
+ 'prison', 'basics:icon-prison',
2814
+ 'pub', 'basics:icon-beer',
2815
+ //'public_building', 'basics:icon-public_building',
2816
+ 'recycling', 'basics:icon-recycling',
2817
+ 'restaurant', 'basics:icon-restaurant',
2818
+ 'school', 'basics:icon-school',
2819
+ 'shelter', 'basics:icon-shelter',
2820
+ 'telephone', 'basics:icon-telephone',
2821
+ 'theatre', 'basics:icon-theatre',
2822
+ 'toilets', 'basics:icon-toilet',
2823
+ 'townhall', 'basics:icon-town_hall',
2824
+ //'university', 'basics:icon-university',
2825
+ 'vending_machine', 'basics:icon-vendingmachine',
2826
+ 'veterinary', 'basics:icon-veterinary',
2827
+ 'waste_basket', 'basics:icon-waste_basket',
2828
+ '',
2829
+ ],
2830
+ },
2831
+ 'poi-leisure': {
2832
+ image: ['match',
2833
+ ['get', 'leisure'],
2834
+ 'golf_course', 'basics:icon-golf',
2835
+ 'ice_rink', 'basics:icon-icerink',
2836
+ 'pitch', 'basics:icon-pitch',
2837
+ //'sports_centre', 'basics:icon-sports_centre',
2838
+ 'stadium', 'basics:icon-stadium',
2839
+ 'swimming_pool', 'basics:icon-swimming',
2840
+ 'water_park', 'basics:icon-waterpark',
2841
+ 'basics:icon-sports',
2842
+ ],
2843
+ },
2844
+ 'poi-tourism': {
2845
+ image: ['match',
2846
+ ['get', 'tourism'],
2847
+ //'alpine_hut', 'basics:icon-alpine_hut',
2848
+ //'bed_and_breakfast', 'basics:icon-bed_and_breakfast',
2849
+ //'camp_site', 'basics:icon-camp_site',
2850
+ //'caravan_site', 'basics:icon-caravan_site',
2851
+ 'chalet', 'basics:icon-chalet',
2852
+ //'guest_house', 'basics:icon-guest_house',
2853
+ //'hostel', 'basics:icon-hostel',
2854
+ //'hotel', 'basics:icon-hotel',
2855
+ 'information', 'basics:transport-information',
2856
+ //'motel', 'basics:icon-motel',
2857
+ 'picnic_site', 'basics:icon-picnic_site',
2858
+ //'theme_park', 'basics:icon-theme_park',
2859
+ 'viewpoint', 'basics:icon-viewpoint',
2860
+ 'zoo', 'basics:icon-zoo',
2861
+ '',
2862
+ ],
2863
+ },
2864
+ 'poi-shop': {
2865
+ image: ['match',
2866
+ ['get', 'shop'],
2867
+ 'alcohol', 'basics:icon-alcohol_shop',
2868
+ 'bakery', 'basics:icon-bakery',
2869
+ 'beauty', 'basics:icon-beauty',
2870
+ 'beverages', 'basics:icon-beverages',
2871
+ //'bicycle', 'basics:icon-bicycle',
2872
+ 'books', 'basics:icon-books',
2873
+ 'butcher', 'basics:icon-butcher',
2874
+ //'car', 'basics:icon-car',
2875
+ 'chemist', 'basics:icon-chemist',
2876
+ 'clothes', 'basics:icon-clothes',
2877
+ //'computer', 'basics:icon-computer',
2878
+ //'convinience', 'basics:icon-convinience',
2879
+ //'department_store', 'basics:icon-department_store',
2880
+ 'doityourself', 'basics:icon-doityourself',
2881
+ 'dry_cleaning', 'basics:icon-drycleaning',
2882
+ 'florist', 'basics:icon-florist',
2883
+ 'furniture', 'basics:icon-furniture',
2884
+ 'garden_centre', 'basics:icon-garden_centre',
2885
+ 'general', 'basics:icon-shop',
2886
+ 'gift', 'basics:icon-gift',
2887
+ 'greengrocer', 'basics:icon-greengrocer',
2888
+ 'hairdresser', 'basics:icon-hairdresser',
2889
+ 'hardware', 'basics:icon-hardware',
2890
+ 'jewelry', 'basics:icon-jewelry_store',
2891
+ 'kiosk', 'basics:icon-kiosk',
2892
+ 'laundry', 'basics:icon-laundry',
2893
+ //'mall', 'basics:icon-mall',
2894
+ //'mobile_phone', 'basics:icon-mobile_phone',
2895
+ 'newsagent', 'basics:icon-newsagent',
2896
+ 'optican', 'basics:icon-optician',
2897
+ 'outdoor', 'basics:icon-outdoor',
2898
+ 'shoes', 'basics:icon-shoes',
2899
+ 'sports', 'basics:icon-sports',
2900
+ 'stationery', 'basics:icon-stationery',
2901
+ //'supermarket', 'basics:icon-supermarket',
2902
+ 'toys', 'basics:icon-toys',
2903
+ 'travel_agency', 'basics:icon-travel_agent',
2904
+ 'video', 'basics:icon-video',
2905
+ 'basics:icon-shop',
2906
+ ],
2907
+ },
2908
+ 'poi-man_made': {
2909
+ image: ['match',
2910
+ ['get', 'man_made'],
2911
+ 'lighthouse', 'basics:icon-lighthouse',
2912
+ 'surveillance', 'basics:icon-surveillance',
2913
+ 'tower', 'basics:icon-observation_tower',
2914
+ //'wastewater_plant', 'basics:icon-wastewater_plant',
2915
+ //'water_well', 'basics:icon-water_well',
2916
+ //'water_works', 'basics:icon-water_works',
2917
+ 'watermill', 'basics:icon-watermill',
2918
+ 'windmill', 'basics:icon-windmill',
2919
+ '',
2920
+ ],
2921
+ },
2922
+ 'poi-historic': {
2923
+ image: ['match',
2924
+ ['get', 'historic'],
2925
+ //'archaelogical_site', 'basics:icon-archaelogical_site',
2926
+ 'artwork', 'basics:icon-artwork',
2927
+ //'battlefield', 'basics:icon-battlefield',
2928
+ 'castle', 'basics:icon-castle',
2929
+ //'fort', 'basics:icon-fort',
2930
+ //'memorial', 'basics:icon-memorial',
2931
+ 'monument', 'basics:icon-monument',
2932
+ //'ruins', 'basics:icon-ruins',
2933
+ //'wayside_cross', 'basics:icon-wayside_cross',
2934
+ 'wayside_shrine', 'basics:icon-shrine',
2935
+ 'basics:icon-historic',
2936
+ ],
2937
+ },
2938
+ 'poi-emergency': {
2939
+ image: ['match',
2940
+ ['get', 'emergency'],
2941
+ 'defibrillator', 'basics:icon-defibrillator',
2942
+ 'fire_hydrant', 'basics:icon-hydrant',
2943
+ 'phone', 'basics:icon-emergency_phone',
2944
+ '',
2945
+ ],
2946
+ },
2947
+ /*
2948
+ 'poi-highway': {
2949
+ image: ['match',
2950
+ ['get', 'highway'],
2951
+ //'emergency_access_point', 'basics:icon-emergency_access_point',
2952
+ ''
2953
+ ]
2954
+ },
2955
+ 'poi-office': {
2956
+ image: ['match',
2957
+ ['get', 'office'],
2958
+ //'diplomatic', 'basics:icon-diplomatic',
2959
+ ''
2960
+ ]
2961
+ },
2962
+ */
2963
+ };
2964
+ }
2965
+ }
2966
+
2967
+ class Eclipse extends Colorful {
2968
+ name = 'Eclipse';
2969
+ constructor() {
2970
+ super();
2971
+ this.transformDefaultColors(color => color.invertLuminosity());
2972
+ }
2973
+ }
2974
+
2975
+ class Graybeard extends Colorful {
2976
+ name = 'Graybeard';
2977
+ constructor() {
2978
+ super();
2979
+ this.transformDefaultColors(color => color.saturate(-1));
2980
+ }
2981
+ }
2982
+
2983
+ class Neutrino extends Colorful {
2984
+ name = 'Neutrino';
2985
+ defaultFonts = {
2986
+ regular: 'noto_sans_regular',
2987
+ bold: 'noto_sans_bold',
2988
+ };
2989
+ defaultColors = {
2990
+ /** Color representing land areas. */
2991
+ land: '#f6f0f6',
2992
+ /** Color representing bodies of water such as lakes and rivers. */
2993
+ water: '#cbd2df',
2994
+ /** Color for grassy areas and fields. */
2995
+ grass: '#e7e9e5',
2996
+ /** Color for wooded or forested areas. */
2997
+ wood: '#d9e3d9',
2998
+ /** Color used for agricultural land. */
2999
+ agriculture: '#f8eeee',
3000
+ /** Color for site areas such as parks or recreational facilities. */
3001
+ commercial: '#ebe8e6',
3002
+ /** Primary color for buildings. */
3003
+ building: '#e0d1d9',
3004
+ /** Color for streets and roads. */
3005
+ street: '#ffffff',
3006
+ /** Color used for boundaries, such as national or state lines. */
3007
+ boundary: '#e6ccd8',
3008
+ /** Color for footpaths and pedestrian areas. */
3009
+ foot: '#fef8ff',
3010
+ /** Color used for railways. */
3011
+ rail: '#e8d5e0',
3012
+ /** Primary color used for text labels. */
3013
+ label: '#cbb7b7',
3014
+ // Don't need these colors:
3015
+ buildingbg: '#000',
3016
+ burial: '#000',
3017
+ construction: '#000',
3018
+ cycle: '#000',
3019
+ danger: '#000',
3020
+ disputed: '#000',
3021
+ education: '#000',
3022
+ glacier: '#000',
3023
+ hospital: '#000',
3024
+ industrial: '#000',
3025
+ labelHalo: '#000',
3026
+ leisure: '#000',
3027
+ motorway: '#000',
3028
+ motorwaybg: '#000',
3029
+ park: '#000',
3030
+ parking: '#000',
3031
+ poi: '#000',
3032
+ prison: '#000',
3033
+ residential: '#000',
3034
+ rock: '#000',
3035
+ sand: '#000',
3036
+ shield: '#000',
3037
+ streetbg: '#000',
3038
+ subway: '#000',
3039
+ symbol: '#000',
3040
+ trunk: '#000',
3041
+ trunkbg: '#000',
3042
+ waste: '#000',
3043
+ wetland: '#000',
3044
+ };
3045
+ getStyleRules(options) {
3046
+ const { colors, fonts } = options;
3047
+ return {
3048
+ 'background': {
3049
+ color: colors.land,
3050
+ },
3051
+ 'boundary-{country,state}': {
3052
+ color: colors.boundary,
3053
+ },
3054
+ 'boundary-country:outline': {
3055
+ size: { 2: 2, 10: 6 },
3056
+ opacity: { 2: 0, 4: 0.3 },
3057
+ color: colors.land.lighten(0.05),
3058
+ lineBlur: 1,
3059
+ },
3060
+ 'boundary-country': {
3061
+ size: { 2: 1, 10: 4 },
3062
+ opacity: { 2: 0, 4: 1 },
3063
+ },
3064
+ 'boundary-state:outline': {
3065
+ size: { 7: 3, 10: 5 },
3066
+ opacity: { 7: 0, 8: 0.3 },
3067
+ color: colors.land.lighten(0.05),
3068
+ lineBlur: 1,
3069
+ },
3070
+ 'boundary-state': {
3071
+ size: { 7: 2, 10: 3 },
3072
+ opacity: { 7: 0, 8: 1 },
3073
+ lineDasharray: [0, 1.5, 1, 1.5],
3074
+ lineCap: 'round',
3075
+ lineJoin: 'round',
3076
+ },
3077
+ 'water-*': {
3078
+ color: colors.water,
3079
+ },
3080
+ 'water-area': {
3081
+ opacity: { 4: 0, 6: 1 },
3082
+ },
3083
+ 'water-area-*': {
3084
+ opacity: { 4: 0, 6: 1 },
3085
+ },
3086
+ 'water-{pier,dam}-area': {
3087
+ color: colors.land,
3088
+ opacity: { 12: 0, 13: 1 },
3089
+ },
3090
+ 'water-pier': {
3091
+ color: colors.land,
3092
+ },
3093
+ 'land-*': {
3094
+ color: colors.land,
3095
+ },
3096
+ 'land-forest': {
3097
+ color: colors.wood,
3098
+ opacity: { 7: 0, 8: 1 },
3099
+ },
3100
+ 'land-grass': {
3101
+ color: colors.grass,
3102
+ opacity: { 11: 0, 12: 1 },
3103
+ },
3104
+ 'land-{park,garden,vegetation}': {
3105
+ color: colors.grass.darken(0.05).saturate(0.05),
3106
+ opacity: { 11: 0, 12: 1 },
3107
+ },
3108
+ 'land-agriculture': {
3109
+ color: colors.agriculture,
3110
+ opacity: { 10: 0, 11: 1 },
3111
+ },
3112
+ 'land-{commercial,industrial,residential}': {
3113
+ color: colors.land.darken(0.03),
3114
+ opacity: { 10: 0, 11: 1 },
3115
+ },
3116
+ 'site-{bicycleparking,parking}': {
3117
+ color: colors.commercial,
3118
+ },
3119
+ 'building': {
3120
+ color: colors.building,
3121
+ opacity: { 14: 0, 15: 1 },
3122
+ },
3123
+ 'bridge': {
3124
+ color: colors.land.darken(0.01),
3125
+ },
3126
+ '{tunnel-,bridge-,}street-*': {
3127
+ color: colors.street,
3128
+ size: 1,
3129
+ lineJoin: 'round',
3130
+ lineCap: 'round',
3131
+ },
3132
+ '{tunnel-,}street-*:outline': {
3133
+ color: colors.street.darken(0.1),
3134
+ lineJoin: 'round',
3135
+ lineCap: 'round',
3136
+ },
3137
+ 'tunnel-street-*': {
3138
+ color: colors.street.darken(0.03),
3139
+ },
3140
+ 'tunnel-street-*:outline': {
3141
+ color: colors.street.darken(0.13),
3142
+ lineDasharray: [1, 2],
3143
+ },
3144
+ 'bridge-street-*:outline': {
3145
+ color: colors.street.darken(0.15),
3146
+ },
3147
+ // motorway
3148
+ '{bridge-street,tunnel-street,street}-motorway:outline': {
3149
+ size: { 5: 2, 10: 5, 14: 5, 16: 14, 18: 38, 19: 84, 20: 168 },
3150
+ opacity: { 5: 0, 6: 1 },
3151
+ },
3152
+ '{bridge-street,tunnel-street,street}-motorway': {
3153
+ size: { 5: 1, 10: 4, 14: 4, 16: 12, 18: 36, 19: 80, 20: 160 },
3154
+ opacity: { 5: 0, 6: 1 },
3155
+ },
3156
+ // trunk
3157
+ '{bridge-street,tunnel-street,street}-trunk:outline': {
3158
+ size: { 7: 2, 10: 4, 14: 6, 16: 12, 18: 36, 19: 74, 20: 144 },
3159
+ opacity: { 7: 0, 8: 1 },
3160
+ },
3161
+ '{bridge-street,tunnel-street,street}-trunk': {
3162
+ size: { 7: 1, 10: 3, 14: 5, 16: 10, 18: 34, 19: 70, 20: 140 },
3163
+ opacity: { 7: 0, 8: 1 },
3164
+ },
3165
+ // primary
3166
+ '{bridge-street,tunnel-street,street}-primary:outline': {
3167
+ size: { 7: 2, 10: 4, 14: 6, 16: 12, 18: 36, 19: 74, 20: 144 },
3168
+ opacity: { 7: 0, 8: 1 },
3169
+ },
3170
+ '{bridge-street,tunnel-street,street}-primary': {
3171
+ size: { 7: 1, 10: 3, 14: 5, 16: 10, 18: 34, 19: 70, 20: 140 },
3172
+ opacity: { 7: 0, 8: 1 },
3173
+ },
3174
+ // secondary
3175
+ '{bridge-street,tunnel-street,street}-secondary:outline': {
3176
+ size: { 11: 2, 14: 5, 16: 8, 18: 30, 19: 68, 20: 138 },
3177
+ opacity: { 11: 0, 12: 1 },
3178
+ },
3179
+ '{bridge-street,tunnel-street,street}-secondary': {
3180
+ size: { 11: 1, 14: 4, 16: 6, 18: 28, 19: 64, 20: 130 },
3181
+ opacity: { 11: 0, 12: 1 },
3182
+ },
3183
+ // links
3184
+ '{bridge-street,tunnel-street,street}-motorway-link:outline': {
3185
+ minzoom: 12,
3186
+ size: { 12: 2, 14: 3, 16: 7, 18: 14, 20: 40 },
3187
+ opacity: { 12: 0, 13: 1 },
3188
+ },
3189
+ '{bridge-street,tunnel-street,street}-motorway-link': {
3190
+ minzoom: 12,
3191
+ size: { 12: 1, 14: 2, 16: 5, 18: 12, 20: 38 },
3192
+ opacity: { 12: 0, 13: 1 },
3193
+ },
3194
+ '{bridge-street,tunnel-street,street}-{trunk,primary,secondary}-link:outline': {
3195
+ minzoom: 13,
3196
+ size: { 12: 2, 14: 3, 16: 7, 18: 14, 20: 40 },
3197
+ opacity: { 13: 0, 14: 1 },
3198
+ },
3199
+ '{bridge-street,tunnel-street,street}-{trunk,primary,secondary}-link': {
3200
+ minzoom: 13,
3201
+ size: { 12: 1, 14: 2, 16: 5, 18: 12, 20: 38 },
3202
+ opacity: { 13: 0, 14: 1 },
3203
+ },
3204
+ // minor streets
3205
+ '{bridge-street,tunnel-street,street}-{tertiary,tertiary-link,unclassified,residential,living_street,pedestrian}:outline': {
3206
+ size: { 12: 2, 14: 3, 16: 6, 18: 26, 19: 64, 20: 128 },
3207
+ opacity: { 12: 0, 13: 1 },
3208
+ },
3209
+ '{bridge-street,tunnel-street,street}-{tertiary,tertiary-link,unclassified,residential,living_street,pedestrian}': {
3210
+ size: { 12: 1, 14: 2, 16: 5, 18: 24, 19: 60, 20: 120 },
3211
+ opacity: { 12: 0, 13: 1 },
3212
+ },
3213
+ // service and tracks
3214
+ '{bridge-street,tunnel-street,street}-{service,track}:outline': {
3215
+ size: { 14: 2, 16: 4, 18: 18, 19: 48, 20: 96 },
3216
+ opacity: { 14: 0, 15: 1 },
3217
+ },
3218
+ '{bridge-street,tunnel-street,street}-{service,track}': {
3219
+ size: { 14: 1, 16: 3, 18: 16, 19: 44, 20: 88 },
3220
+ opacity: { 14: 0, 15: 1 },
3221
+ },
3222
+ // ways, surface only
3223
+ 'way-{footway,path,steps}:outline': {
3224
+ size: { 17: 0, 18: 3 },
3225
+ opacity: { 17: 0, 18: 1 },
3226
+ minzoom: 17,
3227
+ color: colors.foot.darken(0.05),
3228
+ },
3229
+ 'way-{footway,path,steps}': {
3230
+ size: { 17: 0, 18: 2 },
3231
+ opacity: { 17: 0, 18: 1 },
3232
+ minzoom: 17,
3233
+ color: colors.foot,
3234
+ },
3235
+ 'street-pedestrian': {
3236
+ size: { 13: 1, 15: 3 },
3237
+ opacity: { 13: 0, 14: 1 },
3238
+ color: colors.foot,
3239
+ },
3240
+ 'street-pedestrian-zone': {
3241
+ color: colors.foot,
3242
+ opacity: { 14: 0, 15: 1 },
3243
+ },
3244
+ // rail
3245
+ '{tunnel-,bridge-,}transport-{rail,lightrail}:outline': {
3246
+ color: colors.rail,
3247
+ size: { 8: 1, 12: 1, 15: 3 },
3248
+ },
3249
+ '{tunnel-,bridge-,}transport-{rail,lightrail}': {
3250
+ color: colors.rail.lighten(0.1),
3251
+ size: { 8: 1, 12: 1, 15: 2 },
3252
+ lineDasharray: [2, 2],
3253
+ },
3254
+ // bridge
3255
+ '{bridge-,}transport-rail:outline': {
3256
+ opacity: { 8: 0, 9: 1 },
3257
+ },
3258
+ '{bridge-,}transport-rail': {
3259
+ opacity: { 14: 0, 15: 1 },
3260
+ },
3261
+ '{bridge-,}transport-lightrail:outline': {
3262
+ opacity: { 11: 0, 12: 1 },
3263
+ },
3264
+ '{bridge-,}transport-lightrail': {
3265
+ opacity: { 14: 0, 15: 1 },
3266
+ },
3267
+ // tunnel
3268
+ 'tunnel-transport-rail:outline': {
3269
+ opacity: { 8: 0, 9: 0.3 },
3270
+ },
3271
+ 'tunnel-transport-rail': {
3272
+ opacity: { 14: 0, 15: 0.3 },
3273
+ },
3274
+ 'tunnel-transport-lightrail:outline': {
3275
+ opacity: { 11: 0, 12: 0.3 },
3276
+ },
3277
+ 'tunnel-transport-lightrail': {
3278
+ opacity: { 14: 0, 15: 0.3 },
3279
+ },
3280
+ // labels
3281
+ 'label-boundary-*': {
3282
+ color: colors.label,
3283
+ font: fonts.bold,
3284
+ textTransform: 'uppercase',
3285
+ textHaloColor: colors.label.lighten(0.5),
3286
+ textHaloWidth: 0.1,
3287
+ textHaloBlur: 1,
3288
+ },
3289
+ 'label-boundary-country-large': {
3290
+ minzoom: 2,
3291
+ size: { 2: 11, 5: 16 },
3292
+ },
3293
+ 'label-boundary-country-medium': {
3294
+ minzoom: 3,
3295
+ size: { 3: 11, 5: 15 },
3296
+ },
3297
+ 'label-boundary-country-small': {
3298
+ minzoom: 4,
3299
+ size: { 4: 11, 5: 14 },
3300
+ },
3301
+ 'label-boundary-state': {
3302
+ minzoom: 5,
3303
+ color: colors.label.lighten(0.05),
3304
+ size: { 5: 8, 8: 12 },
3305
+ },
3306
+ 'label-place-*': {
3307
+ color: colors.label.rotateHue(-15).saturate(1).darken(0.05),
3308
+ font: fonts.regular,
3309
+ textHaloColor: colors.label.lighten(0.5),
3310
+ textHaloWidth: 0.1,
3311
+ textHaloBlur: 1,
3312
+ size: 1,
3313
+ },
3314
+ 'label-place-capital': {
3315
+ minzoom: 5,
3316
+ size: { 5: 12, 10: 16 },
3317
+ },
3318
+ 'label-place-statecapital': {
3319
+ minzoom: 6,
3320
+ size: { 6: 11, 10: 15 },
3321
+ },
3322
+ 'label-place-city': {
3323
+ minzoom: 7,
3324
+ size: { 7: 11, 10: 14 },
3325
+ },
3326
+ 'label-place-town': {
3327
+ minzoom: 8,
3328
+ size: { 8: 11, 12: 14 },
3329
+ },
3330
+ 'label-place-village': {
3331
+ minzoom: 9,
3332
+ size: { 9: 11, 12: 14 },
3333
+ },
3334
+ 'label-place-hamlet': {
3335
+ minzoom: 10,
3336
+ size: { 10: 11, 12: 14 },
3337
+ },
3338
+ // all the city things
3339
+ 'label-place-suburb': {
3340
+ minzoom: 11,
3341
+ size: { 11: 11, 13: 14 },
3342
+ textTransform: 'uppercase',
3343
+ color: colors.label.rotateHue(-30).saturate(1).darken(0.05),
3344
+ },
3345
+ 'label-place-quarter': {
3346
+ minzoom: 13,
3347
+ size: { 13: 13 },
3348
+ textTransform: 'uppercase',
3349
+ color: colors.label.rotateHue(-40).saturate(1).darken(0.05),
3350
+ },
3351
+ 'label-place-neighbourhood': {
3352
+ minzoom: 14,
3353
+ size: { 14: 12 },
3354
+ textTransform: 'uppercase',
3355
+ color: colors.label.rotateHue(-50).saturate(1).darken(0.05),
3356
+ },
3357
+ 'label-motorway-shield': {
3358
+ color: colors.label,
3359
+ font: fonts.regular,
3360
+ textHaloColor: colors.label.saturate(-0.5).lighten(0.1),
3361
+ textHaloWidth: 0.1,
3362
+ textHaloBlur: 1,
3363
+ symbolPlacement: 'line',
3364
+ textAnchor: 'center',
3365
+ minzoom: 14,
3366
+ size: { 14: 8, 18: 10, 20: 16 },
3367
+ },
3368
+ 'label-street-*': {
3369
+ color: colors.label,
3370
+ font: fonts.regular,
3371
+ textHaloColor: colors.label.saturate(-0.5).lighten(0.1),
3372
+ textHaloWidth: 0.1,
3373
+ textHaloBlur: 1,
3374
+ symbolPlacement: 'line',
3375
+ textAnchor: 'center',
3376
+ minzoom: 12,
3377
+ size: { 12: 10, 15: 13 },
3378
+ },
3379
+ };
3380
+ }
3381
+ }
3382
+
3383
+ class Empty extends Colorful {
3384
+ name = 'Empty';
3385
+ getStyleRules(_options) {
3386
+ return {};
3387
+ }
3388
+ }
3389
+
3390
+ // import styles
3391
+ function getStyleBuilder(styleBuilder) {
3392
+ const fn = function (options) {
3393
+ return new styleBuilder().build(options);
3394
+ };
3395
+ fn.getOptions = () => new styleBuilder().getDefaultOptions();
3396
+ return fn;
3397
+ }
3398
+ // generate style builders
3399
+ const colorful = getStyleBuilder(Colorful);
3400
+ const eclipse = getStyleBuilder(Eclipse);
3401
+ const graybeard = getStyleBuilder(Graybeard);
3402
+ const neutrino = getStyleBuilder(Neutrino);
3403
+ getStyleBuilder(Empty);
3404
+
3405
+ /**
3406
+ * Checks if an object adheres to the TileJSON specification.
3407
+ * Throws errors if the object does not conform to the expected structure or types.
3408
+ */
3409
+ function isTileJSONSpecification(spec) {
3410
+ if (typeof spec !== 'object' || spec === null) {
3411
+ throw Error('spec must be an object');
3412
+ }
3413
+ const obj = spec;
3414
+ // Common property validation
3415
+ if (obj.data != null && obj.tilejson !== '3.0.0') {
3416
+ throw Error('spec.tilejson must be "3.0.0"');
3417
+ }
3418
+ if (obj.attribution != null && typeof obj.attribution !== 'string') {
3419
+ throw Error('spec.attribution must be a string if present');
3420
+ }
3421
+ if (obj.bounds != null) {
3422
+ if (!Array.isArray(obj.bounds) || obj.bounds.length !== 4 || obj.bounds.some(num => typeof num !== 'number')) {
3423
+ throw Error('spec.bounds must be an array of four numbers if present');
3424
+ }
3425
+ const a = obj.bounds;
3426
+ if (a[0] < -180 || a[0] > 180)
3427
+ throw Error('spec.bounds[0] must be between -180 and 180');
3428
+ if (a[1] < -90 || a[1] > 90)
3429
+ throw Error('spec.bounds[1] must be between -90 and 90');
3430
+ if (a[2] < -180 || a[2] > 180)
3431
+ throw Error('spec.bounds[2] must be between -180 and 180');
3432
+ if (a[3] < -90 || a[3] > 90)
3433
+ throw Error('spec.bounds[3] must be between -90 and 90');
3434
+ if (a[0] > a[2])
3435
+ throw Error('spec.bounds[0] must be smaller than spec.bounds[2]');
3436
+ if (a[1] > a[3])
3437
+ throw Error('spec.bounds[1] must be smaller than spec.bounds[3]');
3438
+ }
3439
+ if (obj.center != null) {
3440
+ if (!Array.isArray(obj.center) || obj.center.length !== 2 || obj.center.some(num => typeof num !== 'number')) {
3441
+ throw Error('spec.center must be an array of two numbers if present');
3442
+ }
3443
+ const a = obj.center;
3444
+ if (a[0] < -180 || a[0] > 180)
3445
+ throw Error('spec.center[0] must be between -180 and 180');
3446
+ if (a[1] < -90 || a[1] > 90)
3447
+ throw Error('spec.center[1] must be between -90 and 90');
3448
+ }
3449
+ if (obj.data != null && (!Array.isArray(obj.data) || obj.data.some(url => typeof url !== 'string'))) {
3450
+ throw Error('spec.data must be an array of strings if present');
3451
+ }
3452
+ if (obj.description != null && typeof obj.description !== 'string') {
3453
+ throw Error('spec.description must be a string if present');
3454
+ }
3455
+ if (obj.fillzoom != null && (typeof obj.fillzoom !== 'number' || (obj.fillzoom < 0))) {
3456
+ throw Error('spec.fillzoom must be a positive integer if present');
3457
+ }
3458
+ if (obj.grids != null && (!Array.isArray(obj.grids) || obj.grids.some(url => typeof url !== 'string'))) {
3459
+ throw Error('spec.grids must be an array of strings if present');
3460
+ }
3461
+ if (obj.legend != null && typeof obj.legend !== 'string') {
3462
+ throw Error('spec.legend must be a string if present');
3463
+ }
3464
+ if (obj.minzoom != null && (typeof obj.minzoom !== 'number' || (obj.minzoom < 0))) {
3465
+ throw Error('spec.minzoom must be a positive integer if present');
3466
+ }
3467
+ if (obj.maxzoom != null && (typeof obj.maxzoom !== 'number' || (obj.maxzoom < 0))) {
3468
+ throw Error('spec.maxzoom must be a positive integer if present');
3469
+ }
3470
+ if (obj.name != null && typeof obj.name !== 'string') {
3471
+ throw Error('spec.name must be a string if present');
3472
+ }
3473
+ if (obj.scheme != null && obj.scheme !== 'xyz' && obj.scheme !== 'tms') {
3474
+ throw Error('spec.scheme must be "tms" or "xyz" if present');
3475
+ }
3476
+ if (obj.template != null && typeof obj.template !== 'string') {
3477
+ throw Error('spec.template must be a string if present');
3478
+ }
3479
+ if (!Array.isArray(obj.tiles) || obj.tiles.length === 0 || obj.tiles.some(url => typeof url !== 'string')) {
3480
+ throw Error('spec.tiles must be an array of strings');
3481
+ }
3482
+ return true;
3483
+ }
3484
+ function isRasterTileJSONSpecification(spec) {
3485
+ if (!isTileJSONSpecification(spec))
3486
+ ;
3487
+ if (('vector_layers' in spec) && (spec.vector_layers != null))
3488
+ return false;
3489
+ return true;
3490
+ }
3491
+
3492
+ function guessStyle(tileJSON, options) {
3493
+ tileJSON = deepClone(tileJSON);
3494
+ if (options && options.baseUrl) {
3495
+ const { baseUrl } = options;
3496
+ tileJSON.tiles = tileJSON.tiles.map(url => resolveUrl(baseUrl, url));
3497
+ }
3498
+ if (!isTileJSONSpecification(tileJSON))
3499
+ ;
3500
+ let style;
3501
+ if (isRasterTileJSONSpecification(tileJSON)) {
3502
+ style = getRasterStyle(tileJSON);
3503
+ }
3504
+ else {
3505
+ if (isShortbread(tileJSON)) {
3506
+ style = getShortbreadStyle(tileJSON, {
3507
+ baseUrl: options?.baseUrl,
3508
+ glyphs: options?.glyphs,
3509
+ sprite: options?.sprite,
3510
+ });
3511
+ }
3512
+ else {
3513
+ style = getInspectorStyle(tileJSON);
3514
+ }
3515
+ }
3516
+ return style;
3517
+ }
3518
+ function isShortbread(spec) {
3519
+ if (typeof spec !== 'object')
3520
+ return false;
3521
+ if (!('vector_layers' in spec))
3522
+ return false;
3523
+ if (!Array.isArray(spec.vector_layers))
3524
+ return false;
3525
+ const layerIds = new Set(spec.vector_layers.map(l => String(l.id)));
3526
+ const shortbreadIds = ['place_labels', 'boundaries', 'boundary_labels', 'addresses', 'water_lines', 'water_lines_labels', 'dam_lines', 'dam_polygons', 'pier_lines', 'pier_polygons', 'bridges', 'street_polygons', 'streets_polygons_labels', 'ferries', 'streets', 'street_labels', 'street_labels_points', 'aerialways', 'public_transport', 'buildings', 'water_polygons', 'ocean', 'water_polygons_labels', 'land', 'sites', 'pois'];
3527
+ return shortbreadIds.every(id => layerIds.has(id));
3528
+ }
3529
+ function getShortbreadStyle(spec, builderOption) {
3530
+ return colorful({
3531
+ tiles: spec.tiles,
3532
+ baseUrl: builderOption.baseUrl,
3533
+ glyphs: builderOption.glyphs,
3534
+ sprite: builderOption.sprite,
3535
+ });
3536
+ }
3537
+ function getInspectorStyle(spec) {
3538
+ const sourceName = 'vectorSource';
3539
+ const layers = { background: [], circle: [], line: [], fill: [] };
3540
+ layers.background.push({ 'id': 'background', 'type': 'background', 'paint': { 'background-color': '#fff' } });
3541
+ spec.vector_layers.forEach((vector_layer) => {
3542
+ let luminosity = 'bright', saturation, hue;
3543
+ if (/water|ocean|lake|sea|river/.test(vector_layer.id))
3544
+ hue = 'blue';
3545
+ if (/state|country|place/.test(vector_layer.id))
3546
+ hue = 'pink';
3547
+ if (/road|highway|transport|streets/.test(vector_layer.id))
3548
+ hue = 'orange';
3549
+ if (/contour|building/.test(vector_layer.id))
3550
+ hue = 'monochrome';
3551
+ if (vector_layer.id.includes('building'))
3552
+ luminosity = 'dark';
3553
+ if (/contour|landuse/.test(vector_layer.id))
3554
+ hue = 'yellow';
3555
+ if (/wood|forest|park|landcover|land/.test(vector_layer.id))
3556
+ hue = 'green';
3557
+ if (vector_layer.id.includes('point')) {
3558
+ saturation = 'strong';
3559
+ luminosity = 'light';
3560
+ }
3561
+ const color = Color.random({
3562
+ hue,
3563
+ luminosity,
3564
+ saturation,
3565
+ seed: vector_layer.id,
3566
+ opacity: 0.6,
3567
+ }).asString();
3568
+ layers.circle.push({
3569
+ id: `${sourceName}-${vector_layer.id}-circle`,
3570
+ 'source-layer': vector_layer.id,
3571
+ source: sourceName,
3572
+ type: 'circle',
3573
+ filter: ['==', '$type', 'Point'],
3574
+ paint: { 'circle-color': color, 'circle-radius': 2 },
3575
+ });
3576
+ layers.line.push({
3577
+ id: `${sourceName}-${vector_layer.id}-line`,
3578
+ 'source-layer': vector_layer.id,
3579
+ source: sourceName,
3580
+ type: 'line',
3581
+ filter: ['==', '$type', 'LineString'],
3582
+ layout: { 'line-join': 'round', 'line-cap': 'round' },
3583
+ paint: { 'line-color': color },
3584
+ });
3585
+ layers.fill.push({
3586
+ id: `${sourceName}-${vector_layer.id}-fill`,
3587
+ 'source-layer': vector_layer.id,
3588
+ source: sourceName,
3589
+ type: 'fill',
3590
+ filter: ['==', '$type', 'Polygon'],
3591
+ paint: { 'fill-color': color, 'fill-opacity': 0.3, 'fill-antialias': true, 'fill-outline-color': color },
3592
+ });
3593
+ });
3594
+ return {
3595
+ version: 8,
3596
+ sources: {
3597
+ [sourceName]: sourceFromSpec(spec, 'vector'),
3598
+ },
3599
+ layers: [
3600
+ ...layers.background,
3601
+ ...layers.fill,
3602
+ ...layers.line,
3603
+ ...layers.circle,
3604
+ ],
3605
+ };
3606
+ }
3607
+ function getRasterStyle(spec) {
3608
+ const sourceName = 'rasterSource';
3609
+ return {
3610
+ version: 8,
3611
+ sources: {
3612
+ [sourceName]: sourceFromSpec(spec, 'raster'),
3613
+ },
3614
+ layers: [{
3615
+ id: 'raster',
3616
+ type: 'raster',
3617
+ source: sourceName,
3618
+ }],
3619
+ };
3620
+ }
3621
+ function sourceFromSpec(spec, type) {
3622
+ const source = { tiles: spec.tiles, type };
3623
+ if (spec.minzoom != null)
3624
+ source.minzoom = spec.minzoom;
3625
+ if (spec.maxzoom != null)
3626
+ source.maxzoom = spec.maxzoom;
3627
+ if (spec.attribution != null)
3628
+ source.attribution = spec.attribution;
3629
+ if (spec.scheme != null)
3630
+ source.scheme = spec.scheme;
3631
+ return source;
3632
+ }
3633
+
3634
+ const styles = {
3635
+ colorful,
3636
+ eclipse,
3637
+ graybeard,
3638
+ neutrino,
3639
+ };
3640
+
3641
+ exports.Color = Color;
3642
+ exports.colorful = colorful;
3643
+ exports.eclipse = eclipse;
3644
+ exports.graybeard = graybeard;
3645
+ exports.guessStyle = guessStyle;
3646
+ exports.neutrino = neutrino;
3647
+ exports.styles = styles;
3648
+
3649
+ }));
3650
+ //# sourceMappingURL=index.js.map