@mapbox/mapbox-gl-style-spec
Advanced tools
Comparing version 12.0.0 to 13.0.0
@@ -0,2 +1,15 @@ | ||
## 13.0.0 | ||
### ⚠️ Breaking changes | ||
* Align implicit type behavior of `match` expressions with with `case/==` ([#6684](https://github.com/mapbox/mapbox-gl-js/pull/6684)) | ||
* Update spec so that documentation can automatically capture which functions and expressions can be used with which properties ([#6521](https://github.com/mapbox/mapbox-gl-js/pull/6521)) | ||
### ✨ Features and improvements | ||
* Add `feature-state` [#6263](https://github.com/mapbox/mapbox-gl-js/pull/6263) | ||
* Add support for GeoJSON attribution ([#6364](https://github.com/mapbox/mapbox-gl-js/pull/6364)) (h/t [andrewharvey](https://github.com/andrewharvey)) | ||
* Upgrade to Flow 0.69 ([#6594](https://github.com/mapbox/mapbox-gl-js/pull/6594)) | ||
### 🐛 Bug fixes | ||
* Use named exports for style-spec entrypoint module ([#6601](https://github.com/mapbox/mapbox-gl-js/issues/6601) | ||
## 12.0.0 | ||
@@ -3,0 +16,0 @@ |
@@ -172,2 +172,7 @@ // @flow | ||
}, | ||
'feature-state': [ | ||
ValueType, | ||
[StringType], | ||
(ctx, [key]) => get(key.evaluate(ctx), ctx.featureState || {}) | ||
], | ||
'properties': [ | ||
@@ -174,0 +179,0 @@ ObjectType, |
@@ -6,2 +6,3 @@ // @flow | ||
import { typeOf } from '../values'; | ||
import { ValueType, type Type } from '../types'; | ||
@@ -11,3 +12,2 @@ import type { Expression } from '../expression'; | ||
import type EvaluationContext from '../evaluation_context'; | ||
import type { Type } from '../types'; | ||
@@ -89,3 +89,3 @@ // Map input label values to output expression index | ||
const input = context.parse(args[1], 1, inputType); | ||
const input = context.parse(args[1], 1, ValueType); | ||
if (!input) return null; | ||
@@ -97,2 +97,7 @@ | ||
assert(inputType && outputType); | ||
if (input.type.kind !== 'value' && context.concat(1).checkSubtype((inputType: any), input.type)) { | ||
return null; | ||
} | ||
return new Match((inputType: any), (outputType: any), input, cases, outputs, otherwise); | ||
@@ -103,3 +108,4 @@ } | ||
const input = (this.input.evaluate(ctx): any); | ||
return (this.outputs[this.cases[input]] || this.otherwise).evaluate(ctx); | ||
const output = (typeOf(input) === this.inputType && this.outputs[this.cases[input]]) || this.otherwise; | ||
return output.evaluate(ctx); | ||
} | ||
@@ -142,3 +148,3 @@ | ||
const coerceLabel = (label) => this.input.type.kind === 'number' ? Number(label) : label; | ||
const coerceLabel = (label) => this.inputType.kind === 'number' ? Number(label) : label; | ||
@@ -145,0 +151,0 @@ for (const [outputIndex, labels] of groupedByOutput) { |
@@ -5,3 +5,3 @@ // @flow | ||
import type { Feature, GlobalProperties } from './index'; | ||
import type { GlobalProperties, Feature, FeatureState } from './index'; | ||
@@ -13,2 +13,3 @@ const geometryTypes = ['Unknown', 'Point', 'LineString', 'Polygon']; | ||
feature: ?Feature; | ||
featureState: ?FeatureState; | ||
@@ -15,0 +16,0 @@ _parseColorCache: {[string]: ?Color}; |
@@ -18,2 +18,3 @@ // @flow | ||
import { success, error } from '../util/result'; | ||
import { supportsPropertyExpression, supportsZoomExpression, supportsInterpolation } from '../util/properties'; | ||
@@ -33,2 +34,4 @@ import type {Type} from './types'; | ||
export type FeatureState = {[string]: any}; | ||
export type GlobalProperties = $ReadOnly<{ | ||
@@ -58,3 +61,3 @@ zoom: number, | ||
evaluateWithoutErrorHandling(globals: GlobalProperties, feature?: Feature): any { | ||
evaluateWithoutErrorHandling(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState): any { | ||
if (!this._evaluator) { | ||
@@ -66,2 +69,3 @@ this._evaluator = new EvaluationContext(); | ||
this._evaluator.feature = feature; | ||
this._evaluator.featureState = featureState; | ||
@@ -71,3 +75,3 @@ return this.expression.evaluate(this._evaluator); | ||
evaluate(globals: GlobalProperties, feature?: Feature): any { | ||
evaluate(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState): any { | ||
if (!this._evaluator) { | ||
@@ -79,2 +83,3 @@ this._evaluator = new EvaluationContext(); | ||
this._evaluator.feature = feature; | ||
this._evaluator.featureState = featureState; | ||
@@ -129,2 +134,3 @@ try { | ||
kind: Kind; | ||
isStateDependent: boolean; | ||
_styleExpression: StyleExpression; | ||
@@ -135,10 +141,11 @@ | ||
this._styleExpression = expression; | ||
this.isStateDependent = kind !== 'constant' && !isConstant.isStateConstant(expression.expression); | ||
} | ||
evaluateWithoutErrorHandling(globals: GlobalProperties, feature?: Feature): any { | ||
return this._styleExpression.evaluateWithoutErrorHandling(globals, feature); | ||
evaluateWithoutErrorHandling(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState): any { | ||
return this._styleExpression.evaluateWithoutErrorHandling(globals, feature, featureState); | ||
} | ||
evaluate(globals: GlobalProperties, feature?: Feature): any { | ||
return this._styleExpression.evaluate(globals, feature); | ||
evaluate(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState): any { | ||
return this._styleExpression.evaluate(globals, feature, featureState); | ||
} | ||
@@ -150,2 +157,3 @@ } | ||
zoomStops: Array<number>; | ||
isStateDependent: boolean; | ||
@@ -159,2 +167,3 @@ _styleExpression: StyleExpression; | ||
this._styleExpression = expression; | ||
this.isStateDependent = kind !== 'camera' && !isConstant.isStateConstant(expression.expression); | ||
if (zoomCurve instanceof Interpolate) { | ||
@@ -165,8 +174,8 @@ this._interpolationType = zoomCurve.interpolation; | ||
evaluateWithoutErrorHandling(globals: GlobalProperties, feature?: Feature): any { | ||
return this._styleExpression.evaluateWithoutErrorHandling(globals, feature); | ||
evaluateWithoutErrorHandling(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState): any { | ||
return this._styleExpression.evaluateWithoutErrorHandling(globals, feature, featureState); | ||
} | ||
evaluate(globals: GlobalProperties, feature?: Feature): any { | ||
return this._styleExpression.evaluate(globals, feature); | ||
evaluate(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState): any { | ||
return this._styleExpression.evaluate(globals, feature, featureState); | ||
} | ||
@@ -190,3 +199,4 @@ | ||
kind: 'source', | ||
+evaluate: (globals: GlobalProperties, feature?: Feature) => any, | ||
isStateDependent: boolean, | ||
+evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState) => any, | ||
}; | ||
@@ -196,3 +206,3 @@ | ||
kind: 'camera', | ||
+evaluate: (globals: GlobalProperties, feature?: Feature) => any, | ||
+evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState) => any, | ||
+interpolationFactor: (input: number, lower: number, upper: number) => number, | ||
@@ -204,3 +214,4 @@ zoomStops: Array<number> | ||
kind: 'composite', | ||
+evaluate: (globals: GlobalProperties, feature?: Feature) => any, | ||
isStateDependent: boolean, | ||
+evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState) => any, | ||
+interpolationFactor: (input: number, lower: number, upper: number) => number, | ||
@@ -225,8 +236,8 @@ zoomStops: Array<number> | ||
const isFeatureConstant = isConstant.isFeatureConstant(parsed); | ||
if (!isFeatureConstant && !propertySpec['property-function']) { | ||
return error([new ParsingError('', 'property expressions not supported')]); | ||
if (!isFeatureConstant && !supportsPropertyExpression(propertySpec)) { | ||
return error([new ParsingError('', 'data expressions not supported')]); | ||
} | ||
const isZoomConstant = isConstant.isGlobalPropertyConstant(parsed, ['zoom']); | ||
if (!isZoomConstant && propertySpec['zoom-function'] === false) { | ||
if (!isZoomConstant && !supportsZoomExpression(propertySpec)) { | ||
return error([new ParsingError('', 'zoom expressions not supported')]); | ||
@@ -240,3 +251,3 @@ } | ||
return error([zoomCurve]); | ||
} else if (zoomCurve instanceof Interpolate && propertySpec['function'] === 'piecewise-constant') { | ||
} else if (zoomCurve instanceof Interpolate && !supportsInterpolation(propertySpec)) { | ||
return error([new ParsingError('', '"interpolate" expressions cannot be used with this property')]); | ||
@@ -243,0 +254,0 @@ } |
@@ -11,2 +11,4 @@ // @flow | ||
return false; | ||
} else if (e.name === 'feature-state') { | ||
return false; | ||
} else if (e.name === 'has' && e.args.length === 1) { | ||
@@ -32,2 +34,15 @@ return false; | ||
function isStateConstant(e: Expression) { | ||
if (e instanceof CompoundExpression) { | ||
if (e.name === 'feature-state') { | ||
return false; | ||
} | ||
} | ||
let result = true; | ||
e.eachChild(arg => { | ||
if (result && !isStateConstant(arg)) { result = false; } | ||
}); | ||
return result; | ||
} | ||
function isGlobalPropertyConstant(e: Expression, properties: Array<string>) { | ||
@@ -42,2 +57,2 @@ if (e instanceof CompoundExpression && properties.indexOf(e.name) >= 0) { return false; } | ||
export { isFeatureConstant, isGlobalPropertyConstant }; | ||
export { isFeatureConstant, isGlobalPropertyConstant, isStateConstant }; |
@@ -50,5 +50,8 @@ // @flow | ||
'default': false, | ||
'function': true, | ||
'property-function': true, | ||
'zoom-function': true | ||
'transition': false, | ||
'property-type': 'data-driven', | ||
'expression': { | ||
'interpolated': false, | ||
'parameters': ['zoom', 'feature'] | ||
} | ||
}; | ||
@@ -55,0 +58,0 @@ |
@@ -113,2 +113,3 @@ // Generated code; do not edit. Edit build/generate-flow-typed-style-spec.js instead. | ||
"maxzoom"?: number, | ||
"attribution"?: string, | ||
"buffer"?: number, | ||
@@ -115,0 +116,0 @@ "tolerance"?: number, |
@@ -8,2 +8,3 @@ import type Pbf from 'pbf'; | ||
declare interface VectorTileLayer { | ||
version?: number; | ||
name: string; | ||
@@ -10,0 +11,0 @@ extent: number; |
@@ -144,33 +144,20 @@ // @flow | ||
const inputType = typeof stops[0][0]; | ||
assert( | ||
inputType === 'string' || | ||
inputType === 'number' || | ||
inputType === 'boolean' | ||
); | ||
let input = [inputType, ['get', parameters.property]]; | ||
let expression; | ||
let isStep = false; | ||
if (type === 'categorical' && inputType === 'boolean') { | ||
if (type === 'categorical' && typeof stops[0][0] === 'boolean') { | ||
assert(parameters.stops.length > 0 && parameters.stops.length <= 2); | ||
if (parameters.stops[0][0] === false) { | ||
input = ['!', input]; | ||
expression = ['case']; | ||
for (const stop of stops) { | ||
expression.push(['==', ['get', parameters.property], stop[0]], stop[1]); | ||
} | ||
expression = [ 'case', input, parameters.stops[0][1] ]; | ||
if (parameters.stops.length > 1) { | ||
expression.push(parameters.stops[1][1]); | ||
} else { | ||
expression.push(defaultExpression); | ||
} | ||
expression.push(defaultExpression); | ||
return expression; | ||
} else if (type === 'categorical') { | ||
expression = ['match', input]; | ||
expression = ['match', ['get', parameters.property]]; | ||
} else if (type === 'interval') { | ||
expression = ['step', input]; | ||
expression = ['step', ['number', ['get', parameters.property]]]; | ||
isStep = true; | ||
} else if (type === 'exponential') { | ||
const base = parameters.base !== undefined ? parameters.base : 1; | ||
expression = ['interpolate', ['exponential', base], input]; | ||
expression = ['interpolate', ['exponential', base], ['number', ['get', parameters.property]]]; | ||
} else { | ||
@@ -240,6 +227,5 @@ throw new Error(`Unknown property function type ${type}`); | ||
return parameters.type; | ||
} else if (propertySpec.function) { | ||
return propertySpec.function === 'interpolated' ? 'exponential' : 'interval'; | ||
} else { | ||
return 'exponential'; | ||
assert(propertySpec.expression); | ||
return (propertySpec.expression: any).interpolated ? 'exponential' : 'interval'; | ||
} | ||
@@ -246,0 +232,0 @@ } |
@@ -8,2 +8,3 @@ | ||
import Interpolate from '../expression/definitions/interpolate'; | ||
import { supportsInterpolation } from '../util/properties'; | ||
@@ -23,3 +24,3 @@ export function isFunction(value) { | ||
const zoomDependent = zoomAndFeatureDependent || !featureDependent; | ||
const type = parameters.type || (propertySpec.function === 'interpolated' ? 'exponential' : 'interval'); | ||
const type = parameters.type || (supportsInterpolation(propertySpec) ? 'exponential' : 'interval'); | ||
@@ -26,0 +27,0 @@ if (isColor) { |
{ | ||
"name": "@mapbox/mapbox-gl-style-spec", | ||
"description": "a specification for mapbox gl styles", | ||
"version": "12.0.0", | ||
"version": "13.0.0", | ||
"author": "Mapbox", | ||
@@ -6,0 +6,0 @@ "keywords": [ |
// @flow | ||
type ExpressionType = 'data-driven' | 'cross-faded' | 'cross-faded-data-driven' | 'color-ramp' | 'data-constant' | 'constant'; | ||
type ExpressionParameters = Array<'zoom' | 'feature' | 'heatmap-density' | 'line-progress'>; | ||
type ExpressionSpecification = { | ||
interpolated: boolean, | ||
parameters: ExpressionParameters | ||
} | ||
export type StylePropertySpecification = { | ||
type: 'number', | ||
'function': boolean, | ||
'property-function': boolean, | ||
'zoom-function': boolean, | ||
'property-type': ExpressionType, | ||
expression?: ExpressionSpecification, | ||
transition: boolean, | ||
default?: number | ||
} | { | ||
type: 'string', | ||
'function': boolean, | ||
'property-function': boolean, | ||
'zoom-function': boolean, | ||
'property-type': ExpressionType, | ||
expression?: ExpressionSpecification, | ||
transition: boolean, | ||
default?: string, | ||
@@ -18,18 +26,18 @@ tokens?: boolean | ||
type: 'boolean', | ||
'function': boolean, | ||
'property-function': boolean, | ||
'zoom-function': boolean, | ||
'property-type': ExpressionType, | ||
expression?: ExpressionSpecification, | ||
transition: boolean, | ||
default?: boolean | ||
} | { | ||
type: 'enum', | ||
'function': boolean, | ||
'property-function': boolean, | ||
'zoom-function': boolean, | ||
'property-type': ExpressionType, | ||
expression?: ExpressionSpecification, | ||
values: {[string]: {}}, | ||
transition: boolean, | ||
default?: string | ||
} | { | ||
type: 'color', | ||
'function': boolean, | ||
'property-function': boolean, | ||
'zoom-function': boolean, | ||
'property-type': ExpressionType, | ||
expression?: ExpressionSpecification, | ||
transition: boolean, | ||
default?: string | ||
@@ -39,6 +47,6 @@ } | { | ||
value: 'number', | ||
'function': boolean, | ||
'property-function': boolean, | ||
'zoom-function': boolean, | ||
'property-type': ExpressionType, | ||
expression?: ExpressionSpecification, | ||
length?: number, | ||
transition: boolean, | ||
default?: Array<number> | ||
@@ -48,6 +56,6 @@ } | { | ||
value: 'string', | ||
'function': boolean, | ||
'property-function': boolean, | ||
'zoom-function': boolean, | ||
'property-type': ExpressionType, | ||
expression?: ExpressionSpecification, | ||
length?: number, | ||
transition: boolean, | ||
default?: Array<string> | ||
@@ -54,0 +62,0 @@ }; |
@@ -7,2 +7,3 @@ // @flow | ||
import { deepUnbundle } from '../util/unbundle_jsonlint'; | ||
import { isStateConstant } from '../expression/is_constant'; | ||
@@ -22,3 +23,7 @@ export default function validateExpression(options: any) { | ||
if (options.expressionContext === 'property' && options.propertyType === 'layout' && | ||
(!isStateConstant((expression.value: any)._styleExpression.expression))) { | ||
return [new ValidationError(options.key, options.value, '"feature-state" data expressions are not supported with layout properties.')]; | ||
} | ||
return []; | ||
} |
@@ -9,2 +9,7 @@ | ||
import { unbundle } from '../util/unbundle_jsonlint'; | ||
import { | ||
supportsPropertyExpression, | ||
supportsZoomExpression, | ||
supportsInterpolation | ||
} from '../util/properties'; | ||
@@ -46,3 +51,3 @@ export default function validateFunction(options) { | ||
if (functionType === 'exponential' && options.valueSpec['function'] === 'piecewise-constant') { | ||
if (functionType === 'exponential' && options.valueSpec.expression && !supportsInterpolation(options.valueSpec)) { | ||
errors.push(new ValidationError(options.key, options.value, 'exponential functions not supported')); | ||
@@ -52,5 +57,5 @@ } | ||
if (options.styleSpec.$version >= 8) { | ||
if (isPropertyFunction && !options.valueSpec['property-function']) { | ||
if (isPropertyFunction && !supportsPropertyExpression(options.valueSpec)) { | ||
errors.push(new ValidationError(options.key, options.value, 'property functions not supported')); | ||
} else if (isZoomFunction && !options.valueSpec['zoom-function'] && options.objectKey !== 'heatmap-color' && options.objectKey !== 'line-gradient') { | ||
} else if (isZoomFunction && !supportsZoomExpression(options.valueSpec)) { | ||
errors.push(new ValidationError(options.key, options.value, 'zoom functions not supported')); | ||
@@ -166,3 +171,3 @@ } | ||
let message = `number expected, ${type} found`; | ||
if (functionValueSpec['property-function'] && functionType === undefined) { | ||
if (supportsPropertyExpression(functionValueSpec) && functionType === undefined) { | ||
message += '\nIf you intended to use a categorical function, specify `"type": "categorical"`.'; | ||
@@ -169,0 +174,0 @@ } |
@@ -7,2 +7,3 @@ | ||
import { unbundle, deepUnbundle } from '../util/unbundle_jsonlint'; | ||
import { supportsPropertyExpression } from '../util/properties'; | ||
@@ -36,3 +37,3 @@ export default function validateProperty(options, propertyType) { | ||
let tokenMatch; | ||
if (getType(value) === 'string' && valueSpec['property-function'] && !valueSpec.tokens && (tokenMatch = /^{([^}]+)}$/.exec(value))) { | ||
if (getType(value) === 'string' && supportsPropertyExpression(valueSpec) && !valueSpec.tokens && (tokenMatch = /^{([^}]+)}$/.exec(value))) { | ||
return [new ValidationError( | ||
@@ -62,4 +63,5 @@ key, value, | ||
expressionContext: 'property', | ||
propertyType: propertyType, | ||
propertyKey | ||
})); | ||
} |
@@ -57,6 +57,6 @@ | ||
if (valueSpec.function && isFunction(unbundle(value))) { | ||
if (valueSpec.expression && isFunction(unbundle(value))) { | ||
return validateFunction(options); | ||
} else if (valueSpec.function && isExpression(deepUnbundle(value))) { | ||
} else if (valueSpec.expression && isExpression(deepUnbundle(value))) { | ||
return validateExpression(options); | ||
@@ -63,0 +63,0 @@ |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
0
413264
102
11723