@versatiles/style 4.0.5 → 4.1.0

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/package.json CHANGED
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "name": "@versatiles/style",
3
- "version": "4.0.5",
3
+ "version": "4.1.0",
4
4
  "description": "Generate StyleJSON for MapLibre",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
- "check": "npm run build && npm run lint && npm run test",
9
- "build": "npm run build-browser && npm run build-node && npm run build-styles",
10
- "build-browser": "rollup -c=rollup.config.js",
8
+ "check": "npm run lint && npm run build && npm run test",
9
+ "build": "rm -rf release; npm run build-browser && npm run build-node && npm run build-styles && npm run build-sprites",
10
+ "build-browser": "rollup -c=rollup.config.js; tar -cf - release/versatiles-style/versatiles-style.* | gzip -9 > release/versatiles-style.tar.gz; rm -rf release/versatiles-style",
11
11
  "build-node": "rm -rf dist && tsc -p tsconfig.node.json && chmod +x dist/index.js",
12
12
  "build-styles": "tsx scripts/build-styles.ts",
13
- "doc": "vrt ts2md src/index.ts tsconfig.node.json | vrt insertmd README.md '# API'",
14
- "lint": "npm run build-browser && eslint --color .",
13
+ "build-sprites": "tsx scripts/build-sprites.ts",
14
+ "// dev": "????",
15
+ "// doc": "vrt ts2md src/index.ts tsconfig.node.json | vrt insertmd README.md '# API'",
16
+ "lint": "eslint --color .",
15
17
  "release": "npx vrt release-npm",
16
- "test": "npm run test-typescript && npm run test-node && npm run test-browser",
17
- "test-browser": "npm run build-browser && jest -c=jest.config.browser.ts",
18
- "test-coverage": "NODE_OPTIONS=--experimental-vm-modules jest -c=jest.config.coverage.ts",
19
- "test-node": "npm run build-node && NODE_OPTIONS=--experimental-vm-modules jest -c=jest.config.node.ts",
20
- "test-typescript": "NODE_OPTIONS=--experimental-vm-modules jest -c=jest.config.typescript.ts",
18
+ "test": "npm run test-typescript",
19
+ "test-coverage": "NODE_OPTIONS=--experimental-vm-modules jest --coverage",
20
+ "test-typescript": "NODE_OPTIONS=--experimental-vm-modules jest",
21
21
  "upgrade": "npm-check-updates -u && rm -f package-lock.json && rm -rf node_modules; npm i"
22
22
  },
23
23
  "repository": {
@@ -42,21 +42,26 @@
42
42
  "@rollup/plugin-node-resolve": "^15.2.3",
43
43
  "@rollup/plugin-terser": "^0.4.4",
44
44
  "@rollup/plugin-typescript": "^11.1.6",
45
+ "@types/bin-pack": "^1.0.3",
45
46
  "@types/brace-expansion": "^1.1.2",
46
47
  "@types/inquirer": "^9.0.7",
47
48
  "@types/jest": "^29.5.12",
48
49
  "@types/node": "^20.11.19",
50
+ "@types/tar-stream": "^3.1.3",
49
51
  "@typescript-eslint/eslint-plugin": "^7.0.1",
50
52
  "@typescript-eslint/parser": "^7.0.1",
51
- "@versatiles/release-tool": "^1.2.1",
53
+ "@versatiles/release-tool": "^1.2.2",
54
+ "bin-pack": "^1.0.2",
52
55
  "eslint": "^8.56.0",
53
56
  "inquirer": "^9.2.14",
54
57
  "jest": "^29.7.0",
55
58
  "jest-environment-jsdom": "^29.7.0",
56
59
  "jest-ts-webcompat-resolver": "^1.0.0",
57
60
  "npm-check-updates": "^16.14.15",
58
- "rollup": "^4.11.0",
61
+ "rollup": "^4.12.0",
59
62
  "rollup-plugin-dts": "^6.1.0",
63
+ "sharp": "^0.33.2",
64
+ "tar-stream": "^3.1.7",
60
65
  "ts-jest": "^29.1.2",
61
66
  "ts-node": "^10.9.2",
62
67
  "tsx": "^4.7.1",
@@ -1 +0,0 @@
1
- export {};
@@ -1,67 +0,0 @@
1
- /* eslint-disable @typescript-eslint/naming-convention */
2
- import { guessStyle, styles } from './index.js';
3
- describe('styles', () => {
4
- it('should be all styles', () => {
5
- expect(Array.from(Object.keys(styles)).sort())
6
- .toStrictEqual(['colorful', 'graybeard', 'neutrino']);
7
- });
8
- Object.entries(styles).forEach(([name, builder]) => {
9
- it(`should create and test an instance of ${name}`, () => {
10
- expect(typeof builder).toBe('function');
11
- const style = builder({ baseUrl: 'https://example.org' });
12
- expect(JSON.stringify(style).length).toBeGreaterThan(50000);
13
- expect(style.name).toBe('versatiles-' + name);
14
- expect(style.glyphs).toBe('https://example.org/assets/fonts/{fontstack}/{range}.pbf');
15
- expect(style.sprite).toBe('https://example.org/assets/sprites/sprites');
16
- expect(Object.keys(style.sources).join(',')).toBe('versatiles-shortbread');
17
- expect(style.sources['versatiles-shortbread'].tiles).toEqual(['https://example.org/tiles/osm/{z}/{x}/{y}']);
18
- });
19
- });
20
- });
21
- describe('Colorful', () => {
22
- const style = styles.colorful({
23
- baseUrl: 'https://dev.null',
24
- colors: { commercial: '#f00' },
25
- });
26
- expect(style.glyphs).toBe('https://dev.null/assets/fonts/{fontstack}/{range}.pbf');
27
- const paint = style.layers.find(l => l.id === 'land-commercial')?.paint;
28
- expect(paint).toBeDefined();
29
- if (paint == null)
30
- throw Error();
31
- expect(paint).toHaveProperty('fill-color');
32
- if (!('fill-color' in paint))
33
- throw Error();
34
- expect(paint['fill-color']).toBe('#ff0000');
35
- });
36
- describe('guessStyle', () => {
37
- const tiles = ['https://fancy.map/tiles/{z}/{x}/{y}'];
38
- const vectorLayers = [{ id: 'hallo', fields: { label: 'String' } }];
39
- it('should build raster styles', () => {
40
- const style = guessStyle({
41
- tiles,
42
- format: 'png',
43
- });
44
- expect(style).toStrictEqual({
45
- layers: [{ id: 'raster', source: 'rasterSource', type: 'raster' }],
46
- sources: { rasterSource: { format: 'png', tilejson: '3.0.0', tiles, type: 'raster' } },
47
- version: 8,
48
- });
49
- });
50
- it('should build vector styles', () => {
51
- const style = guessStyle({
52
- tiles,
53
- format: 'pbf',
54
- vectorLayers,
55
- });
56
- expect(style).toStrictEqual({
57
- layers: [
58
- { id: 'background', paint: { 'background-color': '#fff' }, type: 'background' },
59
- { 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' },
60
- { 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' },
61
- { 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' },
62
- ],
63
- sources: { vectorSource: { format: 'pbf', tilejson: '3.0.0', tiles, type: 'vector', vector_layers: vectorLayers } },
64
- 'version': 8,
65
- });
66
- });
67
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,68 +0,0 @@
1
- /* eslint-disable @typescript-eslint/naming-convention */
2
- import Color from 'color';
3
- import StyleBuilder from './build_style.js';
4
- // Mock class for abstract class StyleBuilder
5
- class MockStyleBuilder extends StyleBuilder {
6
- name = 'mock';
7
- defaultFonts = { regular: 'Arial' };
8
- defaultColors = { primary: '#FF8800' };
9
- negateColors() {
10
- this.transformDefaultColors(color => color.negate());
11
- }
12
- getStyleRules(opt) {
13
- for (const color of Object.values(opt.colors))
14
- if (!(color instanceof Color))
15
- throw Error();
16
- for (const font of Object.values(opt.fonts))
17
- if (typeof font !== 'string')
18
- throw Error();
19
- return {
20
- 'water-area': {
21
- textColor: opt.colors.primary,
22
- textSize: 12,
23
- textFont: opt.fonts.regular,
24
- },
25
- };
26
- }
27
- }
28
- describe('StyleBuilder', () => {
29
- let builder;
30
- beforeEach(() => {
31
- builder = new MockStyleBuilder();
32
- });
33
- it('should create an instance of StyleBuilder', () => {
34
- expect(builder).toBeInstanceOf(StyleBuilder);
35
- });
36
- it('should build a MaplibreStyle object', () => {
37
- const style = builder.build();
38
- expect(style).toBeDefined();
39
- expect(style).toHaveProperty('name');
40
- expect(style).toHaveProperty('layers');
41
- expect(style).toHaveProperty('glyphs');
42
- expect(style).toHaveProperty('sprite');
43
- });
44
- it('should transform colors correctly', () => {
45
- const initialColor = builder.defaultColors.primary;
46
- builder.negateColors();
47
- expect(builder.defaultColors.primary).not.toBe(initialColor);
48
- expect(builder.defaultColors.primary).toBe(Color(initialColor).negate().hexa());
49
- });
50
- describe('build method', () => {
51
- it('should create a style object', () => {
52
- const style = builder.build();
53
- expect(style).toBeDefined();
54
- expect(style).toHaveProperty('layers');
55
- expect(style).toHaveProperty('name');
56
- expect(style).toHaveProperty('glyphs');
57
- expect(style).toHaveProperty('sprite');
58
- });
59
- it('should resolve urls correctly', () => {
60
- const style = builder.build({ baseUrl: 'https://my.base.url/' });
61
- expect(style.glyphs).toBe('https://my.base.url/assets/fonts/{fontstack}/{range}.pbf');
62
- expect(style.sprite).toBe('https://my.base.url/assets/sprites/sprites');
63
- const source = style.sources['versatiles-shortbread'];
64
- expect(source).toHaveProperty('tiles');
65
- expect(source.tiles[0]).toBe('https://my.base.url/tiles/osm/{z}/{x}/{y}');
66
- });
67
- });
68
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,55 +0,0 @@
1
- import { decorate } from './decorator.js';
2
- import Color from 'color';
3
- describe('decorate function', () => {
4
- const mockLayers = [
5
- { id: 'layer1', type: 'fill', layout: {}, paint: {}, source: 'versatiles-shortbread' },
6
- { id: 'layer2', type: 'line', layout: {}, paint: {}, source: 'versatiles-shortbread' },
7
- ];
8
- const mockRules = {
9
- 'layer1': {
10
- color: '#ff0000',
11
- visibility: 'none',
12
- },
13
- 'layer2': {
14
- color: 'rgba(0, 255, 0, 0.5)',
15
- visibility: 'visible',
16
- },
17
- };
18
- it('should return an array of layers', () => {
19
- const result = decorate(mockLayers, mockRules);
20
- expect(Array.isArray(result)).toBe(true);
21
- });
22
- it('should apply styles from rules to the corresponding layers', () => {
23
- const result = decorate(mockLayers, mockRules);
24
- result.forEach(layer => {
25
- if (layer.id === 'layer1') {
26
- expect(layer.paint).toHaveProperty('fill-color', '#ff0000');
27
- expect(layer.layout).toHaveProperty('visibility', 'none');
28
- }
29
- if (layer.id === 'layer2') {
30
- expect(layer.paint).toHaveProperty('line-color', 'rgba(0, 255, 0, 0.5)');
31
- expect(layer.layout).toHaveProperty('visibility', 'visible');
32
- }
33
- });
34
- });
35
- it('should handle color conversion correctly', () => {
36
- const colorRule = {
37
- 'layer1': {
38
- paintColor: new Color('#ff0000'),
39
- },
40
- };
41
- const result = decorate(mockLayers, colorRule);
42
- const layer0 = result[0];
43
- expect(layer0).toBeDefined();
44
- if (typeof layer0 === 'undefined')
45
- return;
46
- expect(layer0.paint).toBeDefined();
47
- if (typeof layer0.paint === 'undefined')
48
- return;
49
- expect(layer0.paint).toHaveProperty('fill-color', '#ff0000');
50
- });
51
- it('should discard layers that have no style rules applied', () => {
52
- const result = decorate(mockLayers, {});
53
- expect(result.length).toBe(0);
54
- });
55
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,142 +0,0 @@
1
- /* eslint-disable @typescript-eslint/naming-convention */
2
- import getTemplate from './shortbread/template.js';
3
- import guessStyle from './guess_style.js';
4
- describe('guessStyle', () => {
5
- const tiles = ['https://example.com/tiles/{z}/{x}/{y}'];
6
- const vectorLayersSomething = [{ id: 'geometry', fields: { label: 'String', height: 'Number' } }];
7
- const vectorLayersShortbread = getTemplate().sources['versatiles-shortbread'].vector_layers;
8
- it('should build raster styles', () => {
9
- const type = 'raster';
10
- const format = 'avif';
11
- expect(guessStyle({ tiles, format }))
12
- .toStrictEqual({
13
- version: 8,
14
- sources: { rasterSource: { format, tilejson: '3.0.0', tiles, type } },
15
- layers: [{ id: 'raster', source: 'rasterSource', type }],
16
- });
17
- });
18
- it('should build vector inspector styles', () => {
19
- const type = 'vector';
20
- const format = 'pbf';
21
- expect(guessStyle({ tiles, format, vectorLayers: vectorLayersSomething }))
22
- .toStrictEqual({
23
- version: 8,
24
- sources: { vectorSource: { format, tilejson: '3.0.0', tiles, type, vector_layers: vectorLayersSomething } },
25
- layers: [
26
- {
27
- id: 'background',
28
- type: 'background',
29
- paint: { 'background-color': '#fff' },
30
- },
31
- {
32
- id: 'vectorSource-geometry-fill',
33
- type: 'fill',
34
- source: 'vectorSource',
35
- 'source-layer': 'geometry',
36
- filter: ['==', '$type', 'Polygon'],
37
- paint: { 'fill-antialias': true, 'fill-color': 'hsla(7,57%,56%,0.6)', 'fill-opacity': 0.3, 'fill-outline-color': 'hsla(7,57%,56%,0.6)' },
38
- },
39
- {
40
- id: 'vectorSource-geometry-line',
41
- type: 'line',
42
- source: 'vectorSource',
43
- 'source-layer': 'geometry',
44
- filter: ['==', '$type', 'LineString'],
45
- layout: { 'line-cap': 'round', 'line-join': 'round' },
46
- paint: { 'line-color': 'hsla(7,57%,56%,0.6)' },
47
- },
48
- {
49
- id: 'vectorSource-geometry-circle',
50
- type: 'circle',
51
- source: 'vectorSource',
52
- 'source-layer': 'geometry',
53
- filter: ['==', '$type', 'Point'],
54
- paint: { 'circle-color': 'hsla(7,57%,56%,0.6)', 'circle-radius': 2 },
55
- },
56
- ],
57
- });
58
- });
59
- it('should build shortbread vector styles', () => {
60
- const type = 'vector';
61
- const format = 'pbf';
62
- const style = guessStyle({ tiles, format, vectorLayers: vectorLayersShortbread, baseUrl: 'http://example.com' });
63
- expect(style.layers.length).toBe(236);
64
- style.layers = [];
65
- expect(style).toStrictEqual({
66
- glyphs: 'http://example.com/assets/fonts/{fontstack}/{range}.pbf',
67
- metadata: {
68
- license: 'https://creativecommons.org/publicdomain/zero/1.0/',
69
- 'maputnik:renderer': 'mbgljs',
70
- },
71
- name: 'versatiles-colorful',
72
- sprite: 'http://example.com/assets/sprites/sprites',
73
- layers: [],
74
- sources: {
75
- 'versatiles-shortbread': {
76
- attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
77
- bounds: [-180, -85.0511287798066, 180, 85.0511287798066],
78
- format,
79
- maxzoom: 14,
80
- minzoom: 0,
81
- scheme: 'xyz',
82
- tilejson: '3.0.0',
83
- tiles,
84
- type,
85
- vector_layers: vectorLayersShortbread,
86
- },
87
- },
88
- version: 8,
89
- });
90
- });
91
- const cases = [
92
- { type: 'image', options: { tiles, format: 'png' } },
93
- { type: 'inspector', options: { tiles, format: 'pbf', vectorLayers: vectorLayersSomething } },
94
- { type: 'shortbread', options: { tiles, format: 'pbf', vectorLayers: vectorLayersShortbread } },
95
- ];
96
- describe('minzoom sets zoom', () => {
97
- cases.forEach(({ type, options }) => {
98
- it(type, () => {
99
- expect(guessStyle({ ...options, minzoom: 5 })).toHaveProperty('zoom', 5);
100
- });
101
- });
102
- });
103
- describe('bounds sets center', () => {
104
- cases.forEach(({ type, options }) => {
105
- it(type, () => {
106
- expect(guessStyle({ ...options, bounds: [1, 2, 3, 4] })).toHaveProperty('center', [2, 3]);
107
- });
108
- });
109
- });
110
- describe('center sets center', () => {
111
- cases.forEach(({ type, options }) => {
112
- it(type, () => {
113
- expect(guessStyle({ ...options, center: [12, 34] })).toHaveProperty('center', [12, 34]);
114
- });
115
- });
116
- });
117
- describe('center overrides bounds', () => {
118
- cases.forEach(({ type, options }) => {
119
- it(type, () => {
120
- expect(guessStyle({ ...options, bounds: [1, 2, 3, 4], center: [12, 34] })).toHaveProperty('center', [12, 34]);
121
- });
122
- });
123
- });
124
- describe('absolute tile urls override baseUrl', () => {
125
- cases.forEach(({ type, options }) => {
126
- it(type, () => {
127
- const style = guessStyle({ ...options, tiles: ['https://example1.org/tiles/{z}/{x}/{y}'], baseUrl: 'https://example2.org/' });
128
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
129
- expect(Object.values(style.sources)[0].tiles).toEqual(['https://example1.org/tiles/{z}/{x}/{y}']);
130
- });
131
- });
132
- });
133
- describe('relative tile urls are resolved with baseUrl', () => {
134
- cases.forEach(({ type, options }) => {
135
- it(type, () => {
136
- const style = guessStyle({ ...options, tiles: ['./{z}/{x}/{y}'], baseUrl: 'https://example2.org/tiles/' });
137
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
138
- expect(Object.values(style.sources)[0].tiles).toEqual(['https://example2.org/tiles/{z}/{x}/{y}']);
139
- });
140
- });
141
- });
142
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,68 +0,0 @@
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,13 +0,0 @@
1
- import type { StyleRules, StyleRulesOptions } from './types.js';
2
- import StyleBuilder from './build_style.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,192 +0,0 @@
1
- import { getDefaultRecolorFlags, recolor } from './recolor.js';
2
- import StyleBuilder from './build_style.js';
3
- describe('colorTransformer', () => {
4
- describe('getDefaultRecolorFlags', () => {
5
- it('should return the default color transformer flags', () => {
6
- const defaultFlags = getDefaultRecolorFlags();
7
- expect(defaultFlags).toEqual({
8
- invert: false,
9
- rotate: 0,
10
- saturate: 0,
11
- gamma: 1,
12
- contrast: 1,
13
- brightness: 0,
14
- tint: 0,
15
- tintColor: '#FF0000',
16
- });
17
- });
18
- });
19
- describe('recolor', () => {
20
- it('should not alter the colors if no flags are provided', () => {
21
- const colors = getDefaultColors();
22
- recolor(colors, {});
23
- expect(colors).toEqual(getDefaultColors());
24
- });
25
- describe('invert', () => {
26
- it('should invert colors when invert flag is true', () => {
27
- const colors = getDefaultColors();
28
- recolor(colors, { invert: true });
29
- expect(colors2string(colors)).toBe('0055AA00,FF005555,AAFF00AA,55AAFFFF');
30
- });
31
- });
32
- describe('rotate', () => {
33
- it('should rotate colors 120°', () => {
34
- const colors = getDefaultColors();
35
- recolor(colors, { rotate: 120 });
36
- expect(colors2string(colors)).toBe('55FFAA00,AA00FF55,FF5500AA,00AA55FF');
37
- });
38
- it('should rotate colors 180°', () => {
39
- const colors = getDefaultColors();
40
- recolor(colors, { rotate: 180 });
41
- expect(colors2string(colors)).toBe('55AAFF00,FF005555,AAFF00AA,0055AAFF');
42
- });
43
- it('should rotate colors 240°', () => {
44
- const colors = getDefaultColors();
45
- recolor(colors, { rotate: 240 });
46
- expect(colors2string(colors)).toBe('AA55FF00,FFAA0055,00FF55AA,5500AAFF');
47
- });
48
- });
49
- describe('saturation', () => {
50
- it('should remove any saturation', () => {
51
- const colors = getDefaultColors();
52
- recolor(colors, { saturate: -1.0 });
53
- expect(colors2string(colors)).toBe('AAAAAA00,80808055,808080AA,555555FF');
54
- });
55
- it('should decrease saturation', () => {
56
- const colors = getDefaultColors();
57
- recolor(colors, { saturate: -0.5 });
58
- expect(colors2string(colors)).toBe('D4AA7F00,40BF9555,6A40BFAA,7F552AFF');
59
- });
60
- it('should increase saturation', () => {
61
- const colors = getDefaultColors();
62
- recolor(colors, { saturate: 0.5 });
63
- expect(colors2string(colors)).toBe('FFAA2A00,00FFBF55,4000FFAA,D45500FF');
64
- });
65
- it('should maximize saturation', () => {
66
- const colors = getDefaultColors();
67
- recolor(colors, { saturate: 1.0 });
68
- expect(colors2string(colors)).toBe('FFAA0000,00FFD455,2B00FFAA,FF5500FF');
69
- });
70
- });
71
- describe('gamma', () => {
72
- it('should decrease gamma', () => {
73
- const colors = getDefaultColors();
74
- recolor(colors, { gamma: 0.5 });
75
- expect(colors2string(colors)).toBe('FFD09300,00FFD055,9300FFAA,D09300FF');
76
- });
77
- it('should increase gamma', () => {
78
- const colors = getDefaultColors();
79
- recolor(colors, { gamma: 2 });
80
- expect(colors2string(colors)).toBe('FF711C00,00FF7155,1C00FFAA,711C00FF');
81
- });
82
- });
83
- describe('contrast', () => {
84
- it('should remove any contrast', () => {
85
- const colors = getDefaultColors();
86
- recolor(colors, { contrast: 0 });
87
- expect(colors2string(colors)).toBe('80808000,80808055,808080AA,808080FF');
88
- });
89
- it('should decrease contrast', () => {
90
- const colors = getDefaultColors();
91
- recolor(colors, { contrast: 0.5 });
92
- expect(colors2string(colors)).toBe('BF956A00,40BF9555,6A40BFAA,956A40FF');
93
- });
94
- it('should increase contrast', () => {
95
- const colors = getDefaultColors();
96
- recolor(colors, { contrast: 2 });
97
- expect(colors2string(colors)).toBe('FFD52B00,00FFD555,2B00FFAA,D52B00FF');
98
- });
99
- it('should maximize contrast', () => {
100
- const colors = getDefaultColors();
101
- recolor(colors, { contrast: Infinity });
102
- expect(colors2string(colors)).toBe('FFFF0000,00FFFF55,0000FFAA,FF0000FF');
103
- });
104
- });
105
- describe('contrast', () => {
106
- it('should remove any brightness', () => {
107
- const colors = getDefaultColors();
108
- recolor(colors, { brightness: -1 });
109
- expect(colors2string(colors)).toBe('00000000,00000055,000000AA,000000FF');
110
- });
111
- it('should decrease brightness', () => {
112
- const colors = getDefaultColors();
113
- recolor(colors, { brightness: -0.5 });
114
- expect(colors2string(colors)).toBe('80552B00,00805555,2B0080AA,552B00FF');
115
- });
116
- it('should increase brightness', () => {
117
- const colors = getDefaultColors();
118
- recolor(colors, { brightness: 0.5 });
119
- expect(colors2string(colors)).toBe('FFD5AA00,80FFD555,AA80FFAA,D5AA80FF');
120
- });
121
- it('should maximize brightness', () => {
122
- const colors = getDefaultColors();
123
- recolor(colors, { brightness: 1 });
124
- expect(colors2string(colors)).toBe('FFFFFF00,FFFFFF55,FFFFFFAA,FFFFFFFF');
125
- });
126
- });
127
- describe('tint', () => {
128
- it('should not tint at all', () => {
129
- const colors = getDefaultColors();
130
- recolor(colors, { tint: 0, tintColor: '#F00' });
131
- expect(colors2string(colors)).toBe('FFAA5500,00FFAA55,5500FFAA,AA5500FF');
132
- });
133
- it('should tint a little bit red', () => {
134
- const colors = getDefaultColors();
135
- recolor(colors, { tint: 0.5, tintColor: '#F00' });
136
- expect(colors2string(colors)).toBe('FF552B00,80805555,AA0080AA,D52B00FF');
137
- });
138
- it('should tint a little bit yellow', () => {
139
- const colors = getDefaultColors();
140
- recolor(colors, { tint: 0.5, tintColor: '#FF0' });
141
- expect(colors2string(colors)).toBe('FFD52B00,80FF5555,AA8080AA,D5AA00FF');
142
- });
143
- it('should tint a little bit green', () => {
144
- const colors = getDefaultColors();
145
- recolor(colors, { tint: 0.5, tintColor: '#0F0' });
146
- expect(colors2string(colors)).toBe('80D52B00,00FF5555,2B8080AA,55AA00FF');
147
- });
148
- it('should tint a little bit blue', () => {
149
- const colors = getDefaultColors();
150
- recolor(colors, { tint: 0.5, tintColor: '#00F' });
151
- expect(colors2string(colors)).toBe('8055AA00,0080D555,2B00FFAA,552B80FF');
152
- });
153
- it('should tint a strongly orange', () => {
154
- const colors = getDefaultColors();
155
- recolor(colors, { tint: 0.5, tintColor: '#F80' });
156
- expect(colors2string(colors)).toBe('FF992B00,80C45555,AA4480AA,D56F00FF');
157
- });
158
- it('should tint a strongly blue', () => {
159
- const colors = getDefaultColors();
160
- recolor(colors, { tint: 0.5, tintColor: '#00F' });
161
- expect(colors2string(colors)).toBe('8055AA00,0080D555,2B00FFAA,552B80FF');
162
- });
163
- });
164
- });
165
- });
166
- function colors2string(colors) {
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.');
187
- }
188
- }
189
- function getDefaultColors() {
190
- const style = new TestStyle();
191
- return style.getColors(style.defaultColors);
192
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,27 +0,0 @@
1
- import getLayers from './layers.js';
2
- describe('layers', () => {
3
- it('should return an array of MaplibreLayer', () => {
4
- const languageSuffix = '_en';
5
- const layers = getLayers({ languageSuffix });
6
- expect(Array.isArray(layers)).toBe(true);
7
- expect(layers).not.toHaveLength(0);
8
- layers.forEach((layer) => {
9
- expect(layer).toHaveProperty('id');
10
- expect(layer).toHaveProperty('type');
11
- });
12
- });
13
- it('should handle language suffix correctly', () => {
14
- const languageSuffix = '_en';
15
- const layers = getLayers({ languageSuffix });
16
- const labelLayer = layers.find((layer) => layer.id === 'label-street-pedestrian');
17
- expect(labelLayer).toBeDefined();
18
- expect(labelLayer.layout?.['text-field']).toContain('{name_en}');
19
- });
20
- it('should create appropriate filters for land layers', () => {
21
- const languageSuffix = '_en';
22
- const layers = getLayers({ languageSuffix });
23
- const landLayer = layers.find((layer) => layer.id === 'land-agriculture');
24
- expect(landLayer).toBeDefined();
25
- expect(landLayer.filter).toEqual(['all', ['in', 'kind', 'brownfield', 'farmland', 'farmyard', 'greenfield', 'greenhouse_horticulture', 'orchard', 'plant_nursery', 'vineyard']]);
26
- });
27
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,36 +0,0 @@
1
- import propertyLookup from './properties.js';
2
- describe('propertyLookup', () => {
3
- it('should be a Map', () => {
4
- expect(propertyLookup).toBeInstanceOf(Map);
5
- });
6
- it('should contain keys for each type and property', () => {
7
- const expectedTypes = ['background', 'fill', 'line', 'symbol'];
8
- expectedTypes.forEach(type => {
9
- propertyLookup.forEach((value, key) => {
10
- if (key.startsWith(type)) {
11
- expect(key).toMatch(new RegExp(`^${type}\/`));
12
- }
13
- });
14
- });
15
- });
16
- it('should contain the correct properties for each type', () => {
17
- const expectedProps = ['filter', 'maxzoom', 'minzoom', 'visibility'];
18
- expectedProps.forEach(prop => {
19
- propertyLookup.forEach((value, key) => {
20
- if (key.endsWith(prop)) {
21
- expect(value.some((p) => p.key === prop)).toBeTruthy();
22
- }
23
- });
24
- });
25
- });
26
- it('should store properties with the correct structure', () => {
27
- propertyLookup.forEach(properties => {
28
- properties.forEach((prop) => {
29
- expect(prop).toHaveProperty('key');
30
- expect(prop).toHaveProperty('parent');
31
- expect(prop).toHaveProperty('valueType');
32
- expect(['layer', 'layout', 'paint']).toContain(prop.parent);
33
- });
34
- });
35
- });
36
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,55 +0,0 @@
1
- /* eslint-disable @typescript-eslint/naming-convention */
2
- import getTemplate from './template.js';
3
- describe('getTemplate', () => {
4
- const styleTemplate = getTemplate();
5
- it('returns a style object with the correct version', () => {
6
- expect(styleTemplate).toHaveProperty('version', 8);
7
- });
8
- it('has the expected name for the style', () => {
9
- expect(styleTemplate).toHaveProperty('name', 'versatiles');
10
- });
11
- it('contains metadata with expected properties', () => {
12
- expect(styleTemplate).toHaveProperty('metadata');
13
- expect(styleTemplate.metadata).toHaveProperty('maputnik:renderer', 'mbgljs');
14
- expect(styleTemplate.metadata).toHaveProperty('license', 'https://creativecommons.org/publicdomain/zero/1.0/');
15
- });
16
- it('specifies glyphs and sprite URLs correctly', () => {
17
- expect(styleTemplate).toHaveProperty('glyphs', 'https://tiles.versatiles.org/fonts/{fontstack}/{range}.pbf');
18
- expect(styleTemplate).toHaveProperty('sprite', 'https://tiles.versatiles.org/sprites/sprites');
19
- });
20
- it('defines sources with required properties', () => {
21
- expect(styleTemplate).toHaveProperty('sources');
22
- const sources = styleTemplate.sources['versatiles-shortbread'];
23
- expect(sources).toHaveProperty('type', 'vector');
24
- expect(sources).toHaveProperty('scheme', 'xyz');
25
- expect(sources).toHaveProperty('format', 'pbf');
26
- expect(sources).toHaveProperty('tiles');
27
- expect(sources.tiles).toContain('https://tiles.versatiles.org/tiles/osm/{z}/{x}/{y}');
28
- });
29
- it('sets bounds to the expected global extent', () => {
30
- const expectedBounds = [-180, -85.0511287798066, 180, 85.0511287798066];
31
- expect(styleTemplate.sources['versatiles-shortbread']).toHaveProperty('bounds', expectedBounds);
32
- });
33
- it('has layers array initialized as empty', () => {
34
- expect(styleTemplate).toHaveProperty('layers');
35
- expect(styleTemplate.layers).toEqual([]);
36
- });
37
- describe('sources vector_layers validation', () => {
38
- expect(styleTemplate).toBeDefined();
39
- expect(styleTemplate.sources).toBeDefined();
40
- const { vector_layers } = styleTemplate.sources['versatiles-shortbread'];
41
- expect(typeof vector_layers).toBe('object');
42
- it('contains vector_layers with id and fields', () => {
43
- expect(Array.isArray(vector_layers)).toBeTruthy();
44
- vector_layers.forEach(layer => {
45
- expect(layer).toHaveProperty('id');
46
- expect(layer).toHaveProperty('fields');
47
- });
48
- });
49
- it('has maxzoom set to 14 for all vector layers', () => {
50
- vector_layers.forEach(layer => {
51
- expect(layer).toHaveProperty('maxzoom', 14);
52
- });
53
- });
54
- });
55
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,115 +0,0 @@
1
- import { deepClone, isSimpleObject, isBasicType, deepMerge, resolveUrl } from './utils.js';
2
- import Color from 'color';
3
- describe('deepClone', () => {
4
- it('clones primitive types correctly', () => {
5
- expect(deepClone(10)).toBe(10);
6
- expect(deepClone(true)).toBe(true);
7
- expect(deepClone('string')).toBe('string');
8
- });
9
- it('clones an array correctly', () => {
10
- const array = [1, 2, { a: 'b' }];
11
- const clonedArray = deepClone(array);
12
- expect(clonedArray).toEqual(array);
13
- expect(clonedArray).not.toBe(array);
14
- });
15
- it('clones a simple object correctly', () => {
16
- const obj = { a: 'b', c: 1, d: true };
17
- const clonedObj = deepClone(obj);
18
- expect(clonedObj).toEqual(obj);
19
- expect(clonedObj).not.toBe(obj);
20
- });
21
- it('clones a Color instance correctly', () => {
22
- const color = new Color('#FF5733');
23
- const clonedColor = deepClone(color);
24
- expect(clonedColor.hex()).toBe(color.hex());
25
- });
26
- it('throws an error for non-implemented types', () => {
27
- const func = () => true;
28
- expect(() => deepClone(func)).toThrow('Not implemented yet: "function" case');
29
- });
30
- });
31
- describe('isSimpleObject', () => {
32
- it('identifies simple objects correctly', () => {
33
- expect(isSimpleObject({ a: 1 })).toBe(true);
34
- });
35
- it('rejects non-objects', () => {
36
- expect(isSimpleObject(1)).toBe(false);
37
- expect(isSimpleObject('a')).toBe(false);
38
- expect(isSimpleObject(true)).toBe(false);
39
- });
40
- it('rejects arrays', () => {
41
- expect(isSimpleObject([1, 2, 3])).toBe(false);
42
- });
43
- it('rejects objects with prototype properties', () => {
44
- class MyClass {
45
- #property = true;
46
- }
47
- expect(isSimpleObject(new MyClass())).toBe(false);
48
- });
49
- });
50
- describe('isBasicType', () => {
51
- it('returns true for basic types', () => {
52
- expect(isBasicType(1)).toBe(true);
53
- expect(isBasicType('string')).toBe(true);
54
- expect(isBasicType(true)).toBe(true);
55
- expect(isBasicType(undefined)).toBe(true);
56
- });
57
- it('returns false for objects', () => {
58
- expect(isBasicType({})).toBe(false);
59
- expect(isBasicType([])).toBe(false);
60
- });
61
- it('throws error for functions', () => {
62
- expect(() => isBasicType(() => true)).toThrow();
63
- });
64
- });
65
- describe('deepMerge', () => {
66
- it('merges simple objects correctly', () => {
67
- const target = { a: 1, b: 2 };
68
- const source = { b: 3, c: 4 };
69
- const result = deepMerge(target, source);
70
- expect(result).toEqual({ a: 1, b: 3, c: 4 });
71
- });
72
- it('merges nested objects correctly', () => {
73
- const target = { a: { d: 4 }, b: 2 };
74
- const source = { a: { e: 5 }, b: 3 };
75
- const result = deepMerge(target, source);
76
- expect(result).toEqual({ a: { d: 4, e: 5 }, b: 3 });
77
- });
78
- it('overrides primitives with object types', () => {
79
- const target = { a: 1, b: 2 };
80
- const source = { a: { d: 4 }, b: { e: 5 } };
81
- const result = deepMerge(target, source);
82
- expect(result).toEqual({ a: { d: 4 }, b: { e: 5 } });
83
- });
84
- it('merges Color instances correctly', () => {
85
- const target = { color: new Color('#FF5733') };
86
- const source = { color: new Color('#33FF57') };
87
- const result = deepMerge(target, source);
88
- expect(result.color.hex()).toBe(source.color.hex());
89
- });
90
- it('throws error for unpredicted cases', () => {
91
- const target = { a: () => false };
92
- const source = { a: { b: 1 } };
93
- expect(() => deepMerge(target, source)).toThrow('Not implemented yet: "function" case');
94
- });
95
- });
96
- describe('resolveUrl', () => {
97
- test('should correctly resolve a relative URL with a base URL', () => {
98
- expect(resolveUrl('http://example.com/', 'path/page')).toBe('http://example.com/path/page');
99
- });
100
- test('should return the same URL if base URL is empty', () => {
101
- expect(resolveUrl('', 'http://example.com/path/page')).toBe('http://example.com/path/page');
102
- });
103
- test('should return the correct URL if absolute URL is used', () => {
104
- expect(resolveUrl('http://example1.com', 'http://example.com/path/page')).toBe('http://example.com/path/page');
105
- });
106
- test('should handle URLs with special characters', () => {
107
- expect(resolveUrl('http://example.com/', 'path/{param}')).toBe('http://example.com/path/{param}');
108
- });
109
- test('should handle URLs already containing encoded special characters', () => {
110
- expect(resolveUrl('http://example.com/', 'path/%7Bparam%7D')).toBe('http://example.com/path/{param}');
111
- });
112
- test('should throw error for invalid base URL', () => {
113
- expect(() => resolveUrl('invalid-base', 'path/page')).toThrow('Invalid URL');
114
- });
115
- });