@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.
- package/dist/index.d.ts +274 -14
- package/dist/index.js +3650 -11
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
- package/src/color/abstract.ts +83 -0
- package/src/color/hsl.test.ts +182 -0
- package/src/color/hsl.ts +122 -0
- package/src/color/hsv.test.ts +174 -0
- package/src/color/hsv.ts +100 -0
- package/src/color/index.test.ts +119 -0
- package/src/color/index.ts +38 -0
- package/src/color/random.test.ts +35 -0
- package/src/color/random.ts +165 -0
- package/src/color/rgb.test.ts +227 -0
- package/src/color/rgb.ts +248 -0
- package/src/color/utils.test.ts +86 -0
- package/src/color/utils.ts +13 -0
- package/src/guess_style/guess_style.test.ts +134 -0
- package/src/guess_style/guess_style.ts +166 -0
- package/{dist/guess_style/index.d.ts → src/guess_style/index.ts} +1 -0
- package/src/index.test.ts +77 -0
- package/src/index.ts +18 -0
- package/src/lib/utils.test.ts +197 -0
- package/src/lib/utils.ts +134 -0
- package/{dist/shortbread/index.d.ts → src/shortbread/index.ts} +1 -0
- package/src/shortbread/layers.test.ts +36 -0
- package/src/shortbread/layers.ts +564 -0
- package/src/shortbread/properties.test.ts +44 -0
- package/src/shortbread/properties.ts +142 -0
- package/src/shortbread/template.test.ts +43 -0
- package/src/shortbread/template.ts +343 -0
- package/src/style_builder/decorator.test.ts +67 -0
- package/src/style_builder/decorator.ts +135 -0
- package/src/style_builder/recolor.test.ts +306 -0
- package/src/style_builder/recolor.ts +110 -0
- package/src/style_builder/style_builder.test.ts +103 -0
- package/src/style_builder/style_builder.ts +134 -0
- package/src/style_builder/types.ts +141 -0
- package/src/styles/LICENSE.md +41 -0
- package/src/styles/colorful.ts +1041 -0
- package/src/styles/eclipse.ts +11 -0
- package/{dist/styles/empty.d.ts → src/styles/empty.ts} +7 -3
- package/src/styles/graybeard.ts +11 -0
- package/src/styles/index.ts +33 -0
- package/src/styles/neutrino.ts +429 -0
- package/{dist/types/index.d.ts → src/types/index.ts} +1 -0
- package/{dist/types/maplibre.d.ts → src/types/maplibre.ts} +3 -0
- package/src/types/tilejson.test.ts +94 -0
- package/src/types/tilejson.ts +125 -0
- package/src/types/vector_layer.test.ts +64 -0
- package/src/types/vector_layer.ts +69 -0
- package/dist/color/abstract.d.ts +0 -34
- package/dist/color/abstract.js +0 -53
- package/dist/color/abstract.js.map +0 -1
- package/dist/color/hsl.d.ts +0 -23
- package/dist/color/hsl.js +0 -98
- package/dist/color/hsl.js.map +0 -1
- package/dist/color/hsv.d.ts +0 -20
- package/dist/color/hsv.js +0 -100
- package/dist/color/hsv.js.map +0 -1
- package/dist/color/index.d.ts +0 -6
- package/dist/color/index.js +0 -29
- package/dist/color/index.js.map +0 -1
- package/dist/color/random.d.ts +0 -9
- package/dist/color/random.js +0 -134
- package/dist/color/random.js.map +0 -1
- package/dist/color/rgb.d.ts +0 -28
- package/dist/color/rgb.js +0 -195
- package/dist/color/rgb.js.map +0 -1
- package/dist/color/utils.d.ts +0 -3
- package/dist/color/utils.js +0 -10
- package/dist/color/utils.js.map +0 -1
- package/dist/guess_style/guess_style.d.ts +0 -8
- package/dist/guess_style/guess_style.js +0 -147
- package/dist/guess_style/guess_style.js.map +0 -1
- package/dist/guess_style/index.js +0 -2
- package/dist/guess_style/index.js.map +0 -1
- package/dist/lib/utils.d.ts +0 -6
- package/dist/lib/utils.js +0 -126
- package/dist/lib/utils.js.map +0 -1
- package/dist/shortbread/index.js +0 -3
- package/dist/shortbread/index.js.map +0 -1
- package/dist/shortbread/layers.d.ts +0 -5
- package/dist/shortbread/layers.js +0 -521
- package/dist/shortbread/layers.js.map +0 -1
- package/dist/shortbread/properties.d.ts +0 -7
- package/dist/shortbread/properties.js +0 -125
- package/dist/shortbread/properties.js.map +0 -1
- package/dist/shortbread/template.d.ts +0 -4
- package/dist/shortbread/template.js +0 -339
- package/dist/shortbread/template.js.map +0 -1
- package/dist/style_builder/decorator.d.ts +0 -4
- package/dist/style_builder/decorator.js +0 -127
- package/dist/style_builder/decorator.js.map +0 -1
- package/dist/style_builder/recolor.d.ts +0 -22
- package/dist/style_builder/recolor.js +0 -89
- package/dist/style_builder/recolor.js.map +0 -1
- package/dist/style_builder/style_builder.d.ts +0 -15
- package/dist/style_builder/style_builder.js +0 -106
- package/dist/style_builder/style_builder.js.map +0 -1
- package/dist/style_builder/types.d.ts +0 -122
- package/dist/style_builder/types.js +0 -3
- package/dist/style_builder/types.js.map +0 -1
- package/dist/styles/colorful.d.ts +0 -11
- package/dist/styles/colorful.js +0 -956
- package/dist/styles/colorful.js.map +0 -1
- package/dist/styles/eclipse.d.ts +0 -5
- package/dist/styles/eclipse.js +0 -9
- package/dist/styles/eclipse.js.map +0 -1
- package/dist/styles/empty.js +0 -8
- package/dist/styles/empty.js.map +0 -1
- package/dist/styles/graybeard.d.ts +0 -5
- package/dist/styles/graybeard.js +0 -9
- package/dist/styles/graybeard.js.map +0 -1
- package/dist/styles/index.d.ts +0 -11
- package/dist/styles/index.js +0 -20
- package/dist/styles/index.js.map +0 -1
- package/dist/styles/neutrino.d.ts +0 -11
- package/dist/styles/neutrino.js +0 -401
- package/dist/styles/neutrino.js.map +0 -1
- package/dist/types/index.js +0 -3
- package/dist/types/index.js.map +0 -1
- package/dist/types/maplibre.js +0 -2
- package/dist/types/maplibre.js.map +0 -1
- package/dist/types/tilejson.d.ts +0 -32
- package/dist/types/tilejson.js +0 -87
- package/dist/types/tilejson.js.map +0 -1
- package/dist/types/vector_layer.d.ts +0 -14
- package/dist/types/vector_layer.js +0 -51
- package/dist/types/vector_layer.js.map +0 -1
|
@@ -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
|
+
}
|
|
@@ -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';
|