From 5059738058b421be4d31eb09d8d8fb4881abaecc Mon Sep 17 00:00:00 2001 From: Anand Thakker Date: Thu, 28 Sep 2017 11:54:35 -0400 Subject: [PATCH] Add ['heatmap-density'] expression (#5350) * Add ['heatmap-density'] expression * Fix lint --- src/style-spec/function/compile.js | 4 ++-- src/style-spec/function/convert.js | 13 ++++++++----- src/style-spec/function/definitions/index.js | 3 ++- src/style-spec/function/index.js | 4 ++-- src/style-spec/function/is_constant.js | 8 ++++---- src/style-spec/function/parse_expression.js | 5 +++-- src/style/light.js | 9 +++++---- src/style/style_declaration.js | 4 ++-- src/style/style_layer.js | 9 +++++---- src/style/style_layer/heatmap_style_layer.js | 2 +- src/style/style_transition.js | 5 +++-- 11 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/style-spec/function/compile.js b/src/style-spec/function/compile.js index 353782186ee..3e3fd40ce69 100644 --- a/src/style-spec/function/compile.js +++ b/src/style-spec/function/compile.js @@ -12,7 +12,7 @@ const definitions = require('./definitions'); const evaluationContext = require('./evaluation_context'); const { isFeatureConstant, - isZoomConstant + isGlobalPropertyConstant } = require('./is_constant'); import type { Type } from './types.js'; @@ -76,7 +76,7 @@ function compileExpression( function: compiled, functionSource: compilationContext.getPrelude(), isFeatureConstant: isFeatureConstant(parsed), - isZoomConstant: isZoomConstant(parsed), + isZoomConstant: isGlobalPropertyConstant(parsed, ['zoom']), expression: parsed }; } diff --git a/src/style-spec/function/convert.js b/src/style-spec/function/convert.js index 495b7510759..ea79ca4d59a 100644 --- a/src/style-spec/function/convert.js +++ b/src/style-spec/function/convert.js @@ -4,7 +4,7 @@ const extend = require('../util/extend'); module.exports.function = convertFunction; module.exports.value = convertValue; -function convertFunction(parameters, propertySpec) { +function convertFunction(parameters, propertySpec, name) { let expression; parameters = extend({}, parameters); @@ -31,7 +31,10 @@ function convertFunction(parameters, propertySpec) { throw new Error('Unimplemented'); } - if (zoomAndFeatureDependent) { + if (name === 'heatmap-color') { + assert(zoomDependent); + expression = convertZoomFunction(parameters, propertySpec, stops, ['heatmap-density']); + } else if (zoomAndFeatureDependent) { expression = convertZoomAndPropertyFunction(parameters, propertySpec, stops, defaultExpression); } else if (zoomDependent) { expression = convertZoomFunction(parameters, propertySpec, stops); @@ -178,16 +181,16 @@ function convertPropertyFunction(parameters, propertySpec, stops, defaultExpress return expression; } -function convertZoomFunction(parameters, propertySpec, stops) { +function convertZoomFunction(parameters, propertySpec, stops, input = ['zoom']) { const type = getFunctionType(parameters, propertySpec); let expression; let isStep = false; if (type === 'interval') { - expression = ['curve', ['step'], ['zoom']]; + expression = ['curve', ['step'], input]; isStep = true; } else if (type === 'exponential') { const base = parameters.base !== undefined ? parameters.base : 1; - expression = ['curve', ['exponential', base], ['zoom']]; + expression = ['curve', ['exponential', base], input]; } else { throw new Error(`Unknown zoom function type "${type}"`); } diff --git a/src/style-spec/function/definitions/index.js b/src/style-spec/function/definitions/index.js index 28033958774..e30430afb74 100644 --- a/src/style-spec/function/definitions/index.js +++ b/src/style-spec/function/definitions/index.js @@ -102,7 +102,8 @@ CompoundExpression.register(expressions, { 'id': [ ValueType, [], () => `('id' in $feature) ? $feature.id : null` ], - 'zoom': [ NumberType, [], () => '$globalProperties.zoom' ], + 'zoom': [ NumberType, [], () => '$globalProperties.zoom || 0' ], + 'heatmap-density': [ NumberType, [], () => '$globalProperties.heatmapDensity || 0' ], '+': defineBinaryMathOp('+', true), '*': defineBinaryMathOp('*', true), '-': { diff --git a/src/style-spec/function/index.js b/src/style-spec/function/index.js index ddd10096281..a3a281754b7 100644 --- a/src/style-spec/function/index.js +++ b/src/style-spec/function/index.js @@ -61,7 +61,7 @@ type StylePropertySpecification = { type StylePropertyValue = null | string | number | Array | Array; type FunctionParameters = DataDrivenPropertyValueSpecification -function createFunction(parameters: FunctionParameters, propertySpec: StylePropertySpecification): StyleFunction { +function createFunction(parameters: FunctionParameters, propertySpec: StylePropertySpecification, name: string): StyleFunction { if (typeof parameters === 'string' && propertySpec.type === 'color') { const color = parseColor(parameters); return { @@ -86,7 +86,7 @@ function createFunction(parameters: FunctionParameters, propertySpec: StylePrope if (parameters.expression) { expr = parameters.expression; } else { - expr = convert.function(parameters, propertySpec); + expr = convert.function(parameters, propertySpec, name); isConvertedStopFunction = true; if (parameters && typeof parameters.default !== 'undefined') { defaultValue = parameters.default; diff --git a/src/style-spec/function/is_constant.js b/src/style-spec/function/is_constant.js index c7dca51472c..08c2484afea 100644 --- a/src/style-spec/function/is_constant.js +++ b/src/style-spec/function/is_constant.js @@ -26,16 +26,16 @@ function isFeatureConstant(e: Expression) { return result; } -function isZoomConstant(e: Expression) { - if (e instanceof CompoundExpression && e.name === 'zoom') { return false; } +function isGlobalPropertyConstant(e: Expression, properties: Array) { + if (e instanceof CompoundExpression && properties.indexOf(e.name) >= 0) { return false; } let result = true; e.eachChild((arg) => { - if (result && !isZoomConstant(arg)) { result = false; } + if (result && !isGlobalPropertyConstant(arg, properties)) { result = false; } }); return result; } module.exports = { isFeatureConstant, - isZoomConstant, + isGlobalPropertyConstant, }; diff --git a/src/style-spec/function/parse_expression.js b/src/style-spec/function/parse_expression.js index 35d19743a9f..b0501f39e8e 100644 --- a/src/style-spec/function/parse_expression.js +++ b/src/style-spec/function/parse_expression.js @@ -89,7 +89,7 @@ function parseExpression(expr: mixed, context: ParsingContext): ?Expression { function isConstant(expression: Expression) { // requires within function body to workaround circular dependency const {CompoundExpression} = require('./compound_expression'); - const {isZoomConstant, isFeatureConstant} = require('./is_constant'); + const {isGlobalPropertyConstant, isFeatureConstant} = require('./is_constant'); const Var = require('./definitions/var'); if (expression instanceof Var) { @@ -106,7 +106,8 @@ function isConstant(expression: Expression) { return false; } - return isZoomConstant(expression) && isFeatureConstant(expression); + return isFeatureConstant(expression) && + isGlobalPropertyConstant(expression, ['zoom', 'heatmap-density']); } module.exports = parseExpression; diff --git a/src/style/light.js b/src/style/light.js index 1e9ab7d5bb1..1ff20f51c00 100644 --- a/src/style/light.js +++ b/src/style/light.js @@ -8,6 +8,7 @@ const StyleDeclaration = require('./style_declaration'); const StyleTransition = require('./style_transition'); import type AnimationLoop from './animation_loop'; +import type {GlobalProperties} from './style_layer'; const TRANSITION_SUFFIX = '-transition'; const properties = ['anchor', 'color', 'position', 'intensity']; @@ -42,7 +43,7 @@ class Light extends Evented { }, lightOpts); for (const prop of properties) { - this._declarations[prop] = new StyleDeclaration(specifications[prop], lightOpts[prop]); + this._declarations[prop] = new StyleDeclaration(specifications[prop], lightOpts[prop], prop); } return this; @@ -70,7 +71,7 @@ class Light extends Evented { } } - getLightValue(property: string, globalProperties: {zoom: number}) { + getLightValue(property: string, globalProperties: GlobalProperties) { if (property === 'position') { const calculated: any = this._transitions[property].calculate(globalProperties), cartesian = util.sphericalToCartesian(calculated); @@ -95,7 +96,7 @@ class Light extends Evented { } else if (value === null || value === undefined) { delete this._declarations[key]; } else { - this._declarations[key] = new StyleDeclaration(specifications[key], value); + this._declarations[key] = new StyleDeclaration(specifications[key], value, key); } } } @@ -111,7 +112,7 @@ class Light extends Evented { const spec = specifications[property]; if (declaration === null || declaration === undefined) { - declaration = new StyleDeclaration(spec, spec.default); + declaration = new StyleDeclaration(spec, spec.default, property); } if (oldTransition && oldTransition.declaration.json === declaration.json) return; diff --git a/src/style/style_declaration.js b/src/style/style_declaration.js index d14a3dcc1b9..4c0de808a6d 100644 --- a/src/style/style_declaration.js +++ b/src/style/style_declaration.js @@ -17,7 +17,7 @@ class StyleDeclaration { minimum: number; function: StyleFunction; - constructor(reference: any, value: any) { + constructor(reference: any, value: any, name: string) { this.value = util.clone(value); this.isFunction = createFunction.isFunctionDefinition(value); @@ -25,7 +25,7 @@ class StyleDeclaration { this.json = JSON.stringify(this.value); this.minimum = reference.minimum; - this.function = createFunction(this.value, reference); + this.function = createFunction(this.value, reference, name); } calculate(globalProperties: {+zoom?: number} = {}, feature?: Feature) { diff --git a/src/style/style_layer.js b/src/style/style_layer.js index 55db9c8861d..8aaa7d065ec 100644 --- a/src/style/style_layer.js +++ b/src/style/style_layer.js @@ -14,7 +14,8 @@ import type {Feature} from '../style-spec/function'; import type RenderTexture from '../render/render_texture'; export type GlobalProperties = { - zoom: number + zoom?: number, + heatmapDensity?: number }; export type FeatureProperties = { @@ -117,7 +118,7 @@ class StyleLayer extends Evented { } else { const key = `layers.${this.id}.layout.${name}`; if (this._validate(validateStyle.layoutProperty, key, name, value, options)) return; - this._layoutDeclarations[name] = new StyleDeclaration(this._layoutSpecifications[name], value); + this._layoutDeclarations[name] = new StyleDeclaration(this._layoutSpecifications[name], value, name); } this._updateLayoutValue(name); } @@ -161,7 +162,7 @@ class StyleLayer extends Evented { delete this._paintDeclarations[klass || ''][name]; } else { if (this._validate(validateStyle.paintProperty, validateStyleKey, name, value, options)) return; - this._paintDeclarations[klass || ''][name] = new StyleDeclaration(this._paintSpecifications[name], value); + this._paintDeclarations[klass || ''][name] = new StyleDeclaration(this._paintSpecifications[name], value, name); } } } @@ -284,7 +285,7 @@ class StyleLayer extends Evented { const spec = this._paintSpecifications[name]; if (declaration === null || declaration === undefined) { - declaration = new StyleDeclaration(spec, spec.default); + declaration = new StyleDeclaration(spec, spec.default, name); } if (oldTransition && oldTransition.declaration.json === declaration.json) return; diff --git a/src/style/style_layer/heatmap_style_layer.js b/src/style/style_layer/heatmap_style_layer.js index 8297eba18be..92522263dac 100644 --- a/src/style/style_layer/heatmap_style_layer.js +++ b/src/style/style_layer/heatmap_style_layer.js @@ -33,7 +33,7 @@ class HeatmapStyleLayer extends StyleLayer { if (name === 'heatmap-color') { const len = this.colorRampData.length; for (let i = 4; i < len; i += 4) { - const pxColor = this.getPaintValue('heatmap-color', {zoom: i / len}); + const pxColor = this.getPaintValue('heatmap-color', {heatmapDensity: i / len}); const alpha = pxColor[3]; // the colors are being unpremultiplied because getPaintValue returns // premultiplied values, and the Texture class expects unpremultiplied ones diff --git a/src/style/style_transition.js b/src/style/style_transition.js index a1a72a66705..ec5986cf618 100644 --- a/src/style/style_transition.js +++ b/src/style/style_transition.js @@ -6,6 +6,7 @@ const interpolate = require('../style-spec/util/interpolate'); import type StyleDeclaration from './style_declaration'; import type {Feature} from '../style-spec/function'; +import type {GlobalProperties} from '../style/style_layer'; const fakeZoomHistory = { lastIntegerZoom: 0, lastIntegerZoomTime: 0, lastZoom: 0 }; @@ -58,7 +59,7 @@ class StyleTransition { /* * Return the value of the transitioning property. */ - calculate(globalProperties?: {zoom: number}, feature?: Feature, time?: number) { + calculate(globalProperties?: GlobalProperties, feature?: Feature, time?: number) { const value = this._calculateTargetValue(globalProperties, feature); if (this.instant()) @@ -74,7 +75,7 @@ class StyleTransition { return this.interp(oldValue, value, t); } - _calculateTargetValue(globalProperties?: {zoom: number}, feature?: Feature) { + _calculateTargetValue(globalProperties?: GlobalProperties, feature?: Feature) { if (!this.zoomTransitioned) return this.declaration.calculate(globalProperties, feature);