@versatiles/style 5.2.5 → 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 (102) hide show
  1. package/dist/index.d.ts +274 -14
  2. package/dist/index.js +3650 -11
  3. package/dist/index.js.map +1 -0
  4. package/package.json +8 -7
  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/hsl.d.ts +0 -23
  55. package/dist/color/hsl.js +0 -98
  56. package/dist/color/hsv.d.ts +0 -20
  57. package/dist/color/hsv.js +0 -100
  58. package/dist/color/index.d.ts +0 -6
  59. package/dist/color/index.js +0 -29
  60. package/dist/color/random.d.ts +0 -9
  61. package/dist/color/random.js +0 -134
  62. package/dist/color/rgb.d.ts +0 -28
  63. package/dist/color/rgb.js +0 -195
  64. package/dist/color/utils.d.ts +0 -3
  65. package/dist/color/utils.js +0 -10
  66. package/dist/guess_style/guess_style.d.ts +0 -8
  67. package/dist/guess_style/guess_style.js +0 -147
  68. package/dist/guess_style/index.js +0 -2
  69. package/dist/lib/utils.d.ts +0 -6
  70. package/dist/lib/utils.js +0 -126
  71. package/dist/shortbread/index.js +0 -3
  72. package/dist/shortbread/layers.d.ts +0 -5
  73. package/dist/shortbread/layers.js +0 -521
  74. package/dist/shortbread/properties.d.ts +0 -7
  75. package/dist/shortbread/properties.js +0 -125
  76. package/dist/shortbread/template.d.ts +0 -4
  77. package/dist/shortbread/template.js +0 -339
  78. package/dist/style_builder/decorator.d.ts +0 -4
  79. package/dist/style_builder/decorator.js +0 -127
  80. package/dist/style_builder/recolor.d.ts +0 -22
  81. package/dist/style_builder/recolor.js +0 -89
  82. package/dist/style_builder/style_builder.d.ts +0 -15
  83. package/dist/style_builder/style_builder.js +0 -106
  84. package/dist/style_builder/types.d.ts +0 -122
  85. package/dist/style_builder/types.js +0 -3
  86. package/dist/styles/colorful.d.ts +0 -11
  87. package/dist/styles/colorful.js +0 -956
  88. package/dist/styles/eclipse.d.ts +0 -5
  89. package/dist/styles/eclipse.js +0 -9
  90. package/dist/styles/empty.js +0 -8
  91. package/dist/styles/graybeard.d.ts +0 -5
  92. package/dist/styles/graybeard.js +0 -9
  93. package/dist/styles/index.d.ts +0 -11
  94. package/dist/styles/index.js +0 -20
  95. package/dist/styles/neutrino.d.ts +0 -11
  96. package/dist/styles/neutrino.js +0 -401
  97. package/dist/types/index.js +0 -3
  98. package/dist/types/maplibre.js +0 -2
  99. package/dist/types/tilejson.d.ts +0 -32
  100. package/dist/types/tilejson.js +0 -87
  101. package/dist/types/vector_layer.d.ts +0 -14
  102. package/dist/types/vector_layer.js +0 -51
