diff --git a/build/generate-style-spec.ts b/build/generate-style-spec.ts index 303d8de49..d0e3170e2 100644 --- a/build/generate-style-spec.ts +++ b/build/generate-style-spec.ts @@ -207,9 +207,7 @@ export type ExpressionSpecification = | ['distance', unknown | ExpressionSpecification] // Ramps, scales, curves | ['interpolate', InterpolationSpecification, number | ExpressionSpecification, - ...(number | number[] | ColorSpecification | ExpressionSpecification)[]] // alternating number and number | number[] | ColorSpecification - | ['interpolate-projection', InterpolationSpecification, number | ExpressionSpecification, - ...(number | string)[]] // alternating Projection + ...(number | number[] | ColorSpecification | ExpressionSpecification | ProjectionSpecification )[]] // alternating number and number | number[] | ColorSpecification | ['interpolate-hcl', InterpolationSpecification, number | ExpressionSpecification, ...(number | ColorSpecification)[]] // alternating number and ColorSpecificaton | ['interpolate-lab', InterpolationSpecification, number | ExpressionSpecification, diff --git a/docs/types.md b/docs/types.md index 659514271..3de9171f8 100644 --- a/docs/types.md +++ b/docs/types.md @@ -176,10 +176,10 @@ output at zoom 11.1: "mercator" #### Animate between different projections based on zoom level** -Use a [`camera expression`](./expressions.md#camera-expressions), to animate between projections based on zoom, using [`interpolate-projection`](./expressions.md#interpolate-projection) function. The example below will yield an adaptive globe that interpolates from `vertical-perspective` to `mercator` between zoom 10 and 12. +Use a [`camera expression`](./expressions.md#camera-expressions), to animate between projections based on zoom, using [`interpolate`](./expressions.md#interpolate) function. The example below will yield an adaptive globe that interpolates from `vertical-perspective` to `mercator` between zoom 10 and 12. ```ts -type: ["interpolate-projection", ["linear"], ["zoom"], +type: ["interpolate", ["linear"], ["zoom"], 10,"vertical-perspective", 12,"mercator" ] @@ -205,4 +205,4 @@ There are also additional presets that yield commonly used expressions: | Preset | Full value | Description | |--------|------------|-------------| -| `globe` | `["interpolate-projection", ["linear"], ["zoom"],`
`10, "vertical-perspective", 12, "mercator"]` | Adaptive globe: interpolates from vertical-perspective to mercator projection between zoom levels 10 and 12. | +| `globe` | `["interpolate", ["linear"], ["zoom"],`
`10, "vertical-perspective", 12, "mercator"]` | Adaptive globe: interpolates from vertical-perspective to mercator projection between zoom levels 10 and 12. | diff --git a/src/expression/definitions/coercion.ts b/src/expression/definitions/coercion.ts index c38d1f998..e4b27c7a4 100644 --- a/src/expression/definitions/coercion.ts +++ b/src/expression/definitions/coercion.ts @@ -123,6 +123,8 @@ class Coercion implements Expression { return Formatted.fromString(valueToString(this.args[0].evaluate(ctx))); case 'resolvedImage': return ResolvedImage.fromString(valueToString(this.args[0].evaluate(ctx))); + case 'projection': + return this.args[0].evaluate(ctx); default: return valueToString(this.args[0].evaluate(ctx)); } diff --git a/src/expression/definitions/index.ts b/src/expression/definitions/index.ts index b32766659..8832a0a90 100644 --- a/src/expression/definitions/index.ts +++ b/src/expression/definitions/index.ts @@ -52,7 +52,6 @@ export const expressions: ExpressionRegistry = { 'interpolate': Interpolate, 'interpolate-hcl': Interpolate, 'interpolate-lab': Interpolate, - 'interpolate-projection': Interpolate, 'length': Length, 'let': Let, 'literal': Literal, diff --git a/src/expression/definitions/interpolate.ts b/src/expression/definitions/interpolate.ts index ec0b13760..585f721e6 100644 --- a/src/expression/definitions/interpolate.ts +++ b/src/expression/definitions/interpolate.ts @@ -20,7 +20,7 @@ export type InterpolationType = { controlPoints: [number, number, number, number]; }; type InterpolatedValueType = NumberTypeT | ColorTypeT | ProjectionTypeT | PaddingTypeT | VariableAnchorOffsetCollectionTypeT | ArrayType; -type InterpolationOperator = 'interpolate' | 'interpolate-hcl' | 'interpolate-lab' | 'interpolate-projection'; +type InterpolationOperator = 'interpolate' | 'interpolate-hcl' | 'interpolate-lab'; class Interpolate implements Expression { type: InterpolatedValueType; @@ -108,8 +108,6 @@ class Interpolate implements Expression { let outputType: Type = null; if (operator === 'interpolate-hcl' || operator === 'interpolate-lab') { outputType = ColorType; - } else if (operator === 'interpolate-projection') { - outputType = ProjectionType; } else if (context.expectedType && context.expectedType.kind !== 'value') { outputType = context.expectedType; } @@ -128,7 +126,6 @@ class Interpolate implements Expression { if (stops.length && stops[stops.length - 1][0] >= label) { return context.error('Input/output pairs for "interpolate" expressions must be arranged with input values in strictly ascending order.', labelKey) as null; } - const parsed = context.parse(value, valueKey, outputType); if (!parsed) return null; outputType = outputType || parsed.type; @@ -187,14 +184,13 @@ class Interpolate implements Expression { return interpolate.padding(outputLower, outputUpper, t); case 'variableAnchorOffsetCollection': return interpolate.variableAnchorOffsetCollection(outputLower, outputUpper, t); + case 'projection': + return interpolate.projection(outputLower, outputUpper, t); } case 'interpolate-hcl': return interpolate.color(outputLower, outputUpper, t, 'hcl'); case 'interpolate-lab': return interpolate.color(outputLower, outputUpper, t, 'lab'); - case 'interpolate-projection': { - return interpolate.projection(outputLower, outputUpper, t); - } } } diff --git a/src/expression/index.ts b/src/expression/index.ts index 8df563433..1253baa90 100644 --- a/src/expression/index.ts +++ b/src/expression/index.ts @@ -334,7 +334,7 @@ export function createPropertyExpression(expressionInput: unknown, propertySpec: } import {isFunction, createFunction} from '../function'; -import {Color, VariableAnchorOffsetCollection} from './values'; +import {Color, Projection, VariableAnchorOffsetCollection} from './values'; // serialization wrapper for old-style stop functions normalized to the // expression interface @@ -391,6 +391,8 @@ export function normalizePropertyExpression( constant = Padding.parse(value as (number | number[])); } else if (specification.type === 'variableAnchorOffsetCollection' && Array.isArray(value)) { constant = VariableAnchorOffsetCollection.parse(value as VariableAnchorOffsetCollectionSpecification); + } else if (specification.type === 'projection' && typeof value === 'string') { + constant = value; } return { kind: 'constant', @@ -477,6 +479,8 @@ function getDefaultValue(spec: StylePropertySpecification): Value { return Padding.parse(spec.default) || null; } else if (spec.type === 'variableAnchorOffsetCollection') { return VariableAnchorOffsetCollection.parse(spec.default) || null; + } else if (spec.type === 'projection') { + return Projection.parse(spec.default) || null; } else if (spec.default === undefined) { return null; } else { diff --git a/src/expression/parsing_context.ts b/src/expression/parsing_context.ts index 03d6564f4..335dfd4eb 100644 --- a/src/expression/parsing_context.ts +++ b/src/expression/parsing_context.ts @@ -120,7 +120,7 @@ class ParsingContext { // if ((expected.kind === 'string' || expected.kind === 'number' || expected.kind === 'boolean' || expected.kind === 'object' || expected.kind === 'array') && actual.kind === 'value') { parsed = annotate(parsed, expected, options.typeAnnotation || 'assert'); - } else if ((expected.kind === 'projection') && (actual.kind === 'string')) { + } else if ((expected.kind === 'projection') && (actual.kind === 'string' || actual.kind === 'array')) { parsed = annotate(parsed, expected, options.typeAnnotation || 'coerce'); } else if ((expected.kind === 'color' || expected.kind === 'formatted' || expected.kind === 'resolvedImage') && (actual.kind === 'value' || actual.kind === 'string')) { parsed = annotate(parsed, expected, options.typeAnnotation || 'coerce'); diff --git a/src/index.ts b/src/index.ts index a15cc7648..ca439a3b4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -67,6 +67,12 @@ export type StylePropertySpecification = { expression?: ExpressionSpecificationDefinition; transition: boolean; default?: VariableAnchorOffsetCollectionSpecification; +} | { + type: 'projection'; + 'property-type': ExpressionType; + expression?: ExpressionSpecificationDefinition; + transition: boolean; + default?: ProjectionSpecification; }; import v8Spec from './reference/v8.json' with {type: 'json'}; @@ -107,7 +113,7 @@ import {typeOf} from './expression/values'; import FormatExpression from './expression/definitions/format'; import Literal from './expression/definitions/literal'; import CompoundExpression from './expression/compound_expression'; -import {VariableAnchorOffsetCollectionSpecification} from './types.g'; +import {ProjectionSpecification, VariableAnchorOffsetCollectionSpecification} from './types.g'; import format from './format'; import validate from './validate/validate'; import migrate from './migrate'; diff --git a/src/reference/v8.json b/src/reference/v8.json index 5e083926c..e3c96d3e5 100644 --- a/src/reference/v8.json +++ b/src/reference/v8.json @@ -130,7 +130,7 @@ "doc": "The projection configuration", "example": { "type": [ - "interpolate-projection", + "interpolate", ["linear"], ["zoom"], 10, "globe", @@ -3063,24 +3063,6 @@ } } }, - "interpolate-projection": { - "doc": "Produces continuous, smooth results by interpolating between pairs of input and output values (\"stops\"). Works like `interpolate`, but the output type must be a [`projection`](./types.md#projection).", - "example": { - "syntax": { - "method": ["[\"linear\"] | [\"exponential\", base] | [\"cubic-bezier\", x1, y1, x2, y2]", "number", "string", "number", "..."], - "result": "projection" - }, - "value": ["interpolate-projection", ["linear"], ["zoom"], 10, "vertical-perspective", 12, "mercator"] - }, - "group": "Ramps, scales, curves", - "sdk-support": { - "basic functionality": { - "js": "https://github.com/maplibre/maplibre-gl-js/issues/5039", - "ios": "https://github.com/maplibre/maplibre-native/issues/3013", - "android": "https://github.com/maplibre/maplibre-native/issues/3013" - } - } - }, "ln2": { "doc": "Returns mathematical constant ln(2).", "example": { @@ -4597,7 +4579,13 @@ "type": "projection", "doc": "The projection type. Can be specified as a string, a transition state, or an expression.", "default": "mercator", - "property-type": "data-constant" + "property-type": "data-constant", + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + } } }, "paint": [ diff --git a/src/util/projection.ts b/src/util/projection.ts index d0e7043c6..ecdcaa814 100644 --- a/src/util/projection.ts +++ b/src/util/projection.ts @@ -15,6 +15,19 @@ export default class Projection extends Array { toString() { return `["${this[0]}", "${this[1]}", ${this[2]}]`; } + + static parse(input: any): Projection { + if (input instanceof Projection) { + return input; + } + if (Array.isArray(input) && input.length === 3) { + return new Projection(input[0], input[1], input[2]); + } + if (typeof input === 'string') { + return new Projection(input, input, 1); + } + return undefined; + } } export function isProjectionConfig(value: unknown): value is ProjectionSpecification { @@ -23,7 +36,7 @@ export function isProjectionConfig(value: unknown): value is ProjectionSpecifica export function isPropertyValueSpecification(value: unknown): value is PropertyValueSpecification { - if (['interpolate-projection', 'step', 'literal'].includes(value[0])) { + if (['interpolate', 'step', 'literal'].includes(value[0])) { return true } return false diff --git a/src/validate/validate_projection.test.ts b/src/validate/validate_projection.test.ts index 30d7789c4..1ab2e30bb 100644 --- a/src/validate/validate_projection.test.ts +++ b/src/validate/validate_projection.test.ts @@ -42,7 +42,7 @@ describe('validateProjection function', () => { }); test('Should return no errors when projection is valid interpolation-projection expression', () => { - const errors = validateProjection({value: ['interpolate-projection', ['linear'], ['zoom'], 0, 'mercator', 5, 'vertical-perspective'], key}); + const errors = validateProjection({value: ['interpolate', ['linear'], ['zoom'], 0, 'mercator', 5, 'vertical-perspective'], key}); expect(errors).toHaveLength(0); }); diff --git a/src/validate/validate_projectionconfig.test.ts b/src/validate/validate_projectionconfig.test.ts index 40448ae82..b822c3e88 100644 --- a/src/validate/validate_projectionconfig.test.ts +++ b/src/validate/validate_projectionconfig.test.ts @@ -42,7 +42,7 @@ describe('Validate projectionConfig', () => { }); test('should parse iterpolate-projection', () => { - const errors = validateProjectionConfig({validateSpec, value: {'type': ['interpolate-projection', ['linear'], ['zoom'], 0, 'mercator', 5, 'vertical-perspective']}, styleSpec: v8, style: {} as any}); + const errors = validateProjectionConfig({validateSpec, value: {'type': ['interpolate', ['linear'], ['zoom'], 0, 'mercator', 5, 'vertical-perspective']}, styleSpec: v8, style: {} as any}); expect(errors).toHaveLength(0); }); diff --git a/test/integration/expression/tests/interpolate-projection/higher-than-stop/test.json b/test/integration/expression/tests/interpolate-projection/higher-than-stop/test.json index 7e92658e4..5acf555c2 100644 --- a/test/integration/expression/tests/interpolate-projection/higher-than-stop/test.json +++ b/test/integration/expression/tests/interpolate-projection/higher-than-stop/test.json @@ -1,6 +1,16 @@ { + "propertySpec": { + "type": "projection", + "property-type": "data-constant", + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + } + }, "expression": [ - "interpolate-projection", + "interpolate", [ "linear" ], diff --git a/test/integration/expression/tests/interpolate-projection/linear/test.json b/test/integration/expression/tests/interpolate-projection/linear/test.json index 8c104c8f1..ced897e9d 100644 --- a/test/integration/expression/tests/interpolate-projection/linear/test.json +++ b/test/integration/expression/tests/interpolate-projection/linear/test.json @@ -1,6 +1,16 @@ { + "propertySpec": { + "type": "projection", + "property-type": "data-constant", + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + } + }, "expression": [ - "interpolate-projection", + "interpolate", [ "linear" ], diff --git a/test/integration/expression/tests/interpolate-projection/lower-than-stop/test.json b/test/integration/expression/tests/interpolate-projection/lower-than-stop/test.json index 348227556..1b7bf17da 100644 --- a/test/integration/expression/tests/interpolate-projection/lower-than-stop/test.json +++ b/test/integration/expression/tests/interpolate-projection/lower-than-stop/test.json @@ -1,6 +1,16 @@ { + "propertySpec": { + "type": "projection", + "property-type": "data-constant", + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + } + }, "expression": [ - "interpolate-projection", + "interpolate", [ "linear" ], diff --git a/test/integration/expression/tests/interpolate-projection/same-from-to/test.json b/test/integration/expression/tests/interpolate-projection/same-from-to/test.json index 31517dd60..4e3acc6ca 100644 --- a/test/integration/expression/tests/interpolate-projection/same-from-to/test.json +++ b/test/integration/expression/tests/interpolate-projection/same-from-to/test.json @@ -1,6 +1,16 @@ { + "propertySpec": { + "type": "projection", + "property-type": "data-constant", + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + } + }, "expression": [ - "interpolate-projection", + "interpolate", [ "linear" ], diff --git a/test/integration/expression/tests/zoom/interpolate-projection/test.json b/test/integration/expression/tests/zoom/interpolate-projection/test.json index 1b7c7c815..35041d3ca 100644 --- a/test/integration/expression/tests/zoom/interpolate-projection/test.json +++ b/test/integration/expression/tests/zoom/interpolate-projection/test.json @@ -1,6 +1,16 @@ { + "propertySpec": { + "type": "projection", + "property-type": "data-constant", + "expression": { + "interpolated": true, + "parameters": [ + "zoom" + ] + } + }, "expression": [ - "interpolate-projection", + "interpolate", [ "linear" ],