@versatiles/style 3.5.2 → 3.6.1

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.
package/README.MD CHANGED
@@ -7,20 +7,20 @@ Programatically generates StyleJSON for MapLibre.
7
7
 
8
8
  ![Example: Colorful Style](docs/colorful.png)
9
9
 
10
- ## Styles
10
+ # Styles
11
11
 
12
12
  * Colorful - colorful, full featured map
13
13
  * Neutrino - light basemap
14
14
  * Graybeard - gray basemap
15
15
 
16
- ## Download
16
+ # Download styles for versatiles.org
17
17
 
18
18
  You can download the latest StyleJSONs from the [latest release](https://github.com/versatiles-org/versatiles-style/releases/latest/).
19
19
  We provide each style with and without labels, and also in multiple languages.
20
20
 
21
21
  Be aware that styles already include `tiles.versatiles.org` as source for tiles, fonts (glyphs) and icons (sprites). So you might want to update the URLs in the JSON.
22
22
 
23
- ## in a web browser
23
+ # Create styles in the frontend (web browser)
24
24
 
25
25
  Download latest release:
26
26
 
@@ -35,19 +35,17 @@ Use it in:
35
35
  <script src="maplibre-gl.js"></script>
36
36
  <script src="versatiles-style.js"></script>
37
37
  <script>
38
- const baseUrl = new URL('/', window.location.href).href;
38
+ const styleBuilder = new VersaTilesStyle.Colorful();
39
+ styleBuilder.tilesUrl = ['tiles/{z}/{x}/{y}'];
40
+
39
41
  const map = new maplibregl.Map({
40
42
  container: 'map',
41
- style: versatiles_style.colorful({
42
- glyphsUrl: baseUrl + 'fonts/{fontstack}/{range}.pbf',
43
- spriteUrl: baseUrl + 'sprites/sprites',
44
- tilesUrl: [baseUrl + 'tiles/{z}/{x}/{y}'],
45
- })
43
+ style: styleBuilder.build()
46
44
  });
47
45
  <script>
48
46
  ```
49
47
 
50
- ## as Node.js module
48
+ # Create styles in the backend (Node.js)
51
49
 
52
50
  Install `versatiles-style` via NPM:
53
51
 
@@ -58,11 +56,109 @@ npm install versatiles-style
58
56
  Use it in Node.js:
59
57
 
60
58
  ```javascript
61
- import { colorful } from 'versatiles-style';
62
- let style = colorful({language:'de'});
63
- writeFileSync('style.json', JSON.stringify(style));
59
+ import { Colorful } from 'versatiles-style';
60
+ let style = new Colorful();
61
+ style.language = 'de';
62
+ writeFileSync('style.json', JSON.stringify(style.build()));
64
63
  ```
65
64
 
66
- ### Node.js API
65
+ # API
67
66
 
68
67
  <!--- This chapter is generated automatically --->
68
+
69
+ ## Interfaces
70
+
71
+ ### Interface: `TileJSONSpecificationRaster`<a id="interface_tilejsonspecificationraster"></a>
72
+
73
+ ```typescript
74
+ interface {
75
+ attribution?: string;
76
+ bounds?: [number, number, number, number];
77
+ center?: [number, number];
78
+ description?: string;
79
+ fillzoom?: number;
80
+ format: "avif" | "jpg" | "png" | "webp";
81
+ grids?: string[];
82
+ legend?: string;
83
+ maxzoom?: number;
84
+ minzoom?: number;
85
+ name?: string;
86
+ scheme?: "xyz" | "tms";
87
+ template?: string;
88
+ tilejson?: "3.0.0";
89
+ tiles: string[];
90
+ type: "raster";
91
+ }
92
+ ```
93
+
94
+ ### Interface: `TileJSONSpecificationVector`<a id="interface_tilejsonspecificationvector"></a>
95
+
96
+ ```typescript
97
+ interface {
98
+ attribution?: string;
99
+ bounds?: [number, number, number, number];
100
+ center?: [number, number];
101
+ description?: string;
102
+ fillzoom?: number;
103
+ format: "pbf";
104
+ grids?: string[];
105
+ legend?: string;
106
+ maxzoom?: number;
107
+ minzoom?: number;
108
+ name?: string;
109
+ scheme?: "xyz" | "tms";
110
+ template?: string;
111
+ tilejson?: "3.0.0";
112
+ tiles: string[];
113
+ type: "vector";
114
+ vector_layers: VectorLayer[];
115
+ }
116
+ ```
117
+
118
+ ## Type Aliases
119
+
120
+ ### Type: `TileJSONSpecification`<a id="type_tilejsonspecification"></a>
121
+
122
+ **Type:** <code>[TileJSONSpecificationRaster](#interface_tilejsonspecificationraster) | [TileJSONSpecificationVector](#interface_tilejsonspecificationvector)</code>
123
+
124
+ ## Functions
125
+
126
+ ### Method: `colorful(options)`
127
+
128
+ <sup><a href="https://github.com/versatiles-org/versatiles-style/blob/516812e/src/index.ts#L4">\[src]</a></sup>
129
+
130
+ **Parameters:**
131
+
132
+ * <code>options: StylemakerOptions<default></code> (optional)
133
+
134
+ **Returns:** <code>MaplibreStyle</code>
135
+
136
+ ### Method: `graybeard(options)`
137
+
138
+ <sup><a href="https://github.com/versatiles-org/versatiles-style/blob/516812e/src/index.ts#L9">\[src]</a></sup>
139
+
140
+ **Parameters:**
141
+
142
+ * <code>options: StylemakerOptions<default></code> (optional)
143
+
144
+ **Returns:** <code>MaplibreStyle</code>
145
+
146
+ ### Method: `guessStyle(spec)`
147
+
148
+ <sup><a href="https://github.com/versatiles-org/versatiles-style/blob/516812e/src/lib/style_guesser.ts#L12">\[src]</a></sup>
149
+
150
+ **Parameters:**
151
+
152
+ * <code>spec: TileJSONOption</code>
153
+
154
+ **Returns:** <code>MaplibreStyle</code>
155
+
156
+ ### Method: `neutrino(options)`
157
+
158
+ <sup><a href="https://github.com/versatiles-org/versatiles-style/blob/516812e/src/index.ts#L14">\[src]</a></sup>
159
+
160
+ **Parameters:**
161
+
162
+ * <code>options: StylemakerOptions<default></code> (optional)
163
+
164
+ **Returns:** <code>MaplibreStyle</code>
package/dist/index.d.ts CHANGED
@@ -1,4 +1,9 @@
1
- export type { MaplibreStyle } from './lib/types.js';
2
- export { default as Colorful } from './style/colorful.js';
3
- export { default as Graybeard } from './style/graybeard.js';
4
- export { default as Neutrino } from './style/neutrino.js';
1
+ import type { MaplibreStyle, StylemakerOptions } from './lib/types.js';
2
+ import Colorful from './style/colorful.js';
3
+ export declare function colorful(options?: StylemakerOptions<Colorful>): MaplibreStyle;
4
+ import Graybeard from './style/graybeard.js';
5
+ export declare function graybeard(options?: StylemakerOptions<Graybeard>): MaplibreStyle;
6
+ import Neutrino from './style/neutrino.js';
7
+ export declare function neutrino(options?: StylemakerOptions<Neutrino>): MaplibreStyle;
8
+ export type { TileJSONSpecification, TileJSONSpecificationRaster, TileJSONSpecificationVector } from './lib/types.js';
9
+ export { default as guessStyle } from './lib/style_guesser.js';
package/dist/index.js CHANGED
@@ -1,3 +1,13 @@
1
- export { default as Colorful } from './style/colorful.js';
2
- export { default as Graybeard } from './style/graybeard.js';
3
- export { default as Neutrino } from './style/neutrino.js';
1
+ import Colorful from './style/colorful.js';
2
+ export function colorful(options) {
3
+ return new Colorful().build(options);
4
+ }
5
+ import Graybeard from './style/graybeard.js';
6
+ export function graybeard(options) {
7
+ return new Graybeard().build(options);
8
+ }
9
+ import Neutrino from './style/neutrino.js';
10
+ export function neutrino(options) {
11
+ return new Neutrino().build(options);
12
+ }
13
+ export { default as guessStyle } from './lib/style_guesser.js';
@@ -1,26 +1,17 @@
1
- import { Colorful } from './index.js';
2
- import * as builderClasses from './index.js';
3
- import StyleBuilder from './lib/style_builder.js';
1
+ /* eslint-disable @typescript-eslint/naming-convention */
2
+ import * as builders from './index.js';
4
3
  describe('Style Builders', () => {
5
- const styleNames = [
6
- 'Colorful',
7
- 'Graybeard',
8
- 'Neutrino',
4
+ const styles = [
5
+ { name: 'Colorful', builder: builders.colorful },
6
+ { name: 'Graybeard', builder: builders.graybeard },
7
+ { name: 'Neutrino', builder: builders.neutrino },
9
8
  ];
10
- it(`should have the correct ${styleNames.length} styles`, () => {
11
- const keys1 = Array.from(Object.keys(builderClasses)).sort();
12
- const keys2 = styleNames.sort();
13
- expect(keys1).toEqual(keys2);
14
- });
15
- Object.entries(builderClasses).forEach(([styleName, builderClass]) => {
16
- it(`should create and test an instance of ${styleName}`, () => {
17
- const builder = new builderClass();
18
- expect(builder).toBeInstanceOf(StyleBuilder);
19
- expect(typeof builder.name).toBe('string');
20
- builder.baseUrl = 'https://example.org';
21
- const style = builder.build();
9
+ styles.forEach(({ name, builder }) => {
10
+ it(`should create and test an instance of ${name}`, () => {
11
+ expect(typeof builder).toBe('function');
12
+ const style = builder({ baseUrl: 'https://example.org' });
22
13
  expect(JSON.stringify(style).length).toBeGreaterThan(50000);
23
- expect(style.name).toBe('versatiles-' + styleName.toLowerCase());
14
+ expect(style.name).toBe('versatiles-' + name.toLowerCase());
24
15
  expect(style.glyphs).toBe('https://example.org/assets/fonts/{fontstack}/{range}.pbf');
25
16
  expect(style.sprite).toBe('https://example.org/assets/sprites/sprites');
26
17
  expect(Object.keys(style.sources).join(',')).toBe('versatiles-shortbread');
@@ -29,9 +20,49 @@ describe('Style Builders', () => {
29
20
  });
30
21
  });
31
22
  describe('Colorful', () => {
32
- const colorful = new Colorful();
33
- colorful.baseUrl = 'https://dev.null';
34
- colorful.colors.commercial = '#f00';
35
- const style = colorful.build();
23
+ const style = builders.colorful({
24
+ baseUrl: 'https://dev.null',
25
+ colors: { commercial: '#f00' },
26
+ });
36
27
  expect(style.glyphs).toBe('https://dev.null/assets/fonts/{fontstack}/{range}.pbf');
28
+ const paint = style.layers.find(l => l.id === 'land-commercial')?.paint;
29
+ expect(paint).toBeDefined();
30
+ if (paint == null)
31
+ throw Error();
32
+ expect(paint).toHaveProperty('fill-color');
33
+ if (!('fill-color' in paint))
34
+ throw Error();
35
+ expect(paint['fill-color']).toBe('#ff0000');
36
+ });
37
+ describe('guessStyle', () => {
38
+ const tiles = ['https://fancy.map/tiles/{z}/{x}/{y}'];
39
+ const vector_layers = [{ id: 'hallo', fields: { label: 'String' } }];
40
+ it('should build raster styles', () => {
41
+ const style = builders.guessStyle({
42
+ tiles,
43
+ format: 'png',
44
+ });
45
+ expect(style).toStrictEqual({
46
+ layers: [{ id: 'raster', source: 'rasterSource', type: 'raster' }],
47
+ sources: { rasterSource: { format: 'png', tilejson: '3.0.0', tiles, type: 'raster' } },
48
+ version: 8,
49
+ });
50
+ });
51
+ it('should build vector styles', () => {
52
+ const style = builders.guessStyle({
53
+ tiles,
54
+ format: 'pbf',
55
+ vector_layers,
56
+ });
57
+ expect(style).toStrictEqual({
58
+ layers: [
59
+ { id: 'background', paint: { 'background-color': '#fff' }, type: 'background' },
60
+ { id: 'vectorSource-hallo-fill', filter: ['==', '$type', 'Polygon'], paint: { 'fill-antialias': true, 'fill-color': 'hsla(14,50%,52%,0.6)', 'fill-opacity': 0.3, 'fill-outline-color': 'hsla(14,50%,52%,0.6)' }, source: 'vectorSource', 'source-layer': 'hallo', type: 'fill' },
61
+ { id: 'vectorSource-hallo-line', filter: ['==', '$type', 'LineString'], layout: { 'line-cap': 'round', 'line-join': 'round' }, paint: { 'line-color': 'hsla(14,50%,52%,0.6)' }, source: 'vectorSource', 'source-layer': 'hallo', type: 'line' },
62
+ { id: 'vectorSource-hallo-circle', filter: ['==', '$type', 'Point'], paint: { 'circle-color': 'hsla(14,50%,52%,0.6)', 'circle-radius': 2 }, source: 'vectorSource', 'source-layer': 'hallo', type: 'circle' },
63
+ ],
64
+ sources: { vectorSource: { format: 'pbf', tilejson: '3.0.0', tiles, type: 'vector', vector_layers } },
65
+ 'version': 8,
66
+ });
67
+ });
37
68
  });
@@ -9,6 +9,8 @@ export function decorate(layers, rules) {
9
9
  const layerStyles = new Map();
10
10
  // Iterate through the generated layer style rules
11
11
  Object.entries(rules).forEach(([idDef, layerStyle]) => {
12
+ if (layerStyle == null)
13
+ return;
12
14
  // Expand any braces in IDs and filter them through a RegExp if necessary
13
15
  const ids = expandBraces(idDef).flatMap(id => {
14
16
  if (!id.includes('*'))
@@ -41,6 +43,8 @@ export function decorate(layers, rules) {
41
43
  // Function to process each style attribute for the layer
42
44
  function processStyling(layer, styleRule) {
43
45
  for (const [ruleKeyCamelCase, ruleValue] of Object.entries(styleRule)) {
46
+ if (ruleValue == null)
47
+ continue;
44
48
  // CamelCase to not-camel-case
45
49
  const ruleKey = ruleKeyCamelCase.replace(/[A-Z]/g, c => '-' + c.toLowerCase());
46
50
  const propertyDefs = maplibreProperties.get(layer.type + '/' + ruleKey);
@@ -0,0 +1,9 @@
1
+ export interface RandomColorOptions {
2
+ seed?: string;
3
+ hue?: number | string;
4
+ opacity?: number;
5
+ luminosity?: number | string;
6
+ saturation?: number | string;
7
+ }
8
+ export type RandomColorFunction = (options?: RandomColorOptions) => string;
9
+ export default function randomColorGenerator(startSeed?: number | string): RandomColorFunction;
@@ -0,0 +1,150 @@
1
+ export default function randomColorGenerator(startSeed) {
2
+ let seed = inputToSeed(startSeed);
3
+ const colorDictionary = initColorDictionary();
4
+ return randomColor;
5
+ function randomColor(options) {
6
+ options ??= {};
7
+ if (options.seed != null) {
8
+ seed = inputToSeed(options.seed);
9
+ }
10
+ options.opacity ??= 1;
11
+ const H = pickHue(options);
12
+ const S = pickSaturation(H, options);
13
+ const V = pickBrightness(H, S, options);
14
+ const hsl = HSVtoHSL([H, S, V]).map(v => v.toFixed(0));
15
+ if (options.opacity === 1) {
16
+ return `hsl(${hsl[0]},${hsl[1]}%,${hsl[2]}%)`;
17
+ }
18
+ else {
19
+ return `hsla(${hsl[0]},${hsl[1]}%,${hsl[2]}%,${options.opacity})`;
20
+ }
21
+ }
22
+ function pickHue(options) {
23
+ let hue = randomWithin(getHueRange(options.hue));
24
+ if (hue < 0)
25
+ hue = 360 + hue;
26
+ return hue;
27
+ }
28
+ function pickSaturation(hue, options) {
29
+ if (options.hue === 'monochrome')
30
+ return 0;
31
+ if (options.luminosity === 'random')
32
+ return randomWithin([0, 100]);
33
+ const { saturationRange } = getColorInfo(hue);
34
+ let [sMin, sMax] = saturationRange;
35
+ if (options.saturation === 'strong')
36
+ return sMax;
37
+ switch (options.luminosity) {
38
+ case 'bright':
39
+ sMin = 55;
40
+ break;
41
+ case 'dark':
42
+ sMin = sMax - 10;
43
+ break;
44
+ case 'light':
45
+ sMax = 55;
46
+ break;
47
+ default:
48
+ }
49
+ return randomWithin([sMin, sMax]);
50
+ }
51
+ function pickBrightness(h, s, options) {
52
+ let bMin = getMinimumBrightness(h, s), bMax = 100;
53
+ switch (options.luminosity) {
54
+ case 'dark':
55
+ bMax = Math.min(100, bMin + 20);
56
+ break;
57
+ case 'light':
58
+ bMin = (bMax + bMin) / 2;
59
+ break;
60
+ case 'random':
61
+ bMin = 0;
62
+ bMax = 100;
63
+ break;
64
+ default:
65
+ }
66
+ return randomWithin([bMin, bMax]);
67
+ }
68
+ function getMinimumBrightness(h, s) {
69
+ const { lowerBounds } = getColorInfo(h);
70
+ for (let i = 0; i < lowerBounds.length - 1; i++) {
71
+ const [s1, v1] = lowerBounds[i];
72
+ const [s2, v2] = lowerBounds[i + 1];
73
+ if (s >= s1 && s <= s2) {
74
+ const m = (v2 - v1) / (s2 - s1), b = v1 - m * s1;
75
+ return m * s + b;
76
+ }
77
+ }
78
+ return 0;
79
+ }
80
+ function getHueRange(hue) {
81
+ if (typeof hue === 'number') {
82
+ if (hue < 360 && hue > 0)
83
+ return [hue, hue];
84
+ }
85
+ if (typeof hue === 'string') {
86
+ const color = colorDictionary[hue];
87
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
88
+ if (color?.hueRange)
89
+ return color.hueRange;
90
+ }
91
+ return [0, 360];
92
+ }
93
+ function getColorInfo(hue) {
94
+ // Maps red colors to make picking hue easier
95
+ if (hue >= 334 && hue <= 360)
96
+ hue -= 360;
97
+ for (const colorName in colorDictionary) {
98
+ const color = colorDictionary[colorName];
99
+ if (color.hueRange && hue >= color.hueRange[0] && hue <= color.hueRange[1]) {
100
+ return colorDictionary[colorName];
101
+ }
102
+ }
103
+ throw Error('Color not found');
104
+ }
105
+ function randomWithin(range) {
106
+ //Seeded random algorithm from http://indiegamr.com/generate-repeatable-random-numbers-in-js/
107
+ const max = range[1] || 1;
108
+ const min = range[0] || 0;
109
+ seed = (seed * 9301 + 49297) % 233280;
110
+ const rnd = seed / 233280.0;
111
+ return Math.floor(min + rnd * (max - min));
112
+ }
113
+ function initColorDictionary() {
114
+ const dic = {};
115
+ const defineColor = (name, hueRange, lowerBounds) => {
116
+ const [greyest] = lowerBounds;
117
+ const colorful = lowerBounds[lowerBounds.length - 1];
118
+ dic[name] = {
119
+ hueRange,
120
+ lowerBounds,
121
+ saturationRange: [greyest[0], colorful[0]],
122
+ brightnessRange: [colorful[1], greyest[1]],
123
+ };
124
+ };
125
+ defineColor('monochrome', null, [[0, 0], [100, 0]]);
126
+ defineColor('red', [-26, 18], [[20, 100], [30, 92], [40, 89], [50, 85], [60, 78], [70, 70], [80, 60], [90, 55], [100, 50]]);
127
+ defineColor('orange', [18, 46], [[20, 100], [30, 93], [40, 88], [50, 86], [60, 85], [70, 70], [100, 70]]);
128
+ defineColor('yellow', [46, 62], [[25, 100], [40, 94], [50, 89], [60, 86], [70, 84], [80, 82], [90, 80], [100, 75]]);
129
+ defineColor('green', [62, 178], [[30, 100], [40, 90], [50, 85], [60, 81], [70, 74], [80, 64], [90, 50], [100, 40]]);
130
+ defineColor('blue', [178, 257], [[20, 100], [30, 86], [40, 80], [50, 74], [60, 60], [70, 52], [80, 44], [90, 39], [100, 35]]);
131
+ defineColor('purple', [257, 282], [[20, 100], [30, 87], [40, 79], [50, 70], [60, 65], [70, 59], [80, 52], [90, 45], [100, 42]]);
132
+ defineColor('pink', [282, 334], [[20, 100], [30, 90], [40, 86], [60, 84], [80, 80], [90, 75], [100, 73]]);
133
+ return dic;
134
+ }
135
+ // eslint-disable-next-line @typescript-eslint/naming-convention
136
+ function HSVtoHSL(hsv) {
137
+ const s = hsv[1] / 100, v = hsv[2] / 100, k = (2 - s) * v;
138
+ return [hsv[0], 100 * s * v / (k < 1 ? k : 2 - k), 100 * k / 2];
139
+ }
140
+ function inputToSeed(input) {
141
+ if (input == null)
142
+ return 0;
143
+ if (typeof input === 'number')
144
+ return input;
145
+ let i = 0;
146
+ for (let p = 0; p < input.length; p++)
147
+ i = (i * 0x101 + input.charCodeAt(p)) % 0x100000000;
148
+ return i;
149
+ }
150
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,68 @@
1
+ import randomColorGenerator from './random_color.js';
2
+ describe('RandomColor', () => {
3
+ let randomColor;
4
+ beforeEach(() => {
5
+ randomColor = randomColorGenerator();
6
+ });
7
+ test('constructor initializes without errors', () => {
8
+ expect(randomColor).toBeDefined();
9
+ });
10
+ describe('randomColor method', () => {
11
+ test('returns a valid color string', () => {
12
+ const color = randomColor();
13
+ expect(isValidHSLA(color)).toBeTruthy();
14
+ });
15
+ test('returns correct color string for some test cases', () => {
16
+ expect(randomColor({ hue: 'red' })).toBe('hsl(343,65%,51%)');
17
+ expect(randomColor({ hue: 120 })).toBe('hsl(120,77%,32%)');
18
+ expect(randomColor({ luminosity: 'dark' })).toBe('hsl(135,96%,31%)');
19
+ expect(randomColor({ saturation: 'strong' })).toBe('hsl(193,100%,24%)');
20
+ expect(randomColor({ opacity: 0.5 })).toBe('hsla(242,55%,42%,0.5)');
21
+ expect(randomColor({ seed: 'testSeed' })).toBe('hsl(185,90%,23%)');
22
+ });
23
+ test('consistent color generation with a seed', () => {
24
+ const options = { seed: 'consistentSeed' };
25
+ const color1 = randomColor(options);
26
+ const color2 = randomColor(options);
27
+ expect(color1).toBe(color2);
28
+ });
29
+ test('different color generation without a seed', () => {
30
+ const color1 = randomColor();
31
+ const color2 = randomColor();
32
+ expect(color1).not.toBe(color2);
33
+ });
34
+ });
35
+ });
36
+ function isValidHSLA(textColor) {
37
+ let match, h, s, l, a;
38
+ if (textColor.startsWith('hsla')) {
39
+ match = /^hsla\((\d+),(\d+)%,(\d+)%,([.0-9]+)\)$/.exec(textColor);
40
+ if (match == null)
41
+ return false;
42
+ [, h, s, l, a] = match;
43
+ }
44
+ else {
45
+ match = /^hsl\((\d+),(\d+)%,(\d+)%\)$/.exec(textColor);
46
+ if (match == null)
47
+ return false;
48
+ [, h, s, l] = match;
49
+ a = '1';
50
+ }
51
+ if (!check(h, 0, 360))
52
+ return false;
53
+ if (!check(s, 0, 100))
54
+ return false;
55
+ if (!check(l, 0, 100))
56
+ return false;
57
+ if (!check(a, 0, 1))
58
+ return false;
59
+ return true;
60
+ function check(textNumber, min, max) {
61
+ const value = parseFloat(textNumber);
62
+ if (value < min)
63
+ return false;
64
+ if (value > max)
65
+ return false;
66
+ return true;
67
+ }
68
+ }
@@ -1,3 +1,4 @@
1
- import type { RecolorOptions, StylemakerColorLookup } from './types.js';
1
+ import type { RecolorOptions, StylemakerColors } from './types.js';
2
+ import type StyleBuilder from './style_builder.ts';
2
3
  export declare function getDefaultRecolorFlags(): RecolorOptions;
3
- export declare function recolor(colors: StylemakerColorLookup, opt?: RecolorOptions): void;
4
+ export declare function recolor<Subclass extends StyleBuilder<Subclass>>(colors: StylemakerColors<Subclass>, opt?: RecolorOptions): void;
@@ -29,7 +29,8 @@ export function recolor(colors, opt) {
29
29
  if ((opt.tint !== undefined) && (opt.tintColor !== undefined) && (opt.tint !== 0))
30
30
  tint(opt.tint, Color(opt.tintColor));
31
31
  function forEachColor(callback) {
32
- Object.entries(colors).forEach(([k, c]) => colors[k] = callback(c));
32
+ for (const k in colors)
33
+ colors[k] = callback(colors[k]);
33
34
  }
34
35
  function invert() {
35
36
  forEachColor(c => c.negate());
@@ -1 +1,13 @@
1
- export {};
1
+ import type { StyleRules, StyleRulesOptions } from './types.js';
2
+ import StyleBuilder from './style_builder.js';
3
+ export default class TestStyle extends StyleBuilder<TestStyle> {
4
+ readonly name: string;
5
+ defaultFonts: {};
6
+ defaultColors: {
7
+ c0: string;
8
+ c1: string;
9
+ c2: string;
10
+ c3: string;
11
+ };
12
+ protected getStyleRules(_options: StyleRulesOptions<TestStyle>): StyleRules;
13
+ }
@@ -1,5 +1,5 @@
1
1
  import { getDefaultRecolorFlags, recolor } from './recolor.js';
2
- import Color from 'color';
2
+ import StyleBuilder from './style_builder.js';
3
3
  describe('colorTransformer', () => {
4
4
  describe('getDefaultRecolorFlags', () => {
5
5
  it('should return the default color transformer flags', () => {
@@ -163,17 +163,30 @@ describe('colorTransformer', () => {
163
163
  });
164
164
  });
165
165
  });
166
- function string2colors(colorList) {
167
- return Object.fromEntries(colorList.split(',')
168
- .map((value, index) => ['color' + index, Color('#' + value)]));
169
- }
170
166
  function colors2string(colors) {
171
- const colorArray = [];
172
- for (let i = 0; ('color' + i) in colors; i++) {
173
- colorArray.push(colors['color' + i].hexa().slice(1));
167
+ const colorArray = [
168
+ colors.c0,
169
+ colors.c1,
170
+ colors.c2,
171
+ colors.c3,
172
+ ];
173
+ return colorArray.map(c => c.hexa().slice(1)).join(',');
174
+ }
175
+ export default class TestStyle extends StyleBuilder {
176
+ name = 'teststyle';
177
+ defaultFonts = {};
178
+ defaultColors = {
179
+ c0: '#FFAA5500',
180
+ c1: '#00FFAA55',
181
+ c2: '#5500FFAA',
182
+ c3: '#AA5500FF',
183
+ };
184
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
185
+ getStyleRules(_options) {
186
+ throw new Error('Method not implemented.');
174
187
  }
175
- return colorArray.join(',');
176
188
  }
177
189
  function getDefaultColors() {
178
- return string2colors('FFAA5500,00FFAA55,5500FFAA,AA5500FF');
190
+ const style = new TestStyle();
191
+ return style.getColors(style.defaultColors);
179
192
  }
@@ -316,7 +316,7 @@ export default function getLayers(option) {
316
316
  {
317
317
  id: 'label-motorway-exit',
318
318
  type: 'symbol',
319
- 'source-layer': 'street_labels_points',
319
+ 'source-layer': 'street_labels_points', // docs say `streets_labels_points`, but layer is actually called `street_labels_points`
320
320
  filter: ['==', 'kind', 'motorway_junction'],
321
321
  layout: { 'text-field': '{ref}' },
322
322
  // FIXME shield
@@ -394,7 +394,7 @@ export default function getLayers(option) {
394
394
  },
395
395
  // marking
396
396
  {
397
- id: 'marking-oneway',
397
+ id: 'marking-oneway', // streets → oneway
398
398
  type: 'symbol',
399
399
  'source-layer': 'streets',
400
400
  filter: ['all',
@@ -411,7 +411,7 @@ export default function getLayers(option) {
411
411
  },
412
412
  },
413
413
  {
414
- id: 'marking-oneway-reverse',
414
+ id: 'marking-oneway-reverse', // oneway_reverse
415
415
  type: 'symbol',
416
416
  'source-layer': 'streets',
417
417
  filter: ['all',
@@ -428,7 +428,7 @@ export default function getLayers(option) {
428
428
  },
429
429
  },
430
430
  {
431
- id: 'marking-bicycle',
431
+ id: 'marking-bicycle', // bicycle=designated or kind=cycleway
432
432
  type: 'symbol',
433
433
  'source-layer': 'streets',
434
434
  filter: ['all',