@@ -0,0 +1,86 @@
1
+ import { clamp, formatFloat, mod } from './utils.js';
2
+
3
+ describe('clamp function', () => {
4
+ test('returns the number itself if within the range', () => {
5
+ expect(clamp(5, 0, 10)).toBe(5);
6
+ expect(clamp(0, -10, 10)).toBe(0);
7
+ expect(clamp(-5, -10, 0)).toBe(-5);
8
+ });
9
+
10
+ test('returns the minimum value if number is below range', () => {
11
+ expect(clamp(-5, 0, 10)).toBe(0);
12
+ expect(clamp(-15, -10, 10)).toBe(-10);
13
+ });
14
+
15
+ test('returns the maximum value if number is above range', () => {
16
+ expect(clamp(15, 0, 10)).toBe(10);
17
+ expect(clamp(20, -10, 10)).toBe(10);
18
+ });
19
+
20
+ test('handles edge cases with boundaries', () => {
21
+ expect(clamp(0, 0, 10)).toBe(0);
22
+ expect(clamp(10, 0, 10)).toBe(10);
23
+ });
24
+ });
25
+
26
+ describe('mod function', () => {
27
+ test('returns the correct modulus for positive numbers', () => {
28
+ expect(mod(10, 3)).toBe(1);
29
+ expect(mod(15, 4)).toBe(3);
30
+ });
31
+
32
+ test('handles negative numbers correctly', () => {
33
+ expect(mod(-10, 3)).toBe(2);
34
+ expect(mod(-15, 4)).toBe(1);
35
+ });
36
+
37
+ test('returns 0 for numbers that are exact multiples of max', () => {
38
+ expect(mod(9, 3)).toBe(0);
39
+ expect(mod(20, 5)).toBe(0);
40
+ });
41
+
42
+ test('returns 0 for zero input with positive max', () => {
43
+ expect(mod(0, 5)).toBe(0);
44
+ });
45
+
46
+ test('handles edge cases with 1 as max', () => {
47
+ expect(mod(10, 1)).toBe(0);
48
+ expect(mod(-10, 1)).toBe(0);
49
+ });
50
+ });
51
+ describe('formatFloat function', () => {
52
+ test('formats numbers with specified precision correctly', () => {
53
+ expect(formatFloat(123.4567, 2)).toBe('123.46');
54
+ expect(formatFloat(123.4, 2)).toBe('123.4');
55
+ expect(formatFloat(123.0, 2)).toBe('123');
56
+ });
57
+
58
+ test('handles trailing zeros correctly', () => {
59
+ expect(formatFloat(0.000123, 6)).toBe('0.000123');
60
+ expect(formatFloat(0.000100, 6)).toBe('0.0001');
61
+ expect(formatFloat(10.0, 1)).toBe('10');
62
+ });
63
+
64
+ test('handles edge cases with small numbers and high precision', () => {
65
+ expect(formatFloat(0.0000001234, 10)).toBe('0.0000001234');
66
+ expect(formatFloat(0.0000001234, 5)).toBe('0');
67
+ });
68
+
69
+ test('handles large numbers correctly', () => {
70
+ expect(formatFloat(123456789.123456, 3)).toBe('123456789.123');
71
+ expect(formatFloat(123456789.0, 5)).toBe('123456789');
72
+ });
73
+
74
+ test('handles zero and negative numbers correctly', () => {
75
+ expect(formatFloat(0, 2)).toBe('0');
76
+ expect(formatFloat(-123.456, 1)).toBe('-123.5');
77
+ expect(formatFloat(-0.000123, 6)).toBe('-0.000123');
78
+ });
79
+
80
+ test('handles no unnecessary decimal points', () => {
81
+ expect(formatFloat(10.000, 3)).toBe('10');
82
+ expect(formatFloat(10.010, 3)).toBe('10.01');
83
+ });
84
+ })
85
+
86
+
@@ -0,0 +1,13 @@
1
+
2
+
3
+ export function clamp(num: number, min: number, max: number): number {
4
+ return Math.min(Math.max(min, num), max);
5
+ }
6
+
7
+ export function mod(num: number, max: number): number {
8
+ return ((num % max) + max) % max;
9
+ }
10
+
11
+ export function formatFloat(num: number, precision: number): string {
12
+ return num.toFixed(precision).replace(/0+$/, '').replace(/\.$/, '');
13
+ }
@@ -0,0 +1,134 @@
1
+
2
+ import type { TileJSONSpecification, VectorLayer } from '../types/index.js';
3
+ import { guessStyle } from './guess_style.js';
4
+ import { getShortbreadVectorLayers } from '../shortbread/template.js';
5
+ import { SourceSpecification, StyleSpecification, VectorSourceSpecification } from '@maplibre/maplibre-gl-style-spec';
6
+
7
+ describe('guessStyle', () => {
8
+ const tiles = ['https://example.com/tiles/{z}/{x}/{y}'];
9
+ const vectorLayersSomething: VectorLayer[] = [{ id: 'geometry', fields: { label: 'String', height: 'Number' } }];
10
+ const vectorLayersShortbread: VectorLayer[] = getShortbreadVectorLayers();
11
+
12
+ it('should build raster styles', () => {
13
+ expect(guessStyle({ tiles }))
14
+ .toStrictEqual({
15
+ version: 8,
16
+ sources: { rasterSource: { tiles, type: 'raster' } },
17
+ layers: [{ id: 'raster', source: 'rasterSource', type: 'raster' }],
18
+ });
19
+ });
20
+
21
+ it('should build vector inspector styles', () => {
22
+ expect(guessStyle({ tiles, vector_layers: vectorLayersSomething }))
23
+ .toStrictEqual({
24
+ version: 8,
25
+ sources: { vectorSource: { tiles, type: 'vector' } },
26
+ layers: [
27
+ {
28
+ id: 'background',
29
+ type: 'background',
30
+ paint: { 'background-color': '#fff' },
31
+ },
32
+ {
33
+ id: 'vectorSource-geometry-fill',
34
+ type: 'fill',
35
+ source: 'vectorSource',
36
+ 'source-layer': 'geometry',
37
+ filter: ['==', '$type', 'Polygon'],
38
+ 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)' },
39
+ },
40
+ {
41
+ id: 'vectorSource-geometry-line',
42
+ type: 'line',
43
+ source: 'vectorSource',
44
+ 'source-layer': 'geometry',
45
+ filter: ['==', '$type', 'LineString'],
46
+ layout: { 'line-cap': 'round', 'line-join': 'round' },
47
+ paint: { 'line-color': 'hsla(7,57%,56%,0.6)' },
48
+ },
49
+ {
50
+ id: 'vectorSource-geometry-circle',
51
+ type: 'circle',
52
+ source: 'vectorSource',
53
+ 'source-layer': 'geometry',
54
+ filter: ['==', '$type', 'Point'],
55
+ paint: { 'circle-color': 'hsla(7,57%,56%,0.6)', 'circle-radius': 2 },
56
+ },
57
+ ],
58
+ });
59
+ });
60
+
61
+ it('should build shortbread vector styles', () => {
62
+ const style = guessStyle({ tiles, vector_layers: vectorLayersShortbread }, { baseUrl: 'http://example.com' });
63
+
64
+ expect(style.layers.length).toBe(297);
65
+ style.layers = [];
66
+
67
+ expect(style).toStrictEqual({
68
+ glyphs: 'http://example.com/assets/glyphs/{fontstack}/{range}.pbf',
69
+ metadata: {
70
+ license: 'https://creativecommons.org/publicdomain/zero/1.0/',
71
+ },
72
+ name: 'versatiles-colorful',
73
+ sprite: [{ id: 'basics', url: 'http://example.com/assets/sprites/basics/sprites' }],
74
+ layers: [],
75
+ sources: {
76
+ 'versatiles-shortbread': {
77
+ attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
78
+ bounds: [-180, -85.0511287798066, 180, 85.0511287798066],
79
+ maxzoom: 14,
80
+ minzoom: 0,
81
+ scheme: 'xyz',
82
+ tiles,
83
+ type: 'vector',
84
+ },
85
+ },
86
+ version: 8,
87
+ });
88
+ });
89
+
90
+ const cases: { type: string; tilejson: TileJSONSpecification }[] = [
91
+ { type: 'image', tilejson: { tiles } },
92
+ { type: 'inspector', tilejson: { tiles, vector_layers: vectorLayersSomething } },
93
+ ];
94
+
95
+ function getSource(style: StyleSpecification): SourceSpecification {
96
+ return Object.values(style.sources)[0];
97
+ }
98
+
99
+ describe('minzoom sets minzoom', () => {
100
+ cases.forEach(({ type, tilejson }) => {
101
+ it(type, () => {
102
+ expect(getSource(guessStyle({ ...tilejson, minzoom: 5 }))).toHaveProperty('minzoom', 5);
103
+ });
104
+ });
105
+ });
106
+
107
+ describe('maxzoom sets maxzoom', () => {
108
+ cases.forEach(({ type, tilejson }) => {
109
+ it(type, () => {
110
+ expect(getSource(guessStyle({ ...tilejson, maxzoom: 5 }))).toHaveProperty('maxzoom', 5);
111
+ });
112
+ });
113
+ });
114
+
115
+ describe('absolute tile urls override baseUrl', () => {
116
+ cases.forEach(({ type, tilejson }) => {
117
+ it(type, () => {
118
+ const style = guessStyle({ ...tilejson, tiles: ['https://example1.org/tiles/{z}/{x}/{y}'] }, { baseUrl: 'https://example2.org/' });
119
+ const source = Object.values(style.sources)[0] as VectorSourceSpecification;
120
+ expect(source.tiles).toEqual(['https://example1.org/tiles/{z}/{x}/{y}']);
121
+ });
122
+ });
123
+ });
124
+
125
+ describe('relative tile urls are resolved with baseUrl', () => {
126
+ cases.forEach(({ type, tilejson }) => {
127
+ it(type, () => {
128
+ const style = guessStyle({ ...tilejson, tiles: ['./{z}/{x}/{y}'] }, { baseUrl: 'https://example2.org/tiles/' });
129
+ const source = Object.values(style.sources)[0] as VectorSourceSpecification;
130
+ expect(source.tiles).toEqual(['https://example2.org/tiles/{z}/{x}/{y}']);
131
+ });
132
+ });
133
+ });
134
+ });
@@ -0,0 +1,166 @@
1
+ import type { TileJSONSpecification, TileJSONSpecificationRaster, TileJSONSpecificationVector, VectorLayer } from '../types/index.js';
2
+ import { isTileJSONSpecification } from '../types/index.js';
3
+ import { deepClone, resolveUrl } from '../lib/utils.js';
4
+ import type { BackgroundLayerSpecification, CircleLayerSpecification, FillLayerSpecification, LineLayerSpecification, RasterSourceSpecification, SourceSpecification, SpriteSpecification, StyleSpecification, VectorSourceSpecification } from '@maplibre/maplibre-gl-style-spec';
5
+ import { colorful } from '../styles/index.js';
6
+ import { Color } from '../color/index.js';
7
+ import { isRasterTileJSONSpecification } from '../types/tilejson.js';
8
+
9
+ export interface GuessStyleOptions {
10
+ baseUrl?: string;
11
+ glyphs?: string;
12
+ sprite?: SpriteSpecification;
13
+ }
14
+
15
+ export function guessStyle(tileJSON: TileJSONSpecification, options?: GuessStyleOptions): StyleSpecification {
16
+ tileJSON = deepClone(tileJSON);
17
+
18
+ if (options && options.baseUrl) {
19
+ const { baseUrl } = options;
20
+ tileJSON.tiles = tileJSON.tiles.map(url => resolveUrl(baseUrl, url));
21
+ }
22
+
23
+ if (!isTileJSONSpecification(tileJSON)) throw Error('Invalid TileJSON specification');
24
+
25
+ let style: StyleSpecification;
26
+ if (isRasterTileJSONSpecification(tileJSON)) {
27
+ style = getRasterStyle(tileJSON);
28
+ } else {
29
+ if (isShortbread(tileJSON)) {
30
+ style = getShortbreadStyle(tileJSON, {
31
+ baseUrl: options?.baseUrl,
32
+ glyphs: options?.glyphs,
33
+ sprite: options?.sprite,
34
+ });
35
+ } else {
36
+ style = getInspectorStyle(tileJSON);
37
+ }
38
+ }
39
+
40
+ return style;
41
+ }
42
+
43
+ function isShortbread(spec: TileJSONSpecificationVector): boolean {
44
+ if (typeof spec !== 'object') return false;
45
+ if (!('vector_layers' in spec)) return false;
46
+ if (!Array.isArray(spec.vector_layers)) return false;
47
+
48
+
49
+ const layerIds = new Set(spec.vector_layers.map(l => String(l.id)));
50
+ 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'];
51
+ return shortbreadIds.every(id => layerIds.has(id));
52
+ }
53
+
54
+ function getShortbreadStyle(spec: TileJSONSpecificationVector, builderOption: { baseUrl?: string; glyphs?: string; sprite?: SpriteSpecification }): StyleSpecification {
55
+ return colorful({
56
+ tiles: spec.tiles,
57
+ baseUrl: builderOption.baseUrl,
58
+ glyphs: builderOption.glyphs,
59
+ sprite: builderOption.sprite,
60
+ });
61
+ }
62
+
63
+ function getInspectorStyle(spec: TileJSONSpecificationVector): StyleSpecification {
64
+ const sourceName = 'vectorSource';
65
+
66
+ const layers: {
67
+ background: BackgroundLayerSpecification[];
68
+ circle: CircleLayerSpecification[];
69
+ line: LineLayerSpecification[];
70
+ fill: FillLayerSpecification[];
71
+ } = { background: [], circle: [], line: [], fill: [] };
72
+
73
+ layers.background.push({ 'id': 'background', 'type': 'background', 'paint': { 'background-color': '#fff' } });
74
+
75
+ spec.vector_layers.forEach((vector_layer: VectorLayer) => {
76
+ let luminosity = 'bright', saturation, hue;
77
+
78
+ if (/water|ocean|lake|sea|river/.test(vector_layer.id)) hue = 'blue';
79
+ if (/state|country|place/.test(vector_layer.id)) hue = 'pink';
80
+ if (/road|highway|transport|streets/.test(vector_layer.id)) hue = 'orange';
81
+ if (/contour|building/.test(vector_layer.id)) hue = 'monochrome';
82
+ if (vector_layer.id.includes('building')) luminosity = 'dark';
83
+ if (/contour|landuse/.test(vector_layer.id)) hue = 'yellow';
84
+ if (/wood|forest|park|landcover|land/.test(vector_layer.id)) hue = 'green';
85
+
86
+ if (vector_layer.id.includes('point')) {
87
+ saturation = 'strong';
88
+ luminosity = 'light';
89
+ }
90
+
91
+ const color = Color.random({
92
+ hue,
93
+ luminosity,
94
+ saturation,
95
+ seed: vector_layer.id,
96
+ opacity: 0.6,
97
+ }).asString();
98
+
99
+ layers.circle.push({
100
+ id: `${sourceName}-${vector_layer.id}-circle`,
101
+ 'source-layer': vector_layer.id,
102
+ source: sourceName,
103
+ type: 'circle',
104
+ filter: ['==', '$type', 'Point'],
105
+ paint: { 'circle-color': color, 'circle-radius': 2 },
106
+ });
107
+
108
+ layers.line.push({
109
+ id: `${sourceName}-${vector_layer.id}-line`,
110
+ 'source-layer': vector_layer.id,
111
+ source: sourceName,
112
+ type: 'line',
113
+ filter: ['==', '$type', 'LineString'],
114
+ layout: { 'line-join': 'round', 'line-cap': 'round' },
115
+ paint: { 'line-color': color },
116
+ });
117
+
118
+ layers.fill.push({
119
+ id: `${sourceName}-${vector_layer.id}-fill`,
120
+ 'source-layer': vector_layer.id,
121
+ source: sourceName,
122
+ type: 'fill',
123
+ filter: ['==', '$type', 'Polygon'],
124
+ paint: { 'fill-color': color, 'fill-opacity': 0.3, 'fill-antialias': true, 'fill-outline-color': color },
125
+ });
126
+ });
127
+
128
+ return {
129
+ version: 8,
130
+ sources: {
131
+ [sourceName]: sourceFromSpec(spec, 'vector'),
132
+ },
133
+ layers: [
134
+ ...layers.background,
135
+ ...layers.fill,
136
+ ...layers.line,
137
+ ...layers.circle,
138
+ ],
139
+ };
140
+ }
141
+
142
+ function getRasterStyle(spec: TileJSONSpecificationRaster): StyleSpecification {
143
+ const sourceName = 'rasterSource';
144
+ return {
145
+ version: 8,
146
+ sources: {
147
+ [sourceName]: sourceFromSpec(spec, 'raster'),
148
+ },
149
+ layers: [{
150
+ id: 'raster',
151
+ type: 'raster',
152
+ source: sourceName,
153
+ }],
154
+ };
155
+ }
156
+
157
+ function sourceFromSpec(spec: TileJSONSpecification, type: 'raster' | 'vector'): SourceSpecification {
158
+ const source: RasterSourceSpecification | VectorSourceSpecification = { tiles: spec.tiles, type };
159
+
160
+ if (spec.minzoom != null) source.minzoom = spec.minzoom;
161
+ if (spec.maxzoom != null) source.maxzoom = spec.maxzoom;
162
+ if (spec.attribution != null) source.attribution = spec.attribution;
163
+ if (spec.scheme != null) source.scheme = spec.scheme;
164
+
165
+ return source;
166
+ }
@@ -1,2 +1,3 @@
1
+
1
2
  export type { GuessStyleOptions } from './guess_style.js';
