Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Socket
Sign inDemoInstall

@mapbox/mapbox-gl-style-spec

Package Overview
Dependencies
Maintainers
14
Versions
101
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mapbox/mapbox-gl-style-spec - npm Package Compare versions

Comparing version 13.22.0 to 13.23.0-beta.1

validate/validate_projection.js

11

CHANGELOG.md

@@ -1,5 +0,14 @@

# 13.22.0-beta.1
# 13.23.0-beta.1
### ✨ Features and improvements
* Add a `projection` root property that allows a non-mercator projection to be set as a style's default projection. ([#11124](https://github.com/mapbox/mapbox-gl-js/pull/11124))
* Add support for using `["pitch"]` and `["distance-from-camera"]` expressions within the `filter` of a symbol layer. ([#10795](https://github.com/mapbox/mapbox-gl-js/pull/10795))
# 13.22.0
### ✨ Features and improvements
* Added `protected` field to mapbox-api-supported validation. ([#10968](https://github.com/mapbox/mapbox-gl-js/pull/10968))
# 13.21.0

@@ -6,0 +15,0 @@

@@ -109,4 +109,8 @@

*/
setFog: 'setFog'
setFog: 'setFog',
/*
* { command: 'setProjection', args: [projectionProperties] }
*/
setProjection: 'setProjection'
};

@@ -367,2 +371,5 @@

}
if (!isEqual(before.projection, after.projection)) {
commands.push({command: operations.setProjection, args: [after.projection]});
}

@@ -369,0 +376,0 @@ // Handle changes to `sources`

@@ -204,2 +204,12 @@ // @flow

],
'pitch': [
NumberType,
[],
(ctx) => ctx.globals.pitch || 0
],
'distance-from-center': [
NumberType,
[],
(ctx) => ctx.distanceFromCenter()
],
'heatmap-density': [

@@ -206,0 +216,0 @@ NumberType,

// @flow
import {Color} from './values.js';
import type Point from '@mapbox/point-geometry';
import type {FormattedSection} from './types/formatted.js';
import type {GlobalProperties, Feature, FeatureState} from './index.js';
import type {CanonicalTileID} from '../../source/tile_id.js';
import type {FeatureDistanceData} from '../feature_filter/index.js';

@@ -17,2 +20,4 @@ const geometryTypes = ['Unknown', 'Point', 'LineString', 'Polygon'];

canonical: ?CanonicalTileID;
featureTileCoord: ?Point;
featureDistanceData: ?FeatureDistanceData;

@@ -29,2 +34,4 @@ _parseColorCache: {[_: string]: ?Color};

this.canonical = null;
this.featureTileCoord = null;
this.featureDistanceData = null;
}

@@ -52,2 +59,25 @@

distanceFromCenter() {
if (this.featureTileCoord && this.featureDistanceData) {
const c = this.featureDistanceData.center;
const scale = this.featureDistanceData.scale;
const {x, y} = this.featureTileCoord;
// Calculate the distance vector `d` (left handed)
const dX = x * scale - c[0];
const dY = y * scale - c[1];
// The bearing vector `b` (left handed)
const bX = this.featureDistanceData.bearing[0];
const bY = this.featureDistanceData.bearing[1];
// Distance is calculated as `dot(d, v)`
const dist = (bX * dX + bY * dY);
return dist;
}
return 0;
}
parseColor(input: string): ?Color {

@@ -54,0 +84,0 @@ let cached = this._parseColorCache[input];

12

expression/index.js

@@ -30,2 +30,3 @@ // @flow

import type {CanonicalTileID} from '../../source/tile_id.js';
import type {FeatureDistanceData} from '../feature_filter/index.js';

@@ -44,2 +45,3 @@ export type Feature = {

zoom: number,
pitch?: number,
heatmapDensity?: number,

@@ -68,3 +70,3 @@ lineProgress?: number,

evaluateWithoutErrorHandling(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection): any {
evaluateWithoutErrorHandling(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection, featureTileCoord?: Point, featureDistanceData?: FeatureDistanceData): any {
this._evaluator.globals = globals;

@@ -76,2 +78,4 @@ this._evaluator.feature = feature;

this._evaluator.formattedSection = formattedSection;
this._evaluator.featureTileCoord = featureTileCoord || null;
this._evaluator.featureDistanceData = featureDistanceData || null;

@@ -81,3 +85,3 @@ return this.expression.evaluate(this._evaluator);

evaluate(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection): any {
evaluate(globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection, featureTileCoord?: Point, featureDistanceData?: FeatureDistanceData): any {
this._evaluator.globals = globals;

@@ -89,2 +93,4 @@ this._evaluator.feature = feature || null;

this._evaluator.formattedSection = formattedSection || null;
this._evaluator.featureTileCoord = featureTileCoord || null;
this._evaluator.featureDistanceData = featureDistanceData || null;

@@ -242,3 +248,3 @@ try {

const isZoomConstant = isConstant.isGlobalPropertyConstant(parsed, ['zoom']);
const isZoomConstant = isConstant.isGlobalPropertyConstant(parsed, ['zoom', 'pitch', 'distance-from-center']);
if (!isZoomConstant && !supportsZoomExpression(propertySpec)) {

@@ -245,0 +251,0 @@ return error([new ParsingError('', 'zoom expressions not supported')]);

@@ -232,3 +232,3 @@ // @flow

return isFeatureConstant(expression) &&
isGlobalPropertyConstant(expression, ['zoom', 'heatmap-density', 'line-progress', 'sky-radial-progress', 'accumulated', 'is-supported-script']);
isGlobalPropertyConstant(expression, ['zoom', 'heatmap-density', 'line-progress', 'sky-radial-progress', 'accumulated', 'is-supported-script', 'pitch', 'distance-from-center']);
}
// @flow
import {createExpression} from '../expression/index.js';
import {isFeatureConstant} from '../expression/is_constant.js';
import latest from '../reference/latest.js';
import type {GlobalProperties, Feature} from '../expression/index.js';
import type {CanonicalTileID} from '../../source/tile_id.js';
import type Point from '@mapbox/point-geometry';
type FilterExpression = (globalProperties: GlobalProperties, feature: Feature, canonical?: CanonicalTileID) => boolean;
export type FeatureFilter ={filter: FilterExpression, needGeometry: boolean};
export type FeatureDistanceData = {bearing: [number, number], center: [number, number], scale: number};
type FilterExpression = (globalProperties: GlobalProperties, feature: Feature, canonical?: CanonicalTileID, featureTileCoord?: Point, featureDistanceData?: FeatureDistanceData) => boolean;
export type FeatureFilter = {filter: FilterExpression, dynamicFilter?: FilterExpression, needGeometry: boolean, needFeature: boolean};
export default createFilter;
export {isExpressionFilter};
export {isExpressionFilter, isDynamicFilter, extractStaticFilter};

@@ -55,13 +59,2 @@ function isExpressionFilter(filter: any) {

const filterSpec = {
'type': 'boolean',
'default': false,
'transition': false,
'property-type': 'data-driven',
'expression': {
'interpolated': false,
'parameters': ['zoom', 'feature']
}
};
/**

@@ -74,7 +67,8 @@ * Given a filter expressed as nested arrays, return a new function

* @param {Array} filter mapbox gl filter
* @param {string} layerType the type of the layer this filter will be applied to.
* @returns {Function} filter-evaluating function
*/
function createFilter(filter: any): FeatureFilter {
function createFilter(filter: any, layerType?: string = 'fill'): FeatureFilter {
if (filter === null || filter === undefined) {
return {filter: () => true, needGeometry: false};
return {filter: () => true, needGeometry: false, needFeature: false};
}

@@ -85,13 +79,179 @@

}
const filterExp = ((filter: any): string[] | string | boolean);
const compiled = createExpression(filter, filterSpec);
if (compiled.result === 'error') {
throw new Error(compiled.value.map(err => `${err.key}: ${err.message}`).join(', '));
let staticFilter = true;
try {
staticFilter = extractStaticFilter(filterExp);
} catch (e) {
console.warn(
`Failed to extract static filter. Filter will continue working, but at higher memory usage and slower framerate.
This is most likely a bug, please report this via https://github.com/mapbox/mapbox-gl-js/issues/new?assignees=&labels=&template=Bug_report.md
and paste the contents of this message in the report.
Thank you!
Filter Expression:
${JSON.stringify(filterExp, null, 2)}
`);
}
// Compile the static component of the filter
const filterSpec = latest[`filter_${layerType}`];
const compiledStaticFilter = createExpression(staticFilter, filterSpec);
let filterFunc = null;
if (compiledStaticFilter.result === 'error') {
throw new Error(compiledStaticFilter.value.map(err => `${err.key}: ${err.message}`).join(', '));
} else {
const needGeometry = geometryNeeded(filter);
return {filter: (globalProperties: GlobalProperties, feature: Feature, canonical?: CanonicalTileID) => compiled.value.evaluate(globalProperties, feature, {}, canonical),
needGeometry};
filterFunc = (globalProperties: GlobalProperties, feature: Feature, canonical?: CanonicalTileID) => compiledStaticFilter.value.evaluate(globalProperties, feature, {}, canonical);
}
// If the static component is not equal to the entire filter then we have a dynamic component
// Compile the dynamic component separately
let dynamicFilterFunc = null;
let needFeature = null;
if (staticFilter !== filterExp) {
const compiledDynamicFilter = createExpression(filterExp, filterSpec);
if (compiledDynamicFilter.result === 'error') {
throw new Error(compiledDynamicFilter.value.map(err => `${err.key}: ${err.message}`).join(', '));
} else {
dynamicFilterFunc = (globalProperties: GlobalProperties, feature: Feature, canonical?: CanonicalTileID, featureTileCoord?: Point, featureDistanceData?: FeatureDistanceData) => compiledDynamicFilter.value.evaluate(globalProperties, feature, {}, canonical, undefined, undefined, featureTileCoord, featureDistanceData);
needFeature = !isFeatureConstant(compiledDynamicFilter.value.expression);
}
}
filterFunc = ((filterFunc: any): FilterExpression);
const needGeometry = geometryNeeded(staticFilter);
return {
filter: filterFunc,
dynamicFilter: dynamicFilterFunc ? dynamicFilterFunc : undefined,
needGeometry,
needFeature: !!needFeature
};
}
function extractStaticFilter(filter: any): any {
if (!isDynamicFilter(filter)) {
return filter;
}
// Shallow copy so we can replace expressions in-place
let result = filter.slice();
// 1. Union branches
unionDynamicBranches(result);
// 2. Collapse dynamic conditions to `true`
result = collapseDynamicBooleanExpressions(result);
return result;
}
function collapseDynamicBooleanExpressions(expression: any): any {
if (!Array.isArray(expression)) {
return expression;
}
const collapsed = collapsedExpression(expression);
if (collapsed === true) {
return collapsed;
} else {
return collapsed.map((subExpression) => collapseDynamicBooleanExpressions(subExpression));
}
}
/**
* Traverses the expression and replaces all instances of branching on a
* `dynamic` conditional (such as `['pitch']` or `['distance-from-center']`)
* into an `any` expression.
* This ensures that all possible outcomes of a `dynamic` branch are considered
* when evaluating the expression upfront during filtering.
*
* @param {Array<any>} filter the filter expression mutated in-place.
*/
function unionDynamicBranches(filter: any) {
let isBranchingDynamically = false;
const branches = [];
if (filter[0] === 'case') {
for (let i = 1; i < filter.length - 1; i += 2) {
isBranchingDynamically = isBranchingDynamically || isDynamicFilter(filter[i]);
branches.push(filter[i + 1]);
}
branches.push(filter[filter.length - 1]);
} else if (filter[0] === 'match') {
isBranchingDynamically = isBranchingDynamically || isDynamicFilter(filter[1]);
for (let i = 2; i < filter.length - 1; i += 2) {
branches.push(filter[i + 1]);
}
branches.push(filter[filter.length - 1]);
} else if (filter[0] === 'step') {
isBranchingDynamically = isBranchingDynamically || isDynamicFilter(filter[1]);
for (let i = 1; i < filter.length - 1; i += 2) {
branches.push(filter[i + 1]);
}
}
if (isBranchingDynamically) {
filter.length = 0;
filter.push('any', ...branches);
}
// traverse and recurse into children
for (let i = 1; i < filter.length; i++) {
unionDynamicBranches(filter[i]);
}
}
function isDynamicFilter(filter: any): boolean {
// Base Cases
if (!Array.isArray(filter)) {
return false;
}
if (isRootExpressionDynamic(filter[0])) {
return true;
}
for (let i = 1; i < filter.length; i++) {
const child = filter[i];
if (isDynamicFilter(child)) {
return true;
}
}
return false;
}
function isRootExpressionDynamic(expression: string): boolean {
return expression === 'pitch' ||
expression === 'distance-from-center';
}
const dynamicConditionExpressions = new Set([
'in',
'==',
'!=',
'>',
'>=',
'<',
'<=',
'to-boolean'
]);
function collapsedExpression(expression: any): any {
if (dynamicConditionExpressions.has(expression[0])) {
for (let i = 1; i < expression.length; i++) {
const param = expression[i];
if (isDynamicFilter(param)) {
return true;
}
}
}
return expression;
}
// Comparison function to sort numbers and strings

@@ -98,0 +258,0 @@ function compare(a, b) {

{
"name": "@mapbox/mapbox-gl-style-spec",
"description": "a specification for mapbox gl styles",
"version": "13.22.0",
"version": "13.23.0-beta.1",
"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' | 'feature-state' | 'heatmap-density' | 'line-progress' | 'sky-radial-progress'>;
type ExpressionParameters = Array<'zoom' | 'feature' | 'feature-state' | 'heatmap-density' | 'line-progress' | 'sky-radial-progress' | 'pitch' | 'distance-from-center'>;

@@ -6,0 +6,0 @@ type ExpressionSpecification = {

@@ -75,2 +75,3 @@ // @flow

"transition"?: TransitionSpecification,
"projection"?: ProjectionSpecification,
"layers": Array<LayerSpecification>

@@ -97,2 +98,8 @@ |}

export type ProjectionSpecification = {|
"name": "albers" | "equalEarth" | "equirectangular" | "lambertConformalConic" | "mercator" | "naturalEarth" | "winkelTripel",
"center"?: [number, number],
"parallels"?: [number, number]
|}
export type VectorSourceSpecification = {

@@ -99,0 +106,0 @@ "type": "vector",

@@ -8,3 +8,6 @@ // @flow

import {isStateConstant, isGlobalPropertyConstant, isFeatureConstant} from '../expression/is_constant.js';
import CompoundExpression from '../expression/compound_expression.js';
import type {Expression} from '../expression/expression.js';
export default function validateExpression(options: any): Array<ValidationError> {

@@ -30,4 +33,4 @@ const expression = (options.expressionContext === 'property' ? createPropertyExpression : createExpression)(deepUnbundle(options.value), options.valueSpec);

if (options.expressionContext === 'filter' && !isStateConstant(expressionObj)) {
return [new ValidationError(options.key, options.value, '"feature-state" data expressions are not supported with filters.')];
if (options.expressionContext === 'filter') {
return disallowedFilterParameters(expressionObj, options);
}

@@ -46,1 +49,29 @@

}
export function disallowedFilterParameters(e: Expression, options: any): Array<ValidationError> {
const disallowedParameters = new Set([
'zoom',
'feature-state',
'pitch',
'distance-from-center'
]);
for (const param of options.valueSpec.expression.parameters) {
disallowedParameters.delete(param);
}
if (disallowedParameters.size === 0) {
return [];
}
const errors = [];
if (e instanceof CompoundExpression) {
if (disallowedParameters.has(e.name)) {
return [new ValidationError(options.key, options.value, `["${e.name}"] expression is not supported in a filter for a ${options.object.type} layer with id: ${options.object.id}`)];
}
}
e.eachChild((arg) => {
errors.push(...disallowedFilterParameters(arg, options));
});
return errors;
}

@@ -12,5 +12,7 @@

if (isExpressionFilter(deepUnbundle(options.value))) {
const layerType = deepUnbundle(options.layerType);
return validateExpression(extend({}, options, {
expressionContext: 'filter',
valueSpec: {value: 'boolean'}
// We default to a layerType of `fill` because that points to a non-dynamic filter definition within the style-spec.
valueSpec: options.styleSpec[`filter_${layerType || 'fill'}`]
}));

@@ -17,0 +19,0 @@ } else {

@@ -101,3 +101,5 @@

},
filter: validateFilter,
filter(options) {
return validateFilter(extend({layerType: type}, options));
},
layout(options) {

@@ -104,0 +106,0 @@ return validateObject({

@@ -25,2 +25,3 @@

import validateImage from './validate_image.js';
import validateProjection from './validate_projection.js';

@@ -47,3 +48,4 @@ const VALIDATORS = {

'formatted': validateFormatted,
'resolvedImage': validateImage
'resolvedImage': validateImage,
'projection': validateProjection
};

@@ -50,0 +52,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc