geostyler-openlayers-parser
Version:
GeoStyler Style Parser implementation for OpenLayers styles
388 lines • 12.2 kB
JavaScript
import { invert } from 'lodash';
import OlStyleUtil from './OlStyleUtil';
const comparisonFilterNames = [
'==',
'!=',
'<',
'<=',
'>',
'>=',
'between',
];
const filterNames = [
...comparisonFilterNames,
'all',
'any',
'!'
];
// TODO continue here
const filterNameMap = {
'==': '==',
'*=': null,
'!=': '!=',
'<': '<',
'<=': '<=',
'>': '>',
'>=': '>=',
'<=x<=': 'between',
'&&': 'all',
'||': 'any',
'!': '!',
};
const invertedFilterMap = invert(filterNameMap);
const expressionNames = [
'get',
'*',
'/',
'+',
'-',
'%',
'^',
'abs',
'floor',
'round',
'ceil',
'sin',
'cos',
'atan',
'sqrt',
'case',
'interpolate',
'string',
'<',
'<=',
'>',
'>=',
'==',
'!=',
'!',
'all',
'any',
'between',
'in',
'to-string',
];
const functionNameMap = {
// ---- string ----
numberFormat: null,
// numberFormat: 'number-format', // TODO: this could be done in theory but gs and mb use different format approaches
strAbbreviate: null,
strCapitalize: null,
strConcat: null,
strDefaultIfBlank: 'string',
strEndsWith: null,
strEqualsIgnoreCase: null,
strIndexOf: null,
strLastIndexOf: null,
strLength: null,
strMatches: null,
strReplace: null,
strStartsWith: null,
strStripAccents: null,
strSubstring: null,
strSubstringStart: null,
strToLowerCase: null,
strToUpperCase: null,
strToString: 'to-string',
strTrim: null,
// ---- number ----
add: '+',
abs: 'abs',
acos: null,
asin: null,
// openlayers uses atan if only one argument is passed
// atan2 is used for two arguments
atan: 'atan',
atan2: 'atan',
ceil: 'ceil',
cos: 'cos',
div: '/',
exp: null,
floor: 'floor',
interpolate: 'interpolate',
log: null,
// – : 'ln2'
// – : 'log10'
// – : 'log2'
max: null,
min: null,
modulo: '%',
mul: '*',
pi: null,
// - : 'e',
pow: '^',
random: null,
rint: null,
round: 'round',
sin: 'sin',
sqrt: 'sqrt',
sub: '-',
tan: null,
toDegrees: null,
toNumber: null,
toRadians: null,
// ---- boolean ----
all: 'all',
// eslint-disable-next-line id-blacklist
any: 'any',
between: 'between',
double2bool: null,
equalTo: '==',
greaterThan: '>',
greaterThanOrEqualTo: '>=',
in: 'in',
lessThan: '<',
lessThanOrEqualTo: '<=',
not: '!',
notEqualTo: '!=',
parseBoolean: null,
// ---- unknown ----
case: 'case',
property: 'get',
step: null
};
const invertedFunctionNameMap = invert(functionNameMap);
class OlFlatStyleUtil {
static isFlatRule(flatStyle) {
return 'style' in flatStyle;
}
static isFlatRuleArray(flatStyleLike) {
const isArray = Array.isArray(flatStyleLike);
if (!isArray) {
return false;
}
const hasFlatRules = flatStyleLike.every(style => OlFlatStyleUtil.isFlatRule(style));
return hasFlatRules;
}
static isFlatStyle(flatStyleLike) {
const isArray = Array.isArray(flatStyleLike);
if (isArray) {
return false;
}
return !('style' in flatStyleLike);
}
static isFlatStyleArray(flatStyleLike) {
const isArray = Array.isArray(flatStyleLike);
if (!isArray) {
return false;
}
const isFlatRuleArray = OlFlatStyleUtil.isFlatRuleArray(flatStyleLike);
if (isFlatRuleArray) {
return false;
}
const hasFlatStyles = flatStyleLike.every(style => OlFlatStyleUtil.isFlatStyle(style));
return hasFlatStyles;
}
static isExpression(flatStyleProp) {
const isUndefined = flatStyleProp === undefined;
const isArray = Array.isArray(flatStyleProp);
if (isUndefined || !isArray) {
return false;
}
const hasOperator = typeof flatStyleProp[0] == 'string';
const isExpressionName = expressionNames.includes(flatStyleProp[0]);
return hasOperator && isExpressionName;
}
static isFilter(flatStyleProp) {
const isUndefined = flatStyleProp === undefined;
const isArray = Array.isArray(flatStyleProp);
if (isUndefined || !isArray) {
return false;
}
const hasOperator = typeof flatStyleProp[0] == 'string';
const isFilterName = filterNames.includes(flatStyleProp[0]);
return hasOperator && isFilterName;
}
static isComparisonFilter(filter) {
const isUndefined = filter === undefined;
const isArray = Array.isArray(filter);
if (isUndefined || !isArray) {
return false;
}
const hasOperator = typeof filter[0] == 'string';
const isComparisonFilterName = comparisonFilterNames.includes(filter[0]);
return hasOperator && isComparisonFilterName;
}
static getColorAndOpacity(flatStyleProp) {
if (flatStyleProp === undefined) {
return [undefined, undefined];
}
const isArray = Array.isArray(flatStyleProp);
if (!isArray) {
let hexColor = OlStyleUtil.getHexColor(flatStyleProp);
if (!hexColor) {
throw new Error('Invalid color value');
}
const hasAlpha = hexColor.length > 7;
if (hasAlpha) {
hexColor = hexColor.slice(0, 7);
}
const opacity = OlStyleUtil.getOpacity(flatStyleProp);
return [hexColor, opacity];
}
return [
OlStyleUtil.getHexCodeFromRgbArray(flatStyleProp),
flatStyleProp[3]
];
}
static hasFlatFill(flatStyle) {
const hasFill = ('fill-color' in flatStyle) && flatStyle['fill-color'] !== undefined;
return hasFill;
}
static hasFlatStroke(flatStyle) {
const hasStrokeColor = ('stroke-color' in flatStyle) && flatStyle['stroke-color'] !== undefined;
const hasStrokeWidth = ('stroke-width' in flatStyle) && flatStyle['stroke-width'] !== undefined;
return hasStrokeColor || hasStrokeWidth;
}
static hasFlatText(flatStyle) {
const hasTextValue = ('text-value' in flatStyle) && flatStyle['text-value'] !== undefined;
return hasTextValue;
}
static hasFlatIcon(flatStyle) {
const hasIconSrc = ('icon-src' in flatStyle) && flatStyle['icon-src'] !== undefined;
return hasIconSrc;
}
static hasFlatCircle(flatStyle) {
const hasCircleRadius = ('circle-radius' in flatStyle) && flatStyle['circle-radius'] !== undefined;
return hasCircleRadius;
}
static olExpressionToGsExpression(olExpression) {
if (!OlFlatStyleUtil.isExpression(olExpression)) {
return olExpression;
}
const olExpressionName = olExpression[0];
const functionName = invertedFunctionNameMap[olExpressionName];
let func;
const args = olExpression.slice(1);
switch (functionName) {
case 'case': {
const gsArgs = [];
const fallback = OlFlatStyleUtil.olExpressionToGsExpression(args.pop());
args.forEach((a, index) => {
const gsIndex = Math.floor(index / 2);
if (index % 2 === 0) {
gsArgs[gsIndex] = {
case: OlFlatStyleUtil.olExpressionToGsExpression(a)
};
}
else {
gsArgs[gsIndex] = {
...gsArgs[gsIndex],
value: OlFlatStyleUtil.olExpressionToGsExpression(a)
};
}
});
func = {
name: functionName,
args: [fallback, ...gsArgs]
};
break;
}
case 'interpolate': {
// currently only supporting linear interpolation
const interpolationType = args.shift()[0];
const input = OlFlatStyleUtil.olExpressionToGsExpression(args.shift());
const gsArgs = [];
args.forEach((a, index) => {
const gsIndex = Math.floor(index / 2);
if (index % 2 === 0) {
gsArgs[gsIndex] = {
stop: OlFlatStyleUtil.olExpressionToGsExpression(a)
};
}
else {
gsArgs[gsIndex] = {
...gsArgs[gsIndex],
value: OlFlatStyleUtil.olExpressionToGsExpression(a)
};
}
});
// adding the interpolation type and the input as the first args
gsArgs.unshift({ name: interpolationType }, input);
func = {
name: functionName,
args: gsArgs
};
break;
}
case 'strDefaultIfBlank': {
func = {
name: functionName,
// gs function only allows two args
args: args
.slice(0, 2)
.map(OlFlatStyleUtil.olExpressionToGsExpression)
};
break;
}
case 'in': {
const needle = OlFlatStyleUtil.olExpressionToGsExpression(args.shift());
let haystack = [];
if (args[0] === 'literal') {
haystack = args[0].pop();
}
else {
haystack = args[0];
}
func = {
name: functionName,
args: [needle, ...haystack]
};
break;
}
case 'atan':
case 'atan2': {
const atanFunc = {
name: args.length === 1 ? 'atan' : 'atan2',
args: args.map(OlFlatStyleUtil.olExpressionToGsExpression)
};
if (args.length === 1) {
func = atanFunc;
}
else {
func = atanFunc;
}
break;
}
default:
func = {
name: functionName,
args: args.map(OlFlatStyleUtil.olExpressionToGsExpression)
};
break;
}
return func;
}
static olFilterToGsFilter(olFilter) {
const isExpression = OlFlatStyleUtil.isExpression(olFilter);
const isFilter = OlFlatStyleUtil.isFilter(olFilter);
const isComparisonFilter = OlFlatStyleUtil.isComparisonFilter(olFilter);
if (!isFilter && !isExpression) {
return olFilter;
}
let filter;
if (isFilter) {
const olExpressionName = olFilter[0];
const filterName = invertedFilterMap[olExpressionName];
const args = olFilter.slice(1);
let propertyName = args.shift();
// In GeoStyler, the first argument of a comparison filter
// is the property name as plain string. So if the first argument
// is a 'get' expression, we extract the property name from it.
if (isComparisonFilter && Array.isArray(propertyName) && propertyName[0] === 'get') {
propertyName = propertyName[1];
}
filter = [
filterName,
OlFlatStyleUtil.olFilterToGsFilter(propertyName),
...args.map(OlFlatStyleUtil.olFilterToGsFilter)
];
}
else {
filter = OlFlatStyleUtil.olExpressionToGsExpression(olFilter);
}
return filter;
}
}
export default OlFlatStyleUtil;
//# sourceMappingURL=OlFlatStyleUtil.js.map