maplibre-gl 2.2.0-pre.3 → 2.2.1

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 (121) hide show
  1. package/README.md +73 -14
  2. package/build/generate-debug-index-file.ts +19 -0
  3. package/build/generate-style-code.ts +6 -1
  4. package/build/generate-style-spec.ts +151 -35
  5. package/build/generate-typings.ts +1 -1
  6. package/build/rollup_plugins.ts +4 -1
  7. package/dist/maplibre-gl-csp-worker.js +1 -1
  8. package/dist/maplibre-gl-csp-worker.js.map +1 -1
  9. package/dist/maplibre-gl-csp.js +1 -1
  10. package/dist/maplibre-gl-csp.js.map +1 -1
  11. package/dist/maplibre-gl-dev.js +364 -3124
  12. package/dist/maplibre-gl.css +1 -1
  13. package/dist/maplibre-gl.d.ts +439 -151
  14. package/dist/maplibre-gl.js +4 -4
  15. package/dist/maplibre-gl.js.map +1 -1
  16. package/package.json +68 -65
  17. package/src/css/maplibre-gl.css +39 -32
  18. package/src/data/bucket/fill_bucket.test.ts +1 -0
  19. package/src/data/bucket/symbol_bucket.test.ts +2 -0
  20. package/src/data/evaluation_feature.ts +1 -1
  21. package/src/data/program_configuration.ts +2 -2
  22. package/src/geo/transform.ts +4 -4
  23. package/src/gl/vertex_buffer.ts +4 -4
  24. package/src/index.ts +1 -1
  25. package/src/render/image_atlas.ts +1 -0
  26. package/src/render/image_manager.ts +1 -0
  27. package/src/render/program/debug_program.ts +1 -1
  28. package/src/render/render_to_texture.ts +3 -0
  29. package/src/render/terrain.ts +21 -21
  30. package/src/shaders/README.md +2 -2
  31. package/src/source/geojson_worker_source.test.ts +2 -2
  32. package/src/source/geojson_wrapper.test.ts +1 -1
  33. package/src/source/image_source.test.ts +8 -8
  34. package/src/source/image_source.ts +1 -1
  35. package/src/source/raster_tile_source.test.ts +1 -1
  36. package/src/source/source_cache.test.ts +12 -12
  37. package/src/source/source_cache.ts +1 -1
  38. package/src/source/terrain_source_cache.ts +3 -3
  39. package/src/source/vector_tile_source.test.ts +1 -1
  40. package/src/source/vector_tile_worker_source.test.ts +1 -1
  41. package/src/source/video_source.test.ts +2 -2
  42. package/src/source/worker.ts +2 -4
  43. package/src/style/light.test.ts +1 -1
  44. package/src/style/style.test.ts +3 -3
  45. package/src/style/style.ts +2 -2
  46. package/src/style/style_layer/background_style_layer_properties.g.ts +1 -0
  47. package/src/style/style_layer/circle_style_layer_properties.g.ts +1 -0
  48. package/src/style/style_layer/fill_extrusion_style_layer_properties.g.ts +1 -0
  49. package/src/style/style_layer/fill_style_layer_properties.g.ts +1 -0
  50. package/src/style/style_layer/heatmap_style_layer_properties.g.ts +1 -0
  51. package/src/style/style_layer/hillshade_style_layer_properties.g.ts +1 -0
  52. package/src/style/style_layer/line_style_layer_properties.g.ts +1 -0
  53. package/src/style/style_layer/raster_style_layer_properties.g.ts +1 -0
  54. package/src/style/style_layer/symbol_style_layer.ts +16 -1
  55. package/src/style/style_layer/symbol_style_layer_properties.g.ts +4 -3
  56. package/src/style-spec/CHANGELOG.md +5 -0
  57. package/src/style-spec/composite.test.ts +2 -0
  58. package/src/style-spec/diff.test.ts +3 -3
  59. package/src/style-spec/empty.ts +3 -2
  60. package/src/style-spec/expression/definitions/coercion.ts +13 -2
  61. package/src/style-spec/expression/definitions/interpolate.ts +1 -0
  62. package/src/style-spec/expression/expression.test.ts +1 -1
  63. package/src/style-spec/expression/expression.ts +3 -0
  64. package/src/style-spec/expression/index.ts +8 -2
  65. package/src/style-spec/expression/parsing_context.ts +2 -0
  66. package/src/style-spec/expression/types.ts +6 -1
  67. package/src/style-spec/expression/values.ts +9 -4
  68. package/src/style-spec/feature_filter/convert.ts +65 -65
  69. package/src/style-spec/feature_filter/feature_filter.test.ts +45 -4
  70. package/src/style-spec/feature_filter/index.ts +2 -1
  71. package/src/style-spec/function/index.test.ts +117 -1
  72. package/src/style-spec/function/index.ts +24 -12
  73. package/src/style-spec/migrate/expressions.ts +1 -1
  74. package/src/style-spec/migrate/v8.test.ts +2 -0
  75. package/src/style-spec/migrate/v9.test.ts +6 -4
  76. package/src/style-spec/migrate.test.ts +3 -1
  77. package/src/style-spec/package.json +1 -1
  78. package/src/style-spec/reference/latest.ts +2 -2
  79. package/src/style-spec/reference/v8.json +9 -6
  80. package/src/style-spec/style-spec.test.ts +2 -1
  81. package/src/style-spec/style-spec.ts +18 -9
  82. package/src/style-spec/types.g.ts +152 -36
  83. package/src/style-spec/util/interpolate.test.ts +5 -0
  84. package/src/style-spec/util/interpolate.ts +12 -0
  85. package/src/style-spec/util/padding.test.ts +27 -0
  86. package/src/style-spec/util/padding.ts +64 -0
  87. package/src/style-spec/validate/validate.ts +3 -1
  88. package/src/style-spec/validate/validate_padding.test.ts +82 -0
  89. package/src/style-spec/validate/validate_padding.ts +36 -0
  90. package/src/style-spec/validate_style.ts +1 -1
  91. package/src/symbol/check_max_angle.test.ts +5 -5
  92. package/src/symbol/collision_feature.test.ts +22 -5
  93. package/src/symbol/collision_feature.ts +7 -5
  94. package/src/symbol/collision_index.ts +1 -1
  95. package/src/symbol/get_anchors.test.ts +4 -4
  96. package/src/symbol/projection.ts +1 -1
  97. package/src/symbol/quads.test.ts +1 -1
  98. package/src/symbol/shaping.ts +10 -10
  99. package/src/symbol/symbol_layout.ts +5 -4
  100. package/src/symbol/symbol_style_layer.test.ts +1 -1
  101. package/src/ui/camera.test.ts +11 -11
  102. package/src/ui/control/geolocate_control.ts +1 -1
  103. package/src/ui/control/terrain_control.ts +4 -4
  104. package/src/ui/handler/cooperative_gestures.test.ts +167 -0
  105. package/src/ui/handler/drag_pan.test.ts +2 -1
  106. package/src/ui/handler/scroll_zoom.ts +7 -0
  107. package/src/ui/handler/touch_pan.ts +22 -2
  108. package/src/ui/handler/touch_zoom_rotate.ts +18 -1
  109. package/src/ui/handler_manager.ts +2 -2
  110. package/src/ui/map.test.ts +16 -16
  111. package/src/ui/map.ts +76 -8
  112. package/src/ui/map_events.test.ts +33 -32
  113. package/src/ui/popup.test.ts +2 -2
  114. package/src/util/ajax.test.ts +5 -5
  115. package/src/util/ajax.ts +1 -1
  116. package/src/util/classify_rings.test.ts +27 -27
  117. package/src/util/primitives.ts +4 -4
  118. package/src/util/resolve_tokens.test.ts +1 -1
  119. package/src/util/tile_request_cache.test.ts +5 -5
  120. package/src/util/util.test.ts +5 -5
  121. package/src/util/util.ts +2 -3
