@mapwhit/tilerenderer 0.48.0 → 0.50.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/build/min/package.json +1 -1
- package/build/min/src/shaders/fill_extrusion.fragment.glsl.txt +1 -9
- package/build/min/src/shaders/fill_extrusion.vertex.glsl.txt +4 -4
- package/package.json +3 -2
- package/src/data/array_types.js +169 -0
- package/src/data/bucket/symbol_attributes.js +18 -0
- package/src/data/bucket/symbol_bucket.js +108 -78
- package/src/geo/transform.js +13 -5
- package/src/render/glyph_atlas.js +3 -6
- package/src/render/image_atlas.js +3 -6
- package/src/render/image_manager.js +41 -41
- package/src/shaders/fill_extrusion.fragment.glsl +0 -7
- package/src/shaders/fill_extrusion.vertex.glsl +4 -4
- package/src/style/style_layer/symbol_style_layer_properties.js +6 -0
- package/src/style-spec/expression/definitions/assertion.js +52 -5
- package/src/style-spec/expression/definitions/coalesce.js +1 -3
- package/src/style-spec/expression/definitions/coercion.js +32 -21
- package/src/style-spec/expression/definitions/collator.js +1 -23
- package/src/style-spec/expression/definitions/{formatted.js → format.js} +3 -29
- package/src/style-spec/expression/definitions/index.js +8 -26
- package/src/style-spec/expression/definitions/let.js +1 -1
- package/src/style-spec/expression/definitions/literal.js +1 -1
- package/src/style-spec/expression/evaluation_context.js +3 -0
- package/src/style-spec/expression/index.js +18 -17
- package/src/style-spec/expression/parsing_context.js +28 -24
- package/src/style-spec/expression/types/collator.js +24 -0
- package/src/style-spec/expression/types/formatted.js +39 -0
- package/src/style-spec/expression/types.js +1 -1
- package/src/style-spec/expression/values.js +24 -1
- package/src/style-spec/feature_filter/convert.js +197 -0
- package/src/style-spec/feature_filter/index.js +4 -1
- package/src/style-spec/function/convert.js +86 -102
- package/src/style-spec/function/index.js +4 -0
- package/src/style-spec/reference/v8.json +43 -6
- package/src/symbol/collision_index.js +0 -1
- package/src/symbol/cross_tile_symbol_index.js +12 -7
- package/src/symbol/mergelines.js +2 -2
- package/src/symbol/placement.js +71 -54
- package/src/symbol/shaping.js +9 -18
- package/src/symbol/symbol_layout.js +33 -33
- package/src/symbol/transform_text.js +5 -8
- package/src/style-spec/expression/definitions/array.js +0 -82
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
const ShelfPack = require('@mapbox/shelf-pack');
|
|
2
|
-
|
|
3
1
|
const { RGBAImage } = require('../util/image');
|
|
4
2
|
const { register } = require('../util/transfer_registry');
|
|
3
|
+
const potpack = require('potpack');
|
|
5
4
|
|
|
6
5
|
const padding = 1;
|
|
7
6
|
|
|
@@ -32,7 +31,6 @@ class ImageAtlas {
|
|
|
32
31
|
constructor(icons, patterns) {
|
|
33
32
|
const iconPositions = {};
|
|
34
33
|
const patternPositions = {};
|
|
35
|
-
const pack = new ShelfPack(0, 0, { autoResize: true });
|
|
36
34
|
const bins = [];
|
|
37
35
|
for (const id in icons) {
|
|
38
36
|
const src = icons[id];
|
|
@@ -58,9 +56,8 @@ class ImageAtlas {
|
|
|
58
56
|
patternPositions[id] = new ImagePosition(bin, src);
|
|
59
57
|
}
|
|
60
58
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const image = new RGBAImage({ width: pack.w, height: pack.h });
|
|
59
|
+
const { w, h } = potpack(bins);
|
|
60
|
+
const image = new RGBAImage({ width: w || 1, height: h || 1 });
|
|
64
61
|
|
|
65
62
|
for (const id in icons) {
|
|
66
63
|
const src = icons[id];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const potpack = require('potpack');
|
|
2
2
|
|
|
3
3
|
const { RGBAImage } = require('../util/image');
|
|
4
4
|
const { ImagePosition } = require('./image_atlas');
|
|
@@ -26,9 +26,8 @@ class ImageManager {
|
|
|
26
26
|
constructor() {
|
|
27
27
|
this.images = {};
|
|
28
28
|
|
|
29
|
-
this.shelfPack = new ShelfPack(64, 64, { autoResize: true });
|
|
30
29
|
this.patterns = {};
|
|
31
|
-
this.atlasImage = new RGBAImage({ width:
|
|
30
|
+
this.atlasImage = new RGBAImage({ width: 1, height: 1 });
|
|
32
31
|
this.dirty = true;
|
|
33
32
|
}
|
|
34
33
|
|
|
@@ -56,12 +55,7 @@ class ImageManager {
|
|
|
56
55
|
removeImage(id) {
|
|
57
56
|
assert(this.images[id]);
|
|
58
57
|
delete this.images[id];
|
|
59
|
-
|
|
60
|
-
const pattern = this.patterns[id];
|
|
61
|
-
if (pattern) {
|
|
62
|
-
this.shelfPack.unref(pattern.bin);
|
|
63
|
-
delete this.patterns[id];
|
|
64
|
-
}
|
|
58
|
+
delete this.patterns[id];
|
|
65
59
|
}
|
|
66
60
|
|
|
67
61
|
listImages() {
|
|
@@ -89,10 +83,8 @@ class ImageManager {
|
|
|
89
83
|
// Pattern stuff
|
|
90
84
|
|
|
91
85
|
getPixelSize() {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
height: this.shelfPack.h
|
|
95
|
-
};
|
|
86
|
+
const { width, height } = this.atlasImage;
|
|
87
|
+
return { width, height };
|
|
96
88
|
}
|
|
97
89
|
|
|
98
90
|
getPattern(id) {
|
|
@@ -106,36 +98,13 @@ class ImageManager {
|
|
|
106
98
|
return null;
|
|
107
99
|
}
|
|
108
100
|
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
const bin = this.shelfPack.packOne(width, height);
|
|
113
|
-
if (!bin) {
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
this.atlasImage.resize(this.getPixelSize());
|
|
118
|
-
|
|
119
|
-
const src = image.data;
|
|
120
|
-
const dst = this.atlasImage;
|
|
121
|
-
|
|
122
|
-
const x = bin.x + padding;
|
|
123
|
-
const y = bin.y + padding;
|
|
124
|
-
const w = src.width;
|
|
125
|
-
const h = src.height;
|
|
126
|
-
|
|
127
|
-
RGBAImage.copy(src, dst, { x: 0, y: 0 }, { x, y }, { width: w, height: h });
|
|
128
|
-
|
|
129
|
-
// Add 1 pixel wrapped padding on each side of the image.
|
|
130
|
-
RGBAImage.copy(src, dst, { x: 0, y: h - 1 }, { x: x, y: y - 1 }, { width: w, height: 1 }); // T
|
|
131
|
-
RGBAImage.copy(src, dst, { x: 0, y: 0 }, { x: x, y: y + h }, { width: w, height: 1 }); // B
|
|
132
|
-
RGBAImage.copy(src, dst, { x: w - 1, y: 0 }, { x: x - 1, y: y }, { width: 1, height: h }); // L
|
|
133
|
-
RGBAImage.copy(src, dst, { x: 0, y: 0 }, { x: x + w, y: y }, { width: 1, height: h }); // R
|
|
134
|
-
|
|
135
|
-
this.dirty = true;
|
|
136
|
-
|
|
101
|
+
const w = image.data.width + padding * 2;
|
|
102
|
+
const h = image.data.height + padding * 2;
|
|
103
|
+
const bin = { w, h, x: 0, y: 0 };
|
|
137
104
|
const position = new ImagePosition(bin, image);
|
|
138
105
|
this.patterns[id] = { bin, position };
|
|
106
|
+
this._updatePatternAtlas();
|
|
107
|
+
|
|
139
108
|
return position;
|
|
140
109
|
}
|
|
141
110
|
|
|
@@ -150,6 +119,37 @@ class ImageManager {
|
|
|
150
119
|
|
|
151
120
|
this.atlasTexture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE);
|
|
152
121
|
}
|
|
122
|
+
|
|
123
|
+
_updatePatternAtlas() {
|
|
124
|
+
const bins = [];
|
|
125
|
+
for (const id in this.patterns) {
|
|
126
|
+
bins.push(this.patterns[id].bin);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const { w, h } = potpack(bins);
|
|
130
|
+
|
|
131
|
+
const dst = this.atlasImage;
|
|
132
|
+
dst.resize({ width: w ?? 1, height: h ?? 1 });
|
|
133
|
+
|
|
134
|
+
for (const id in this.patterns) {
|
|
135
|
+
const { bin } = this.patterns[id];
|
|
136
|
+
const x = bin.x + padding;
|
|
137
|
+
const y = bin.y + padding;
|
|
138
|
+
const src = this.images[id].data;
|
|
139
|
+
const w = src.width;
|
|
140
|
+
const h = src.height;
|
|
141
|
+
|
|
142
|
+
RGBAImage.copy(src, dst, { x: 0, y: 0 }, { x, y }, { width: w, height: h });
|
|
143
|
+
|
|
144
|
+
// Add 1 pixel wrapped padding on each side of the image.
|
|
145
|
+
RGBAImage.copy(src, dst, { x: 0, y: h - 1 }, { x: x, y: y - 1 }, { width: w, height: 1 }); // T
|
|
146
|
+
RGBAImage.copy(src, dst, { x: 0, y: 0 }, { x: x, y: y + h }, { width: w, height: 1 }); // B
|
|
147
|
+
RGBAImage.copy(src, dst, { x: w - 1, y: 0 }, { x: x - 1, y: y }, { width: 1, height: h }); // L
|
|
148
|
+
RGBAImage.copy(src, dst, { x: 0, y: 0 }, { x: x + w, y: y }, { width: 1, height: h }); // R
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
this.dirty = true;
|
|
152
|
+
}
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
module.exports = ImageManager;
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
varying vec4 v_color;
|
|
2
|
-
#pragma mapbox: define lowp float base
|
|
3
|
-
#pragma mapbox: define lowp float height
|
|
4
|
-
#pragma mapbox: define highp vec4 color
|
|
5
2
|
|
|
6
3
|
void main() {
|
|
7
|
-
#pragma mapbox: initialize lowp float base
|
|
8
|
-
#pragma mapbox: initialize lowp float height
|
|
9
|
-
#pragma mapbox: initialize highp vec4 color
|
|
10
|
-
|
|
11
4
|
gl_FragColor = v_color;
|
|
12
5
|
|
|
13
6
|
#ifdef OVERDRAW_INSPECTOR
|
|
@@ -10,14 +10,14 @@ attribute vec4 a_normal_ed;
|
|
|
10
10
|
|
|
11
11
|
varying vec4 v_color;
|
|
12
12
|
|
|
13
|
-
#pragma mapbox: define
|
|
14
|
-
#pragma mapbox: define
|
|
13
|
+
#pragma mapbox: define highp float base
|
|
14
|
+
#pragma mapbox: define highp float height
|
|
15
15
|
|
|
16
16
|
#pragma mapbox: define highp vec4 color
|
|
17
17
|
|
|
18
18
|
void main() {
|
|
19
|
-
#pragma mapbox: initialize
|
|
20
|
-
#pragma mapbox: initialize
|
|
19
|
+
#pragma mapbox: initialize highp float base
|
|
20
|
+
#pragma mapbox: initialize highp float height
|
|
21
21
|
#pragma mapbox: initialize highp vec4 color
|
|
22
22
|
|
|
23
23
|
vec3 normal = a_normal_ed.xyz;
|
|
@@ -19,6 +19,12 @@ const layout = new Properties({
|
|
|
19
19
|
default: false,
|
|
20
20
|
expression: { parameters: ['zoom'] }
|
|
21
21
|
}),
|
|
22
|
+
'symbol-z-order': new DataConstantProperty({
|
|
23
|
+
type: 'enum',
|
|
24
|
+
values: ['viewport-y', 'source'],
|
|
25
|
+
default: 'viewport-y',
|
|
26
|
+
expression: { parameters: ['zoom'] }
|
|
27
|
+
}),
|
|
22
28
|
'icon-allow-overlap': new DataConstantProperty({
|
|
23
29
|
type: 'boolean',
|
|
24
30
|
default: false,
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
const assert = require('assert');
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const {
|
|
4
|
+
ObjectType,
|
|
5
|
+
ValueType,
|
|
6
|
+
StringType,
|
|
7
|
+
NumberType,
|
|
8
|
+
BooleanType,
|
|
9
|
+
checkSubtype,
|
|
10
|
+
toString,
|
|
11
|
+
array
|
|
12
|
+
} = require('../types');
|
|
4
13
|
const RuntimeError = require('../runtime_error');
|
|
5
14
|
const { typeOf } = require('../values');
|
|
6
15
|
|
|
@@ -20,13 +29,39 @@ class Assertion {
|
|
|
20
29
|
static parse(args, context) {
|
|
21
30
|
if (args.length < 2) return context.error('Expected at least one argument.');
|
|
22
31
|
|
|
32
|
+
let i = 1;
|
|
33
|
+
let type;
|
|
34
|
+
|
|
23
35
|
const name = args[0];
|
|
24
|
-
|
|
36
|
+
if (name === 'array') {
|
|
37
|
+
let itemType;
|
|
38
|
+
if (args.length > 2) {
|
|
39
|
+
const type = args[1];
|
|
40
|
+
if (typeof type !== 'string' || !(type in types) || type === 'object')
|
|
41
|
+
return context.error('The item type argument of "array" must be one of string, number, boolean', 1);
|
|
42
|
+
itemType = types[type];
|
|
43
|
+
i++;
|
|
44
|
+
} else {
|
|
45
|
+
itemType = ValueType;
|
|
46
|
+
}
|
|
25
47
|
|
|
26
|
-
|
|
48
|
+
let N;
|
|
49
|
+
if (args.length > 3) {
|
|
50
|
+
if (args[2] !== null && (typeof args[2] !== 'number' || args[2] < 0 || args[2] !== Math.floor(args[2]))) {
|
|
51
|
+
return context.error('The length argument to "array" must be a positive integer literal', 2);
|
|
52
|
+
}
|
|
53
|
+
N = args[2];
|
|
54
|
+
i++;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
type = array(itemType, N);
|
|
58
|
+
} else {
|
|
59
|
+
assert(types[name], name);
|
|
60
|
+
type = types[name];
|
|
61
|
+
}
|
|
27
62
|
|
|
28
63
|
const parsed = [];
|
|
29
|
-
for (
|
|
64
|
+
for (; i < args.length; i++) {
|
|
30
65
|
const input = context.parse(args[i], i, ValueType);
|
|
31
66
|
if (!input) return null;
|
|
32
67
|
parsed.push(input);
|
|
@@ -62,7 +97,19 @@ class Assertion {
|
|
|
62
97
|
}
|
|
63
98
|
|
|
64
99
|
serialize() {
|
|
65
|
-
|
|
100
|
+
const type = this.type;
|
|
101
|
+
const serialized = [type.kind];
|
|
102
|
+
if (type.kind === 'array') {
|
|
103
|
+
const itemType = type.itemType;
|
|
104
|
+
if (itemType.kind === 'string' || itemType.kind === 'number' || itemType.kind === 'boolean') {
|
|
105
|
+
serialized.push(itemType.kind);
|
|
106
|
+
const N = type.N;
|
|
107
|
+
if (typeof N === 'number' || this.args.length > 1) {
|
|
108
|
+
serialized.push(N);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return serialized.concat(this.args.map(arg => arg.serialize()));
|
|
66
113
|
}
|
|
67
114
|
}
|
|
68
115
|
|
|
@@ -20,9 +20,7 @@ class Coalesce {
|
|
|
20
20
|
const parsedArgs = [];
|
|
21
21
|
|
|
22
22
|
for (const arg of args.slice(1)) {
|
|
23
|
-
const parsed = context.parse(arg, 1 + parsedArgs.length, outputType, undefined, {
|
|
24
|
-
omitTypeAnnotations: true
|
|
25
|
-
});
|
|
23
|
+
const parsed = context.parse(arg, 1 + parsedArgs.length, outputType, undefined, { typeAnnotation: 'omit' });
|
|
26
24
|
if (!parsed) return null;
|
|
27
25
|
outputType = outputType || parsed.type;
|
|
28
26
|
parsedArgs.push(parsed);
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
const assert = require('assert');
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
const { Color, validateRGBA } = require('../values');
|
|
3
|
+
const { BooleanType, ColorType, NumberType, StringType, ValueType } = require('../types');
|
|
4
|
+
const { Color, toString: valueToString, validateRGBA } = require('../values');
|
|
5
5
|
const RuntimeError = require('../runtime_error');
|
|
6
|
-
const {
|
|
6
|
+
const { FormatExpression } = require('../definitions/format');
|
|
7
|
+
const { Formatted } = require('../types/formatted');
|
|
7
8
|
|
|
8
9
|
const types = {
|
|
10
|
+
'to-boolean': BooleanType,
|
|
11
|
+
'to-color': ColorType,
|
|
9
12
|
'to-number': NumberType,
|
|
10
|
-
'to-
|
|
13
|
+
'to-string': StringType
|
|
11
14
|
};
|
|
12
15
|
|
|
13
16
|
/**
|
|
@@ -29,6 +32,9 @@ class Coercion {
|
|
|
29
32
|
const name = args[0];
|
|
30
33
|
assert(types[name], name);
|
|
31
34
|
|
|
35
|
+
if ((name === 'to-boolean' || name === 'to-string') && args.length !== 2)
|
|
36
|
+
return context.error('Expected one argument.');
|
|
37
|
+
|
|
32
38
|
const type = types[name];
|
|
33
39
|
|
|
34
40
|
const parsed = [];
|
|
@@ -42,12 +48,18 @@ class Coercion {
|
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
evaluate(ctx) {
|
|
51
|
+
if (this.type.kind === 'boolean') {
|
|
52
|
+
return Boolean(this.args[0].evaluate(ctx));
|
|
53
|
+
}
|
|
45
54
|
if (this.type.kind === 'color') {
|
|
46
55
|
let input;
|
|
47
56
|
let error;
|
|
48
57
|
for (const arg of this.args) {
|
|
49
58
|
input = arg.evaluate(ctx);
|
|
50
59
|
error = null;
|
|
60
|
+
if (input instanceof Color) {
|
|
61
|
+
return input;
|
|
62
|
+
}
|
|
51
63
|
if (typeof input === 'string') {
|
|
52
64
|
const c = ctx.parseColor(input);
|
|
53
65
|
if (c) return c;
|
|
@@ -66,27 +78,23 @@ class Coercion {
|
|
|
66
78
|
error || `Could not parse color from value '${typeof input === 'string' ? input : JSON.stringify(input)}'`
|
|
67
79
|
);
|
|
68
80
|
}
|
|
69
|
-
if (this.type.kind === '
|
|
70
|
-
let
|
|
81
|
+
if (this.type.kind === 'number') {
|
|
82
|
+
let value = null;
|
|
71
83
|
for (const arg of this.args) {
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
|
|
75
|
-
|
|
84
|
+
value = arg.evaluate(ctx);
|
|
85
|
+
if (value === null) return 0;
|
|
86
|
+
const num = Number(value);
|
|
87
|
+
if (isNaN(num)) continue;
|
|
88
|
+
return num;
|
|
76
89
|
}
|
|
77
|
-
throw new RuntimeError(
|
|
78
|
-
`Could not parse formatted text from value '${typeof input === 'string' ? input : JSON.stringify(input)}'`
|
|
79
|
-
);
|
|
90
|
+
throw new RuntimeError(`Could not convert ${JSON.stringify(value)} to number.`);
|
|
80
91
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const num = Number(value);
|
|
86
|
-
if (isNaN(num)) continue;
|
|
87
|
-
return num;
|
|
92
|
+
if (this.type.kind === 'formatted') {
|
|
93
|
+
// There is no explicit 'to-formatted' but this coercion can be implicitly
|
|
94
|
+
// created by properties that expect the 'formatted' type.
|
|
95
|
+
return Formatted.fromString(valueToString(this.args[0].evaluate(ctx)));
|
|
88
96
|
}
|
|
89
|
-
|
|
97
|
+
return valueToString(this.args[0].evaluate(ctx));
|
|
90
98
|
}
|
|
91
99
|
|
|
92
100
|
eachChild(fn) {
|
|
@@ -98,6 +106,9 @@ class Coercion {
|
|
|
98
106
|
}
|
|
99
107
|
|
|
100
108
|
serialize() {
|
|
109
|
+
if (this.type.kind === 'formatted') {
|
|
110
|
+
return new FormatExpression([{ text: this.args[0], scale: null, font: null }]).serialize();
|
|
111
|
+
}
|
|
101
112
|
const serialized = [`to-${this.type.kind}`];
|
|
102
113
|
this.eachChild(child => {
|
|
103
114
|
serialized.push(child.serialize());
|
|
@@ -1,27 +1,5 @@
|
|
|
1
1
|
const { StringType, BooleanType, CollatorType } = require('../types');
|
|
2
|
-
|
|
3
|
-
class Collator {
|
|
4
|
-
constructor(caseSensitive, diacriticSensitive, locale) {
|
|
5
|
-
if (caseSensitive) this.sensitivity = diacriticSensitive ? 'variant' : 'case';
|
|
6
|
-
else this.sensitivity = diacriticSensitive ? 'accent' : 'base';
|
|
7
|
-
|
|
8
|
-
this.locale = locale;
|
|
9
|
-
this.collator = new Intl.Collator(this.locale ? this.locale : [], {
|
|
10
|
-
sensitivity: this.sensitivity,
|
|
11
|
-
usage: 'search'
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
compare(lhs, rhs) {
|
|
16
|
-
return this.collator.compare(lhs, rhs);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
resolvedLocale() {
|
|
20
|
-
// We create a Collator without "usage: search" because we don't want
|
|
21
|
-
// the search options encoded in our result (e.g. "en-u-co-search")
|
|
22
|
-
return new Intl.Collator(this.locale ? this.locale : []).resolvedOptions().locale;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
2
|
+
const { Collator } = require('../types/collator');
|
|
25
3
|
|
|
26
4
|
class CollatorExpression {
|
|
27
5
|
constructor(caseSensitive, diacriticSensitive, locale) {
|
|
@@ -1,32 +1,6 @@
|
|
|
1
1
|
const { NumberType, ValueType, FormattedType, array, StringType } = require('../types');
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
constructor(text, scale, fontStack) {
|
|
5
|
-
this.text = text;
|
|
6
|
-
this.scale = scale;
|
|
7
|
-
this.fontStack = fontStack;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
class Formatted {
|
|
12
|
-
constructor(sections) {
|
|
13
|
-
this.sections = sections;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
toString() {
|
|
17
|
-
return this.sections.map(section => section.text).join('');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
serialize() {
|
|
21
|
-
const serialized = ['format'];
|
|
22
|
-
for (const section of this.sections) {
|
|
23
|
-
serialized.push(section.text);
|
|
24
|
-
const fontStack = section.fontStack ? ['literal', section.fontStack.split(',')] : null;
|
|
25
|
-
serialized.push({ 'text-font': fontStack, 'font-scale': section.scale });
|
|
26
|
-
}
|
|
27
|
-
return serialized;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
2
|
+
const { Formatted, FormattedSection } = require('../types/formatted');
|
|
3
|
+
const { toString } = require('../values');
|
|
30
4
|
|
|
31
5
|
class FormatExpression {
|
|
32
6
|
constructor(sections) {
|
|
@@ -77,7 +51,7 @@ class FormatExpression {
|
|
|
77
51
|
this.sections.map(
|
|
78
52
|
section =>
|
|
79
53
|
new FormattedSection(
|
|
80
|
-
section.text.evaluate(ctx)
|
|
54
|
+
toString(section.text.evaluate(ctx)),
|
|
81
55
|
section.scale ? section.scale.evaluate(ctx) : null,
|
|
82
56
|
section.font ? section.font.evaluate(ctx).join(',') : null
|
|
83
57
|
)
|
|
@@ -8,17 +8,16 @@ const {
|
|
|
8
8
|
ErrorType,
|
|
9
9
|
CollatorType,
|
|
10
10
|
array,
|
|
11
|
-
toString
|
|
11
|
+
toString: typeToString
|
|
12
12
|
} = require('../types');
|
|
13
13
|
|
|
14
|
-
const { typeOf, Color, validateRGBA } = require('../values');
|
|
14
|
+
const { typeOf, Color, validateRGBA, toString: valueToString } = require('../values');
|
|
15
15
|
const CompoundExpression = require('../compound_expression');
|
|
16
16
|
const RuntimeError = require('../runtime_error');
|
|
17
17
|
const Let = require('./let');
|
|
18
18
|
const Var = require('./var');
|
|
19
19
|
const Literal = require('./literal');
|
|
20
20
|
const Assertion = require('./assertion');
|
|
21
|
-
const ArrayAssertion = require('./array');
|
|
22
21
|
const Coercion = require('./coercion');
|
|
23
22
|
const At = require('./at');
|
|
24
23
|
const Match = require('./match');
|
|
@@ -28,7 +27,7 @@ const Interpolate = require('./interpolate');
|
|
|
28
27
|
const Coalesce = require('./coalesce');
|
|
29
28
|
const { Equals, NotEquals, LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual } = require('./comparison');
|
|
30
29
|
const { CollatorExpression } = require('./collator');
|
|
31
|
-
const {
|
|
30
|
+
const { FormatExpression } = require('./format');
|
|
32
31
|
const Length = require('./length');
|
|
33
32
|
|
|
34
33
|
const expressions = {
|
|
@@ -39,7 +38,7 @@ const expressions = {
|
|
|
39
38
|
'<': LessThan,
|
|
40
39
|
'>=': GreaterThanOrEqual,
|
|
41
40
|
'<=': LessThanOrEqual,
|
|
42
|
-
array:
|
|
41
|
+
array: Assertion,
|
|
43
42
|
at: At,
|
|
44
43
|
boolean: Assertion,
|
|
45
44
|
case: Case,
|
|
@@ -57,8 +56,10 @@ const expressions = {
|
|
|
57
56
|
object: Assertion,
|
|
58
57
|
step: Step,
|
|
59
58
|
string: Assertion,
|
|
59
|
+
'to-boolean': Coercion,
|
|
60
60
|
'to-color': Coercion,
|
|
61
61
|
'to-number': Coercion,
|
|
62
|
+
'to-string': Coercion,
|
|
62
63
|
var: Var
|
|
63
64
|
};
|
|
64
65
|
|
|
@@ -103,26 +104,7 @@ CompoundExpression.register(expressions, {
|
|
|
103
104
|
throw new RuntimeError(v.evaluate(ctx));
|
|
104
105
|
}
|
|
105
106
|
],
|
|
106
|
-
typeof: [StringType, [ValueType], (ctx, [v]) =>
|
|
107
|
-
'to-string': [
|
|
108
|
-
StringType,
|
|
109
|
-
[ValueType],
|
|
110
|
-
(ctx, [v]) => {
|
|
111
|
-
v = v.evaluate(ctx);
|
|
112
|
-
const type = typeof v;
|
|
113
|
-
if (v === null) {
|
|
114
|
-
return '';
|
|
115
|
-
}
|
|
116
|
-
if (type === 'string' || type === 'number' || type === 'boolean') {
|
|
117
|
-
return String(v);
|
|
118
|
-
}
|
|
119
|
-
if (v instanceof Color || v instanceof Formatted) {
|
|
120
|
-
return v.toString();
|
|
121
|
-
}
|
|
122
|
-
return JSON.stringify(v);
|
|
123
|
-
}
|
|
124
|
-
],
|
|
125
|
-
'to-boolean': [BooleanType, [ValueType], (ctx, [v]) => Boolean(v.evaluate(ctx))],
|
|
107
|
+
typeof: [StringType, [ValueType], (ctx, [v]) => typeToString(typeOf(v.evaluate(ctx)))],
|
|
126
108
|
'to-rgba': [
|
|
127
109
|
array(NumberType, 4),
|
|
128
110
|
[ColorType],
|
|
@@ -350,7 +332,7 @@ CompoundExpression.register(expressions, {
|
|
|
350
332
|
],
|
|
351
333
|
upcase: [StringType, [StringType], (ctx, [s]) => s.evaluate(ctx).toUpperCase()],
|
|
352
334
|
downcase: [StringType, [StringType], (ctx, [s]) => s.evaluate(ctx).toLowerCase()],
|
|
353
|
-
concat: [StringType, varargs(
|
|
335
|
+
concat: [StringType, varargs(ValueType), (ctx, args) => args.map(arg => valueToString(arg.evaluate(ctx))).join('')],
|
|
354
336
|
'resolved-locale': [StringType, [CollatorType], (ctx, [collator]) => collator.evaluate(ctx).resolvedLocale()]
|
|
355
337
|
});
|
|
356
338
|
|
|
@@ -37,7 +37,7 @@ class Let {
|
|
|
37
37
|
bindings.push([name, value]);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
const result = context.parse(args[args.length - 1], args.length - 1,
|
|
40
|
+
const result = context.parse(args[args.length - 1], args.length - 1, context.expectedType, bindings);
|
|
41
41
|
if (!result) return null;
|
|
42
42
|
|
|
43
43
|
return new Let(bindings, result);
|
|
@@ -18,17 +18,12 @@ class StyleExpression {
|
|
|
18
18
|
constructor(expression, propertySpec) {
|
|
19
19
|
this.expression = expression;
|
|
20
20
|
this._warningHistory = {};
|
|
21
|
+
this._evaluator = new EvaluationContext();
|
|
21
22
|
this._defaultValue = getDefaultValue(propertySpec);
|
|
22
|
-
|
|
23
|
-
this._enumValues = propertySpec.values;
|
|
24
|
-
}
|
|
23
|
+
this._enumValues = propertySpec.type === 'enum' ? propertySpec.values : null;
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
evaluateWithoutErrorHandling(globals, feature, featureState) {
|
|
28
|
-
if (!this._evaluator) {
|
|
29
|
-
this._evaluator = new EvaluationContext();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
27
|
this._evaluator.globals = globals;
|
|
33
28
|
this._evaluator.feature = feature;
|
|
34
29
|
this._evaluator.featureState = featureState;
|
|
@@ -37,13 +32,9 @@ class StyleExpression {
|
|
|
37
32
|
}
|
|
38
33
|
|
|
39
34
|
evaluate(globals, feature, featureState) {
|
|
40
|
-
if (!this._evaluator) {
|
|
41
|
-
this._evaluator = new EvaluationContext();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
35
|
this._evaluator.globals = globals;
|
|
45
|
-
this._evaluator.feature = feature;
|
|
46
|
-
this._evaluator.featureState = featureState;
|
|
36
|
+
this._evaluator.feature = feature || null;
|
|
37
|
+
this._evaluator.featureState = featureState || null;
|
|
47
38
|
|
|
48
39
|
try {
|
|
49
40
|
const val = this.expression.evaluate(this._evaluator);
|
|
@@ -90,7 +81,16 @@ function isExpression(expression) {
|
|
|
90
81
|
*/
|
|
91
82
|
function createExpression(expression, propertySpec) {
|
|
92
83
|
const parser = new ParsingContext(definitions, [], getExpectedType(propertySpec));
|
|
93
|
-
|
|
84
|
+
|
|
85
|
+
// For string-valued properties, coerce to string at the top level rather than asserting.
|
|
86
|
+
const parsed = parser.parse(
|
|
87
|
+
expression,
|
|
88
|
+
undefined,
|
|
89
|
+
undefined,
|
|
90
|
+
undefined,
|
|
91
|
+
propertySpec.type === 'string' ? { typeAnnotation: 'coerce' } : undefined
|
|
92
|
+
);
|
|
93
|
+
|
|
94
94
|
if (!parsed) {
|
|
95
95
|
assert(parser.errors.length > 0);
|
|
96
96
|
return error(parser.errors);
|
|
@@ -283,7 +283,7 @@ function findZoomCurve(expression) {
|
|
|
283
283
|
return result;
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
-
const { ColorType, StringType, NumberType, BooleanType, ValueType, array } = require('./types');
|
|
286
|
+
const { ColorType, StringType, NumberType, BooleanType, ValueType, FormattedType, array } = require('./types');
|
|
287
287
|
|
|
288
288
|
function getExpectedType(spec) {
|
|
289
289
|
const types = {
|
|
@@ -291,14 +291,15 @@ function getExpectedType(spec) {
|
|
|
291
291
|
string: StringType,
|
|
292
292
|
number: NumberType,
|
|
293
293
|
enum: StringType,
|
|
294
|
-
boolean: BooleanType
|
|
294
|
+
boolean: BooleanType,
|
|
295
|
+
formatted: FormattedType
|
|
295
296
|
};
|
|
296
297
|
|
|
297
298
|
if (spec.type === 'array') {
|
|
298
299
|
return array(types[spec.value] || ValueType, spec.length);
|
|
299
300
|
}
|
|
300
301
|
|
|
301
|
-
return types[spec.type]
|
|
302
|
+
return types[spec.type];
|
|
302
303
|
}
|
|
303
304
|
|
|
304
305
|
function getDefaultValue(spec) {
|