@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
|
@@ -2,86 +2,71 @@ const assert = require('assert');
|
|
|
2
2
|
|
|
3
3
|
module.exports = convertFunction;
|
|
4
4
|
|
|
5
|
-
function
|
|
6
|
-
|
|
5
|
+
function convertLiteral(value) {
|
|
6
|
+
return typeof value === 'object' ? ['literal', value] : value;
|
|
7
|
+
}
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
let
|
|
10
|
-
if (
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
defaultExpression = convertValue(propertySpec.default, propertySpec);
|
|
14
|
-
if (defaultExpression === null) {
|
|
15
|
-
defaultExpression = ['error', 'No default property value available.'];
|
|
16
|
-
}
|
|
9
|
+
function convertFunction(parameters, propertySpec) {
|
|
10
|
+
let stops = parameters.stops;
|
|
11
|
+
if (!stops) {
|
|
12
|
+
// identity function
|
|
13
|
+
return convertIdentityFunction(parameters, propertySpec);
|
|
17
14
|
}
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const zoomDependent = zoomAndFeatureDependent || !featureDependent;
|
|
23
|
-
|
|
24
|
-
const stops = parameters.stops.map(stop => {
|
|
25
|
-
if (!featureDependent && propertySpec.tokens && typeof stop[1] === 'string') {
|
|
26
|
-
return [stop[0], convertTokenString(stop[1])];
|
|
27
|
-
}
|
|
28
|
-
return [stop[0], convertValue(stop[1], propertySpec)];
|
|
29
|
-
});
|
|
16
|
+
const zoomAndFeatureDependent = stops && typeof stops[0][0] === 'object';
|
|
17
|
+
const featureDependent = zoomAndFeatureDependent || parameters.property !== undefined;
|
|
18
|
+
const zoomDependent = zoomAndFeatureDependent || !featureDependent;
|
|
30
19
|
|
|
31
|
-
|
|
32
|
-
|
|
20
|
+
stops = stops.map(stop => {
|
|
21
|
+
if (!featureDependent && propertySpec.tokens && typeof stop[1] === 'string') {
|
|
22
|
+
return [stop[0], convertTokenString(stop[1])];
|
|
33
23
|
}
|
|
24
|
+
return [stop[0], convertLiteral(stop[1])];
|
|
25
|
+
});
|
|
34
26
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
} else if (zoomDependent) {
|
|
38
|
-
expression = convertZoomFunction(parameters, propertySpec, stops);
|
|
39
|
-
} else {
|
|
40
|
-
expression = convertPropertyFunction(parameters, propertySpec, stops, defaultExpression);
|
|
41
|
-
}
|
|
42
|
-
} else {
|
|
43
|
-
// identity function
|
|
44
|
-
expression = convertIdentityFunction(parameters, propertySpec, defaultExpression);
|
|
27
|
+
if (zoomAndFeatureDependent) {
|
|
28
|
+
return convertZoomAndPropertyFunction(parameters, propertySpec, stops);
|
|
45
29
|
}
|
|
46
|
-
|
|
47
|
-
|
|
30
|
+
if (zoomDependent) {
|
|
31
|
+
return convertZoomFunction(parameters, propertySpec, stops);
|
|
32
|
+
}
|
|
33
|
+
return convertPropertyFunction(parameters, propertySpec, stops);
|
|
48
34
|
}
|
|
49
35
|
|
|
50
|
-
function convertIdentityFunction(parameters, propertySpec
|
|
36
|
+
function convertIdentityFunction(parameters, propertySpec) {
|
|
51
37
|
const get = ['get', parameters.property];
|
|
52
38
|
|
|
53
|
-
if (
|
|
54
|
-
|
|
39
|
+
if (parameters.default === undefined) {
|
|
40
|
+
// By default, expressions for string-valued properties get coerced. To preserve
|
|
41
|
+
// legacy function semantics, insert an explicit assertion instead.
|
|
42
|
+
return propertySpec.type === 'string' ? ['string', get] : get;
|
|
55
43
|
}
|
|
56
|
-
if (propertySpec.type === '
|
|
57
|
-
return ['
|
|
44
|
+
if (propertySpec.type === 'enum') {
|
|
45
|
+
return ['match', get, Object.keys(propertySpec.values), get, parameters.default];
|
|
58
46
|
}
|
|
47
|
+
const expression = [
|
|
48
|
+
propertySpec.type === 'color' ? 'to-color' : propertySpec.type,
|
|
49
|
+
get,
|
|
50
|
+
convertLiteral(parameters.default)
|
|
51
|
+
];
|
|
59
52
|
if (propertySpec.type === 'array') {
|
|
60
|
-
|
|
53
|
+
expression.splice(1, 0, propertySpec.value, propertySpec.length || null);
|
|
61
54
|
}
|
|
62
|
-
|
|
63
|
-
return [
|
|
64
|
-
'let',
|
|
65
|
-
'property_value',
|
|
66
|
-
['string', get],
|
|
67
|
-
['match', ['var', 'property_value'], propertySpec.values, ['var', 'property_value'], defaultExpression]
|
|
68
|
-
];
|
|
69
|
-
}
|
|
70
|
-
return parameters.default === undefined ? get : [propertySpec.type, get, parameters.default];
|
|
55
|
+
return expression;
|
|
71
56
|
}
|
|
72
57
|
|
|
73
|
-
function
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
58
|
+
function getInterpolateOperator(parameters) {
|
|
59
|
+
switch (parameters.colorSpace) {
|
|
60
|
+
case 'hcl':
|
|
61
|
+
return 'interpolate-hcl';
|
|
62
|
+
case 'lab':
|
|
63
|
+
return 'interpolate-lab';
|
|
64
|
+
default:
|
|
65
|
+
return 'interpolate';
|
|
77
66
|
}
|
|
78
|
-
if (spec.type === 'array') {
|
|
79
|
-
return ['literal', value];
|
|
80
|
-
}
|
|
81
|
-
return value;
|
|
82
67
|
}
|
|
83
68
|
|
|
84
|
-
function convertZoomAndPropertyFunction(parameters, propertySpec, stops
|
|
69
|
+
function convertZoomAndPropertyFunction(parameters, propertySpec, stops) {
|
|
85
70
|
const featureFunctionParameters = {};
|
|
86
71
|
const featureFunctionStops = {};
|
|
87
72
|
const zoomStops = [];
|
|
@@ -107,15 +92,10 @@ function convertZoomAndPropertyFunction(parameters, propertySpec, stops, default
|
|
|
107
92
|
// otherwise.
|
|
108
93
|
const functionType = getFunctionType({}, propertySpec);
|
|
109
94
|
if (functionType === 'exponential') {
|
|
110
|
-
const expression = [
|
|
95
|
+
const expression = [getInterpolateOperator(parameters), ['linear'], ['zoom']];
|
|
111
96
|
|
|
112
97
|
for (const z of zoomStops) {
|
|
113
|
-
const output = convertPropertyFunction(
|
|
114
|
-
featureFunctionParameters[z],
|
|
115
|
-
propertySpec,
|
|
116
|
-
featureFunctionStops[z],
|
|
117
|
-
defaultExpression
|
|
118
|
-
);
|
|
98
|
+
const output = convertPropertyFunction(featureFunctionParameters[z], propertySpec, featureFunctionStops[z]);
|
|
119
99
|
appendStopPair(expression, z, output, false);
|
|
120
100
|
}
|
|
121
101
|
|
|
@@ -124,12 +104,7 @@ function convertZoomAndPropertyFunction(parameters, propertySpec, stops, default
|
|
|
124
104
|
const expression = ['step', ['zoom']];
|
|
125
105
|
|
|
126
106
|
for (const z of zoomStops) {
|
|
127
|
-
const output = convertPropertyFunction(
|
|
128
|
-
featureFunctionParameters[z],
|
|
129
|
-
propertySpec,
|
|
130
|
-
featureFunctionStops[z],
|
|
131
|
-
defaultExpression
|
|
132
|
-
);
|
|
107
|
+
const output = convertPropertyFunction(featureFunctionParameters[z], propertySpec, featureFunctionStops[z]);
|
|
133
108
|
appendStopPair(expression, z, output, true);
|
|
134
109
|
}
|
|
135
110
|
|
|
@@ -138,43 +113,52 @@ function convertZoomAndPropertyFunction(parameters, propertySpec, stops, default
|
|
|
138
113
|
return expression;
|
|
139
114
|
}
|
|
140
115
|
|
|
141
|
-
function
|
|
142
|
-
|
|
116
|
+
function coalesce(a, b) {
|
|
117
|
+
if (a !== undefined) return a;
|
|
118
|
+
if (b !== undefined) return b;
|
|
119
|
+
}
|
|
143
120
|
|
|
144
|
-
|
|
145
|
-
|
|
121
|
+
function convertPropertyFunction(parameters, propertySpec, stops) {
|
|
122
|
+
const type = getFunctionType(parameters, propertySpec);
|
|
123
|
+
const get = ['get', parameters.property];
|
|
146
124
|
if (type === 'categorical' && typeof stops[0][0] === 'boolean') {
|
|
147
125
|
assert(parameters.stops.length > 0 && parameters.stops.length <= 2);
|
|
148
|
-
expression = ['case'];
|
|
126
|
+
const expression = ['case'];
|
|
149
127
|
for (const stop of stops) {
|
|
150
|
-
expression.push(['==',
|
|
128
|
+
expression.push(['==', get, stop[0]], stop[1]);
|
|
151
129
|
}
|
|
152
|
-
expression.push(
|
|
130
|
+
expression.push(convertLiteral(coalesce(parameters.default, propertySpec.default)));
|
|
153
131
|
return expression;
|
|
154
132
|
}
|
|
155
133
|
if (type === 'categorical') {
|
|
156
|
-
expression = ['match',
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
expression = ['interpolate', ['exponential', base], ['number', ['get', parameters.property]]];
|
|
163
|
-
} else {
|
|
164
|
-
throw new Error(`Unknown property function type ${type}`);
|
|
134
|
+
const expression = ['match', get];
|
|
135
|
+
for (const stop of stops) {
|
|
136
|
+
appendStopPair(expression, stop[0], stop[1], false);
|
|
137
|
+
}
|
|
138
|
+
expression.push(convertLiteral(coalesce(parameters.default, propertySpec.default)));
|
|
139
|
+
return expression;
|
|
165
140
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
141
|
+
if (type === 'interval') {
|
|
142
|
+
const expression = ['step', ['number', get]];
|
|
143
|
+
for (const stop of stops) {
|
|
144
|
+
appendStopPair(expression, stop[0], stop[1], true);
|
|
145
|
+
}
|
|
146
|
+
fixupDegenerateStepCurve(expression);
|
|
147
|
+
return parameters.default === undefined
|
|
148
|
+
? expression
|
|
149
|
+
: ['case', ['==', ['typeof', get], 'number'], expression, convertLiteral(parameters.default)];
|
|
169
150
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
expression
|
|
151
|
+
if (type === 'exponential') {
|
|
152
|
+
const base = parameters.base !== undefined ? parameters.base : 1;
|
|
153
|
+
const expression = [getInterpolateOperator(parameters), ['exponential', base], ['number', get]];
|
|
154
|
+
for (const stop of stops) {
|
|
155
|
+
appendStopPair(expression, stop[0], stop[1], false);
|
|
156
|
+
}
|
|
157
|
+
return parameters.default === undefined
|
|
158
|
+
? expression
|
|
159
|
+
: ['case', ['==', ['typeof', get], 'number'], expression, convertLiteral(parameters.default)];
|
|
173
160
|
}
|
|
174
|
-
|
|
175
|
-
fixupDegenerateStepCurve(expression);
|
|
176
|
-
|
|
177
|
-
return expression;
|
|
161
|
+
throw new Error(`Unknown property function type ${type}`);
|
|
178
162
|
}
|
|
179
163
|
|
|
180
164
|
function convertZoomFunction(parameters, propertySpec, stops, input = ['zoom']) {
|
|
@@ -186,7 +170,7 @@ function convertZoomFunction(parameters, propertySpec, stops, input = ['zoom'])
|
|
|
186
170
|
isStep = true;
|
|
187
171
|
} else if (type === 'exponential') {
|
|
188
172
|
const base = parameters.base !== undefined ? parameters.base : 1;
|
|
189
|
-
expression = [
|
|
173
|
+
expression = [getInterpolateOperator(parameters), ['exponential', base], input];
|
|
190
174
|
} else {
|
|
191
175
|
throw new Error(`Unknown zoom function type "${type}"`);
|
|
192
176
|
}
|
|
@@ -239,7 +223,7 @@ function convertTokenString(s) {
|
|
|
239
223
|
const literal = s.slice(pos, re.lastIndex - match[0].length);
|
|
240
224
|
pos = re.lastIndex;
|
|
241
225
|
if (literal.length > 0) result.push(literal);
|
|
242
|
-
result.push(['
|
|
226
|
+
result.push(['get', match[1]]);
|
|
243
227
|
}
|
|
244
228
|
|
|
245
229
|
if (result.length === 1) {
|
|
@@ -249,7 +233,7 @@ function convertTokenString(s) {
|
|
|
249
233
|
if (pos < s.length) {
|
|
250
234
|
result.push(s.slice(pos));
|
|
251
235
|
} else if (result.length === 2) {
|
|
252
|
-
return result[1];
|
|
236
|
+
return ['to-string', result[1]];
|
|
253
237
|
}
|
|
254
238
|
|
|
255
239
|
return result;
|
|
@@ -3,6 +3,7 @@ const Color = require('../util/color');
|
|
|
3
3
|
const getType = require('../util/get_type');
|
|
4
4
|
const interpolate = require('../util/interpolate');
|
|
5
5
|
const Interpolate = require('../expression/definitions/interpolate');
|
|
6
|
+
const { Formatted } = require('../expression/types/formatted');
|
|
6
7
|
const { supportsInterpolation } = require('../util/properties');
|
|
7
8
|
|
|
8
9
|
module.exports = {
|
|
@@ -203,6 +204,9 @@ function evaluateIdentityFunction(parameters, propertySpec, input) {
|
|
|
203
204
|
case 'color':
|
|
204
205
|
input = Color.parse(input);
|
|
205
206
|
break;
|
|
207
|
+
case 'formatted':
|
|
208
|
+
input = Formatted.fromString(input.toString());
|
|
209
|
+
break;
|
|
206
210
|
case 'enum':
|
|
207
211
|
if (!propertySpec.values.includes(input)) {
|
|
208
212
|
input = undefined;
|
|
@@ -70,12 +70,12 @@
|
|
|
70
70
|
},
|
|
71
71
|
"sprite": {
|
|
72
72
|
"type": "string",
|
|
73
|
-
"doc": "A base URL for retrieving the sprite image and metadata. The extensions `.png`, `.json` and scale factor `@2x.png` will be automatically appended. This property is required if any layer uses the `background-pattern`, `fill-pattern`, `line-pattern`, `fill-extrusion-pattern`, or `icon-image` properties.",
|
|
73
|
+
"doc": "A base URL for retrieving the sprite image and metadata. The extensions `.png`, `.json` and scale factor `@2x.png` will be automatically appended. This property is required if any layer uses the `background-pattern`, `fill-pattern`, `line-pattern`, `fill-extrusion-pattern`, or `icon-image` properties. The URL must be absolute, containing the [scheme, authority and path components](https://en.wikipedia.org/wiki/URL#Syntax).",
|
|
74
74
|
"example": "mapbox://sprites/mapbox/bright-v8"
|
|
75
75
|
},
|
|
76
76
|
"glyphs": {
|
|
77
77
|
"type": "string",
|
|
78
|
-
"doc": "A URL template for loading signed-distance-field glyph sets in PBF format. The URL must include `{fontstack}` and `{range}` tokens. This property is required if any layer uses the `text-field` layout property.",
|
|
78
|
+
"doc": "A URL template for loading signed-distance-field glyph sets in PBF format. The URL must include `{fontstack}` and `{range}` tokens. This property is required if any layer uses the `text-field` layout property. The URL must be absolute, containing the [scheme, authority and path components](https://en.wikipedia.org/wiki/URL#Syntax).",
|
|
79
79
|
"example": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf"
|
|
80
80
|
},
|
|
81
81
|
"transition": {
|
|
@@ -946,6 +946,32 @@
|
|
|
946
946
|
},
|
|
947
947
|
"property-type": "data-constant"
|
|
948
948
|
},
|
|
949
|
+
"symbol-z-order": {
|
|
950
|
+
"type": "enum",
|
|
951
|
+
"values": {
|
|
952
|
+
"viewport-y": {
|
|
953
|
+
"doc": "Symbols will be sorted by their y-position relative to the viewport."
|
|
954
|
+
},
|
|
955
|
+
"source": {
|
|
956
|
+
"doc": "Symbols will be rendered in the same order as the source data with no sorting applied."
|
|
957
|
+
}
|
|
958
|
+
},
|
|
959
|
+
"default": "viewport-y",
|
|
960
|
+
"doc": "Controls the order in which overlapping symbols in the same layer are rendered",
|
|
961
|
+
"sdk-support": {
|
|
962
|
+
"basic functionality": {
|
|
963
|
+
"js": "0.49.0"
|
|
964
|
+
},
|
|
965
|
+
"data-driven styling": {}
|
|
966
|
+
},
|
|
967
|
+
"expression": {
|
|
968
|
+
"interpolated": false,
|
|
969
|
+
"parameters": [
|
|
970
|
+
"zoom"
|
|
971
|
+
]
|
|
972
|
+
},
|
|
973
|
+
"property-type": "data-constant"
|
|
974
|
+
},
|
|
949
975
|
"icon-allow-overlap": {
|
|
950
976
|
"type": "boolean",
|
|
951
977
|
"default": false,
|
|
@@ -3122,7 +3148,7 @@
|
|
|
3122
3148
|
}
|
|
3123
3149
|
},
|
|
3124
3150
|
"concat": {
|
|
3125
|
-
"doc": "Returns a `string` consisting of the concatenation of the inputs.
|
|
3151
|
+
"doc": "Returns a `string` consisting of the concatenation of the inputs. Each input is converted to a string as if by `to-string`.",
|
|
3126
3152
|
"group": "String",
|
|
3127
3153
|
"sdk-support": {
|
|
3128
3154
|
"basic functionality": {
|
|
@@ -3458,7 +3484,10 @@
|
|
|
3458
3484
|
"macos": "0.1.0"
|
|
3459
3485
|
},
|
|
3460
3486
|
"data-driven styling": {
|
|
3461
|
-
"js": "0.
|
|
3487
|
+
"js": "0.49.0",
|
|
3488
|
+
"android": "6.5.0",
|
|
3489
|
+
"macos": "0.11.0",
|
|
3490
|
+
"ios": "4.4.0"
|
|
3462
3491
|
}
|
|
3463
3492
|
},
|
|
3464
3493
|
"expression": {
|
|
@@ -3600,7 +3629,12 @@
|
|
|
3600
3629
|
"ios": "3.6.0",
|
|
3601
3630
|
"macos": "0.5.0"
|
|
3602
3631
|
},
|
|
3603
|
-
"data-driven styling": {
|
|
3632
|
+
"data-driven styling": {
|
|
3633
|
+
"js": "0.49.0",
|
|
3634
|
+
"android": "6.5.0",
|
|
3635
|
+
"macos": "0.11.0",
|
|
3636
|
+
"ios": "4.4.0"
|
|
3637
|
+
}
|
|
3604
3638
|
},
|
|
3605
3639
|
"expression": {
|
|
3606
3640
|
"interpolated": false,
|
|
@@ -3980,7 +4014,10 @@
|
|
|
3980
4014
|
"macos": "0.1.0"
|
|
3981
4015
|
},
|
|
3982
4016
|
"data-driven styling": {
|
|
3983
|
-
"js": "0.
|
|
4017
|
+
"js": "0.49.0",
|
|
4018
|
+
"android": "6.5.0",
|
|
4019
|
+
"macos": "0.11.0",
|
|
4020
|
+
"ios": "4.4.0"
|
|
3984
4021
|
}
|
|
3985
4022
|
},
|
|
3986
4023
|
"expression": {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const EXTENT = require('../data/extent');
|
|
2
2
|
|
|
3
|
+
const { SymbolInstanceArray } = require('../data/array_types');
|
|
4
|
+
|
|
3
5
|
/*
|
|
4
6
|
The CrossTileSymbolIndex generally works on the assumption that
|
|
5
7
|
a conceptual "unique symbol" can be identified by the text of
|
|
@@ -23,7 +25,8 @@ class TileLayerIndex {
|
|
|
23
25
|
this.indexedSymbolInstances = {};
|
|
24
26
|
this.bucketInstanceId = bucketInstanceId;
|
|
25
27
|
|
|
26
|
-
for (
|
|
28
|
+
for (let i = 0; i < symbolInstances.length; i++) {
|
|
29
|
+
const symbolInstance = symbolInstances.get(i);
|
|
27
30
|
const key = symbolInstance.key;
|
|
28
31
|
if (!this.indexedSymbolInstances[key]) {
|
|
29
32
|
this.indexedSymbolInstances[key] = [];
|
|
@@ -46,10 +49,9 @@ class TileLayerIndex {
|
|
|
46
49
|
getScaledCoordinates(symbolInstance, childTileID) {
|
|
47
50
|
const zDifference = childTileID.canonical.z - this.tileID.canonical.z;
|
|
48
51
|
const scale = roundingFactor / 2 ** zDifference;
|
|
49
|
-
const anchor = symbolInstance.anchor;
|
|
50
52
|
return {
|
|
51
|
-
x: Math.floor((childTileID.canonical.x * EXTENT +
|
|
52
|
-
y: Math.floor((childTileID.canonical.y * EXTENT +
|
|
53
|
+
x: Math.floor((childTileID.canonical.x * EXTENT + symbolInstance.anchorX) * scale),
|
|
54
|
+
y: Math.floor((childTileID.canonical.y * EXTENT + symbolInstance.anchorY) * scale)
|
|
53
55
|
};
|
|
54
56
|
}
|
|
55
57
|
|
|
@@ -57,7 +59,8 @@ class TileLayerIndex {
|
|
|
57
59
|
const tolerance =
|
|
58
60
|
this.tileID.canonical.z < newTileID.canonical.z ? 1 : 2 ** (this.tileID.canonical.z - newTileID.canonical.z);
|
|
59
61
|
|
|
60
|
-
for (
|
|
62
|
+
for (let i = 0; i < symbolInstances.length; i++) {
|
|
63
|
+
const symbolInstance = symbolInstances.get(i);
|
|
61
64
|
if (symbolInstance.crossTileID) {
|
|
62
65
|
// already has a match, skip
|
|
63
66
|
continue;
|
|
@@ -143,7 +146,8 @@ class CrossTileSymbolLayerIndex {
|
|
|
143
146
|
this.removeBucketCrossTileIDs(tileID.overscaledZ, this.indexes[tileID.overscaledZ][tileID.key]);
|
|
144
147
|
}
|
|
145
148
|
|
|
146
|
-
for (
|
|
149
|
+
for (let i = 0; i < bucket.symbolInstances.length; i++) {
|
|
150
|
+
const symbolInstance = bucket.symbolInstances.get(i);
|
|
147
151
|
symbolInstance.crossTileID = 0;
|
|
148
152
|
}
|
|
149
153
|
|
|
@@ -170,7 +174,8 @@ class CrossTileSymbolLayerIndex {
|
|
|
170
174
|
}
|
|
171
175
|
}
|
|
172
176
|
|
|
173
|
-
for (
|
|
177
|
+
for (let i = 0; i < bucket.symbolInstances.length; i++) {
|
|
178
|
+
const symbolInstance = bucket.symbolInstances.get(i);
|
|
174
179
|
if (!symbolInstance.crossTileID) {
|
|
175
180
|
// symbol did not match any known symbol, assign a new id
|
|
176
181
|
symbolInstance.crossTileID = crossTileIDs.generate();
|
package/src/symbol/mergelines.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { Formatted } = require('../style-spec/expression/
|
|
1
|
+
const { Formatted } = require('../style-spec/expression/types/formatted');
|
|
2
2
|
|
|
3
3
|
module.exports = function (features) {
|
|
4
4
|
const leftIndex = new Map();
|
|
@@ -8,7 +8,7 @@ module.exports = function (features) {
|
|
|
8
8
|
|
|
9
9
|
for (let k = 0; k < features.length; k++) {
|
|
10
10
|
const { geometry, text: featureText } = features[k];
|
|
11
|
-
const text = featureText
|
|
11
|
+
const text = featureText ? featureText.toString() : null;
|
|
12
12
|
|
|
13
13
|
if (!text) {
|
|
14
14
|
add(k);
|
package/src/symbol/placement.js
CHANGED
|
@@ -170,9 +170,33 @@ class Placement {
|
|
|
170
170
|
const textOptional = layout.get('text-optional');
|
|
171
171
|
const iconOptional = layout.get('icon-optional');
|
|
172
172
|
|
|
173
|
+
const textAllowOverlap = layout.get('text-allow-overlap');
|
|
174
|
+
const iconAllowOverlap = layout.get('icon-allow-overlap');
|
|
175
|
+
// This logic is similar to the "defaultOpacityState" logic below in updateBucketOpacities
|
|
176
|
+
// If we know a symbol is always supposed to show, force it to be marked visible even if
|
|
177
|
+
// it wasn't placed into the collision index (because some or all of it was outside the range
|
|
178
|
+
// of the collision grid).
|
|
179
|
+
// There is a subtle edge case here we're accepting:
|
|
180
|
+
// Symbol A has text-allow-overlap: true, icon-allow-overlap: true, icon-optional: false
|
|
181
|
+
// A's icon is outside the grid, so doesn't get placed
|
|
182
|
+
// A's text would be inside grid, but doesn't get placed because of icon-optional: false
|
|
183
|
+
// We still show A because of the allow-overlap settings.
|
|
184
|
+
// Symbol B has allow-overlap: false, and gets placed where A's text would be
|
|
185
|
+
// On panning in, there is a short period when Symbol B and Symbol A will overlap
|
|
186
|
+
// This is the reverse of our normal policy of "fade in on pan", but should look like any other
|
|
187
|
+
// collision and hopefully not be too noticeable.
|
|
188
|
+
// See https://github.com/mapbox/mapbox-gl-js/issues/7172
|
|
189
|
+
const alwaysShowText = textAllowOverlap && (iconAllowOverlap || !bucket.hasIconData() || iconOptional);
|
|
190
|
+
const alwaysShowIcon = iconAllowOverlap && (textAllowOverlap || !bucket.hasTextData() || textOptional);
|
|
191
|
+
|
|
173
192
|
const collisionGroup = this.collisionGroups.get(bucket.sourceID);
|
|
174
193
|
|
|
175
|
-
|
|
194
|
+
if (!bucket.collisionArrays && collisionBoxArray) {
|
|
195
|
+
bucket.deserializeCollisionBoxes(collisionBoxArray);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
for (let i = 0; i < bucket.symbolInstances.length; i++) {
|
|
199
|
+
const symbolInstance = bucket.symbolInstances.get(i);
|
|
176
200
|
if (!seenCrossTileIDs[symbolInstance.crossTileID]) {
|
|
177
201
|
if (holdingForFade) {
|
|
178
202
|
// Mark all symbols from this tile as "not placed", but don't add to seenCrossTileIDs, because we don't
|
|
@@ -192,22 +216,14 @@ class Placement {
|
|
|
192
216
|
let textFeatureIndex = 0;
|
|
193
217
|
let iconFeatureIndex = 0;
|
|
194
218
|
|
|
195
|
-
|
|
196
|
-
symbolInstance.collisionArrays = bucket.deserializeCollisionBoxes(
|
|
197
|
-
collisionBoxArray,
|
|
198
|
-
symbolInstance.textBoxStartIndex,
|
|
199
|
-
symbolInstance.textBoxEndIndex,
|
|
200
|
-
symbolInstance.iconBoxStartIndex,
|
|
201
|
-
symbolInstance.iconBoxEndIndex
|
|
202
|
-
);
|
|
203
|
-
}
|
|
219
|
+
const collisionArrays = bucket.collisionArrays[i];
|
|
204
220
|
|
|
205
|
-
if (
|
|
206
|
-
textFeatureIndex =
|
|
221
|
+
if (collisionArrays.textFeatureIndex) {
|
|
222
|
+
textFeatureIndex = collisionArrays.textFeatureIndex;
|
|
207
223
|
}
|
|
208
|
-
if (
|
|
224
|
+
if (collisionArrays.textBox) {
|
|
209
225
|
placedGlyphBoxes = this.collisionIndex.placeCollisionBox(
|
|
210
|
-
|
|
226
|
+
collisionArrays.textBox,
|
|
211
227
|
layout.get('text-allow-overlap'),
|
|
212
228
|
textPixelRatio,
|
|
213
229
|
posMatrix,
|
|
@@ -216,9 +232,9 @@ class Placement {
|
|
|
216
232
|
placeText = placedGlyphBoxes.box.length > 0;
|
|
217
233
|
offscreen = offscreen && placedGlyphBoxes.offscreen;
|
|
218
234
|
}
|
|
219
|
-
const textCircles =
|
|
235
|
+
const textCircles = collisionArrays.textCircles;
|
|
220
236
|
if (textCircles) {
|
|
221
|
-
const placedSymbol = bucket.text.placedSymbolArray.get(symbolInstance.
|
|
237
|
+
const placedSymbol = bucket.text.placedSymbolArray.get(symbolInstance.horizontalPlacedTextSymbolIndex);
|
|
222
238
|
const fontSize = symbolSize.evaluateSizeForFeature(
|
|
223
239
|
bucket.textSizeData,
|
|
224
240
|
partiallyEvaluatedTextSize,
|
|
@@ -229,7 +245,6 @@ class Placement {
|
|
|
229
245
|
layout.get('text-allow-overlap'),
|
|
230
246
|
scale,
|
|
231
247
|
textPixelRatio,
|
|
232
|
-
symbolInstance.key,
|
|
233
248
|
placedSymbol,
|
|
234
249
|
bucket.lineVertexArray,
|
|
235
250
|
bucket.glyphOffsetArray,
|
|
@@ -248,12 +263,12 @@ class Placement {
|
|
|
248
263
|
offscreen = offscreen && placedGlyphCircles.offscreen;
|
|
249
264
|
}
|
|
250
265
|
|
|
251
|
-
if (
|
|
252
|
-
iconFeatureIndex =
|
|
266
|
+
if (collisionArrays.iconFeatureIndex) {
|
|
267
|
+
iconFeatureIndex = collisionArrays.iconFeatureIndex;
|
|
253
268
|
}
|
|
254
|
-
if (
|
|
269
|
+
if (collisionArrays.iconBox) {
|
|
255
270
|
placedIconBoxes = this.collisionIndex.placeCollisionBox(
|
|
256
|
-
|
|
271
|
+
collisionArrays.iconBox,
|
|
257
272
|
layout.get('icon-allow-overlap'),
|
|
258
273
|
textPixelRatio,
|
|
259
274
|
posMatrix,
|
|
@@ -308,8 +323,8 @@ class Placement {
|
|
|
308
323
|
assert(bucket.bucketInstanceId !== 0);
|
|
309
324
|
|
|
310
325
|
this.placements[symbolInstance.crossTileID] = new JointPlacement(
|
|
311
|
-
placeText,
|
|
312
|
-
placeIcon,
|
|
326
|
+
placeText || (alwaysShowText && placedGlyphBoxes),
|
|
327
|
+
placeIcon || alwaysShowIcon,
|
|
313
328
|
offscreen || bucket.justReloaded
|
|
314
329
|
);
|
|
315
330
|
seenCrossTileIDs[symbolInstance.crossTileID] = true;
|
|
@@ -412,8 +427,16 @@ class Placement {
|
|
|
412
427
|
true
|
|
413
428
|
);
|
|
414
429
|
|
|
430
|
+
if (
|
|
431
|
+
!bucket.collisionArrays &&
|
|
432
|
+
collisionBoxArray &&
|
|
433
|
+
(bucket.hasCollisionBoxData() || bucket.hasCollisionCircleData())
|
|
434
|
+
) {
|
|
435
|
+
bucket.deserializeCollisionBoxes(collisionBoxArray);
|
|
436
|
+
}
|
|
437
|
+
|
|
415
438
|
for (let s = 0; s < bucket.symbolInstances.length; s++) {
|
|
416
|
-
const symbolInstance = bucket.symbolInstances
|
|
439
|
+
const symbolInstance = bucket.symbolInstances.get(s);
|
|
417
440
|
const isDuplicate = seenCrossTileIDs[symbolInstance.crossTileID];
|
|
418
441
|
|
|
419
442
|
let opacityState = this.opacities[symbolInstance.crossTileID];
|
|
@@ -438,11 +461,14 @@ class Placement {
|
|
|
438
461
|
for (let i = 0; i < opacityEntryCount; i++) {
|
|
439
462
|
bucket.text.opacityVertexArray.emplaceBack(packedOpacity);
|
|
440
463
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
464
|
+
// If this label is completely faded, mark it so that we don't have to calculate
|
|
465
|
+
// its position at render time
|
|
466
|
+
bucket.text.placedSymbolArray.get(symbolInstance.horizontalPlacedTextSymbolIndex).hidden =
|
|
467
|
+
opacityState.text.isHidden();
|
|
468
|
+
|
|
469
|
+
if (symbolInstance.verticalPlacedTextSymbolIndex >= 0) {
|
|
470
|
+
bucket.text.placedSymbolArray.get(symbolInstance.verticalPlacedTextSymbolIndex).hidden =
|
|
471
|
+
opacityState.text.isHidden();
|
|
446
472
|
}
|
|
447
473
|
}
|
|
448
474
|
|
|
@@ -451,35 +477,26 @@ class Placement {
|
|
|
451
477
|
for (let i = 0; i < symbolInstance.numIconVertices / 4; i++) {
|
|
452
478
|
bucket.icon.opacityVertexArray.emplaceBack(packedOpacity);
|
|
453
479
|
}
|
|
454
|
-
|
|
455
|
-
placedSymbol.hidden = opacityState.icon.isHidden();
|
|
480
|
+
bucket.icon.placedSymbolArray.get(s).hidden = opacityState.icon.isHidden();
|
|
456
481
|
}
|
|
457
482
|
|
|
458
|
-
if (
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
symbolInstance.iconBoxEndIndex
|
|
465
|
-
);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
const collisionArrays = symbolInstance.collisionArrays;
|
|
469
|
-
if (collisionArrays) {
|
|
470
|
-
if (collisionArrays.textBox && bucket.hasCollisionBoxData()) {
|
|
471
|
-
updateCollisionVertices(bucket.collisionBox.collisionVertexArray, opacityState.text.placed, false);
|
|
472
|
-
}
|
|
483
|
+
if (bucket.hasCollisionBoxData() || bucket.hasCollisionCircleData()) {
|
|
484
|
+
const collisionArrays = bucket.collisionArrays[s];
|
|
485
|
+
if (collisionArrays) {
|
|
486
|
+
if (collisionArrays.textBox) {
|
|
487
|
+
updateCollisionVertices(bucket.collisionBox.collisionVertexArray, opacityState.text.placed, false);
|
|
488
|
+
}
|
|
473
489
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
490
|
+
if (collisionArrays.iconBox) {
|
|
491
|
+
updateCollisionVertices(bucket.collisionBox.collisionVertexArray, opacityState.icon.placed, false);
|
|
492
|
+
}
|
|
477
493
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
494
|
+
const textCircles = collisionArrays.textCircles;
|
|
495
|
+
if (textCircles && bucket.hasCollisionCircleData()) {
|
|
496
|
+
for (let k = 0; k < textCircles.length; k += 5) {
|
|
497
|
+
const notUsed = isDuplicate || textCircles[k + 4] === 0;
|
|
498
|
+
updateCollisionVertices(bucket.collisionCircle.collisionVertexArray, opacityState.text.placed, notUsed);
|
|
499
|
+
}
|
|
483
500
|
}
|
|
484
501
|
}
|
|
485
502
|
}
|