@@ -1,17 +1,8 @@
1
1
  import {isExpressionFilter} from './index';
2
2
 
3
- import type {FilterSpecification} from '../types.g';
3
+ import type {ExpressionFilterSpecification, ExpressionInputType, ExpressionSpecification, FilterSpecification, LegacyFilterSpecification} from '../types.g';
4
4
 
5
- type ExpectedTypes = {[_: string]: 'string' | 'number' | 'boolean'};
6
-
7
- /**
8
- * Convert the given legacy filter to (the JSON representation of) an
9
- * equivalent expression
10
- * @private
11
- */
12
- export default function convertFilter(filter: FilterSpecification): unknown {
13
- return _convertFilter(filter, {});
14
- }
5
+ type ExpectedTypes = {[_: string]: ExpressionInputType};
15
6
 
16
7
  /*
17
8
  * Convert the given filter to an expression, storing the expected types for
@@ -61,51 +52,58 @@ export default function convertFilter(filter: FilterSpecification): unknown {
61
52
  * false (legacy filter semantics) are equivalent: they cause the filter to
62
53
  * produce a `false` result.
63
54
  */
64
- function _convertFilter(filter: FilterSpecification, expectedTypes: ExpectedTypes): unknown {
65
- if (isExpressionFilter(filter)) { return filter; }
66
-
55
+ export default function convertFilter(filter: FilterSpecification, expectedTypes: ExpectedTypes = {}): ExpressionFilterSpecification {
56
+ if (isExpressionFilter(filter)) return filter;
67
57
  if (!filter) return true;
68
- const op = filter[0];
69
- if (filter.length <= 1) return (op !== 'any');
70
-
71
- let converted;
72
-
73
- if (
74
- op === '==' ||
75
- op === '!=' ||
76
- op === '<' ||
77
- op === '>' ||
78
- op === '<=' ||
79
- op === '>='
80
- ) {
81
- const [, property, value] = filter;
82
- converted = convertComparisonOp(property as string, value, op, expectedTypes);
83
- } else if (op === 'any') {
84
- const children = (filter).slice(1).map((f: FilterSpecification) => {
85
- const types = {};
86
- const child = _convertFilter(f, types);
87
- const typechecks = runtimeTypeChecks(types);
88
- return typechecks === true ? child : ['case', typechecks, child, false];
89
- });
90
- return ['any'].concat(children as string[]);
91
- } else if (op === 'all') {
92
- const children = (filter).slice(1).map(f => _convertFilter(f as FilterSpecification, expectedTypes));
93
- return children.length > 1 ? ['all'].concat(children as string[]) : [].concat(...children);
94
- } else if (op === 'none') {
95
- return ['!', _convertFilter(['any'].concat(filter.slice(1) as string[]) as string[], {})];
96
- } else if (op === 'in') {
97
- converted = convertInOp(filter[1] as string, filter.slice(2));
98
- } else if (op === '!in') {
99
- converted = convertInOp(filter[1] as string, filter.slice(2), true);
100
- } else if (op === 'has') {
101
- converted = convertHasOp(filter[1] as string);
102
- } else if (op === '!has') {
103
- converted = ['!', convertHasOp(filter[1] as string)];
104
- } else {
105
- converted = true;
106
- }
107
58
 
108
- return converted;
59
+ const legacyFilter = filter as LegacyFilterSpecification;
60
+ const legacyOp = legacyFilter[0];
61
+ if (filter.length <= 1) return (legacyOp !== 'any');
62
+
63
+ switch (legacyOp) {
64
+ case '==':
65
+ case '!=':
66
+ case '<':
67
+ case '>':
68
+ case '<=':
69
+ case '>=': {
70
+ const [, property, value] = filter;
71
+ return convertComparisonOp(property as string, value, legacyOp, expectedTypes);
72
+ }
73
+ case 'any': {
74
+ const [, ...conditions] = legacyFilter;
75
+ const children = conditions.map((f: LegacyFilterSpecification) => {
76
+ const types = {};
77
+ const child = convertFilter(f, types);
78
+ const typechecks = runtimeTypeChecks(types);
79
+ return typechecks === true ? child : ['case', typechecks, child, false] as ExpressionSpecification;
80
+ });
81
+ return ['any', ...children];
82
+ }
83
+ case 'all': {
84
+ const [, ...conditions] = legacyFilter;
85
+ const children = conditions.map(f => convertFilter(f, expectedTypes));
86
+ return children.length > 1 ? ['all', ...children] : children[0];
87
+ }
88
+ case 'none': {
89
+ const [, ...conditions] = legacyFilter;
90
+ return ['!', convertFilter(['any', ...conditions], {})];
91
+ }
92
+ case 'in': {
93
+ const [, property, ...values] = legacyFilter;
94
+ return convertInOp(property, values);
95
+ }
96
+ case '!in': {
97
+ const [, property, ...values] = legacyFilter;
98
+ return convertInOp(property, values, true);
99
+ }
100
+ case 'has':
101
+ return convertHasOp(legacyFilter[1]);
102
+ case '!has':
103
+ return ['!', convertHasOp(legacyFilter[1])];
104
+ default:
105
+ return true;
106
+ }
109
107
  }
110
108
 
111
109
  // Given a set of feature properties and an expected type for each one,
@@ -116,7 +114,7 @@ function _convertFilter(filter: FilterSpecification, expectedTypes: ExpectedType
116
114
  // ['==', ['typeof', ['get', 'name'], 'string']],
117
115
  // ['==', ['typeof', ['get', 'population'], 'number]]
118
116
  // ]
119
- function runtimeTypeChecks(expectedTypes: ExpectedTypes) {
117
+ function runtimeTypeChecks(expectedTypes: ExpectedTypes): ExpressionFilterSpecification {
120
118
  const conditions = [];
121
119
  for (const property in expectedTypes) {
122
120
  const get = property === '$id' ? ['id'] : ['get', property];
@@ -124,13 +122,13 @@ function runtimeTypeChecks(expectedTypes: ExpectedTypes) {
124
122
  }
125
123
  if (conditions.length === 0) return true;
126
124
  if (conditions.length === 1) return conditions[0];
127
- return ['all'].concat(conditions);
125
+ return ['all', ...conditions];
128
126
  }
129
127
 
130
- function convertComparisonOp(property: string, value: any, op: string, expectedTypes?: ExpectedTypes | null) {
128
+ function convertComparisonOp(property: string, value: any, op: string, expectedTypes?: ExpectedTypes | null): ExpressionFilterSpecification {
131
129
  let get;
132
130
  if (property === '$type') {
133
- return [op, ['geometry-type'], value];
131
+ return [op, ['geometry-type'], value] as ExpressionFilterSpecification;
134
132
  } else if (property === '$id') {
135
133
  get = ['id'];
136
134
  } else {
@@ -156,13 +154,13 @@ function convertComparisonOp(property: string, value: any, op: string, expectedT
156
154
  ];
157
155
  }
158
156
 
159
- return [op, get, value];
157
+ return [op, get, value] as ExpressionFilterSpecification;
160
158
  }
161
159
 
162
- function convertInOp(property: string, values: Array<any>, negate = false) {
160
+ function convertInOp(property: string, values: Array<any>, negate = false): ExpressionFilterSpecification {
163
161
  if (values.length === 0) return negate;
164
162
 
165
- let get;
163
+ let get: ExpressionSpecification;
166
164
  if (property === '$type') {
167
165
  get = ['geometry-type'];
168
166
  } else if (property === '$id') {
@@ -190,12 +188,14 @@ function convertInOp(property: string, values: Array<any>, negate = false) {
190
188
  return ['match', get, uniqueValues, !negate, negate];
191
189
  }
192
190
 
193
- return [ negate ? 'all' : 'any' as any].concat(
194
- values.map(v => [negate ? '!=' : '==', get, v])
195
- );
191
+ if (negate) {
192
+ return ['all', ...values.map(v => ['!=', get, v] as ExpressionSpecification)];
193
+ } else {
194
+ return ['any', ...values.map(v => ['==', get, v] as ExpressionSpecification)];
195
+ }
196
196
  }
197
197
 
198
- function convertHasOp(property: string) {
198
+ function convertHasOp(property: string): ExpressionFilterSpecification {
199
199
  if (property === '$type') {
200
200
  return true;
201
201
  } else if (property === '$id') {
@@ -5,10 +5,39 @@ import Point from '@mapbox/point-geometry';
5
5
  import MercatorCoordinate from '../../geo/mercator_coordinate';
6
6
  import EXTENT from '../../data/extent';
7
7
  import {CanonicalTileID} from '../../source/tile_id';
8
- import {FilterSpecification} from '../types.g';
8
+ import {ExpressionFilterSpecification, FilterSpecification} from '../types.g';
9
9
  import {Feature} from '../expression';
10
10
 
11
11
  describe('filter', () => {
12
+ test('exprssions transpilation test', () => {
13
+ function compileTimeCheck(_: ExpressionFilterSpecification) {
14
+ expect(true).toBeTruthy();
15
+ }
16
+ compileTimeCheck(['any']);
17
+ compileTimeCheck(['at', 2, ['array', 1, 2, 3]]);
18
+ compileTimeCheck(['case', ['has', 'color'], ['get', 'color'], 'white']);
19
+ compileTimeCheck(['case', ['all', ['has', 'point_count'], ['<', ['get', 'point_count'], 3]], ['get', 'cluster_routes'], '']);
20
+ compileTimeCheck(['interpolate', ['linear'], ['get', 'point_count'], 2, 18.0, 10, 24.0]);
21
+ compileTimeCheck(['case', ['has', 'point_count'], ['interpolate', ['linear'], ['get', 'point_count'], 2, 18.0, 10, 24.0], 12.0]);
22
+ compileTimeCheck([
23
+ 'case',
24
+ ['has', 'point_count'], ['interpolate', ['linear'], ['get', 'point_count'], 2, '#ccc', 10, '#444'],
25
+ ['has', 'priorityValue'], ['interpolate', ['linear'], ['get', 'priorityValue'], 0, '#ff9', 1, '#f66'],
26
+ '#fcaf3e'
27
+ ]);
28
+ compileTimeCheck([
29
+ 'case',
30
+ ['==', ['get', 'CAPITAL'], 1], 'city-capital',
31
+ ['>=', ['get', 'POPULATION'], 1000000], 'city-1M',
32
+ ['>=', ['get', 'POPULATION'], 500000], 'city-500k',
33
+ ['>=', ['get', 'POPULATION'], 100000], 'city-100k',
34
+ 'city'
35
+ ]);
36
+ compileTimeCheck(['match', ['get', 'TYPE'], ['TARGETPOINT:HOSPITAL'], true, false]);
37
+ compileTimeCheck(['match', ['get', 'TYPE'], ['ADIZ', 'AMA', 'AWY', 'CLASS', 'NO-FIR', 'OCA', 'OTA', 'P', 'RAS', 'RCA', 'UTA', 'UTA-P'], true, false]);
38
+ compileTimeCheck(['==', ['get', 'MILITARYAIRPORT'], 1]);
39
+ });
40
+
12
41
  test('expression, zoom', () => {
13
42
  const f = createFilter(['>=', ['number', ['get', 'x']], ['zoom']]).filter;
14
43
  expect(f({zoom: 1}, {properties: {x: 0}} as any as Feature)).toBe(false);
@@ -52,6 +81,18 @@ describe('filter', () => {
52
81
  expect(createFilter(['any', false, false]).filter(undefined, undefined)).toBe(false);
53
82
  });
54
83
 
84
+ test('expression, literal', () => {
85
+ expect(createFilter(['literal', true]).filter(undefined, undefined)).toBe(true);
86
+ expect(createFilter(['literal', false]).filter(undefined, undefined)).toBe(false);
87
+ });
88
+
89
+ test('expression, match', () => {
90
+ const match = createFilter(['match', ['get', 'x'], ['a', 'b', 'c'], true, false]).filter;
91
+ expect(match(undefined, {properties: {x: 'a'}} as any as Feature)).toBe(true);
92
+ expect(match(undefined, {properties: {x: 'c'}} as any as Feature)).toBe(true);
93
+ expect(match(undefined, {properties: {x: 'd'}} as any as Feature)).toBe(false);
94
+ });
95
+
55
96
  test('expression, type error', () => {
56
97
  expect(() => {
57
98
  createFilter(['==', ['number', ['get', 'x']], ['string', ['get', 'y']]]);
@@ -77,7 +118,7 @@ describe('filter', () => {
77
118
  };
78
119
  const withinFilter = createFilter(['within', {'type': 'Polygon', 'coordinates': [[[0, 0], [5, 0], [5, 5], [0, 5], [0, 0]]]}]);
79
120
  expect(withinFilter.needGeometry).toBe(true);
80
- const canonical = {z: 3, x: 3, y:3} as CanonicalTileID;
121
+ const canonical = {z: 3, x: 3, y: 3} as CanonicalTileID;
81
122
  expect(
82
123
  withinFilter.filter({zoom: 3}, {type: 1, geometry: [[getPointFromLngLat(2, 2, canonical)]]} as Feature, canonical)
83
124
  ).toBe(true);
@@ -179,14 +220,14 @@ describe('convert legacy filters to expressions', () => {
179
220
  ['LineString', 'Point', 'Polygon'],
180
221
  true,
181
222
  false
182
- ] as FilterSpecification,
223
+ ],
183
224
  [
184
225
  'match',
185
226
  ['get', 'type'],
186
227
  ['island'],
187
228
  true,
188
229
  false
189
- ] as FilterSpecification
230
+ ]
190
231
  ];
191
232
 
192
233
  const converted = convertFilter(filter);
@@ -2,6 +2,7 @@ import {createExpression} from '../expression';
2
2
  import type {GlobalProperties, Feature} from '../expression';
3
3
  import type {CanonicalTileID} from '../../source/tile_id';
4
4
  import {StylePropertySpecification} from '../style-spec';
5
+ import {ExpressionFilterSpecification} from '../types.g';
5
6
 
6
7
  type FilterExpression = (
7
8
  globalProperties: GlobalProperties,
@@ -17,7 +18,7 @@ export type FeatureFilter = {
17
18
  export default createFilter;
18
19
  export {isExpressionFilter};
19
20
 
20
- function isExpressionFilter(filter: any) {
21
+ function isExpressionFilter(filter: any): filter is ExpressionFilterSpecification {
21
22
  if (filter === true || filter === false) {
22
23
  return true;
23
24
  }
@@ -1,6 +1,7 @@
1
1
  import {createFunction} from './index';
2
2
  import Color from '../util/color';
3
3
  import Formatted from '../expression/types/formatted';
4
+ import Padding from '../util/padding';
4
5
 
5
6
  describe('binary search', () => {
6
7
  test('will eventually terminate.', () => {
@@ -235,6 +236,19 @@ describe('exponential function', () => {
235
236
  expect(params).toEqual(paramsCopy);
236
237
  });
237
238
 
239
+ test('padding', () => {
240
+ const f = createFunction({
241
+ type: 'exponential',
242
+ stops: [[1, 2], [11, [2, 5, 2, 7]]]
243
+ }, {
244
+ type: 'padding'
245
+ }).evaluate;
246
+
247
+ expect(f({zoom: 0}, undefined)).toEqual(new Padding([2, 2, 2, 2]));
248
+ expect(f({zoom: 5}, undefined)).toEqual(new Padding([2, 3.2, 2, 4]));
249
+ expect(f({zoom: 11}, undefined)).toEqual(new Padding([2, 5, 2, 7]));
250
+ });
251
+
238
252
  test('property present', () => {
239
253
  const f = createFunction({
240
254
  property: 'foo',
@@ -532,11 +546,24 @@ describe('interval function', () => {
532
546
  }).evaluate;
533
547
 
534
548
  expect(f({zoom: 0}, undefined)).toEqual(new Color(1, 0, 0, 1));
535
- expect(f({zoom: 0}, undefined)).toEqual(new Color(1, 0, 0, 1));
549
+ expect(f({zoom: 10}, undefined)).toEqual(new Color(1, 0, 0, 1));
536
550
  expect(f({zoom: 11}, undefined)).toEqual(new Color(0, 0, 1, 1));
537
551
 
538
552
  });
539
553
 
554
+ test('padding', () => {
555
+ const f = createFunction({
556
+ type: 'interval',
557
+ stops: [[1, 2], [11, 4]]
558
+ }, {
559
+ type: 'padding'
560
+ }).evaluate;
561
+
562
+ expect(f({zoom: 0}, undefined)).toEqual(new Padding([2, 2, 2, 2]));
563
+ expect(f({zoom: 10}, undefined)).toEqual(new Padding([2, 2, 2, 2]));
564
+ expect(f({zoom: 11}, undefined)).toEqual(new Padding([4, 4, 4, 4]));
565
+ });
566
+
540
567
  test('property present', () => {
541
568
  const f = createFunction({
542
569
  property: 'foo',
@@ -746,6 +773,47 @@ describe('categorical function', () => {
746
773
 
747
774
  });
748
775
 
776
+ test('padding', () => {
777
+ const f = createFunction({
778
+ property: 'foo',
779
+ type: 'categorical',
780
+ stops: [[0, 2], [1, 4]]
781
+ }, {
782
+ type: 'padding'
783
+ }).evaluate;
784
+
785
+ expect(f({zoom: 0}, {properties: {foo: 0}})).toEqual(new Padding([2, 2, 2, 2]));
786
+ expect(f({zoom: 1}, {properties: {foo: 1}})).toEqual(new Padding([4, 4, 4, 4]));
787
+ });
788
+
789
+ test('padding function default', () => {
790
+ const f = createFunction({
791
+ property: 'foo',
792
+ type: 'categorical',
793
+ stops: [[0, 2], [1, 4]],
794
+ default: 6
795
+ }, {
796
+ type: 'padding'
797
+ }).evaluate;
798
+
799
+ expect(f({zoom: 0}, {properties: {}})).toEqual(new Padding([6, 6, 6, 6]));
800
+ expect(f({zoom: 0}, {properties: {foo: 3}})).toEqual(new Padding([6, 6, 6, 6]));
801
+ });
802
+
803
+ test('padding spec default', () => {
804
+ const f = createFunction({
805
+ property: 'foo',
806
+ type: 'categorical',
807
+ stops: [[0, 2], [1, 4]]
808
+ }, {
809
+ type: 'padding',
810
+ default: 6
811
+ }).evaluate;
812
+
813
+ expect(f({zoom: 0}, {properties: {}})).toEqual(new Padding([6, 6, 6, 6]));
814
+ expect(f({zoom: 0}, {properties: {foo: 3}})).toEqual(new Padding([6, 6, 6, 6]));
815
+ });
816
+
749
817
  test('boolean', () => {
750
818
  const f = createFunction({
751
819
  property: 'foo',
@@ -853,6 +921,54 @@ describe('identity function', () => {
853
921
 
854
922
  });
855
923
 
924
+ test('padding', () => {
925
+ const f = createFunction({
926
+ property: 'foo',
927
+ type: 'identity'
928
+ }, {
929
+ type: 'padding'
930
+ }).evaluate;
931
+
932
+ expect(f({zoom: 0}, {properties: {foo: 3}})).toEqual(new Padding([3, 3, 3, 3]));
933
+ expect(f({zoom: 1}, {properties: {foo: [3, 4]}})).toEqual(new Padding([3, 4, 3, 4]));
934
+ });
935
+
936
+ test('padding function default', () => {
937
+ const f = createFunction({
938
+ property: 'foo',
939
+ type: 'identity',
940
+ default: [1, 2, 3, 4]
941
+ }, {
942
+ type: 'padding'
943
+ }).evaluate;
944
+
945
+ expect(f({zoom: 0}, {properties: {}})).toEqual(new Padding([1, 2, 3, 4]));
946
+ });
947
+
948
+ test('padding spec default', () => {
949
+ const f = createFunction({
950
+ property: 'foo',
951
+ type: 'identity'
952
+ }, {
953
+ type: 'padding',
954
+ default: [1, 2, 3, 4]
955
+ }).evaluate;
956
+
957
+ expect(f({zoom: 0}, {properties: {}})).toEqual(new Padding([1, 2, 3, 4]));
958
+ });
959
+
960
+ test('padding invalid', () => {
961
+ const f = createFunction({
962
+ property: 'foo',
963
+ type: 'identity'
964
+ }, {
965
+ type: 'padding',
966
+ default: [1, 2, 3, 4]
967
+ }).evaluate;
968
+
969
+ expect(f({zoom: 0}, {properties: {foo: 'invalid'}})).toEqual(new Padding([1, 2, 3, 4]));
970
+ });
971
+
856
972
  test('property type mismatch, function default', () => {
857
973
  const f = createFunction({
858
974
  property: 'foo',
@@ -9,6 +9,7 @@ import Formatted from '../expression/types/formatted';
9
9
  import ResolvedImage from '../expression/types/resolved_image';
10
10
  import {supportsInterpolation} from '../util/properties';
11
11
  import {findStopLessThanOrEqualTo} from '../expression/stops';
12
+ import Padding from '../util/padding';
12
13
 
13
14
  export function isFunction(value) {
14
15
  return typeof value === 'object' && value !== null && !Array.isArray(value);
@@ -25,19 +26,21 @@ export function createFunction(parameters, propertySpec) {
25
26
  const zoomDependent = zoomAndFeatureDependent || !featureDependent;
26
27
  const type = parameters.type || (supportsInterpolation(propertySpec) ? 'exponential' : 'interval');
27
28
 
28
- if (isColor) {
29
+ if (isColor || propertySpec.type === 'padding') {
30
+ const parseFn = isColor ? Color.parse : Padding.parse;
31
+
29
32
  parameters = extend({}, parameters);
30
33
 
31
34
  if (parameters.stops) {
32
35
  parameters.stops = parameters.stops.map((stop) => {
33
- return [stop[0], Color.parse(stop[1])];
36
+ return [stop[0], parseFn(stop[1])];
34
37
  });
35
38
  }
36
39
 
37
40
  if (parameters.default) {
38
- parameters.default = Color.parse(parameters.default);
41
+ parameters.default = parseFn(parameters.default);
39
42
  } else {
40
- parameters.default = Color.parse(propertySpec.default);
43
+ parameters.default = parseFn(propertySpec.default);
41
44
  }
42
45
  }
43
46
 
@@ -198,14 +201,23 @@ function evaluateExponentialFunction(parameters, propertySpec, input) {
198
201
  }
199
202
 
200
203
  function evaluateIdentityFunction(parameters, propertySpec, input) {
201
- if (propertySpec.type === 'color') {
202
- input = Color.parse(input);
203
- } else if (propertySpec.type === 'formatted') {
204
- input = Formatted.fromString(input.toString());
205
- } else if (propertySpec.type === 'resolvedImage') {
206
- input = ResolvedImage.fromString(input.toString());
207
- } else if (getType(input) !== propertySpec.type && (propertySpec.type !== 'enum' || !propertySpec.values[input])) {
208
- input = undefined;
204
+ switch (propertySpec.type) {
205
+ case 'color':
206
+ input = Color.parse(input);
207
+ break;
208
+ case 'formatted':
209
+ input = Formatted.fromString(input.toString());
210
+ break;
211
+ case 'resolvedImage':
212
+ input = ResolvedImage.fromString(input.toString());
213
+ break;
214
+ case 'padding':
215
+ input = Padding.parse(input);
216
+ break;
217
+ default:
218
+ if (getType(input) !== propertySpec.type && (propertySpec.type !== 'enum' || !propertySpec.values[input])) {
219
+ input = undefined;
220
+ }
209
221
  }
210
222
  return coalesce(input, parameters.default, propertySpec.default);
211
223
  }
@@ -15,7 +15,7 @@ export default function expressions(style: StyleSpecification) {
15
15
 
16
16
  eachLayer(style, (layer: LayerSpecification & { filter?: FilterSpecification }) => {
17
17
  if (layer.filter) {
18
- layer.filter = (convertFilter(layer.filter) as any);
18
+ layer.filter = convertFilter(layer.filter);
19
19
  }
20
20
  });
21
21
 
@@ -1,3 +1,5 @@
1
+ // @ts-nocheck
2
+
1
3
  import migrate from './v8';
2
4
 
3
5
  describe('migrate v8', () => {
@@ -1,3 +1,5 @@
1
+ // @ts-nocheck
2
+
1
3
  import migrate from './v9';
2
4
 
3
5
  describe('migrate v9', () => {
@@ -5,7 +7,7 @@ describe('migrate v9', () => {
5
7
  const input = {
6
8
  version: 8,
7
9
  sources: {
8
- a: {type: 'vector', tiles: [ 'http://dev/null' ]}
10
+ a: {type: 'vector', tiles: ['http://dev/null']}
9
11
  },
10
12
  layers: [{
11
13
  id: 'parent',
@@ -21,7 +23,7 @@ describe('migrate v9', () => {
21
23
  expect(migrate(input)).toEqual({
22
24
  version: 9,
23
25
  sources: {
24
- a: {type: 'vector', tiles: [ 'http://dev/null' ]}
26
+ a: {type: 'vector', tiles: ['http://dev/null']}
25
27
  },
26
28
  layers: [{
27
29
  id: 'parent',
@@ -42,7 +44,7 @@ describe('migrate v9', () => {
42
44
  const input = {
43
45
  version: 8,
44
46
  sources: {
45
- a: {type: 'vector', tiles: [ 'http://dev/null' ]}
47
+ a: {type: 'vector', tiles: ['http://dev/null']}
46
48
  },
47
49
  layers: [{
48
50
  id: 'a',
@@ -61,7 +63,7 @@ describe('migrate v9', () => {
61
63
  expect(migrate(input)).toEqual({
62
64
  version: 9,
63
65
  sources: {
64
- a: {type: 'vector', tiles: [ 'http://dev/null' ]}
66
+ a: {type: 'vector', tiles: ['http://dev/null']}
65
67
  },
66
68
  layers: [{
67
69
  id: 'a',
@@ -1,3 +1,5 @@
1
+ // @ts-nocheck
2
+
1
3
  import migrate from './migrate';
2
4
  import * as spec from './style-spec';
3
5
  import v8 from './reference/v8.json';
@@ -102,7 +104,7 @@ describe('migrate', () => {
102
104
  });
103
105
  expect(migrated.layers[0].layout['icon-image']).toEqual([
104
106
  'match',
105
- ['get', 'type' ],
107
+ ['get', 'type'],
106
108
  'park',
107
109
  'some-icon',
108
110
  ''
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@maplibre/maplibre-gl-style-spec",
3
3
  "description": "a specification for maplibre gl styles",
4
- "version": "16.0.0",
4
+ "version": "16.1.0",
5
5
  "author": "MapLibre",
6
6
  "keywords": [
7
7
  "mapbox",
@@ -1,3 +1,3 @@
1
1
 
2
- import spec from './v8.json';
3
- export default spec;
2
+ import spec from './v8.json' assert {type: 'json'};
3
+ export default spec as any;
@@ -1466,11 +1466,10 @@
1466
1466
  "property-type": "data-driven"
1467
1467
  },
1468
1468
  "icon-padding": {
1469
- "type": "number",
1470
- "default": 2,
1471
- "minimum": 0,
1469
+ "type": "padding",
1470
+ "default": [2],
1472
1471
  "units": "pixels",
1473
- "doc": "Size of the additional area around the icon bounding box used for detecting symbol collisions.",
1472
+ "doc": "Size of additional area round the icon bounding box used for detecting symbol collisions. Values are declared using CSS margin shorthand syntax: a single value applies to all four sides; two values apply to [top/bottom, left/right]; three values apply to [top, left/right, bottom]; four values apply to [top, right, bottom, left]. For backwards compatibility, a single bare number is accepted, and treated the same as a one-element array - padding applied to all sides.",
1474
1473
  "requires": [
1475
1474
  "icon-image"
1476
1475
  ],
@@ -1480,15 +1479,19 @@
1480
1479
  "android": "2.0.1",
1481
1480
  "ios": "2.0.0",
1482
1481
  "macos": "0.1.0"
1482
+ },
1483
+ "data-driven styling": {
1484
+ "js": "2.2.0"
1483
1485
  }
1484
1486
  },
1485
1487
  "expression": {
1486
1488
  "interpolated": true,
1487
1489
  "parameters": [
1488
- "zoom"
1490
+ "zoom",
1491
+ "feature"
1489
1492
  ]
1490
1493
  },
1491
- "property-type": "data-constant"
1494
+ "property-type": "data-driven"
1492
1495
  },
1493
1496
  "icon-keep-upright": {
1494
1497
  "type": "boolean",
@@ -67,7 +67,8 @@ function validSchema(k, v, obj, ref, version, kind) {
67
67
  'property-type',
68
68
  'formatted',
69
69
  'resolvedImage',
70
- 'promoteId'
70
+ 'promoteId',
71
+ 'padding'
71
72
  ]);
72
73
  const keys = [
73
74
  'default',