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
28
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.28.0 to 14.0.0-beta.1

data/extent.js

4

bin/gl-style-composite.js

@@ -9,5 +9,5 @@ #!/usr/bin/env node

/* eslint import/no-unresolved: [error, { ignore: ['^@mapbox/mapbox-gl-style-spec$'] }] */
/* eslint import/no-unresolved: [error, { ignore: ['^@mapbox/mapbox-gl-style-spec-private$'] }] */
/* $FlowFixMe[cannot-resolve-module] */
import {format, composite} from '@mapbox/mapbox-gl-style-spec';
import {format, composite} from '@mapbox/mapbox-gl-style-spec-private';

@@ -14,0 +14,0 @@ const argv = minimist(process.argv.slice(2));

@@ -9,5 +9,5 @@ #!/usr/bin/env node

/* eslint import/no-unresolved: [error, { ignore: ['^@mapbox/mapbox-gl-style-spec$'] }] */
/* eslint import/no-unresolved: [error, { ignore: ['^@mapbox/mapbox-gl-style-spec-private$'] }] */
/* $FlowFixMe[cannot-resolve-module] */
import {format} from '@mapbox/mapbox-gl-style-spec';
import {format} from '@mapbox/mapbox-gl-style-spec-private';

@@ -14,0 +14,0 @@ const argv = minimist(process.argv.slice(2));

@@ -9,5 +9,5 @@ #!/usr/bin/env node

/* eslint import/no-unresolved: [error, { ignore: ['^@mapbox/mapbox-gl-style-spec$'] }] */
/* eslint import/no-unresolved: [error, { ignore: ['^@mapbox/mapbox-gl-style-spec-private$'] }] */
/* $FlowFixMe[cannot-resolve-module] */
import {format, migrate} from '@mapbox/mapbox-gl-style-spec';
import {format, migrate} from '@mapbox/mapbox-gl-style-spec-private';

@@ -14,0 +14,0 @@ const argv = minimist(process.argv.slice(2));

@@ -9,5 +9,5 @@ #!/usr/bin/env node

/* eslint import/no-unresolved: [error, { ignore: ['^@mapbox/mapbox-gl-style-spec$'] }] */
/* eslint import/no-unresolved: [error, { ignore: ['^@mapbox/mapbox-gl-style-spec-private$'] }] */
/* $FlowFixMe[cannot-resolve-module] */
import {validate, validateMapboxApiSupported} from '@mapbox/mapbox-gl-style-spec';
import {validate, validateMapboxApiSupported} from '@mapbox/mapbox-gl-style-spec-private';

