-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
增加calculate-expression类型 #13
Merged
Merged
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,10 +10,11 @@ function createFunction(parameters, defaultType) { | |
var fun; | ||
var isFeatureConstant, isZoomConstant; | ||
if (!isFunctionDefinition(parameters)) { | ||
fun = function () { return parameters; }; | ||
fun = function () { | ||
return parameters; | ||
}; | ||
isFeatureConstant = true; | ||
isZoomConstant = true; | ||
|
||
} else { | ||
var zoomAndFeatureDependent = parameters.stops && typeof parameters.stops[0][0] === 'object'; | ||
var featureDependent = zoomAndFeatureDependent || parameters.property !== undefined; | ||
|
@@ -31,6 +32,8 @@ function createFunction(parameters, defaultType) { | |
innerFun = evaluateIdentityFunction; | ||
} else if (type === 'color-interpolate') { | ||
innerFun = evaluateColorInterpolateFunction; | ||
} else if (type === 'calculate-expression') { | ||
innerFun = evaluateCalculateExpressionFunction; | ||
} else { | ||
throw new Error('Unknown function type "' + type + '"'); | ||
} | ||
|
@@ -61,7 +64,6 @@ function createFunction(parameters, defaultType) { | |
}; | ||
isFeatureConstant = false; | ||
isZoomConstant = false; | ||
|
||
} else if (zoomDependent) { | ||
fun = function (zoom) { | ||
const value = innerFun(parameters, zoom); | ||
|
@@ -118,19 +120,10 @@ function evaluateExponentialFunction(parameters, input) { | |
|
||
if (i === 0) { | ||
return parameters.stops[i][1]; | ||
|
||
} else if (i === parameters.stops.length) { | ||
return parameters.stops[i - 1][1]; | ||
|
||
} else { | ||
return interpolate( | ||
input, | ||
base, | ||
parameters.stops[i - 1][0], | ||
parameters.stops[i][0], | ||
parameters.stops[i - 1][1], | ||
parameters.stops[i][1] | ||
); | ||
return interpolate(input, base, parameters.stops[i - 1][0], parameters.stops[i][0], parameters.stops[i - 1][1], parameters.stops[i][1]); | ||
} | ||
} | ||
const COLORIN_OPTIONS = { | ||
|
@@ -160,8 +153,82 @@ function evaluateColorInterpolateFunction(parameters, input) { | |
} | ||
|
||
function evaluateIdentityFunction(parameters, input) { | ||
// console.log('parameters11', parameters) | ||
// console.log('input11', input) | ||
// console.log('coalesce',coalesce(input, parameters.default)) | ||
return coalesce(input, parameters.default); | ||
} | ||
// 2添加修改计算函数 | ||
// 处理空字符串和未定义属性的函数 | ||
function evaluateCalculateExpressionFunction(parameters, input) { | ||
console.log('parameters', parameters); | ||
const targetVariable = String(parameters.property); | ||
const expression = parameters.expression; | ||
const newValue = input; | ||
console.log('input', input); | ||
|
||
// 定义函数来替换表达式中的变量 | ||
function traverseAndAssign(expression, targetVariable, newValue) { | ||
const newValueAsNumber = Number(newValue); | ||
const targetVariableAsString = String(targetVariable); | ||
|
||
if (Array.isArray(expression)) { | ||
return expression.map(subExpr => traverseAndAssign(subExpr, targetVariableAsString, newValueAsNumber)); | ||
} else if (expression === targetVariableAsString) { | ||
return newValueAsNumber; | ||
} else { | ||
return expression; | ||
} | ||
} | ||
|
||
// 定义函数来计算表达式的结果 | ||
function evaluateExpression(expression) { | ||
if (Array.isArray(expression)) { | ||
const operator = expression[0]; | ||
|
||
if (!['+', '-', '*', '/'].includes(operator)) { | ||
throw new Error(`Unknown operator: ${operator}`); | ||
} | ||
|
||
const operands = expression.slice(1).map(op => evaluateExpression(op)); | ||
console.log('operands', operands); | ||
|
||
switch (operator) { | ||
case '+': | ||
return operands.reduce((acc, curr) => acc + curr, 0); | ||
case '-': | ||
return operands.reduce((acc, curr) => acc - curr); | ||
case '*': | ||
return operands.reduce((acc, curr) => acc * curr, 1); | ||
case '/': | ||
// 如果发现有零作为除数,返回默认值 | ||
if (operands.some(op => op === 0)) { | ||
return parameters.default; | ||
} | ||
return operands.reduce((acc, curr) => acc / curr); | ||
default: | ||
throw new Error(`Unsupported operator: ${operator}`); | ||
} | ||
} else if (typeof expression === 'number') { | ||
return expression; | ||
} else { | ||
throw new Error('Invalid expression format'); | ||
} | ||
} | ||
|
||
// 使用 coalesce 函数处理结果中的默认值 | ||
function coalesce(value) { | ||
return value === null || value === undefined || value === '' || isNaN(value) ? parameters.default : value; | ||
} | ||
|
||
// 如果 input 存在且大于0,则处理表达式 | ||
if (input !== undefined && input !== null && input !== '' && !isNaN(input) && !(input < 0)) { | ||
const updatedExpression = traverseAndAssign(expression, targetVariable, newValue); | ||
return coalesce(evaluateExpression(updatedExpression)); | ||
} else { | ||
return coalesce(parameters.default); | ||
} | ||
} | ||
|
||
function interpolate(input, base, inputLower, inputUpper, outputLower, outputUpper) { | ||
if (typeof outputLower === 'function') { | ||
|
@@ -188,7 +255,7 @@ function interpolateNumber(input, base, inputLower, inputUpper, outputLower, out | |
ratio = (Math.pow(base, progress) - 1) / (Math.pow(base, difference) - 1); | ||
} | ||
|
||
return (outputLower * (1 - ratio)) + (outputUpper * ratio); | ||
return outputLower * (1 - ratio) + outputUpper * ratio; | ||
} | ||
|
||
function interpolateArray(input, base, inputLower, inputUpper, outputLower, outputUpper) { | ||
|
@@ -206,9 +273,17 @@ function interpolateArray(input, base, inputLower, inputUpper, outputLower, outp | |
* @memberOf MapboxUtil | ||
*/ | ||
export function isFunctionDefinition(obj) { | ||
return obj && typeof obj === 'object' && (obj.stops || obj.property && obj.type === 'identity'); | ||
return ( | ||
obj && | ||
typeof obj === 'object' && | ||
(obj.stops || (obj.property && obj.type === 'identity') || (obj.expression && obj.type === 'calculate-expression')) | ||
); | ||
} | ||
|
||
// export function isFunctionDefinition(obj) { | ||
// return obj && typeof obj === 'object' && (obj.stops || (obj.property && obj.type === 'identity')); | ||
// } | ||
|
||
/** | ||
* Check if obj's properties has function definition | ||
* @param {Object} obj object to check | ||
|
@@ -228,7 +303,6 @@ export function interpolated(parameters) { | |
return createFunction1(parameters, 'exponential'); | ||
} | ||
|
||
|
||
export function piecewiseConstant(parameters) { | ||
return createFunction1(parameters, 'interval'); | ||
} | ||
|
@@ -239,6 +313,7 @@ export function piecewiseConstant(parameters) { | |
* @return {Object} loaded object | ||
* @memberOf MapboxUtil | ||
*/ | ||
|
||
export function loadFunctionTypes(obj, argFn) { | ||
if (!obj) { | ||
return null; | ||
|
@@ -258,7 +333,7 @@ export function loadFunctionTypes(obj, argFn) { | |
} | ||
return hit ? multResult : obj; | ||
} | ||
var result = { '__fn_types_loaded': true }, | ||
var result = { fnTypesLoaded: true }, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这个属性名因为别的地方也会引用,不能随便修改 |
||
props = [], | ||
p; | ||
for (p in obj) { | ||
|
@@ -316,7 +391,9 @@ export function getFunctionTypeResources(t) { | |
|
||
function createFunction1(parameters, defaultType) { | ||
if (!isFunctionDefinition(parameters)) { | ||
return function () { return parameters; }; | ||
return function () { | ||
return parameters; | ||
}; | ||
} | ||
parameters = JSON.parse(JSON.stringify(parameters)); | ||
let isZoomConstant = true; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,81 +1,77 @@ | ||
var expect = require('expect.js'); | ||
const { loadFunctionTypes } = require('./index'); | ||
import { registerCanvas } from 'colorin'; | ||
const { createCanvas } = require('@napi-rs/canvas'); | ||
const canvas = createCanvas(1, 1); | ||
registerCanvas(canvas); | ||
|
||
const colors = [ | ||
[0, 'red'], | ||
[5, 'black'], | ||
[10, 'white'] | ||
]; | ||
|
||
const symbol = { | ||
markerFill: { | ||
property: 'value', | ||
stops: colors, | ||
type: 'color-interpolate' | ||
}, | ||
}; | ||
|
||
function rgbatoStr(rgba) { | ||
return rgba.join(','); | ||
} | ||
|
||
describe('color-interpolate', () => { | ||
it('interpolate by property', () => { | ||
const result = loadFunctionTypes(symbol, () => { | ||
return [11, { | ||
value: 1 | ||
}]; | ||
}); | ||
expect(rgbatoStr(result.markerFill)).to.be(rgbatoStr([0.788235294117647, 0, 0, 1])); | ||
}); | ||
it('interpolate by property < min', () => { | ||
const result = loadFunctionTypes(symbol, () => { | ||
return [11, { | ||
value: -100 | ||
}]; | ||
}); | ||
expect(rgbatoStr(result.markerFill)).to.be(rgbatoStr([0.9882352941176471, 0, 0, 1])); | ||
}); | ||
it('interpolate by property > max', () => { | ||
const result = loadFunctionTypes(symbol, () => { | ||
return [11, { | ||
value: 100 | ||
}]; | ||
}); | ||
expect(rgbatoStr(result.markerFill)).to.be(rgbatoStr([0.9882352941176471, 0.9882352941176471, 0.9882352941176471, 1])); | ||
}); | ||
it('interpolate by property colors.length=1', () => { | ||
const obj = { | ||
markerFill: { | ||
property: 'value', | ||
stops: colors.slice(0, 1), | ||
type: 'color-interpolate' | ||
}, | ||
}; | ||
const result = loadFunctionTypes(obj, () => { | ||
return [11, { | ||
value: 2 | ||
}]; | ||
}); | ||
expect(result.markerFill).to.be('red'); | ||
}); | ||
it('interpolate by zoom ', () => { | ||
const obj = { | ||
markerFill: { | ||
// property: 'value', | ||
stops: colors, | ||
type: 'color-interpolate' | ||
}, | ||
}; | ||
const result = loadFunctionTypes(obj, () => { | ||
return [6, { | ||
value: 11 | ||
}]; | ||
}); | ||
expect(rgbatoStr(result.markerFill)).to.be(rgbatoStr([0.21176470588235294, 0.21176470588235294, 0.21176470588235294, 1])); | ||
}); | ||
}); | ||
const expect = require('expect.js'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 原来的测试用例不应删除,需要还原回来,用例的分类可以这样处理:
|
||
const { loadFunctionTypes } = require('./index'); | ||
|
||
const symbol = { | ||
lineColor: [0.56, 0.75, 0.13, 1], | ||
lineWidth: { | ||
type: 'calculate-expression', | ||
property: '管径', | ||
expression: ['*', '管径', 4000], | ||
default: 4000, | ||
}, | ||
}; | ||
// 如果输入值缺失、 null、负数、非数值类型、未定义属性、非法运算返回default | ||
describe('calculate-expression', () => { | ||
// 正常情况 | ||
it('calculates based on expression', () => { | ||
const result = loadFunctionTypes(symbol, () => { | ||
return [11, { 管径: 2 }]; | ||
}); | ||
expect(result.lineWidth).to.be(8000); // 2 * 4000 | ||
}); | ||
// 属性缺失 | ||
it('returns default when property is missing', () => { | ||
const result = loadFunctionTypes(symbol, () => { | ||
return [11, {}]; | ||
}); | ||
expect(result.lineWidth).to.be(4000); // default value when 管径 is missing | ||
}); | ||
|
||
// 属性为 null | ||
it('returns default when property is null', () => { | ||
const result = loadFunctionTypes(symbol, () => { | ||
return [11, { 管径: null }]; | ||
}); | ||
expect(result.lineWidth).to.be(4000); // default value when 管径 is null | ||
}); | ||
|
||
// 属性为负数 | ||
it('handles negative value correctly', () => { | ||
const result = loadFunctionTypes(symbol, () => { | ||
return [11, { 管径: -2 }]; | ||
}); | ||
expect(result.lineWidth).to.be(4000); // | ||
}); | ||
|
||
// 属性为非数值类型 | ||
it('handles non-numeric property value', () => { | ||
const result = loadFunctionTypes(symbol, () => { | ||
return [11, { 管径: 'abc' }]; | ||
}); | ||
expect(result.lineWidth).to.be(4000); // 'abc' * 4000 should result in NaN | ||
}); | ||
|
||
// 未定义属性 | ||
it('handles undefined property value', () => { | ||
const result = loadFunctionTypes(symbol, () => { | ||
return [11, { 管径: undefined }]; | ||
}); | ||
expect(result.lineWidth).to.be(4000); // undefined property should return default value | ||
}); | ||
|
||
// 运算方式非法 | ||
it('handles 0 as divisor', () => { | ||
const obj = { | ||
lineWidth: { | ||
type: 'calculate-expression', | ||
property: '管径', | ||
expression: ['/', '管径', 0], | ||
default: 4000, | ||
}, | ||
}; | ||
const result = loadFunctionTypes(obj, () => { | ||
return [11, { 管径: 10 }]; | ||
}); | ||
expect(result.lineWidth).to.be(4000); // 0 as divisor | ||
}); | ||
}); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
需要去掉代码里的console.log