geostyler-sld-parser
Version:
GeoStyler Style Parser implementation for SLD
233 lines • 8.18 kB
JavaScript
import { isGeoStylerFunction, isGeoStylerNumberFunction } from 'geostyler-style';
/**
* Cast to Number if it is not a GeoStylerFunction
*
* @param exp The GeoStylerExpression
* @returns The value cast to a number or the GeoStylerNumberFunction
*/
export function numberExpression(exp) {
return isGeoStylerNumberFunction(exp) ? exp : Number(exp);
}
/**
* This converts a GeoStylerFunction into a fast-xml-parser representation
* of a sld function.
*
* @param geostylerFunction A GeoStylerFunction
* @returns
*/
export function geoStylerFunctionToSldFunction(geostylerFunction) {
const { name } = geostylerFunction;
// TODO: Typing of functions without args should be refactored in geostyler-style
if (name === 'pi' || name === 'random') {
return [{
Function: [],
':@': {
'@_name': name
}
}];
}
if (name === 'property') {
return {
PropertyName: [{
'#text': geostylerFunction.args[0]
}]
};
}
const sldFunctionArgs = geostylerFunction.args.map(arg => {
if (isGeoStylerFunction(arg)) {
const argAsFunction = geoStylerFunctionToSldFunction(arg);
return Array.isArray(argAsFunction) ? argAsFunction[0] : argAsFunction;
}
else {
return {
Literal: [{
'#text': arg
}]
};
}
});
return [{
Function: sldFunctionArgs,
':@': {
'@_name': name
}
}];
}
/**
* This converts the fast-xml-parser representation of a sld function into
* a GeoStylerFunction.
*
* @param sldFunction An array of objects as created by the fast-xml-parser
* @returns The GeoStylerFunction
*/
export function sldFunctionToGeoStylerFunction(sldFunction) {
const name = sldFunction?.[0]?.[':@']?.['@_name'];
const args = sldFunction?.[0].Function.map((sldArg) => {
if (sldArg.Function) {
return sldFunctionToGeoStylerFunction([sldArg]);
}
else if (sldArg.PropertyName) {
return {
name: 'property',
args: [sldArg?.PropertyName?.[0]?.['#text']]
};
}
else {
return sldArg?.Literal?.[0]?.['#text'];
}
});
const geoStylerFunction = { name };
if (args.length > 0) {
geoStylerFunction.args = args;
}
return geoStylerFunction;
}
/**
* Get all child objects with a given tag name.
*
* @param elements An array of objects as created by the fast-xml-parser.
* @param tagName The tagName to get.
* @returns An array of objects as created by the fast-xml-parser.
*/
export function getChildren(elements, tagName) {
return elements?.filter(obj => Object.keys(obj).includes(tagName));
}
/**
* Get the value of a parameter from a specific objects in a list of sld elements.
*
* @param elements An array of objects as created by the fast-xml-parser.
* @param paramKey The name of the parameter to find in the elements.
* @param parameter The parameter name to get.
* @returns The string value of the searched parameter.
*/
export function getTextValueInSldObject(elements, parameter, paramKey) {
if (!elements) {
return undefined;
}
const element = elements
.filter(obj => Object.keys(obj)?.includes(paramKey))
.find(obj => obj?.[':@']?.['@_name'] === parameter);
// we expected a value but received an array so we check if we have a function
if (element?.[paramKey]?.[0]?.Function) {
return sldFunctionToGeoStylerFunction(element?.[paramKey]);
}
// … or a Literal
if (element?.[paramKey]?.[0]?.Literal) {
return element?.[paramKey]?.[0]?.Literal?.[0]?.['#text'];
}
return element?.[paramKey]?.[0]?.['#text'];
}
/**
* Get the value of a Css-/SvgParameter.
*
* @param elements An array of objects as created by the fast-xml-parser.
* @param parameter The parameter name to get.
* @param sldVersion The sldVersion to distinguish if CssParameter or SvgParameter is used.
* @returns The string value of the searched parameter.
*/
export function getParameterValue(elements, parameter, sldVersion) {
const paramKey = sldVersion === '1.0.0' ? 'CssParameter' : 'SvgParameter';
return getTextValueInSldObject(elements, parameter, paramKey);
}
/**
* Get the value of a (GeoServer) VendorOption.
*
* @param elements An array of objects as created by the fast-xml-parser.
* @param name The vendorOption name to get.
* @returns The string value of the searched parameter.
*/
export function getVendorOptionValue(elements, name) {
return getTextValueInSldObject(elements, name, 'VendorOption');
}
/**
* Get the attribute value of an object.
*
* @param obj The object to check.
* @param name The name of the attribute
* @returns The value of the requested parameter (if available)
*/
export function getAttribute(obj, name) {
return obj?.[':@']?.[`${name}`];
}
/**
* Determine if a fast-xml-parser object is a symbolizer representation.
*
* @param obj The object to check.
* @returns Whether the passed object is a symbolizer representation or not.
*/
export function isSymbolizer(obj) {
return Object.keys(obj).some(key => key.endsWith('Symbolizer'));
}
/**
* Generic get function which tries to get the nested value of the given object or array.
* It contains some SLD specific handling and tries to be smart but keep the syntax easy.
* It always takes the first child of an array if no index was specified in the path argument.
* e.g.
* Get text value: get(sldSymbolizer, 'Graphic.Mark.WellKnownName.#text')
* Get an attribute value: get(sldSymbolizer, 'Graphic.ExternalGraphic.OnlineResource.@xlink:href')
* Get a Css-/SvgParameter value: get(sldSymbolizer, 'Graphic.Mark.Fill.$fill-opacity', '1.1.0')
* Use with an index: get(sldObject, 'StyledLayerDescriptor.NamedLayer[1].UserStyle.Title.#text')
*
* @param obj A part of the parser result of the fast-xml-parser.
* @param path The path to get the value from.
* @param sldVersion The SLD version to use.
* @returns
*/
export function get(obj, path, sldVersion) {
const parts = path.split(/\.(.*)/s);
let key = parts[0];
const rest = parts[1];
let target = obj;
let index = 0;
// handle queries for attributes
if (rest?.startsWith('@')) {
target = getChildren(obj, key)[index];
return getAttribute(target, rest.substring(1));
}
if (Array.isArray(obj)) {
// we expected a value
if (key === '#text') {
// … so we check if we have a function
if (target[0]?.Function) {
return sldFunctionToGeoStylerFunction(target);
}
// … or a Literal
if (target[0]?.Literal) {
return target[0]?.Literal?.[0]?.['#text'];
}
}
// we expected a value but received an array so we check if we have a function
if (key === '#text' && target[0]?.Function) {
return sldFunctionToGeoStylerFunction(target);
}
// handle queries for CssParameter/SvgParameter
if (key.startsWith('$') && sldVersion) {
return getParameterValue(target, key.substring(1), sldVersion);
}
// handle queries with specified indexes
if (key.endsWith(']')) {
index = Number(key.split('[')[1].split(']')[0]);
key = key.split('[')[0];
}
target = getChildren(obj, key)[index];
}
if (!target) {
return undefined;
}
if (rest) {
return get(target[key], rest, sldVersion);
}
return target[key];
}
/**
* Returns the keys of an object where the value is equal to the passed in
* value.
*
* @param object The object to get the key from.
* @param value The value to get the matching key from.
* @return The matching keys.
*/
export function keysByValue(object, value) {
return Object.keys(object).filter(key => object[key] === value);
}
//# sourceMappingURL=SldUtil.js.map