@@ -14,0 +14,0 @@ const argv = minimist(process.argv.slice(2), {

@@ -41,3 +41,3 @@ // @flow

const map = Object.create(null);
const map: Object = Object.create(null);
for (let i = 0; i < layers.length; i++) {

@@ -44,0 +44,0 @@ map[layers[i].id] = layers[i];

@@ -5,4 +5,6 @@ // @flow

import type {StyleSpecification} from './types.js';
import type {StyleSpecification, SourceSpecification, LayerSpecification} from './types.js';
type Sources = { [string]: SourceSpecification };
type Command = {

@@ -121,2 +123,12 @@ command: string;

/*
* { command: 'setCamera', args: [cameraProperties] }
*/
setCamera: 'setCamera',
/*
* { command: 'setLights', args: [{light-3d},...] }
*/
setLights: 'setLights',
/*
* { command: 'setProjection', args: [projectionProperties] }

@@ -127,7 +139,7 @@ */

function addSource(sourceId, after, commands) {
function addSource(sourceId: string, after: Sources, commands: Array<Command>) {
commands.push({command: operations.addSource, args: [sourceId, after[sourceId]]});
}
function removeSource(sourceId, commands, sourcesRemoved) {
function removeSource(sourceId: string, commands: Array<Command>, sourcesRemoved: {[string]: true}) {
commands.push({command: operations.removeSource, args: [sourceId]});

@@ -137,3 +149,3 @@ sourcesRemoved[sourceId] = true;

function updateSource(sourceId, after, commands, sourcesRemoved) {
function updateSource(sourceId: string, after: Sources, commands: Array<Command>, sourcesRemoved: {[string]: true}) {
removeSource(sourceId, commands, sourcesRemoved);

@@ -143,3 +155,3 @@ addSource(sourceId, after, commands);

function canUpdateGeoJSON(before, after, sourceId) {
function canUpdateGeoJSON(before: Sources, after: Sources, sourceId: string) {
let prop;

@@ -161,3 +173,3 @@ for (prop in before[sourceId]) {

function diffSources(before, after, commands, sourcesRemoved) {
function diffSources(before: Sources, after: Sources, commands: Array<Command>, sourcesRemoved: {[string]: true}) {
before = before || {};

@@ -179,7 +191,8 @@ after = after || {};

if (!after.hasOwnProperty(sourceId)) continue;
const source = after[sourceId];
if (!before.hasOwnProperty(sourceId)) {
addSource(sourceId, after, commands);
} else if (!isEqual(before[sourceId], after[sourceId])) {
if (before[sourceId].type === 'geojson' && after[sourceId].type === 'geojson' && canUpdateGeoJSON(before, after, sourceId)) {
commands.push({command: operations.setGeoJSONSourceData, args: [sourceId, after[sourceId].data]});
} else if (!isEqual(before[sourceId], source)) {
if (before[sourceId].type === 'geojson' && source.type === 'geojson' && canUpdateGeoJSON(before, after, sourceId)) {
commands.push({command: operations.setGeoJSONSourceData, args: [sourceId, source.data]});
} else {

@@ -193,3 +206,3 @@ // no update command, must remove then add

function diffLayerPropertyChanges(before, after, commands, layerId, klass, command) {
function diffLayerPropertyChanges(before: any, after: any, commands: Array<Command>, layerId: string, klass: ?string, command: string) {
before = before || {};

@@ -214,6 +227,7 @@ after = after || {};

function pluckId(layer) {
function pluckId(layer: LayerSpecification) {
return layer.id;
}
function indexById(group, layer) {
function indexById(group: {[string]: LayerSpecification}, layer: LayerSpecification) {
group[layer.id] = layer;

@@ -223,3 +237,3 @@ return group;

function diffLayers(before, after, commands) {
function diffLayers(before: Array<LayerSpecification>, after: Array<LayerSpecification>, commands: Array<Command>) {
before = before || [];

@@ -240,5 +254,5 @@ after = after || [];

// layers that have been added do not need to be diffed
const clean = Object.create(null);
const clean: Object = Object.create(null);
let i, d, layerId, beforeLayer, afterLayer, insertBeforeLayerId, prop;
let i, d, layerId, beforeLayer: LayerSpecification, afterLayer: LayerSpecification, insertBeforeLayerId, prop;

@@ -291,2 +305,3 @@ // remove layers

// and add it back 'from scratch'.
// $FlowFixMe[prop-missing] - there is no `source-layer` in background and sky layers
if (!isEqual(beforeLayer.source, afterLayer.source) || !isEqual(beforeLayer['source-layer'], afterLayer['source-layer']) || !isEqual(beforeLayer.type, afterLayer.type)) {

@@ -393,2 +408,8 @@ commands.push({command: operations.removeLayer, args: [layerId]});

}
if (!isEqual(before.lights, after.lights)) {
commands.push({command: operations.setLights, args: [after.lights]});
}
if (!isEqual(before.camera, after.camera)) {
commands.push({command: operations.setCamera, args: [after.camera]});
}

@@ -395,0 +416,0 @@ // Handle changes to `sources`

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

// we eventually succeed, we haven't polluted `context.errors`.
signatureContext = new ParsingContext(context.registry, context.path, null, context.scope);
signatureContext = new ParsingContext(context.registry, context.path, null, context.scope, undefined, context.options);

@@ -150,2 +150,3 @@ // First parse all the args, potentially coercing to the

for (const name in definitions) {
// $FlowFixMe[method-unbinding]
registry[name] = CompoundExpression;

@@ -152,0 +153,0 @@ }

@@ -59,3 +59,3 @@ // @flow

let N;
let N: ?number;
if (args.length > 3) {

@@ -69,3 +69,3 @@ if (args[2] !== null &&

}
N = args[2];
N = ((args[2]: any): number);
i++;

@@ -72,0 +72,0 @@ }

@@ -5,4 +5,4 @@ // @flow

import {BooleanType, ColorType, NumberType, StringType, ValueType} from '../types.js';
import {Color, toString as valueToString, validateRGBA} from '../values.js';
import {BooleanType, ColorType, NumberType, StringType, ValueType, array, NullType} from '../types.js';
import {Color, isValue, toString as valueToString, typeOf, validateRGBA} from '../values.js';
import RuntimeError from '../runtime_error.js';

@@ -17,3 +17,4 @@ import Formatted from '../types/formatted.js';

import type EvaluationContext from '../evaluation_context.js';
import type {Type} from '../types.js';
import type {Type, ArrayType} from '../types.js';
import getType from '../../util/get_type.js';

@@ -35,3 +36,3 @@ const types = {

class Coercion implements Expression {
type: Type;
type: Type | ArrayType;
args: Array<Expression>;

@@ -49,14 +50,50 @@

const name: string = (args[0]: any);
assert(types[name], name);
const parsed = [];
let type: Type | ArrayType = NullType;
if (name === 'to-array') {
if (!Array.isArray(args[1])) {
return null;
}
const arrayLength = args[1].length;
if (context.expectedType) {
if (context.expectedType.kind === 'array') {
type = array(context.expectedType.itemType, arrayLength);
} else {
return context.error(`Expected ${context.expectedType.kind} but found array.`);
}
} else if (arrayLength > 0 && isValue(args[1][0])) {
const value = (args[1][0]: any);
type = array(typeOf(value), arrayLength);
} else {
return null;
}
for (let i = 0; i < arrayLength; i++) {
// $FlowIgnore
const member = args[1][i];
let parsedMember;
if (getType(member) === 'array') {
parsedMember = context.parse(member, undefined, type.itemType);
} else {
const memberType = getType(member);
if (memberType !== type.itemType.kind) {
return context.error(`Expected ${type.itemType.kind} but found ${memberType}.`);
}
parsedMember = context.registry['literal'].parse(['literal', member === undefined ? null : member], context);
}
if (!parsedMember) return null;
parsed.push(parsedMember);
}
} else {
assert(types[name], name);
if ((name === 'to-boolean' || name === 'to-string') && args.length !== 2)
return context.error(`Expected one argument.`);
if ((name === 'to-boolean' || name === 'to-string') && args.length !== 2)
return context.error(`Expected one argument.`);
const type = types[name];
type = types[name];
const parsed = [];
for (let i = 1; i < args.length; i++) {
const input = context.parse(args[i], i, ValueType);
if (!input) return null;
parsed.push(input);
for (let i = 1; i < args.length; i++) {
const input = context.parse(args[i], i, ValueType);
if (!input) return null;
parsed.push(input);
}
}

@@ -67,3 +104,3 @@

evaluate(ctx: EvaluationContext): null | boolean | number | string | Color | Formatted | ResolvedImage {
evaluate(ctx: EvaluationContext): any {
if (this.type.kind === 'boolean') {

@@ -110,2 +147,4 @@ return Boolean(this.args[0].evaluate(ctx));

return ResolvedImage.fromString(valueToString(this.args[0].evaluate(ctx)));
} else if (this.type.kind === 'array') {
return this.args.map(arg => { return arg.evaluate(ctx); });
} else {

@@ -133,3 +172,3 @@ return valueToString(this.args[0].evaluate(ctx));

const serialized = [`to-${this.type.kind}`];
const serialized: Array<mixed> = this.type.kind === 'array' ? [] : [`to-${this.type.kind}`];
this.eachChild(child => { serialized.push(child.serialize()); });

@@ -136,0 +175,0 @@ return serialized;

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

// $FlowFixMe[method-unbinding]
return class Comparison implements Expression {

@@ -81,2 +82,3 @@ type: Type;

// $FlowFixMe[method-unbinding]
static parse(args: $ReadOnlyArray<mixed>, context: ParsingContext): ?Expression {

@@ -83,0 +85,0 @@ if (args.length !== 3 && args.length !== 4)

@@ -12,3 +12,3 @@ // @flow

type FormattedSectionExpression = {
export type FormattedSectionExpression = {
// Content of a section may be Image expression or other

@@ -88,3 +88,3 @@ // type of expression that is coercable to 'string'.

evaluate(ctx: EvaluationContext): Formatted {
const evaluateSection = section => {
const evaluateSection = (section: FormattedSectionExpression) => {
const evaluatedContent = section.content.evaluate(ctx);

@@ -91,0 +91,0 @@ if (typeOf(evaluatedContent) === ResolvedImageType) {

@@ -13,26 +13,39 @@ // @flow

type: Type;
input: Expression;
inputPrimary: Expression;
inputSecondary: ?Expression;
constructor(input: Expression) {
constructor(inputPrimary: Expression, inputSecondary: ?Expression) {
this.type = ResolvedImageType;
this.input = input;
this.inputPrimary = inputPrimary;
this.inputSecondary = inputSecondary;
}
static parse(args: $ReadOnlyArray<mixed>, context: ParsingContext): ?Expression {
if (args.length !== 2) {
return context.error(`Expected two arguments.`);
if (args.length < 2) {
return context.error(`Expected two or more arguments.`);
}
const name = context.parse(args[1], 1, StringType);
if (!name) return context.error(`No image name provided.`);
const namePrimary = context.parse(args[1], 1, StringType);
if (!namePrimary) return context.error(`No image name provided.`);
return new ImageExpression(name);
if (args.length === 2) {
return new ImageExpression(namePrimary);
}
const nameSecondary = context.parse(args[2], 1, StringType);
if (!nameSecondary) return context.error(`Secondary image variant is not a string.`);
return new ImageExpression(namePrimary, nameSecondary);
}
evaluate(ctx: EvaluationContext): null | ResolvedImage {
const evaluatedImageName = this.input.evaluate(ctx);
const value = ResolvedImage.fromString(this.inputPrimary.evaluate(ctx), this.inputSecondary ? this.inputSecondary.evaluate(ctx) : undefined);
if (value && ctx.availableImages) {
value.available = ctx.availableImages.indexOf(value.namePrimary) > -1;
// If there's a secondary variant, only mark it available if both are present
if (value.nameSecondary && value.available && ctx.availableImages) {
value.available = ctx.availableImages.indexOf(value.nameSecondary) > -1;
}
}
const value = ResolvedImage.fromString(evaluatedImageName);
if (value && ctx.availableImages) value.available = ctx.availableImages.indexOf(evaluatedImageName) > -1;
return value;

@@ -42,3 +55,6 @@ }

eachChild(fn: (_: Expression) => void) {
fn(this.input);
fn(this.inputPrimary);
if (this.inputSecondary) {
fn(this.inputSecondary);
}
}

@@ -52,4 +68,8 @@

serialize(): SerializedExpression {
return ["image", this.input.serialize()];
if (this.inputSecondary) {
// $FlowIgnore
return ["image", this.inputPrimary.serialize(), this.inputSecondary.serialize()];
}
return ["image", this.inputPrimary.serialize()];
}
}

@@ -17,3 +17,3 @@ // @flow

import {typeOf, Color, validateRGBA, toString as valueToString} from '../values.js';
import {typeOf, Color, validateRGBA, validateHSLA, toString as valueToString} from '../values.js';
import CompoundExpression from '../compound_expression.js';

@@ -49,5 +49,8 @@ import RuntimeError from '../runtime_error.js';

import Within from './within.js';
import Distance from './distance.js';
import type EvaluationContext from '../evaluation_context.js';
import type {Varargs} from '../compound_expression.js';
import type {ExpressionRegistry} from '../expression.js';
import type {Expression, ExpressionRegistry} from '../expression.js';
import {mulberry32} from '../../util/random.js';

@@ -62,25 +65,43 @@ const expressions: ExpressionRegistry = {

'<=': LessThanOrEqual,
// $FlowFixMe[method-unbinding]
'array': Assertion,
// $FlowFixMe[method-unbinding]
'at': At,
'boolean': Assertion,
// $FlowFixMe[method-unbinding]
'case': Case,
// $FlowFixMe[method-unbinding]
'coalesce': Coalesce,
// $FlowFixMe[method-unbinding]
'collator': CollatorExpression,
// $FlowFixMe[method-unbinding]
'format': FormatExpression,
// $FlowFixMe[method-unbinding]
'image': ImageExpression,
// $FlowFixMe[method-unbinding]
'in': In,
// $FlowFixMe[method-unbinding]
'index-of': IndexOf,
// $FlowFixMe[method-unbinding]
'interpolate': Interpolate,
'interpolate-hcl': Interpolate,
'interpolate-lab': Interpolate,
// $FlowFixMe[method-unbinding]
'length': Length,
// $FlowFixMe[method-unbinding]
'let': Let,
// $FlowFixMe[method-unbinding]
'literal': Literal,
// $FlowFixMe[method-unbinding]
'match': Match,
'number': Assertion,
// $FlowFixMe[method-unbinding]
'number-format': NumberFormat,
'object': Assertion,
// $FlowFixMe[method-unbinding]
'slice': Slice,
// $FlowFixMe[method-unbinding]
'step': Step,
'string': Assertion,
// $FlowFixMe[method-unbinding]
'to-boolean': Coercion,

@@ -90,7 +111,11 @@ 'to-color': Coercion,

'to-string': Coercion,
// $FlowFixMe[method-unbinding]
'var': Var,
'within': Within
// $FlowFixMe[method-unbinding]
'within': Within,
// $FlowFixMe[method-unbinding]
'distance': Distance
};
function rgba(ctx, [r, g, b, a]) {
function rgba(ctx: EvaluationContext, [r, g, b, a]: Array<Expression>) {
r = r.evaluate(ctx);

@@ -105,7 +130,20 @@ g = g.evaluate(ctx);

function has(key, obj) {
function hsla(ctx: EvaluationContext, [h, s, l, a]: Array<Expression>) {
h = h.evaluate(ctx);
s = s.evaluate(ctx);
l = l.evaluate(ctx);
const alpha = a ? a.evaluate(ctx) : 1;
const error = validateHSLA(h, s, l, alpha);
if (error) throw new RuntimeError(error);
const colorFunction = `hsla(${h}, ${s}%, ${l}%, ${alpha})`;
const color = Color.parse(colorFunction);
if (!color) throw new RuntimeError(`Failed to parse HSLA color: ${colorFunction}`);
return color;
}
function has(key: string, obj: {[string]: any}): boolean {
return key in obj;
}
function get(key, obj) {
function get(key: string, obj: {[string]: any}) {
const v = obj[key];

@@ -115,3 +153,11 @@ return typeof v === 'undefined' ? null : v;

function binarySearch(v, a, i, j) {
function getConfig(ctx: EvaluationContext, key: string, scope: string) {
if (scope.length) {
key += `\u{1f}${scope}`;
}
const v = ctx.getConfig(key);
return v ? v.evaluate(ctx) : null;
}
function binarySearch(v: any, a: {[number]: any}, i: number, j: number) {
while (i <= j) {

@@ -133,2 +179,15 @@ const m = (i + j) >> 1;

function hashString(str: string) {
let hash = 0;
if (str.length === 0) {
return hash;
}
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return hash;
}
CompoundExpression.register(expressions, {

@@ -162,2 +221,12 @@ 'error': [

],
'hsl': [
ColorType,
[NumberType, NumberType, NumberType],
hsla
],
'hsla': [
ColorType,
[NumberType, NumberType, NumberType, NumberType],
hsla
],
'has': {

@@ -187,2 +256,14 @@ type: BooleanType,

},
'config': {
type: ValueType,
overloads: [
[
[StringType],
(ctx, [key]) => getConfig(ctx, key.evaluate(ctx), '')
], [
[StringType, StringType],
(ctx, [key, scope]) => getConfig(ctx, key.evaluate(ctx), scope.evaluate(ctx))
]
]
},
'feature-state': [

@@ -223,2 +304,7 @@ ValueType,

],
'measure-light': [
NumberType,
[StringType],
(ctx, [s]) => ctx.measureLight(s.evaluate(ctx))
],
'heatmap-density': [

@@ -234,2 +320,7 @@ NumberType,

],
'raster-value': [
NumberType,
[],
(ctx) => ctx.globals.rasterValue || 0
],
'sky-radial-progress': [

@@ -588,5 +679,28 @@ NumberType,

(ctx, [collator]) => collator.evaluate(ctx).resolvedLocale()
]
],
'random': [
NumberType,
[NumberType, NumberType, ValueType],
(ctx, args) => {
const [min, max, seed] = args.map(arg => arg.evaluate(ctx));
if (min > max) {
return min;
}
if (min === max) {
return min;
}
let seedVal;
if (typeof seed === 'string') {
seedVal = hashString(seed);
} else if (typeof seed === 'number') {
seedVal = seed;
} else {
throw new RuntimeError(`Invalid seed input: ${seed}`);
}
const random = mulberry32(seedVal)();
return min + random * (max - min);
}
],
});
export default expressions;

@@ -255,3 +255,3 @@ // @flow

*/
function exponentialInterpolation(input, base, lowerValue, upperValue) {
function exponentialInterpolation(input: number, base: number, lowerValue: number, upperValue: number) {
const difference = upperValue - lowerValue;

@@ -258,0 +258,0 @@ const progress = input - lowerValue;

@@ -40,3 +40,3 @@ // @flow

let inputType;
let outputType;
let outputType: ?Type;
if (context.expectedType && context.expectedType.kind !== 'value') {

@@ -142,3 +142,3 @@ outputType = context.expectedType;

const coerceLabel = (label) => this.inputType.kind === 'number' ? Number(label) : label;
const coerceLabel = (label: number | string) => this.inputType.kind === 'number' ? Number(label) : label;

@@ -145,0 +145,0 @@ for (const [outputIndex, labels] of groupedByOutput) {

@@ -9,18 +9,12 @@ // @flow

import type EvaluationContext from '../evaluation_context.js';
import type {GeoJSON, GeoJSONPolygon, GeoJSONMultiPolygon} from '@mapbox/geojson-types';
import type Point from '@mapbox/point-geometry';
import type {GeoJSON, GeoJSONPosition, GeoJSONPolygon, GeoJSONMultiPolygon} from '@mapbox/geojson-types';
import type {CanonicalTileID} from '../../../source/tile_id.js';
import {updateBBox, boxWithinBox, pointWithinPolygon, segmentIntersectSegment} from '../../util/geometry_util.js';
import type {BBox} from '../../util/geometry_util.js';
type GeoJSONPolygons =| GeoJSONPolygon | GeoJSONMultiPolygon;
// minX, minY, maxX, maxY
type BBox = [number, number, number, number];
const EXTENT = 8192;
function updateBBox(bbox: BBox, coord: [number, number]) {
bbox[0] = Math.min(bbox[0], coord[0]);
bbox[1] = Math.min(bbox[1], coord[1]);
bbox[2] = Math.max(bbox[2], coord[0]);
bbox[3] = Math.max(bbox[3], coord[1]);
}
function mercatorXfromLng(lng: number) {

@@ -34,11 +28,3 @@ return (180 + lng) / 360;

function boxWithinBox(bbox1: BBox, bbox2: BBox) {
if (bbox1[0] <= bbox2[0]) return false;
if (bbox1[2] >= bbox2[2]) return false;
if (bbox1[1] <= bbox2[1]) return false;
if (bbox1[3] >= bbox2[3]) return false;
return true;
}
function getTileCoordinates(p, canonical: CanonicalTileID) {
function getTileCoordinates(p: GeoJSONPosition, canonical: CanonicalTileID) {
const x = mercatorXfromLng(p[0]);

@@ -50,28 +36,3 @@ const y = mercatorYfromLat(p[1]);

function onBoundary(p, p1, p2) {
const x1 = p[0] - p1[0];
const y1 = p[1] - p1[1];
const x2 = p[0] - p2[0];
const y2 = p[1] - p2[1];
return (x1 * y2 - x2 * y1 === 0) && (x1 * x2 <= 0) && (y1 * y2 <= 0);
}
function rayIntersect(p, p1, p2) {
return ((p1[1] > p[1]) !== (p2[1] > p[1])) && (p[0] < (p2[0] - p1[0]) * (p[1] - p1[1]) / (p2[1] - p1[1]) + p1[0]);
}
// ray casting algorithm for detecting if point is in polygon
function pointWithinPolygon(point, rings) {
let inside = false;
for (let i = 0, len = rings.length; i < len; i++) {
const ring = rings[i];
for (let j = 0, len2 = ring.length; j < len2 - 1; j++) {
if (onBoundary(point, ring[j], ring[j + 1])) return false;
if (rayIntersect(point, ring[j], ring[j + 1])) inside = !inside;
}
}
return inside;
}
function pointWithinPolygons(point, polygons) {
function pointWithinPolygons(point: GeoJSONPosition, polygons: Array<Array<Array<GeoJSONPosition>>>) {
for (let i = 0; i < polygons.length; i++) {

@@ -83,41 +44,9 @@ if (pointWithinPolygon(point, polygons[i])) return true;

function perp(v1, v2) {
return (v1[0] * v2[1] - v1[1] * v2[0]);
}
// check if p1 and p2 are in different sides of line segment q1->q2
function twoSided(p1, p2, q1, q2) {
// q1->p1 (x1, y1), q1->p2 (x2, y2), q1->q2 (x3, y3)
const x1 = p1[0] - q1[0];
const y1 = p1[1] - q1[1];
const x2 = p2[0] - q1[0];
const y2 = p2[1] - q1[1];
const x3 = q2[0] - q1[0];
const y3 = q2[1] - q1[1];
const det1 = (x1 * y3 - x3 * y1);
const det2 = (x2 * y3 - x3 * y2);
if ((det1 > 0 && det2 < 0) || (det1 < 0 && det2 > 0)) return true;
return false;
}
// a, b are end points for line segment1, c and d are end points for line segment2
function lineIntersectLine(a, b, c, d) {
// check if two segments are parallel or not
// precondition is end point a, b is inside polygon, if line a->b is
// parallel to polygon edge c->d, then a->b won't intersect with c->d
const vectorP = [b[0] - a[0], b[1] - a[1]];
const vectorQ = [d[0] - c[0], d[1] - c[1]];
if (perp(vectorQ, vectorP) === 0) return false;
// If lines are intersecting with each other, the relative location should be:
// a and b lie in different sides of segment c->d
// c and d lie in different sides of segment a->b
if (twoSided(a, b, c, d) && twoSided(c, d, a, b)) return true;
return false;
}
function lineIntersectPolygon(p1, p2, polygon) {
function lineIntersectPolygon(p1: GeoJSONPosition, p2: GeoJSONPosition, polygon: Array<Array<GeoJSONPosition>>) {
for (const ring of polygon) {
// loop through every edge of the ring
for (let j = 0; j < ring.length - 1; ++j) {
if (lineIntersectLine(p1, p2, ring[j], ring[j + 1])) {
for (let j = 0, len = ring.length, k = len - 1; j < len; k = j++) {
const q1 = ring[k];
const q2 = ring[j];
if (segmentIntersectSegment(p1, p2, q1, q2)) {
return true;

@@ -130,3 +59,3 @@ }

function lineStringWithinPolygon(line, polygon) {
function lineStringWithinPolygon(line: Array<GeoJSONPosition>, polygon: Array<Array<GeoJSONPosition>>) {
// First, check if geometry points of line segments are all inside polygon

@@ -148,3 +77,3 @@ for (let i = 0; i < line.length; ++i) {

function lineStringWithinPolygons(line, polygons) {
function lineStringWithinPolygons(line: Array<GeoJSONPosition>, polygons: Array<Array<Array<GeoJSONPosition>>>) {
for (let i = 0; i < polygons.length; i++) {

@@ -156,3 +85,3 @@ if (lineStringWithinPolygon(line, polygons[i])) return true;

function getTilePolygon(coordinates, bbox: BBox, canonical: CanonicalTileID) {
function getTilePolygon(coordinates: Array<Array<GeoJSONPosition>>, bbox: BBox, canonical: CanonicalTileID) {
const polygon = [];

@@ -171,3 +100,3 @@ for (let i = 0; i < coordinates.length; i++) {

function getTilePolygons(coordinates, bbox, canonical: CanonicalTileID) {
function getTilePolygons(coordinates: Array<Array<Array<GeoJSONPosition>>>, bbox: BBox, canonical: CanonicalTileID) {
const polygons = [];

@@ -181,3 +110,3 @@ for (let i = 0; i < coordinates.length; i++) {

function updatePoint(p, bbox, polyBBox, worldSize) {
function updatePoint(p: GeoJSONPosition, bbox: BBox, polyBBox: Array<number>, worldSize: number) {
if (p[0] < polyBBox[0] || p[0] > polyBBox[2]) {

@@ -194,3 +123,3 @@ const halfWorldSize = worldSize * 0.5;

function resetBBox(bbox) {
function resetBBox(bbox: BBox) {
bbox[0] = bbox[1] = Infinity;

@@ -200,3 +129,3 @@ bbox[2] = bbox[3] = -Infinity;

function getTilePoints(geometry, pointBBox, polyBBox, canonical: CanonicalTileID) {
function getTilePoints(geometry: ?Array<Array<Point>>, pointBBox: BBox, polyBBox: Array<number>, canonical: CanonicalTileID) {
const worldSize = Math.pow(2, canonical.z) * EXTENT;

@@ -216,6 +145,6 @@ const shifts = [canonical.x * EXTENT, canonical.y * EXTENT];

function getTileLines(geometry, lineBBox, polyBBox, canonical: CanonicalTileID) {
function getTileLines(geometry: ?Array<Array<Point>>, lineBBox: BBox, polyBBox: Array<number>, canonical: CanonicalTileID) {
const worldSize = Math.pow(2, canonical.z) * EXTENT;
const shifts = [canonical.x * EXTENT, canonical.y * EXTENT];
const tileLines = [];
const tileLines: Array<Array<GeoJSONPosition>> = [];
if (!geometry) return tileLines;

@@ -225,3 +154,3 @@ for (const line of geometry) {

for (const point of line) {
const p = [point.x + shifts[0], point.y + shifts[1]];
const p: GeoJSONPosition = [point.x + shifts[0], point.y + shifts[1]];
updateBBox(lineBBox, p);

@@ -228,0 +157,0 @@ tileLine.push(p);

// @flow
import {Color} from './values.js';
import type {Expression} from './expression.js';

@@ -22,6 +23,7 @@ import type Point from '@mapbox/point-geometry';

featureDistanceData: ?FeatureDistanceData;
options: ?Map<string, Expression>;
_parseColorCache: {[_: string]: ?Color};
constructor() {
constructor(options?: ?Map<string, Expression>) {
this.globals = (null: any);

@@ -36,2 +38,3 @@ this.feature = null;

this.featureDistanceData = null;
this.options = options;
}

@@ -59,2 +62,6 @@

measureLight(_: string): number {
return this.globals.brightness || 0;
}
distanceFromCenter(): number {

@@ -90,4 +97,8 @@ if (this.featureTileCoord && this.featureDistanceData) {

}
getConfig(id: string): ?Expression {
return this.options ? this.options.get(id) : null;
}
}
export default EvaluationContext;

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

import {success, error} from '../util/result.js';
import {supportsPropertyExpression, supportsZoomExpression, supportsInterpolation} from '../util/properties.js';
import {
supportsPropertyExpression,
supportsZoomExpression,
supportsLightExpression,
supportsInterpolation
} from '../util/properties.js';

@@ -33,13 +38,13 @@ import type {Type, EvaluationKind} from './types.js';

export type Feature = {
+type: 1 | 2 | 3 | 'Unknown' | 'Point' | 'LineString' | 'Polygon',
+id?: number | null,
+properties: {[_: string]: any},
+patterns?: {[_: string]: string},
+geometry?: Array<Array<Point>>
};
export interface Feature {
+type: 1 | 2 | 3 | 'Unknown' | 'Point' | 'LineString' | 'Polygon';
+id?: number | null;
+properties: {[_: string]: any};
+patterns?: {[_: string]: string};
+geometry?: Array<Array<Point>>;
}
export type FeatureState = {[_: string]: any};
export type GlobalProperties = $ReadOnly<{
export interface GlobalProperties {
zoom: number,

@@ -49,6 +54,8 @@ pitch?: number,

lineProgress?: number,
rasterValue?: number,
skyRadialProgress?: number,
isSupportedScript?: (_: string) => boolean,
accumulated?: Value
}>;
+isSupportedScript?: (_: string) => boolean,
accumulated?: Value,
brightness?: number
}

@@ -63,6 +70,6 @@ export class StyleExpression {

constructor(expression: Expression, propertySpec: ?StylePropertySpecification) {
constructor(expression: Expression, propertySpec: ?StylePropertySpecification, options?: ?Map<string, Expression>) {
this.expression = expression;
this._warningHistory = {};
this._evaluator = new EvaluationContext();
this._evaluator = new EvaluationContext(options);
this._defaultValue = propertySpec ? getDefaultValue(propertySpec) : null;

@@ -131,4 +138,4 @@ this._enumValues = propertySpec && propertySpec.type === 'enum' ? propertySpec.values : null;

*/
export function createExpression(expression: mixed, propertySpec: ?StylePropertySpecification): Result<StyleExpression, Array<ParsingError>> {
const parser = new ParsingContext(definitions, [], propertySpec ? getExpectedType(propertySpec) : undefined);
export function createExpression(expression: mixed, propertySpec: ?StylePropertySpecification, options?: ?Map<string, Expression>): Result<StyleExpression, Array<ParsingError>> {
const parser = new ParsingContext(definitions, [], propertySpec ? getExpectedType(propertySpec) : undefined, undefined, undefined, options);

@@ -144,3 +151,3 @@ // For string-valued properties, coerce to string at the top level rather than asserting.

return success(new StyleExpression(parsed, propertySpec));
return success(new StyleExpression(parsed, propertySpec, options));
}

@@ -151,8 +158,12 @@

isStateDependent: boolean;
isConfigDependent: boolean;
_styleExpression: StyleExpression;
isLightConstant: ?boolean;
constructor(kind: Kind, expression: StyleExpression) {
constructor(kind: Kind, expression: StyleExpression, isLightConstant: ?boolean) {
this.kind = kind;
this._styleExpression = expression;
this.isLightConstant = isLightConstant;
this.isStateDependent = kind !== ('constant': EvaluationKind) && !isConstant.isStateConstant(expression.expression);
this.isConfigDependent = !isConstant.isConfigConstant(expression.expression);
}

@@ -173,2 +184,4 @@

isStateDependent: boolean;
isLightConstant: ?boolean;
isConfigDependent: boolean;

@@ -178,3 +191,3 @@ _styleExpression: StyleExpression;

constructor(kind: Kind, expression: StyleExpression, zoomStops: Array<number>, interpolationType?: InterpolationType) {
constructor(kind: Kind, expression: StyleExpression, zoomStops: Array<number>, interpolationType?: InterpolationType, isLightConstant: ?boolean) {
this.kind = kind;

@@ -184,2 +197,4 @@ this.zoomStops = zoomStops;

this.isStateDependent = kind !== ('camera': EvaluationKind) && !isConstant.isStateConstant(expression.expression);
this.isLightConstant = isLightConstant;
this.isConfigDependent = !isConstant.isConfigConstant(expression.expression);
this.interpolationType = interpolationType;

@@ -205,15 +220,20 @@ }

export type ConstantExpression = {
export type ConstantExpression = interface {
kind: 'constant',
isConfigDependent: boolean,
+evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>) => any,
}
export type SourceExpression = {
export type SourceExpression = interface {
kind: 'source',
isStateDependent: boolean,
isLightConstant: ?boolean;
isConfigDependent: boolean,
+evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection) => any,
};
export type CameraExpression = {
export type CameraExpression = interface {
kind: 'camera',
isStateDependent: boolean,
isConfigDependent: boolean,
+evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>) => any,

@@ -225,10 +245,12 @@ +interpolationFactor: (input: number, lower: number, upper: number) => number,

export type CompositeExpression = {
kind: 'composite',
isStateDependent: boolean,
+evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection) => any,
+interpolationFactor: (input: number, lower: number, upper: number) => number,
zoomStops: Array<number>,
interpolationType: ?InterpolationType
};
export interface CompositeExpression {
kind: 'composite';
isStateDependent: boolean;
isLightConstant: ?boolean;
isConfigDependent: boolean;
+evaluate: (globals: GlobalProperties, feature?: Feature, featureState?: FeatureState, canonical?: CanonicalTileID, availableImages?: Array<string>, formattedSection?: FormattedSection) => any;
+interpolationFactor: (input: number, lower: number, upper: number) => number;
zoomStops: Array<number>;
interpolationType: ?InterpolationType;
}

@@ -241,4 +263,4 @@ export type StylePropertyExpression =

export function createPropertyExpression(expression: mixed, propertySpec: StylePropertySpecification): Result<StylePropertyExpression, Array<ParsingError>> {
expression = createExpression(expression, propertySpec);
export function createPropertyExpression(expression: mixed, propertySpec: StylePropertySpecification, options?: ?Map<string, Expression>): Result<StylePropertyExpression, Array<ParsingError>> {
expression = createExpression(expression, propertySpec, options);
if (expression.result === 'error') {

@@ -260,5 +282,11 @@ return expression;

const isLightConstant = isConstant.isGlobalPropertyConstant(parsed, ['measure-light']);
if (!isLightConstant && !supportsLightExpression(propertySpec)) {
return error([new ParsingError('', 'measure-light expression not supported')]);
}
const canRelaxZoomRestriction = propertySpec.expression && propertySpec.expression.relaxZoomRestriction;
const zoomCurve = findZoomCurve(parsed);
if (!zoomCurve && !isZoomConstant) {
return error([new ParsingError('', '"zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.')]);
if (!zoomCurve && !isZoomConstant && !canRelaxZoomRestriction) {
return error([new ParsingError('', '"zoom" expression may only be used as input to a top-level "step" or "interpolate" expression, or in the properties of atmosphere.')]);
} else if (zoomCurve instanceof ParsingError) {

@@ -272,4 +300,6 @@ return error([zoomCurve]);

return success(isFeatureConstant ?
(new ZoomConstantExpression('constant', expression.value): ConstantExpression) :
(new ZoomConstantExpression('source', expression.value): SourceExpression));
// $FlowFixMe[method-unbinding]
(new ZoomConstantExpression('constant', expression.value, isLightConstant): ConstantExpression) :
// $FlowFixMe[method-unbinding]
(new ZoomConstantExpression('source', expression.value, isLightConstant): SourceExpression));
}

@@ -280,4 +310,6 @@

return success(isFeatureConstant ?
(new ZoomDependentExpression('camera', expression.value, zoomCurve.labels, interpolationType): CameraExpression) :
(new ZoomDependentExpression('composite', expression.value, zoomCurve.labels, interpolationType): CompositeExpression));
// $FlowFixMe[method-unbinding]
(new ZoomDependentExpression('camera', expression.value, zoomCurve.labels, interpolationType, isLightConstant): CameraExpression) :
// $FlowFixMe[method-unbinding]
(new ZoomDependentExpression('composite', expression.value, zoomCurve.labels, interpolationType, isLightConstant): CompositeExpression));
}

@@ -317,8 +349,8 @@

export function normalizePropertyExpression<T>(value: PropertyValueSpecification<T>, specification: StylePropertySpecification): StylePropertyExpression {
export function normalizePropertyExpression<T>(value: PropertyValueSpecification<T>, specification: StylePropertySpecification, options?: ?Map<string, Expression>): StylePropertyExpression {
if (isFunction(value)) {
return (new StylePropertyFunction(value, specification): any);
} else if (isExpression(value)) {
const expression = createPropertyExpression(value, specification);
} else if (isExpression(value) || (Array.isArray(value) && value.length > 0)) {
const expression = createPropertyExpression(value, specification, options);
if (expression.result === 'error') {

@@ -337,2 +369,3 @@ // this should have been caught in validation

kind: 'constant',
isConfigDependent: false,
evaluate: () => constant

@@ -374,4 +407,2 @@ };

result = childResult;
} else if (!result && childResult) {
result = new ParsingError('', '"zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.');
} else if (result && childResult && result !== childResult) {

@@ -378,0 +409,0 @@ result = new ParsingError('', 'Only one zoom-based "step" or "interpolate" subexpression may be used in an expression.');

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

import Within from './definitions/within.js';
import Distance from './definitions/distance.js';
import type {Expression} from './expression.js';

@@ -31,2 +32,6 @@

if (e instanceof Distance) {
return false;
}
let result = true;

@@ -52,2 +57,15 @@ e.eachChild(arg => {

function isConfigConstant(e: Expression): boolean {
if (e instanceof CompoundExpression) {
if (e.name === 'config') {
return false;
}
}
let result = true;
e.eachChild(arg => {
if (result && !isConfigConstant(arg)) { result = false; }
});
return result;
}
function isGlobalPropertyConstant(e: Expression, properties: Array<string>): boolean {

@@ -62,2 +80,2 @@ if (e instanceof CompoundExpression && properties.indexOf(e.name) >= 0) { return false; }

export {isFeatureConstant, isGlobalPropertyConstant, isStateConstant};
export {isFeatureConstant, isGlobalPropertyConstant, isStateConstant, isConfigConstant};

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

import Within from './definitions/within.js';
import Distance from './definitions/distance.js';
import {isGlobalPropertyConstant, isFeatureConstant} from './is_constant.js';

@@ -30,2 +31,3 @@ import Var from './definitions/var.js';

errors: Array<ParsingError>;
options: ?Map<string, Expression>;

@@ -43,3 +45,4 @@ // The expected type of this expression. Provided only to allow Expression

scope: Scope = new Scope(),
errors: Array<ParsingError> = []
errors: Array<ParsingError> = [],
options?: ?Map<string, Expression>
) {

@@ -52,2 +55,3 @@ this.registry = registry;

this.expectedType = expectedType;
this.options = options;
}

@@ -69,3 +73,3 @@

): ?Expression {
if (index) {
if (index || expectedType) {
return this.concat(index, expectedType, bindings)._parse(expr, options);

@@ -81,3 +85,3 @@ }

function annotate(parsed, type, typeAnnotation: 'assert' | 'coerce' | 'omit') {
function annotate(parsed: Expression, type: Type, typeAnnotation: 'assert' | 'coerce' | 'omit') {
if (typeAnnotation === 'assert') {

@@ -97,9 +101,3 @@ return new Assertion(type, [parsed]);

const op = expr[0];
if (typeof op !== 'string') {
this.error(`Expression name must be a string, but found ${typeof op} instead. If you wanted a literal array, use ["literal", [...]].`, 0);
return null;
}
const Expr = this.registry[op];
const Expr = typeof expr[0] === 'string' ? this.registry[expr[0]] : undefined;
if (Expr) {

@@ -135,3 +133,3 @@ let parsed = Expr.parse(expr, this);

if (!(parsed instanceof Literal) && (parsed.type.kind !== 'resolvedImage') && isConstant(parsed)) {
const ec = new EvaluationContext();
const ec = new EvaluationContext(this.options);
try {

@@ -148,3 +146,4 @@ parsed = new Literal(parsed.type, parsed.evaluate(ec));

return this.error(`Unknown expression "${op}". If you wanted a literal array, use ["literal", [...]].`, 0);
// Try to parse as array
return Coercion.parse(['to-array', expr], this);
} else if (typeof expr === 'undefined') {

@@ -167,3 +166,3 @@ return this.error(`'undefined' value invalid. Use null instead.`);

*/
concat(index: number, expectedType?: ?Type, bindings?: Array<[string, Expression]>): ParsingContext {
concat(index: ?number, expectedType?: ?Type, bindings?: Array<[string, Expression]>): ParsingContext {
const path = typeof index === 'number' ? this.path.concat(index) : this.path;

@@ -176,3 +175,4 @@ const scope = bindings ? this.scope.concat(bindings) : this.scope;

scope,
this.errors
this.errors,
this.options
);

@@ -211,2 +211,4 @@ }

return false;
} else if (expression instanceof CompoundExpression && expression.name === 'config') {
return false;
} else if (expression instanceof CollatorExpression) {

@@ -217,4 +219,6 @@ // Although the results of a Collator expression with fixed arguments

return false;
} else if (expression instanceof Within) {
} else if (expression instanceof Within) {
return false;
} else if (expression instanceof Distance) {
return false;
}

@@ -245,3 +249,3 @@

return isFeatureConstant(expression) &&
isGlobalPropertyConstant(expression, ['zoom', 'heatmap-density', 'line-progress', 'sky-radial-progress', 'accumulated', 'is-supported-script', 'pitch', 'distance-from-center']);
isGlobalPropertyConstant(expression, ['zoom', 'heatmap-density', 'line-progress', 'raster-value', 'sky-radial-progress', 'accumulated', 'is-supported-script', 'pitch', 'distance-from-center', 'measure-light']);
}

@@ -29,3 +29,4 @@ // @flow

FormattedTypeT |
ResolvedImageTypeT
ResolvedImageTypeT |
ArrayType;

@@ -32,0 +33,0 @@ export type ArrayType = {

@@ -36,3 +36,3 @@ // @flow

return !this.sections.some(section => section.text.length !== 0 ||
(section.image && section.image.name.length !== 0));
(section.image && section.image.namePrimary.length !== 0));
}

@@ -57,3 +57,3 @@

if (section.image) {
serialized.push(["image", section.image.name]);
serialized.push(["image", section.image.namePrimary]);
continue;

@@ -60,0 +60,0 @@ }

// @flow
export type ResolvedImageOptions = {
name: string,
namePrimary: string,
nameSecondary: ?string,
available: boolean

@@ -9,7 +10,11 @@ };

export default class ResolvedImage {
name: string;
namePrimary: string;
nameSecondary: ?string;
available: boolean;
constructor(options: ResolvedImageOptions) {
this.name = options.name;
this.namePrimary = options.namePrimary;
if (options.nameSecondary) {
this.nameSecondary = options.nameSecondary;
}
this.available = options.available;

@@ -19,13 +24,19 @@ }

toString(): string {
return this.name;
if (this.nameSecondary) {
return `[${this.namePrimary},${this.nameSecondary}]`;
}
return this.namePrimary;
}
static fromString(name: string): ResolvedImage | null {
if (!name) return null; // treat empty values as no image
return new ResolvedImage({name, available: false});
static fromString(namePrimary: string, nameSecondary: ?string): ResolvedImage | null {
if (!namePrimary) return null; // treat empty values as no image
return new ResolvedImage({namePrimary, nameSecondary, available: false});
}
serialize(): Array<string> {
return ["image", this.name];
if (this.nameSecondary) {
return ["image", this.namePrimary, this.nameSecondary];
}
return ["image", this.namePrimary];
}
}

@@ -32,2 +32,27 @@ // @flow

export function validateHSLA(h: mixed, s: mixed, l: mixed, a?: mixed): string | null {
if (!(
typeof h === 'number' && h >= 0 && h <= 360
)) {
const value = typeof a === 'number' ? [h, s, l, a] : [h, s, l];
return `Invalid hsla value [${value.join(', ')}]: 'h' must be between 0 and 360.`;
}
if (!(
typeof s === 'number' && s >= 0 && s <= 100 &&
typeof l === 'number' && l >= 0 && l <= 100
)) {
const value = typeof a === 'number' ? [h, s, l, a] : [h, s, l];
return `Invalid hsla value [${value.join(', ')}]: 's', and 'l' must be between 0 and 100.`;
}
if (!(
typeof a === 'undefined' || (typeof a === 'number' && a >= 0 && a <= 1)
)) {
return `Invalid hsla value [${[h, s, l, a].join(', ')}]: 'a' must be between 0 and 1.`;
}
return null;
}
export type Value = null | string | boolean | number | Color | Collator | Formatted | ResolvedImage | $ReadOnlyArray<Value> | { +[string]: Value }

@@ -34,0 +59,0 @@

@@ -164,3 +164,3 @@ // @flow

function convertInOp(property: string, values: Array<any>, negate = false) {
function convertInOp(property: string, values: Array<any>, negate: boolean = false) {
if (values.length === 0) return negate;

@@ -167,0 +167,0 @@

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

if (!isExpressionFilter(filter)) {
// $FlowFixMe[incompatible-call]
filter = convertFilter(filter);

@@ -257,9 +258,9 @@ }

// Comparison function to sort numbers and strings
function compare(a, b) {
function compare(a: number, b: number) {
return a < b ? -1 : a > b ? 1 : 0;
}
function geometryNeeded(filter) {
function geometryNeeded(filter: Array<any> | boolean) {
if (!Array.isArray(filter)) return false;
if (filter[0] === 'within') return true;
if (filter[0] === 'within' || filter[0] === 'distance') return true;
for (let index = 1; index < filter.length; index++) {

@@ -289,3 +290,2 @@ if (geometryNeeded(filter[index])) return true;

op === '!has' ? convertNegation(convertHasOp(filter[1])) :
op === 'within' ? filter :
true;

@@ -292,0 +292,0 @@ return converted;

// @flow strict
type GeoJSONPosition = [number, number] | [number, number, number];
type Geometry<T, C> = { type: T, coordinates: C }
declare module "@mapbox/geojson-types" {
declare export type GeoJSONPosition = [number, number] | [number, number, number];
declare export type GeoJSONPoint = Geometry<'Point', GeoJSONPosition>;

@@ -30,8 +31,8 @@ declare export type GeoJSONMultiPoint = Geometry<'MultiPoint', Array<GeoJSONPosition>>;

declare export type GeoJSONFeature = {
type: 'Feature',
geometry: ?GeoJSONGeometry,
properties: ?{},
id?: number | string
};
declare export interface GeoJSONFeature {
type: 'Feature';
geometry: ?GeoJSONGeometry;
properties: ?{};
id?: number | string;
}

@@ -38,0 +39,0 @@ declare export type GeoJSONFeatureCollection = {

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

exactEquals(Vec3, Vec3): boolean,
clone<T: Vec3>(T): T,

@@ -76,2 +75,3 @@ normalize<T: Vec3>(T, Vec3): T,

adjoint<T: Mat3>(T, Mat3): T,
invert<T: Mat3>(T, Mat3): T,
transpose<T: Mat3>(T, Mat3): T

@@ -99,2 +99,3 @@ };

invert<T: Mat4>(T, Mat4): T,
transpose<T: Mat4>(T, Mat4): T,
copy<T: Mat4>(T, Mat4): T,

@@ -114,4 +115,5 @@ clone<T: Mat4>(T): T

rotateY<T: Quat>(T, Quat, number): T,
rotateZ<T: Quat>(T, Quat, number): T
rotateZ<T: Quat>(T, Quat, number): T,
rotationTo<T:Quat>(T, Quat, Quat): T
}
}

@@ -8,7 +8,18 @@ // @flow

function convertLiteral(value) {
type Stop = [{zoom: number, value: string | number | boolean}, mixed];
type FunctionParameters = {
stops: Array<Stop>;
base: number;
property: string;
type: 'identity' | 'exponential' | 'interval' | 'categorical';
colorSpace: 'rgb' | 'lab' | 'hcl';
default: mixed;
};
function convertLiteral(value: mixed) {
return typeof value === 'object' ? ['literal', value] : value;
}
export default function convertFunction(parameters: any, propertySpec: StylePropertySpecification): ExpressionSpecification {
export default function convertFunction(parameters: FunctionParameters, propertySpec: StylePropertySpecification): ExpressionSpecification {
let stops = parameters.stops;

@@ -40,3 +51,3 @@ if (!stops) {

function convertIdentityFunction(parameters, propertySpec): Array<mixed> {
function convertIdentityFunction(parameters: FunctionParameters, propertySpec: StylePropertySpecification): Array<mixed> {
const get = ['get', parameters.property];

@@ -65,3 +76,3 @@

function getInterpolateOperator(parameters) {
function getInterpolateOperator(parameters: FunctionParameters) {
switch (parameters.colorSpace) {

@@ -74,3 +85,3 @@ case 'hcl': return 'interpolate-hcl';

function convertZoomAndPropertyFunction(parameters, propertySpec, stops) {
function convertZoomAndPropertyFunction(parameters: FunctionParameters, propertySpec: StylePropertySpecification, stops: Array<Stop>) {
const featureFunctionParameters = {};

@@ -123,3 +134,3 @@ const featureFunctionStops = {};

function coalesce(a, b) {
function coalesce(a: mixed, b: mixed) {
if (a !== undefined) return a;

@@ -129,3 +140,3 @@ if (b !== undefined) return b;

function getFallback(parameters, propertySpec) {
function getFallback(parameters: FunctionParameters, propertySpec: StylePropertySpecification) {
const defaultValue = convertLiteral(coalesce(parameters.default, propertySpec.default));

@@ -145,3 +156,3 @@

function convertPropertyFunction(parameters, propertySpec, stops) {
function convertPropertyFunction(parameters: FunctionParameters, propertySpec: StylePropertySpecification, stops: Array<Stop>) {
const type = getFunctionType(parameters, propertySpec);

@@ -199,3 +210,3 @@ const get = ['get', parameters.property];

function convertZoomFunction(parameters, propertySpec, stops, input = ['zoom']) {
function convertZoomFunction(parameters: FunctionParameters, propertySpec: StylePropertySpecification, stops: Array<Stop>, input: Array<string> = ['zoom']) {
const type = getFunctionType(parameters, propertySpec);

@@ -224,3 +235,3 @@ let expression;

function fixupDegenerateStepCurve(expression) {
function fixupDegenerateStepCurve(expression: ExpressionSpecification) {
// degenerate step curve (i.e. a constant function): add a noop stop

@@ -233,3 +244,3 @@ if (expression[0] === 'step' && expression.length === 3) {

function appendStopPair(curve, input, output, isStep) {
function appendStopPair(curve: ExpressionSpecification, input: mixed, output: mixed, isStep: boolean) {
// Skip duplicate stop values. They were not validated for functions, but they are for expressions.

@@ -247,3 +258,3 @@ // https://github.com/mapbox/mapbox-gl-js/issues/4107

function getFunctionType(parameters, propertySpec) {
function getFunctionType(parameters: FunctionParameters, propertySpec: StylePropertySpecification): string {
if (parameters.type) {

@@ -250,0 +261,0 @@ return parameters.type;

@@ -7,3 +7,3 @@ // @flow

function stringify(obj) {
function stringify(obj: any) {
if (typeof obj === 'number' || typeof obj === 'boolean' || typeof obj === 'string' || obj === undefined || obj === null)

@@ -27,3 +27,3 @@ return JSON.stringify(obj);

function getKey(layer) {
function getKey(layer: LayerSpecification) {
let key = '';

@@ -30,0 +30,0 @@ for (const k of refProperties) {

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

if (typeof value === 'object' && !Array.isArray(value)) {
// $FlowFixMe[prop-missing]
// $FlowFixMe[incompatible-call]
// $FlowFixMe[incompatible-variance]
set(convertFunction(value, reference));

@@ -32,0 +35,0 @@ converted.push(path.join('.'));

{
"name": "@mapbox/mapbox-gl-style-spec",
"description": "a specification for mapbox gl styles",
"version": "13.28.0",
"version": "14.0.0-beta.1",
"author": "Mapbox",

@@ -50,5 +50,8 @@ "keywords": [

"rw": "^1.3.3",
"sort-object": "^0.3.2"
"sort-object": "^0.3.2",
"quickselect": "^2.0.0",
"tinyqueue": "^2.0.3",
"cheap-ruler": "^3.0.1"
},
"sideEffects": false
}
// @flow
type ExpressionType = 'data-driven' | 'color-ramp' | 'data-constant' | 'constant';
type ExpressionParameters = Array<'zoom' | 'feature' | 'feature-state' | 'heatmap-density' | 'line-progress' | 'sky-radial-progress' | 'pitch' | 'distance-from-center'>;
type ExpressionParameters = Array<'zoom' | 'feature' | 'feature-state' | 'heatmap-density' | 'line-progress' | 'raster-value' | 'sky-radial-progress' | 'pitch' | 'distance-from-center'>;
type ExpressionSpecification = {
export type ExpressionSpecification = {
interpolated: boolean,
parameters: ExpressionParameters
parameters: ExpressionParameters,
relaxZoomRestriction: boolean
}

@@ -10,0 +11,0 @@

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

"version": 8,
"fragment"?: boolean,
"name"?: string,

@@ -70,5 +71,9 @@ "metadata"?: mixed,

"light"?: LightSpecification,
"lights"?: Array<LightsSpecification>,
"terrain"?: TerrainSpecification,
"fog"?: FogSpecification,
"sources": {[_: string]: SourceSpecification},
"camera"?: CameraSpecification,
"imports"?: Array<ImportSpecification>,
"schema"?: SchemaSpecification,
"sources": SourcesSpecification,
"sprite"?: string,

@@ -78,5 +83,14 @@ "glyphs"?: string,

"projection"?: ProjectionSpecification,
"layers": Array<LayerSpecification>
"layers": Array<LayerSpecification>,
"models"?: ModelsSpecification
|}
export type SourcesSpecification = {
[_: string]: SourceSpecification
}
export type ModelsSpecification = {
[_: string]: ModelSpecification
}
export type LightSpecification = {|

@@ -103,2 +117,6 @@ "anchor"?: PropertyValueSpecification<"map" | "viewport">,

export type CameraSpecification = {|
"camera-projection"?: PropertyValueSpecification<"perspective" | "orthographic">
|}
export type ProjectionSpecification = {|

@@ -110,2 +128,28 @@ "name": "albers" | "equalEarth" | "equirectangular" | "lambertConformalConic" | "mercator" | "naturalEarth" | "winkelTripel" | "globe",

export type ImportSpecification = {|
"id": string,
"url": string,
"config"?: ConfigSpecification,
"data"?: StyleSpecification
|}
export type ConfigSpecification = {
[_: string]: mixed
}
export type SchemaSpecification = {
[_: string]: OptionSpecification
}
export type OptionSpecification = {|
"default": mixed,
"type"?: "string" | "number" | "boolean" | "color",
"array"?: boolean,
"minValue"?: number,
"maxValue"?: number,
"stepValue"?: number,
"values"?: Array<mixed>,
"metadata"?: mixed
|}
export type VectorSourceSpecification = {

@@ -121,3 +165,4 @@ "type": "vector",

"promoteId"?: PromoteIdSpecification,
"volatile"?: boolean
"volatile"?: boolean,
[_: string]: mixed
}

@@ -135,3 +180,4 @@

"attribution"?: string,
"volatile"?: boolean
"volatile"?: boolean,
[_: string]: mixed
}

@@ -149,3 +195,4 @@

"encoding"?: "terrarium" | "mapbox",
"volatile"?: boolean
"volatile"?: boolean,
[_: string]: mixed
}

@@ -183,2 +230,9 @@

export type ModelSourceSpecification = {|
"type": "model" | "batched-model",
"maxzoom"?: number,
"minzoom"?: number,
"tiles"?: Array<string>
|}
export type SourceSpecification =

@@ -191,3 +245,43 @@ | VectorSourceSpecification

| ImageSourceSpecification
| ModelSourceSpecification
export type ModelSpecification = string;
export type AmbientLightSpecification = {|
"id": string,
"properties"?: {|
"color"?: PropertyValueSpecification<ColorSpecification>,
"intensity"?: PropertyValueSpecification<number>
|},
"type": "ambient"
|}
export type DirectionalLightSpecification = {|
"id": string,
"properties"?: {|
"direction"?: PropertyValueSpecification<[number, number]>,
"color"?: PropertyValueSpecification<ColorSpecification>,
"intensity"?: PropertyValueSpecification<number>,
"cast-shadows"?: ExpressionSpecification,
"shadow-intensity"?: PropertyValueSpecification<number>
|},
"type": "directional"
|}
export type FlatLightSpecification = {|
"id": string,
"properties"?: {|
"anchor"?: PropertyValueSpecification<"map" | "viewport">,
"position"?: PropertyValueSpecification<[number, number, number]>,
"color"?: PropertyValueSpecification<ColorSpecification>,
"intensity"?: PropertyValueSpecification<number>
|},
"type": "flat"
|}
export type LightsSpecification =
| AmbientLightSpecification
| DirectionalLightSpecification
| FlatLightSpecification;
export type FillLayerSpecification = {|

@@ -199,2 +293,3 @@ "id": string,

"source-layer"?: string,
"slot"?: string,
"minzoom"?: number,

@@ -205,3 +300,3 @@ "maxzoom"?: number,

"fill-sort-key"?: DataDrivenPropertyValueSpecification<number>,
"visibility"?: "visible" | "none"
"visibility"?: ExpressionSpecification
|},

@@ -215,3 +310,4 @@ "paint"?: {|

"fill-translate-anchor"?: PropertyValueSpecification<"map" | "viewport">,
"fill-pattern"?: DataDrivenPropertyValueSpecification<ResolvedImageSpecification>
"fill-pattern"?: DataDrivenPropertyValueSpecification<ResolvedImageSpecification>,
"fill-emissive-strength"?: PropertyValueSpecification<number>
|}

@@ -226,2 +322,3 @@ |}

"source-layer"?: string,
"slot"?: string,
"minzoom"?: number,

@@ -236,3 +333,3 @@ "maxzoom"?: number,

"line-sort-key"?: DataDrivenPropertyValueSpecification<number>,
"visibility"?: "visible" | "none"
"visibility"?: ExpressionSpecification
|},

@@ -251,3 +348,6 @@ "paint"?: {|

"line-gradient"?: ExpressionSpecification,
"line-trim-offset"?: [number, number]
"line-trim-offset"?: [number, number],
"line-emissive-strength"?: PropertyValueSpecification<number>,
"line-border-width"?: DataDrivenPropertyValueSpecification<number>,
"line-border-color"?: DataDrivenPropertyValueSpecification<ColorSpecification>
|}

@@ -262,2 +362,3 @@ |}

"source-layer"?: string,
"slot"?: string,
"minzoom"?: number,

@@ -277,4 +378,4 @@ "maxzoom"?: number,

"icon-size"?: DataDrivenPropertyValueSpecification<number>,
"icon-text-fit"?: PropertyValueSpecification<"none" | "width" | "height" | "both">,
"icon-text-fit-padding"?: PropertyValueSpecification<[number, number, number, number]>,
"icon-text-fit"?: DataDrivenPropertyValueSpecification<"none" | "width" | "height" | "both">,
"icon-text-fit-padding"?: DataDrivenPropertyValueSpecification<[number, number, number, number]>,
"icon-image"?: DataDrivenPropertyValueSpecification<ResolvedImageSpecification>,

@@ -309,6 +410,8 @@ "icon-rotate"?: DataDrivenPropertyValueSpecification<number>,

"text-optional"?: PropertyValueSpecification<boolean>,
"visibility"?: "visible" | "none"
"visibility"?: ExpressionSpecification
|},
"paint"?: {|
"icon-opacity"?: DataDrivenPropertyValueSpecification<number>,
"icon-emissive-strength"?: DataDrivenPropertyValueSpecification<number>,
"text-emissive-strength"?: DataDrivenPropertyValueSpecification<number>,
"icon-color"?: DataDrivenPropertyValueSpecification<ColorSpecification>,

@@ -320,2 +423,3 @@ "icon-halo-color"?: DataDrivenPropertyValueSpecification<ColorSpecification>,

"icon-translate-anchor"?: PropertyValueSpecification<"map" | "viewport">,
"icon-image-cross-fade"?: DataDrivenPropertyValueSpecification<number>,
"text-opacity"?: DataDrivenPropertyValueSpecification<number>,

@@ -337,2 +441,3 @@ "text-color"?: DataDrivenPropertyValueSpecification<ColorSpecification>,

"source-layer"?: string,
"slot"?: string,
"minzoom"?: number,

@@ -343,3 +448,3 @@ "maxzoom"?: number,

"circle-sort-key"?: DataDrivenPropertyValueSpecification<number>,
"visibility"?: "visible" | "none"
"visibility"?: ExpressionSpecification
|},

@@ -357,3 +462,4 @@ "paint"?: {|

"circle-stroke-color"?: DataDrivenPropertyValueSpecification<ColorSpecification>,
"circle-stroke-opacity"?: DataDrivenPropertyValueSpecification<number>
"circle-stroke-opacity"?: DataDrivenPropertyValueSpecification<number>,
"circle-emissive-strength"?: PropertyValueSpecification<number>
|}

@@ -368,2 +474,3 @@ |}

"source-layer"?: string,
"slot"?: string,
"minzoom"?: number,

@@ -373,3 +480,3 @@ "maxzoom"?: number,

"layout"?: {|
"visibility"?: "visible" | "none"
"visibility"?: ExpressionSpecification
|},

@@ -391,2 +498,3 @@ "paint"?: {|

"source-layer"?: string,
"slot"?: string,
"minzoom"?: number,

@@ -396,4 +504,4 @@ "maxzoom"?: number,

"layout"?: {|
"visibility"?: "visible" | "none",
"fill-extrusion-edge-radius"?: number
"visibility"?: ExpressionSpecification,
"fill-extrusion-edge-radius"?: ExpressionSpecification
|},

@@ -410,3 +518,13 @@ "paint"?: {|

"fill-extrusion-ambient-occlusion-intensity"?: PropertyValueSpecification<number>,
"fill-extrusion-ambient-occlusion-radius"?: PropertyValueSpecification<number>
"fill-extrusion-ambient-occlusion-radius"?: PropertyValueSpecification<number>,
"fill-extrusion-ambient-occlusion-wall-radius"?: PropertyValueSpecification<number>,
"fill-extrusion-ambient-occlusion-ground-radius"?: PropertyValueSpecification<number>,
"fill-extrusion-ambient-occlusion-ground-attenuation"?: PropertyValueSpecification<number>,
"fill-extrusion-flood-light-color"?: PropertyValueSpecification<ColorSpecification>,
"fill-extrusion-flood-light-intensity"?: PropertyValueSpecification<number>,
"fill-extrusion-flood-light-wall-radius"?: DataDrivenPropertyValueSpecification<number>,
"fill-extrusion-flood-light-ground-radius"?: DataDrivenPropertyValueSpecification<number>,
"fill-extrusion-flood-light-ground-attenuation"?: PropertyValueSpecification<number>,
"fill-extrusion-vertical-scale"?: PropertyValueSpecification<number>,
"fill-extrusion-rounded-roof"?: PropertyValueSpecification<boolean>
|}

@@ -421,2 +539,3 @@ |}

"source-layer"?: string,
"slot"?: string,
"minzoom"?: number,

@@ -426,6 +545,9 @@ "maxzoom"?: number,

"layout"?: {|
"visibility"?: "visible" | "none"
"visibility"?: ExpressionSpecification
|},
"paint"?: {|
"raster-opacity"?: PropertyValueSpecification<number>,
"raster-color"?: ExpressionSpecification,
"raster-color-mix"?: PropertyValueSpecification<[number, number, number, number]>,
"raster-color-range"?: PropertyValueSpecification<[number, number]>,
"raster-hue-rotate"?: PropertyValueSpecification<number>,

@@ -447,2 +569,3 @@ "raster-brightness-min"?: PropertyValueSpecification<number>,

"source-layer"?: string,
"slot"?: string,
"minzoom"?: number,

@@ -452,3 +575,3 @@ "maxzoom"?: number,

"layout"?: {|
"visibility"?: "visible" | "none"
"visibility"?: ExpressionSpecification
|},

@@ -465,2 +588,33 @@ "paint"?: {|

export type ModelLayerSpecification = {|
"id": string,
"type": "model",
"metadata"?: mixed,
"source": string,
"source-layer"?: string,
"slot"?: string,
"minzoom"?: number,
"maxzoom"?: number,
"filter"?: FilterSpecification,
"layout"?: {|
"visibility"?: ExpressionSpecification,
"model-id"?: DataDrivenPropertyValueSpecification<string>
|},
"paint"?: {|
"model-opacity"?: PropertyValueSpecification<number>,
"model-rotation"?: DataDrivenPropertyValueSpecification<[number, number, number]>,
"model-scale"?: DataDrivenPropertyValueSpecification<[number, number, number]>,
"model-translation"?: DataDrivenPropertyValueSpecification<[number, number, number]>,
"model-color"?: DataDrivenPropertyValueSpecification<ColorSpecification>,
"model-color-mix-intensity"?: DataDrivenPropertyValueSpecification<number>,
"model-type"?: "common-3d" | "location-indicator",
"model-cast-shadows"?: ExpressionSpecification,
"model-receive-shadows"?: ExpressionSpecification,
"model-ambient-occlusion-intensity"?: PropertyValueSpecification<number>,
"model-emissive-strength"?: DataDrivenPropertyValueSpecification<number>,
"model-roughness"?: DataDrivenPropertyValueSpecification<number>,
"model-height-based-emissive-strength-multiplier"?: DataDrivenPropertyValueSpecification<[number, number, number, number, number]>
|}
|}
export type BackgroundLayerSpecification = {|

@@ -470,6 +624,7 @@ "id": string,

"metadata"?: mixed,
"slot"?: string,
"minzoom"?: number,
"maxzoom"?: number,
"layout"?: {|
"visibility"?: "visible" | "none"
"visibility"?: ExpressionSpecification
|},

@@ -479,3 +634,4 @@ "paint"?: {|

"background-pattern"?: PropertyValueSpecification<ResolvedImageSpecification>,
"background-opacity"?: PropertyValueSpecification<number>
"background-opacity"?: PropertyValueSpecification<number>,
"background-emissive-strength"?: PropertyValueSpecification<number>
|}

@@ -488,6 +644,7 @@ |}

"metadata"?: mixed,
"slot"?: string,
"minzoom"?: number,
"maxzoom"?: number,
"layout"?: {|
"visibility"?: "visible" | "none"
"visibility"?: ExpressionSpecification
|},

@@ -507,2 +664,9 @@ "paint"?: {|

export type SlotLayerSpecification = {|
"id": string,
"type": "slot",
"metadata"?: mixed,
"slot"?: string
|}
export type LayerSpecification =

@@ -517,4 +681,6 @@ | FillLayerSpecification

| HillshadeLayerSpecification
| ModelLayerSpecification
| BackgroundLayerSpecification
| SkyLayerSpecification;
| SkyLayerSpecification
| SlotLayerSpecification;

@@ -111,2 +111,17 @@ // @flow

/**
* Returns an RGB array of values representing the color, unpremultiplied by A and multiplied by a scalar.
*
* @param {number} scale A scale to apply to the unpremultiplied-alpha values.
* @returns An array of RGB color values in the range [0, 1].
*/
toArray01Scaled(scale: number): [number, number, number] {
const {r, g, b, a} = this;
return a === 0 ? [0, 0, 0] : [
(r / a) * scale,
(g / a) * scale,
(b / a) * scale
];
}
/**
* Returns an RGBA array of values representing the color, premultiplied by A.

@@ -125,2 +140,18 @@ *

}
/**
* Returns an RGBA array of values representing the color, unpremultiplied by A, and converted to linear color space.
* The color is defined by sRGB primaries, but the sRGB transfer function is reversed to obtain linear energy.
*
* @returns An array of RGBA color values in the range [0, 1].
*/
toArray01Linear(): [number, number, number, number] {
const {r, g, b, a} = this;
return a === 0 ? [0, 0, 0, 0] : [
Math.pow((r / a), 2.2),
Math.pow((g / a), 2.2),
Math.pow((b / a), 2.2),
a
];
}
}

@@ -127,0 +158,0 @@

// @flow
import type {StylePropertySpecification} from '../style-spec.js';
import type {ExpressionSpecification, StylePropertySpecification} from '../style-spec.js';
function expressionHasParameter(expression: ?ExpressionSpecification, parameter: string): boolean {
return !!expression && !!expression.parameters && expression.parameters.indexOf(parameter) > -1;
}
export function supportsPropertyExpression(spec: StylePropertySpecification): boolean {

@@ -9,4 +13,8 @@ return spec['property-type'] === 'data-driven';

export function supportsLightExpression(spec: StylePropertySpecification): boolean {
return expressionHasParameter(spec.expression, 'measure-light');
}
export function supportsZoomExpression(spec: StylePropertySpecification): boolean {
return !!spec.expression && spec.expression.parameters.indexOf('zoom') > -1;
return expressionHasParameter(spec.expression, 'zoom');
}

@@ -13,0 +21,0 @@

@@ -39,3 +39,3 @@ // @flow

const acceptedSourceTypes = new Set(["vector", "raster", "raster-dem"]);
const acceptedSourceTypes = new Set(["vector", "raster", "raster-dem", "model", "batched-model"]);
function getSourceErrors(source: Object, i: number): Array<ValidationError> {

@@ -72,6 +72,14 @@ const errors = [];

function getSourcesErrors(sources: Object): Array<ValidationError> {
function getMaxSourcesErrors(sourcesCount: number): Array<ValidationError> {
const errors = [];
let count = 0;
if (sourcesCount > MAX_SOURCES_IN_STYLE) {
errors.push(new ValidationError('sources', null, `Styles must contain ${MAX_SOURCES_IN_STYLE} or fewer sources`));
}
return errors;
}
function getSourcesErrors(sources: Object): {errors: Array<ValidationError>, sourcesCount: number} {
const errors = [];
let sourcesCount = 0;
Object.keys(sources).forEach((s: string, i: number) => {

@@ -82,3 +90,3 @@ const sourceErrors = getSourceErrors(sources[s], i);

if (!sourceErrors.length) {
count = count + getSourceCount(sources[s]);
sourcesCount = sourcesCount + getSourceCount(sources[s]);
}

@@ -89,7 +97,31 @@

if (count > MAX_SOURCES_IN_STYLE) {
errors.push(new ValidationError('sources', null, `Styles must contain ${MAX_SOURCES_IN_STYLE} or fewer sources`));
}
return {errors, sourcesCount};
}
return errors;
function getImportErrors(imports: Array<Object> = []): {errors: Array<ValidationError>, sourcesCount: number} {
let errors: Array<ValidationError> = [];
let sourcesCount = 0;
const validateImports = (imports: Array<Object> = []) => {
for (const importSpec of imports) {
const style = importSpec.data;
if (!style) continue;
if (style.imports) {
validateImports(style.imports);
}
errors = errors.concat(getRootErrors(style, Object.keys(v8.$root)));
if (style.sources) {
const sourcesErrors = getSourcesErrors(style.sources);
sourcesCount += sourcesErrors.sourcesCount;
errors = errors.concat(sourcesErrors.errors);
}
}
};
validateImports(imports);
return {errors, sourcesCount};
}

@@ -116,3 +148,5 @@

'visibility',
'protected'
'protected',
'models',
'lights'
];

@@ -186,8 +220,18 @@

let sourcesCount = 0;
if (s.sources) {
errors = errors.concat(getSourcesErrors(s.sources));
const sourcesErrors = getSourcesErrors(s.sources);
sourcesCount += sourcesErrors.sourcesCount;
errors = errors.concat(sourcesErrors.errors);
}
if (s.imports) {
const importsErrors = getImportErrors(s.imports);
sourcesCount += importsErrors.sourcesCount;
errors = errors.concat(importsErrors.errors);
}
errors = errors.concat(getMaxSourcesErrors(sourcesCount));
return errors;
}

@@ -39,7 +39,11 @@ // @flow

validateSource as source,
validateModel as model,
validateLight as light,
validateLayer as layer,
validateFilter as filter,
validateLights as lights,
validateTerrain as terrain,
validateFog as fog,
validatePaintProperty as paintProperty,
validateLayoutProperty as layoutProperty
} from './validate_style.min.js';
// @flow
import validate from './validate/validate.js';
import latestStyleSpec from './reference/latest.js';
import validateGlyphsURL from './validate/validate_glyphs_url.js';
import _validateStyle from './validate/validate_style.js';
import _validateSource from './validate/validate_source.js';
import _validateLight from './validate/validate_light.js';
import _validateLights from './validate/validate_lights.js';
import _validateTerrain from './validate/validate_terrain.js';

@@ -14,9 +14,10 @@ import _validateFog from './validate/validate_fog.js';

import _validateLayoutProperty from './validate/validate_layout_property.js';
import _validateModel from './validate/validate_model.js';
import type {StyleSpecification} from './types.js';
export type ValidationError = {
message: string;
identifier?: ?string;
line?: ?number;
export type ValidationError = interface {
message: string,
identifier?: ?string,
line?: ?number,
};

@@ -42,14 +43,3 @@ export type ValidationErrors = $ReadOnlyArray<ValidationError>;

export function validateStyle(style: StyleSpecification, styleSpec: Object = latestStyleSpec): ValidationErrors {
const errors = validate({
key: '',
value: style,
valueSpec: styleSpec.$root,
styleSpec,
style,
objectElementValidators: {
glyphs: validateGlyphsURL,
'*': () => []
}
});
const errors = _validateStyle(style, styleSpec);
return sortErrors(errors);

@@ -60,2 +50,3 @@ }

export const validateLight: Validator = opts => sortErrors(_validateLight(opts));
export const validateLights: Validator = opts => sortErrors(_validateLights(opts));
export const validateTerrain: Validator = opts => sortErrors(_validateTerrain(opts));

@@ -67,5 +58,6 @@ export const validateFog: Validator = opts => sortErrors(_validateFog(opts));

export const validateLayoutProperty: Validator = opts => sortErrors(_validateLayoutProperty(opts));
export const validateModel: Validator = opts => sortErrors(_validateModel(opts));
function sortErrors(errors) {
function sortErrors(errors: ValidationErrors) {
return errors.slice().sort((a, b) => a.line && b.line ? a.line - b.line : 0);
}

@@ -59,5 +59,5 @@ // @flow

key: `${key}[${i}]`
}));
}, true));
}
return errors;
}

@@ -14,3 +14,3 @@ // @flow

type Options = ValidationOptions & {
layerType: string;
layerType?: string;
}

@@ -32,3 +32,3 @@

function validateNonExpressionFilter(options) {
function validateNonExpressionFilter(options: Options) {
const value = options.value;

@@ -101,3 +101,3 @@ const key = options.key;

for (let i = 1; i < value.length; i++) {
errors = errors.concat(validateNonExpressionFilter({
errors = errors.concat(validateNonExpressionFilter(({
key: `${key}[${i}]`,

@@ -107,3 +107,3 @@ value: value[i],

styleSpec: options.styleSpec
}));
}: any)));
}

@@ -121,12 +121,4 @@ break;

break;
case 'within':
type = getType(value[1]);
if (value.length !== 2) {
errors.push(new ValidationError(key, value, `filter array for "${value[0]}" operator must have 2 elements`));
} else if (type !== 'object') {
errors.push(new ValidationError(`${key}[1]`, value[1], `object expected, ${type} found`));
}
break;
}
return errors;
}

@@ -24,3 +24,3 @@ // @flow

let stopDomainValues: {[string | number]: boolean} = {};
let previousStopDomainValue;
let previousStopDomainValue: ?mixed;
let previousStopDomainZoom;

@@ -165,3 +165,3 @@

function validateStopDomainValue(options: ValidationOptions, stop) {
function validateStopDomainValue(options: ValidationOptions, stop: any) {
const type = getType(options.value);

@@ -168,0 +168,0 @@ const value = unbundle(options.value);

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

errors.push(new ValidationError(key, layer.ref, `ref layer "${ref}" not found`));
// $FlowFixMe[prop-missing] - ref is not defined on the LayerSpecification subtypes
} else if (parent.ref) {

@@ -67,3 +68,3 @@ errors.push(new ValidationError(key, layer.ref, 'ref cannot reference another ref layer'));

}
} else if (!(type === 'background' || type === 'sky')) {
} else if (!(type === 'background' || type === 'sky' || type === 'slot')) {
if (!layer.source) {

@@ -142,3 +143,3 @@ errors.push(new ValidationError(key, layer, 'missing required property "source"'));

'*'(options) {
return validatePaintProperty(extend({layerType: type}, options));
return validatePaintProperty(extend({layerType: type, layer}, options));
}

@@ -145,0 +146,0 @@ }

@@ -8,5 +8,7 @@ // @flow

import {unbundle, deepUnbundle} from '../util/unbundle_jsonlint.js';
import {supportsPropertyExpression} from '../util/properties.js';
import {supportsLightExpression, supportsPropertyExpression, supportsZoomExpression} from '../util/properties.js';
import {isGlobalPropertyConstant} from '../expression/is_constant.js';
import type {ValidationOptions} from './validate.js';
import {createPropertyExpression} from '../expression/index.js';

@@ -16,2 +18,3 @@ export type PropertyValidationOptions = ValidationOptions & {

layerType: string;
layer: Object;
}

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

const style = options.style;
const layer = options.layer;
const styleSpec = options.styleSpec;

@@ -46,8 +50,9 @@ const value = options.value;

let tokenMatch;
let tokenMatch: ?RegExp$matchResult;
if (getType(value) === 'string' && supportsPropertyExpression(valueSpec) && !valueSpec.tokens && (tokenMatch = /^{([^}]+)}$/.exec(value))) {
const example = `\`{ "type": "identity", "property": ${tokenMatch ? JSON.stringify(tokenMatch[1]) : '"_"'} }\``;
return [new ValidationError(
key, value,
`"${propertyKey}" does not support interpolation syntax\n` +
`Use an identity property function instead: \`{ "type": "identity", "property": ${JSON.stringify(tokenMatch[1])} }\`.`)];
`Use an identity property function instead: ${example}.`)];
}

@@ -64,2 +69,11 @@

}
} else if (options.layerType === 'model' && propertyType === 'paint' && layer && layer.layout && layer.layout.hasOwnProperty('model-id')) {
if (supportsPropertyExpression(valueSpec) && (supportsLightExpression(valueSpec) || supportsZoomExpression(valueSpec))) {
// Performance related style spec limitation: zoom and light expressions are not allowed for e.g. trees.
const expression = createPropertyExpression(deepUnbundle(value), valueSpec);
const expressionObj = (expression.value: any).expression || (expression.value: any)._styleExpression.expression;
if (expressionObj && (!isGlobalPropertyConstant(expressionObj, ['zoom']) || !isGlobalPropertyConstant(expressionObj, ['measure-light']))) {
errors.push(new ValidationError(key, value, `${propertyKey} does not support zoom or measure-light expressions when the model layer source is vector tile or GeoJSON.`));
}
}
}

@@ -66,0 +80,0 @@

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

import type {StyleReference} from '../reference/latest.js';
import type {ValidationOptions} from './validate.js';

@@ -105,3 +106,3 @@

function getSourceTypeValues(styleSpec) {
function getSourceTypeValues(styleSpec: StyleReference) {
return styleSpec.source.reduce((memo, source) => {

@@ -116,3 +117,3 @@ const sourceType = styleSpec[source];

function validatePromoteId({key, value}) {
function validatePromoteId({key, value}: $Shape<ValidationOptions>) {
if (getType(value) === 'string') {

@@ -119,0 +120,0 @@ return validateString({key, value});

// @flow
import extend from '../util/extend.js';
import ValidationError from '../error/validation_error.js';
import {unbundle, deepUnbundle} from '../util/unbundle_jsonlint.js';

@@ -8,2 +9,3 @@ import {isExpression} from '../expression/index.js';

import validateImport from './validate_import.js';
import validateFunction from './validate_function.js';

@@ -20,3 +22,5 @@ import validateExpression from './validate_expression.js';

import validateSource from './validate_source.js';
import validateModel from './validate_model.js';
import validateLight from './validate_light.js';
import validateLights from './validate_lights.js';
import validateTerrain from './validate_terrain.js';

@@ -31,3 +35,3 @@ import validateFog from './validate_fog.js';

import type {StyleSpecification} from '../types.js';
import type ValidationError from '../error/validation_error.js';
import getType from '../util/get_type.js';

@@ -48,3 +52,5 @@ const VALIDATORS = {

'source': validateSource,
'model': validateModel,
'light': validateLight,
'light-3d': validateLights,
'terrain': validateTerrain,

@@ -55,3 +61,4 @@ 'fog': validateFog,

'resolvedImage': validateImage,
'projection': validateProjection
'projection': validateProjection,
'import': validateImport
};

@@ -76,3 +83,3 @@

export default function validate(options: ValidationOptions): Array<ValidationError> {
export default function validate(options: ValidationOptions, arrayAsExpression: boolean = false): Array<ValidationError> {
const value = options.value;

@@ -84,9 +91,12 @@ const valueSpec = options.valueSpec;

return validateFunction(options);
} else if (valueSpec.expression && isExpression(deepUnbundle(value))) {
return validateExpression(options);
} else if (valueSpec.type && VALIDATORS[valueSpec.type]) {
return VALIDATORS[valueSpec.type](options);
const valid = VALIDATORS[valueSpec.type](options);
if (arrayAsExpression === true && valid.length > 0 && getType(options.value) === "array") {
// Try to validate as an expression
return validateExpression(options);
} else {
return valid;
}
} else {

@@ -93,0 +103,0 @@ const valid = validateObject(extend({}, options, {

@@ -13,3 +13,3 @@ // @flow

function getPropertyReference(propertyName): StylePropertySpecification {
function getPropertyReference(propertyName: string): StylePropertySpecification {
for (let i = 0; i < Reference.layout.length; i++) {

@@ -54,3 +54,4 @@ for (const key in Reference[Reference.layout[i]]) {

) {
function inner(layer, propertyType: 'paint' | 'layout') {
function inner(layer: LayerSpecification, propertyType: 'paint' | 'layout') {
if (layer.type === 'slot') return;
const properties = (layer[propertyType]: any);

@@ -57,0 +58,0 @@ if (!properties) return;

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