2
3
  export { guessStyle } from './guess_style.js';
@@ -0,0 +1,77 @@
1
+
2
+ import { VectorSourceSpecification } from '@maplibre/maplibre-gl-style-spec';
3
+ import type { VectorLayer } from './index.js';
4
+ import { guessStyle, styles } from './index.js';
5
+
6
+ const { colorful, eclipse, graybeard, neutrino } = styles;
7
+
8
+ describe('styles', () => {
9
+ [
10
+ { name: 'colorful', builder: colorful },
11
+ { name: 'eclipse', builder: eclipse },
12
+ { name: 'graybeard', builder: graybeard },
13
+ { name: 'neutrino', builder: neutrino },
14
+ ].forEach(({ name, builder }) => {
15
+ it(`should create and test an instance of ${name}`, () => {
16
+ expect(typeof builder).toBe('function');
17
+
18
+ const style = builder({ baseUrl: 'https://example.org' });
19
+
20
+ const minSize = (name === 'empty') ? 4000 : 50000;
21
+ expect(JSON.stringify(style).length).toBeGreaterThan(minSize);
22
+
23
+ expect(style.name).toBe('versatiles-' + name);
24
+ expect(style.glyphs).toBe('https://example.org/assets/glyphs/{fontstack}/{range}.pbf');
25
+ expect(style.sprite).toStrictEqual([{ id: 'basics', url: 'https://example.org/assets/sprites/basics/sprites' }]);
26
+ expect(Object.keys(style.sources).join(',')).toBe('versatiles-shortbread');
27
+
28
+ const source = style.sources['versatiles-shortbread'] as VectorSourceSpecification;
29
+ expect(source.tiles).toEqual(['https://example.org/tiles/osm/{z}/{x}/{y}']);
30
+ });
31
+ });
32
+ });
33
+
34
+ describe('Colorful', () => {
35
+ const style = colorful({
36
+ baseUrl: 'https://dev.null',
37
+ colors: { commercial: '#f00' },
38
+ });
39
+ expect(style.glyphs).toBe('https://dev.null/assets/glyphs/{fontstack}/{range}.pbf');
40
+ const paint = style.layers.find(l => l.id === 'land-commercial')?.paint;
41
+
42
+ expect(paint).toBeDefined();
43
+ if (paint == null) throw Error();
44
+
45
+ expect(paint).toHaveProperty('fill-color');
46
+ if (!('fill-color' in paint)) throw Error();
47
+
48
+ expect(paint['fill-color']).toBe('rgb(255,0,0)');
49
+ });
50
+
51
+ describe('guessStyle', () => {
52
+ const tiles = ['https://fancy.map/tiles/{z}/{x}/{y}'];
53
+ const vector_layers: VectorLayer[] = [{ id: 'hallo', fields: { label: 'String' } }];
54
+
55
+ it('should build raster styles', () => {
56
+ const style = guessStyle({ tiles });
57
+ expect(style).toStrictEqual({
58
+ layers: [{ id: 'raster', source: 'rasterSource', type: 'raster' }],
59
+ sources: { rasterSource: { tiles, type: 'raster' } },
60
+ version: 8,
61
+ });
62
+ });
63
+
64
+ it('should build vector styles', () => {
65
+ const style = guessStyle({ tiles, vector_layers });
66
+ expect(style).toStrictEqual({
67
+ layers: [
68
+ { id: 'background', paint: { 'background-color': '#fff' }, type: 'background' },
69
+ { 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' },
70
+ { 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' },
71
+ { 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' },
72
+ ],
73
+ sources: { vectorSource: { tiles, type: 'vector' } },
74
+ 'version': 8,
75
+ });
76
+ });
77
+ });
package/src/index.ts ADDED
@@ -0,0 +1,18 @@
1
+ export { colorful, eclipse, graybeard, neutrino, type StyleBuilderFunction } 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
+
10
+ export type { GuessStyleOptions } from './guess_style/index.js';
11
+ export type { RGB, HSL, HSV, RandomColorOptions } from './color/index.js';
12
+ export type { TileJSONSpecification, TileJSONSpecificationRaster, TileJSONSpecificationVector } from './types/tilejson.js';
13
+ export type { VectorLayer } from './types/index.js';
14
+ export type { StyleBuilderOptions, Language, StyleBuilderColors, StyleBuilderColorsEnsured, StyleBuilderFonts } from './style_builder/types.js';
15
+ export type { RecolorOptions } from './style_builder/recolor.js';
16
+
17
+ export { guessStyle } from './guess_style/index.js';
18
+ export { Color } from './color/index.js';