UNPKG

geostyler-sld-parser

Version:
1,447 lines 71 kB
var Y = Object.defineProperty; var J = (C, e, n) => e in C ? Y(C, e, { enumerable: !0, configurable: !0, writable: !0, value: n }) : C[e] = n; var k = (C, e, n) => J(C, typeof e != "symbol" ? e + "" : e, n); import { isGeoStylerNumberFunction as Q, isGeoStylerFunction as P, isComparisonFilter as Z, isNegationFilter as ee, isCombinationFilter as te } from "geostyler-style"; import { XMLParser as ne, XMLBuilder as ie } from "fast-xml-parser"; import { merge as se, getAttribute as v, getChildren as V, get as d, isSymbolizer as oe, numberExpression as N, getParameterValue as x, sldNumberOperatorOrFunctionOrTextToGeostyler as M, getVendorOptionValue as re, sldFunctionToGeoStylerFunction as ae, keysByValue as j, geoStylerFunctionToSldFunction as T, geoStylerFunctionOrTextToSld as R, getBase64Object as ce, isNumber as W, isString as le } from "./Util/SldUtil.js"; const B = ["1.0.0", "1.1.0"], D = "GeoServer", K = /^ttf:\/\/(.+)#(.+)$/, U = { PropertyIsEqualTo: "==", PropertyIsNotEqualTo: "!=", PropertyIsLike: "*=", PropertyIsLessThan: "<", PropertyIsLessThanOrEqualTo: "<=", PropertyIsGreaterThan: ">", PropertyIsGreaterThanOrEqualTo: ">=", PropertyIsNull: "==", PropertyIsBetween: "<=x<=" }, $ = { Not: "!" }, I = { And: "&&", Or: "||", PropertyIsBetween: "&&" }, q = "http://www.opengeospatial.org/se/units/metre", z = "http://www.opengeospatial.org/se/units/pixel", H = [ "add", "sub", "mul", "div" ], he = { en: { marksymbolizerParseFailedUnknownWellknownName: ({ wellKnownName: C }) => `MarkSymbolizer cannot be parsed. WellKnownName ${C} is not supported.`, noFilterDetected: "No Filter detected.", symbolizerKindParseFailed: ({ sldSymbolizerName: C }) => `Failed to parse SymbolizerKind ${C} from SldRule.`, colorMapEntriesParseFailedColorUndefined: "Cannot parse ColorMapEntries. color is undefined.", contrastEnhancParseFailedHistoAndNormalizeMutuallyExclusive: "Cannot parse ContrastEnhancement. Histogram and Normalize are mutually exclusive.", channelSelectionParseFailedRGBAndGrayscaleMutuallyExclusive: "Cannot parse ChannelSelection. RGB and Grayscale are mutually exclusive.", channelSelectionParseFailedRGBChannelsUndefined: "Cannot parse ChannelSelection. Red, Green and Blue channels must be defined." }, de: {}, fr: { marksymbolizerParseFailedUnknownWellknownName: ({ wellKnownName: C }) => `Échec de lecture du symbole de type MarkSymbolizer. Le WellKnownName ${C} n'est pas supporté.`, noFilterDetected: "Aucun filtre détecté.", symbolizerKindParseFailed: ({ sldSymbolizerName: C }) => `Échec de lecture du type de symbole ${C} à partir de SldRule.`, colorMapEntriesParseFailedColorUndefined: "Lecture de ColorMapEntries échoué. color n'est pas défini.", contrastEnhancParseFailedHistoAndNormalizeMutuallyExclusive: "Échec de lecture des propriétés de contraste ContrastEnhancement échoué. Histogram et Normalize sont mutuellement exclusifs.", channelSelectionParseFailedRGBAndGrayscaleMutuallyExclusive: "Échec de lecture de la sélection de canaux ChannelSelection. RGB et Grayscale sont mutuellement exclusifs.", channelSelectionParseFailedRGBChannelsUndefined: "Échec de lecture de la sélection de canaux ChannelSelection. Les canaux Rouge, Vert et Bleu doivent être définis." } }, u = (C) => C == null; class de { constructor(e) { k(this, "title", "SLD Style Parser"); k(this, "unsupportedProperties", { Symbolizer: { MarkSymbolizer: { avoidEdges: "none", blur: "none", offset: { support: "partial", info: "Only supported for SLD Version 1.1.0" }, offsetAnchor: "none", pitchAlignment: "none", pitchScale: "none", visibility: "none" }, FillSymbolizer: { antialias: "none", opacity: { support: "none", info: "General opacity is not supported. Use fillOpacity and strokeOpacity instead." }, visibility: "none" }, IconSymbolizer: { allowOverlap: "none", anchor: "none", avoidEdges: "none", color: "none", haloBlur: "none", haloColor: "none", haloOpacity: "none", haloWidth: "none", keepUpright: "none", offset: { support: "partial", info: "Only supported for SLD Version 1.1.0" }, offsetAnchor: "none", optional: "none", padding: "none", pitchAlignment: "none", rotationAlignment: "none", textFit: "none", image: { support: "partial", info: "Sprites are not supported" }, textFitPadding: "none", visibility: "none" }, LineSymbolizer: { blur: "none", gapWidth: "none", gradient: "none", miterLimit: "none", roundLimit: "none", spacing: "none", visibility: "none" }, RasterSymbolizer: { brightnessMax: "none", brightnessMin: "none", contrast: "none", fadeDuration: "none", hueRotate: "none", resampling: "none", saturation: "none", visibility: "none" }, TextSymbolizer: { placement: { support: "partial", info: 'Only "line" and "point" are currently supported' } } } }); k(this, "translations", he); k(this, "locale", "en"); k(this, "_parser"); k(this, "_builder"); /** * Array of field / property names in a filter, which are casted to numerics * while parsing a SLD. */ k(this, "_numericFilterFields", []); /** * Array of field / property names in a filter, which are casted to boolean * while parsing a SLD. */ k(this, "_boolFilterFields", []); /** * String indicating the SLD version to use. 1.1.0 will make use of * Symbology Encoding. */ k(this, "_sldVersion", "1.0.0"); /** * Indicate the sld environment to parse the SLD or write the SLD. * This allows or restrict some SLD tags. * @private */ k(this, "_sldEnvironment", null); /** * String indicating the SLD version used in reading mode */ k(this, "_readingSldVersion", "1.0.0"); /** * Used to add a `uom` attribute to the symbolizer tag. Can be one of * `metre`, `foot` or `pixel`. Defaults to pixel. */ k(this, "_symbolizerUnits", "pixel"); /** * Create a template string from a TextSymbolizer Label element. * The ordering of the elemments inside the Label element is preserved. * * Examples: * <Label> * <Literal>foo</Literal> * <PropertyName>bar</PropertyName> * </Label> * --> "foo{{bar}}" * * <Label> * <PropertyName>bar</PropertyName> * <Literal>foo</Literal> * </Label> * --> "{{bar}}foo" * * <Label> * <PropertyName>bar</PropertyName> * <Literal>foo</Literal> * <PropertyName>john</PropertyName> * </Label> * --> "{{bar}}foo{{john}}" * * <Label> * <PropertyName>bar</PropertyName> * <PropertyName>john</PropertyName> * <Literal>foo</Literal> * </Label> * --> "{{bar}}{{john}}foo" * * <Label> * <PropertyName>bar</PropertyName> * <PropertyName>john</PropertyName> * <Literal>foo</Literal> * <PropertyName>doe</PropertyName> * </Label> * --> "{{bar}}{{john}}foo{{doe}}" * * @param sldLabel */ k(this, "getTextSymbolizerLabelFromSldSymbolizer", (e) => { var t; if (Array.isArray(e) && e[0] && typeof e[0] == "object" && (e[0].Function || e[0]["ogc:Function"])) { const i = e[0], s = i.Function || i["ogc:Function"], c = (t = i[":@"]) == null ? void 0 : t["@_name"], a = (r) => { var h, l, p, m, y, F, f, g; return r.PropertyName || r["ogc:PropertyName"] ? { name: "property", args: [((l = (h = r.PropertyName) == null ? void 0 : h[0]) == null ? void 0 : l["#text"]) ?? ((m = (p = r["ogc:PropertyName"]) == null ? void 0 : p[0]) == null ? void 0 : m["#text"])] } : r.Literal || r["ogc:Literal"] ? ((F = (y = r.Literal) == null ? void 0 : y[0]) == null ? void 0 : F["#text"]) !== void 0 ? r.Literal[0]["#text"] : ((g = (f = r["ogc:Literal"]) == null ? void 0 : f[0]) == null ? void 0 : g["#text"]) ?? "" : r.Function || r["ogc:Function"] ? this.getTextSymbolizerLabelFromSldSymbolizer([r]) : r; }, o = s.map(a); return { name: c, args: o }; } return e.map((i) => { var c, a, o, r, h, l; const s = Object.keys(i)[0]; switch (s.replace("ogc:", "")) { case "#text": return i["#text"]; case "Literal": return ((a = (c = i == null ? void 0 : i[s]) == null ? void 0 : c[0]) == null ? void 0 : a["#text"]) || ((l = (h = (r = (o = i == null ? void 0 : i[s]) == null ? void 0 : o[0]) == null ? void 0 : r["#cdata"]) == null ? void 0 : h[0]) == null ? void 0 : l["#text"]); case "PropertyName": return `{{${i[s][0]["#text"]}}}`; default: return ""; } }).join(""); }); /** * Get the Label from a TextSymbolizer * * @param template The Expression<string> representing the label */ k(this, "getSldLabelFromTextSymbolizer", (e) => { if (le(e)) { const n = "{{", t = "}}", i = []; let s = e; for (; s.length; ) { let c = s, a; const o = c.indexOf(n); if (o === -1) { s.includes(" ") || s.includes(` `) ? i.push({ "ogc:Literal": [{ "#cdata": [{ "#text": s }] }] }) : i.push({ "ogc:Literal": [{ "#text": s }] }); break; } o > 0 && (a = c.slice(0, o)), c = c.slice(o + n.length); const r = c.indexOf(t); if (r === -1) { s.includes(" ") || s.includes(` `) ? i.push({ "ogc:Literal": [{ "#cdata": [{ "#text": s }] }] }) : i.push({ "ogc:Literal": [{ "#text": s }] }); break; } const h = c.slice(0, r); c = c.slice(r + t.length), a && (a.includes(" ") || a.includes(` `) ? i.push({ "ogc:Literal": [{ "#cdata": [{ "#text": a }] }] }) : i.push({ "ogc:Literal": [{ "#text": a }] })), i.push({ "ogc:PropertyName": [{ "#text": h }] }), s = c; } return i; } return this.geoStylerFunctionToSldFunctionRecursive(e); }); this.parser = new ne({ ...e == null ? void 0 : e.parserOptions, // Fixed attributes ignoreDeclaration: !0, removeNSPrefix: !0, ignoreAttributes: !1, preserveOrder: !0, trimValues: !0 }), this.builder = new ie({ ...e == null ? void 0 : e.builderOptions, // Fixed attributes cdataPropName: "#cdata", ignoreAttributes: !1, suppressEmptyNode: !0, preserveOrder: !0 }), e != null && e.sldVersion && (this.sldVersion = e == null ? void 0 : e.sldVersion), (e == null ? void 0 : e.sldEnvironment) !== void 0 && (this.sldEnvironment = e.sldEnvironment), e != null && e.locale && (this.locale = e.locale), e != null && e.translations && (this.translations = se(this.translations, e.translations)), Object.assign(this, e); } translate(e, n) { var i, s; const t = ((s = (i = this.translations) == null ? void 0 : i[this.locale]) == null ? void 0 : s[e]) ?? e; return typeof t == "function" ? t(n) : t; } get parser() { return this._parser; } set parser(e) { this._parser = e; } get builder() { return this._builder; } set builder(e) { this._builder = e; } /** * Getter for _numericFilterFields * @return The numericFilterFields */ get numericFilterFields() { return this._numericFilterFields; } /** * Setter for _numericFilterFields * @param numericFilterFields The numericFilterFields to set */ set numericFilterFields(e) { this._numericFilterFields = e; } /** * Getter for _boolFilterFields * @return The boolFilterFields */ get boolFilterFields() { return this._boolFilterFields; } /** * Setter for _boolFilterFields * @param boolFilterFields The boolFilterFields to set */ set boolFilterFields(e) { this._boolFilterFields = e; } /** * Getter for _sldVersion * @return */ get sldVersion() { return this._sldVersion; } /** * Setter for _sldVersion * @param sldVersion The _sldVersion value to set */ set sldVersion(e) { this._sldVersion = e; } /** * Getter for _sldEnvironment * @return SldEnvironment or null. */ get sldEnvironment() { return this._sldEnvironment; } /** * Setter for _sldEnvironment * @param a SldEnvironment or null. */ set sldEnvironment(e) { this._sldEnvironment = e; } /** * Check if the given SldEnvironment match the current environment. * @param env the SldEnvironment to check. * @private */ isSldEnv(e) { return this.sldEnvironment === e; } /** * Getter for _readingSldVersion * @return */ get readingSldVersion() { return this._readingSldVersion; } /** * Setter for _readingSldVersion * @param sldVersion The _readingSldVersion value to set */ set readingSldVersion(e) { this._readingSldVersion = e; } /** * Getter for _symbolizerUnits * @return {string} */ get symbolizerUnits() { return this._symbolizerUnits; } /** * Setter for _symbolizerUnits * @param {string} symbolizerUnits The _symbolizerUnits value to set */ set symbolizerUnits(e) { this._symbolizerUnits = e; } /** * The readStyle implementation of the geostyler-style StyleParser interface. * It reads a SLD as a string and returns a Promise. * The Promise itself resolves with an object containing the geostyler-style. * * @param sldString A SLD as a string. * @return The Promise resolving with an object containing the geostyler-style. */ readStyle(e) { return new Promise((n) => { try { const t = this.parser.parse(e), i = v(t[0], "version"); if (!B.includes(i)) throw new Error(`SLD version must be ${B.toString()}`); this._readingSldVersion = i; const s = this.sldObjectToGeoStylerStyle(t); n({ output: s }); } catch (t) { n({ errors: [t] }); } }); } /** * Get the geostyler-style from a SLD Object (created with fast-xml-parser). * * @param sldObject The SLD object representation (created with fast-xml-parser) * @return The geostyler-style */ sldObjectToGeoStylerStyle(e) { const n = this.getRulesFromSldObject(e); return { name: this.getStyleNameFromSldObject(e), rules: n }; } /** * Get the geostyler-style rules from a SLD Object (created with fast-xml-parser). * * @param sldObject The SLD object representation (created with fast-xml-parser) * @return The geostyler-style rules */ getRulesFromSldObject(e) { const n = V(e[0].StyledLayerDescriptor, "NamedLayer"), t = []; return n.forEach(({ NamedLayer: i }) => { V(i, "UserStyle").forEach(({ UserStyle: s }) => { V(s, "FeatureTypeStyle").forEach(({ FeatureTypeStyle: c }) => { V(c, "Rule").forEach(({ Rule: a }) => { const o = this.getFilterFromRule(a), r = this.getScaleDenominatorFromRule(a), h = this.getSymbolizersFromRule(a), l = d(a, "Title.#text"), p = d(a, "Name.#text"), y = { name: l !== void 0 ? l : p !== void 0 ? p : "" }; o && (y.filter = o), r && (y.scaleDenominator = r), h && (y.symbolizers = h), t.push(y); }); }); }); }), t; } /** * Get the name for the Style from the SLD Object. Returns the Title of the UserStyle * if defined or the Name of the NamedLayer if defined or an empty string. * * @param sldObject The SLD object representation (created with fast-xml-parser) * @return The name to be used for the GeoStyler Style Style */ getStyleNameFromSldObject(e) { const n = d(e, "StyledLayerDescriptor.NamedLayer[0].UserStyle.Name.#text"), t = d(e, "StyledLayerDescriptor.NamedLayer.Name.#text"); return n || t || ""; } /** * Get the geostyler-style Filter from a SLD Rule. * * Currently only supports one Filter per Rule. * * @param sldRule The SLD Rule * @return The geostyler-style Filter */ getFilterFromRule(e) { var s; const n = d(e, "Filter"); if (!n || n.length === 0) return; const t = (s = Object.keys(n[0])) == null ? void 0 : s[0]; return t ? this.getFilterFromOperatorAndComparison(t, n) : void 0; } /** * Get the geostyler-style ScaleDenominator from a SLD Rule. * * @param sldRule The SLD Rule * @return The geostyler-style ScaleDenominator */ getScaleDenominatorFromRule(e) { const n = {}, t = d(e, "MinScaleDenominator.#text"); t && (n.min = Number(t)); const i = d(e, "MaxScaleDenominator.#text"); return i && (n.max = Number(i)), Number.isFinite(n.min) || Number.isFinite(n.max) ? n : void 0; } /** * Get the geostyler-style Symbolizers from a SLD Rule. * * @param sldRule The SLD Rule * @return The geostyler-style Symbolizer array */ getSymbolizersFromRule(e) { return e.filter(oe).map((t) => { const i = Object.keys(t)[0]; switch (i) { case "PointSymbolizer": return this.getPointSymbolizerFromSldSymbolizer(t); case "LineSymbolizer": return this.getLineSymbolizerFromSldSymbolizer(t); case "TextSymbolizer": return this.getTextSymbolizerFromSldSymbolizer(t); case "PolygonSymbolizer": return this.getFillSymbolizerFromSldSymbolizer(t); case "RasterSymbolizer": return this.getRasterSymbolizerFromSldSymbolizer(t); default: throw new Error(this.translate("symbolizerKindParseFailed", { sldSymbolizerName: i })); } }); } /** * Creates a geostyler-style Filter from a given operator name and the js * SLD object representation (created with fast-xml-parser) of the SLD Filter. * * @param sldOperatorName The Name of the SLD Filter Operator * @param sldFilter The SLD Filter * @return The geostyler-style Filter */ getFilterFromOperatorAndComparison(e, n) { var i; let t; if (e === "Function") { const s = Array.isArray(n) ? n[0][":@"]["@_name"] : n[":@"]["@_name"]; e = `PropertyIs${s.charAt(0).toUpperCase() + s.slice(1)}`; } if (e === "PropertyIsBetween") { const s = d(n, "PropertyIsBetween.PropertyName.#text"), c = Number(d(n, "PropertyIsBetween.LowerBoundary.Literal.#text")), a = Number(d(n, "PropertyIsBetween.UpperBoundary.Literal.#text")); t = ["<=x<=", s, c, a]; } else if (Object.keys(U).includes(e)) t = this.getFilterFromComparisonOperator(e, n); else if (Object.keys(I).includes(e)) { const s = I[e], c = (i = d(n, e)) == null ? void 0 : i.map((a) => { var r; const o = (r = Object.keys(a)) == null ? void 0 : r[0]; return this.getFilterFromOperatorAndComparison(o, a); }); t = [ s, ...c ]; } else if (Object.keys($).includes(e)) { const s = $[e], c = n.length ? Object.keys(n[0][e][0])[0] : Object.keys(n[e][0])[0], a = n.length ? n[0][e][0] : n[e][0], o = this.getFilterFromOperatorAndComparison( c, a ); t = [ s, o ]; } else throw new Error(this.translate("noFilterDetected")); return t; } getFilterFromComparisonOperator(e, n) { if (e === "Function") { const a = Array.isArray(n) ? n[0][":@"]["@_name"] : n[":@"]["@_name"]; e = `PropertyIs${a.charAt(0).toUpperCase() + a.slice(1)}`; } const t = U[e], i = !!d(n, "Function"); let s = []; const c = d(n, i ? "Function" : e) || []; return s = c.map((a, o) => { var h; const r = (h = Object.keys(a)) == null ? void 0 : h[0]; if (H.includes(r.toLowerCase())) { const l = a[r]; return this.getFilterArgsFromArithmeticOperators(r, l); } return this.getFilterArgsFromPropertyName(a, c, o); }), e === "PropertyIsNull" && (s[1] = null), [ t, ...s ]; } /** * Creates a FunctionCall from arithmetic operators in SLD filters. * Handles nested arithmetic operations recursively. */ getFilterArgsFromArithmeticOperators(e, n) { const [t, i] = n; return { name: e.toLowerCase(), args: [ this.processArithmeticOperand(t, n), this.processArithmeticOperand(i, n) ] }; } /** * Processes a single operand in an arithmetic operation. * If the operand is itself an arithmetic operator, processes it recursively. */ processArithmeticOperand(e, n) { var i; const t = (i = Object.keys(e)) == null ? void 0 : i[0]; return t && H.includes(t.toLowerCase()) ? this.getFilterArgsFromArithmeticOperators(t, e[t]) : this.getFilterArgsFromPropertyName(e, n, 0); } getFilterArgsFromPropertyName(e, n, t) { const i = d([e], "PropertyName.#text"); return i !== void 0 ? n.length === 1 || t === 0 && d([n[1]], "PropertyName.#text") === void 0 ? i : { name: "property", args: [i] } : d([e], "#text"); } /** * Get offset from Displacement. * In SLD positive y values mean a displacement to the top. * In geostyler-style positive y values mean a displacement to the bottom. * Thus the y value is inverted. * * @param displacement The SLD Displacement object as written by fast-xml-parser * @returns The offset as tuple or undefined if displacement is undefined */ getOffsetFromDisplacement(e) { if (u(e)) return; const n = Number(d(e, "DisplacementX.#text")), t = Number(d(e, "DisplacementY.#text")); return [ Number.isFinite(n) ? N(n) : 0, Number.isFinite(t) && t !== 0 ? -N(t) : 0 ]; } /** * Get the geostyler-style PointSymbolizer from a SLD Symbolizer. * * The opacity of the Symbolizer is taken from the <Graphic>. * * @param sldSymbolizer The SLD Symbolizer * @return The geostyler-style PointSymbolizer */ getPointSymbolizerFromSldSymbolizer(e, n = void 0) { const t = e.PointSymbolizer; let i; return d(t, "Graphic.Mark.WellKnownName.#text"), d(t, "Graphic.ExternalGraphic") ? i = this.getIconSymbolizerFromSldSymbolizer(e) : i = this.getMarkSymbolizerFromSldSymbolizer(e), this.addGeometrySymbolizerFromSld(i, t), i; } /** * Get the geostyler-style LineSymbolizer from a SLD Symbolizer. * * Currently only the CssParameters are available. * * @param sldSymbolizer The SLD Symbolizer * @return The geostyler-style LineSymbolizer */ getLineSymbolizerFromSldSymbolizer(e) { const n = e.LineSymbolizer, t = this.getDistanceUnit(e), i = { kind: "Line" }, s = d(n, "Stroke", this.readingSldVersion), c = x(s, "stroke", this.readingSldVersion), a = x(s, "stroke-width", this.readingSldVersion), o = x(s, "stroke-opacity", this.readingSldVersion), r = x(s, "stroke-linejoin", this.readingSldVersion), h = x(s, "stroke-linecap", this.readingSldVersion), l = x(s, "stroke-dasharray", this.readingSldVersion), p = x(s, "stroke-dashoffset", this.readingSldVersion); if (u(c) || (i.color = c), u(a) || (i.width = N(a)), u(t) || (i.widthUnit = t), u(o) || (i.opacity = N(o)), u(r) || (r === "mitre" ? i.join = "miter" : i.join = r), u(h) || (i.cap = h), !u(l)) { const f = l.split(" ").map(N); i.dasharray = f; } u(p) || (i.dashOffset = N(p)); const m = d(s, "GraphicStroke"); u(m) || (i.graphicStroke = this.getPointSymbolizerFromSldSymbolizer( { PointSymbolizer: m }, t )); const y = d(s, "GraphicFill"); u(y) || (i.graphicFill = this.getPointSymbolizerFromSldSymbolizer( { PointSymbolizer: y }, t )); const F = d(n, "PerpendicularOffset.#text"); return u(F) || (i.perpendicularOffset = N(F)), this.addGeometrySymbolizerFromSld(i, n), i; } /** * Get the geostyler-style TextSymbolizer from a SLD Symbolizer. * * @param sldSymbolizer The SLD Symbolizer * @return The geostyler-style TextSymbolizer */ getTextSymbolizerFromSldSymbolizer(e) { const n = e.TextSymbolizer, t = this.getDistanceUnit(e), i = { kind: "Text" }, s = d(n, "Font"), c = d(n, "Fill"), a = d(n, "Label"), o = d(n, "Halo"), r = d(o, "Fill"), h = x(c, "fill", this.readingSldVersion), l = x(c, "fill-opacity", this.readingSldVersion), p = x(s, "font-family", this.readingSldVersion), m = x(s, "font-style", this.readingSldVersion), y = x(s, "font-size", this.readingSldVersion), F = x(s, "font-weight", this.readingSldVersion), f = x(r, "fill", this.readingSldVersion); u(a) || (i.label = this.getTextSymbolizerLabelFromSldSymbolizer(a)), i.color = h || "#000000", u(l) || (i.opacity = N(l)); const g = d(n, "Halo.Radius.#text"); u(g) || (i.haloWidth = N(g)), u(t) || (i.haloWidthUnit = t); const O = x(r, "fill-opacity", this.readingSldVersion); u(O) || (i.haloOpacity = N(O)), u(f) || (i.haloColor = f); const E = d(n, "LabelPlacement"); if (!u(E)) { const G = d(E, "PointPlacement"), w = d(E, "LinePlacement"); if (u(G)) { if (!u(w) && (i.placement = "line", this.readingSldVersion === "1.1.0")) { const _ = d(w, "IsRepeated.#text"), A = d(w, "Gap.#text"); _ && !u(A) && (i.repeat = N(A)); } } else { i.placement = "point"; const _ = d(G, "AnchorPoint"); if (!u(_)) { const L = d(_, "AnchorPointX.#text"), X = d(_, "AnchorPointY.#text"); i.anchor = this.getAnchorFromSldAnchorPoint(L, X); } const A = d(G, "Displacement"), S = this.getOffsetFromDisplacement(A); u(S) || (i.offset = S); const b = d(G, "Rotation"); u(b) || (i.rotate = M(b[0])); } } return u(p) || (i.font = [p]), u(m) || (i.fontStyle = m.toLowerCase()), u(F) || (i.fontWeight = F.toLowerCase()), u(y) || (i.size = N(y)), u(t) || (i.sizeUnit = t), this.addGeometrySymbolizerFromSld(i, n), i; } /** * Get the geostyler-style FillSymbolizer from a SLD Symbolizer. * * PolygonSymbolizer Stroke is just partially supported. * * @param sldSymbolizer The SLD Symbolizer * @return The geostyler-style FillSymbolizer */ getFillSymbolizerFromSldSymbolizer(e) { const n = e.PolygonSymbolizer, t = this.getDistanceUnit(e), i = { kind: "Fill" }, s = d(n, "Stroke"), c = d(n, "Fill"), a = x(c, "fill-opacity", this.readingSldVersion), o = x(c, "fill", this.readingSldVersion), r = x(s, "stroke", this.readingSldVersion), h = x(s, "stroke-width", this.readingSldVersion), l = x(s, "stroke-opacity", this.readingSldVersion), p = x(s, "stroke-dasharray", this.readingSldVersion), m = x(s, "stroke-linecap", this.readingSldVersion), y = x(s, "stroke-linejoin", this.readingSldVersion), F = d(n, "Fill.GraphicFill"); if (u(F) || (i.graphicFill = this.getPointSymbolizerFromSldSymbolizer( { PointSymbolizer: F } )), this.isSldEnv(D)) { const f = re(n, "graphic-margin"); u(f) || (i.graphicFillPadding = f.split(/\s/).map(N)); } return u(o) || (i.color = o), u(a) || (i.fillOpacity = N(a)), u(r) || (i.outlineColor = r), u(h) || (i.outlineWidth = N(h)), u(t) || (i.outlineWidthUnit = t), u(l) || (i.outlineOpacity = N(l)), u(p) || (i.outlineDasharray = p.split(" ").map(N)), u(m) || (i.outlineCap = m), u(y) || (i.outlineJoin = y), this.addGeometrySymbolizerFromSld(i, n), i; } /** * Get the geostyler-style RasterSymbolizer from a SLD Symbolizer. * * @param sldSymbolizer The SLD Symbolizer */ getRasterSymbolizerFromSldSymbolizer(e) { const n = e.RasterSymbolizer, t = { kind: "Raster" }; let i = d(n, "Opacity.#text"); u(i) || (i = N(i), t.opacity = i); const s = d(n, "ColorMap"), c = d(n, "ColorMap.@type"), a = d(n, "ColorMap.@extended"); if (!u(s)) { const h = this.getColorMapFromSldColorMap(s, c, a); t.colorMap = h; } const o = d(n, "ChannelSelection"); if (!u(o)) { const h = this.getChannelSelectionFromSldChannelSelection(o); t.channelSelection = h; } const r = d(n, "ContrastEnhancement"); if (!u(r)) { const h = this.getContrastEnhancementFromSldContrastEnhancement(r); t.contrastEnhancement = h; } return this.addGeometrySymbolizerFromSld(t, n), t; } /** * Get the geostyler-style MarkSymbolizer from a SLD Symbolizer * * @param sldSymbolizer The SLD Symbolizer * @return The geostyler-style MarkSymbolizer */ getMarkSymbolizerFromSldSymbolizer(e) { const n = e.PointSymbolizer, t = this.getDistanceUnit(e), i = d(n, "Graphic.Mark.WellKnownName.#text"), s = d(n, "Graphic.Mark.Stroke"), c = d(n, "Graphic.Mark.Fill"), a = d(n, "Graphic.Opacity.#text"), o = d(n, "Graphic.Size.#text"), r = d(n, "Graphic.Rotation"), h = x(c, "fill-opacity", this.readingSldVersion), l = x(c, "fill", this.readingSldVersion), p = d(n, "Graphic.Displacement"), m = { kind: "Mark", wellKnownName: "circle" }; u(a) || (m.opacity = N(a)), u(h) || (m.fillOpacity = N(h)), u(l) || (m.color = l), u(r) || (m.rotate = M(r[0])), u(o) || (m.radius = Q(o) ? o : Number(o) / 2), u(t) || (m.radiusUnit = t); const y = this.getOffsetFromDisplacement(p); switch (u(y) || (m.offset = y), i) { case "arrow": case "arrowhead": case "asterisk_fill": case "backslash": case "circle": case "cross": case "cross2": case "cross_fill": case "decagon": case "diagonal_half_square": case "diamond": case "equilateral_triangle": case "filled_arrowhead": case "half_arc": case "half_square": case "heart": case "hexagon": case "horline": case "left_half_triangle": case "line": case "octagon": case "parallelogram_left": case "parallelogram_right": case "pentagon": case "quarter_arc": case "quarter_circle": case "quarter_square": case "right_half_triangle": case "rounded_square": case "semi_circle": case "shield": case "slash": case "square": case "square_with_corners": case "star": case "star_diamond": case "third_arc": case "third_circle": case "trapezoid": case "triangle": case "x": case "shape://vertline": case "shape://horline": case "shape://slash": case "shape://backslash": case "shape://dot": case "shape://plus": case "shape://times": case "shape://oarrow": case "shape://carrow": case "brush://dense1": case "brush://dense2": case "brush://dense3": case "brush://dense4": case "brush://dense5": case "brush://dense6": case "brush://dense7": m.wellKnownName = i; break; case void 0: m.wellKnownName = "square"; break; default: if (K.test(i)) { m.wellKnownName = i; break; } throw new Error( this.translate("marksymbolizerParseFailedUnknownWellknownName", { wellKnownName: i }) ); } const F = x(s, "stroke", this.readingSldVersion); u(F) || (m.strokeColor = F); const f = x(s, "stroke-width", this.readingSldVersion); u(f) || (m.strokeWidth = N(f)), u(t) || (m.strokeWidthUnit = t); const g = x(s, "stroke-opacity", this.readingSldVersion); u(g) || (m.strokeOpacity = N(g)); const O = x(s, "stroke-dasharray", this.readingSldVersion); if (!u(O)) { const E = O.split(" ").map(N); m.strokeDasharray = E; } return m; } /** * Get the geostyler-style IconSymbolizer from a SLD Symbolizer * * @param sldSymbolizer The SLD Symbolizer * @return The geostyler-style IconSymbolizer */ getIconSymbolizerFromSldSymbolizer(e, n = void 0) { const t = e.PointSymbolizer, i = n || this.getDistanceUnit(e); let s = d(t, "Graphic.ExternalGraphic.OnlineResource.@href"); if (!s && this.sldVersion === "1.1.0" && d(t, "Graphic.ExternalGraphic.InlineContent.@encoding") === "base64") { const m = d(t, "Graphic.ExternalGraphic.Format.#text"), y = d(t, "Graphic.ExternalGraphic.InlineContent.#text"); s = `data:${m};base64,${y}`; } const c = { kind: "Icon", image: s }, a = d(t, "Graphic.Opacity.#text"), o = d(t, "Graphic.Size.#text"), r = d(t, "Graphic.Rotation"), h = d(t, "Graphic.Displacement"); u(a) || (c.opacity = N(a)), u(o) || (c.size = N(o)), u(i) || (c.sizeUnit = i), u(r) || (c.rotate = M(r[0])); const l = this.getOffsetFromDisplacement(h); return u(l) || (c.offset = l), c; } /** * Add a "Geometry" function or text from a Sld Symbolizer Geometry nested value. */ addGeometrySymbolizerFromSld(e, n) { const t = d(n, "Geometry"); if (!t) return; const i = ae(t); i && (e.geometry = i); } /** * Get the geostyler-style ColorMap from a SLD ColorMap. * * @param sldColorMap The SLD ColorMap */ getColorMapFromSldColorMap(e, n = "ramp", t) { const i = { type: n }; t && (t === "true" ? i.extended = !0 : i.extended = !1); const s = V(e, "ColorMapEntry"); if (Array.isArray(s)) { const c = s.map((a) => { const o = v(a, "color"); if (!o) throw new Error(this.translate("colorMapEntriesParseFailedColorUndefined")); let r = v(a, "quantity"); r && (r = N(r)); const h = v(a, "label"); let l = v(a, "opacity"); return u(l) || (l = N(l)), { color: o, quantity: r, label: h, opacity: l }; }); i.colorMapEntries = c; } return i; } /** * Get the geostyler-style ContrastEnhancement from a SLD ContrastEnhancement. * * @param sldContrastEnhancement The SLD ContrastEnhancement */ getContrastEnhancementFromSldContrastEnhancement(e) { const n = {}, t = !!d(e, "Histogram"), i = !!d(e, "Normalize"); if (t && i) throw new Error(this.translate("contrastEnhancParseFailedHistoAndNormalizeMutuallyExclusive")); t ? n.enhancementType = "histogram" : i && (n.enhancementType = "normalize"); let s = d(e, "GammaValue.#text"); return s && (s = N(s)), n.gammaValue = s, n; } /** * Get the geostyler-style Channel from a SLD Channel. * * @param sldChannel The SLD Channel */ getChannelFromSldChannel(e) { var s; const t = { sourceChannelName: (s = d(e, "SourceChannelName.#text")) == null ? void 0 : s.toString() }, i = d(e, "ContrastEnhancement"); return i && (t.contrastEnhancement = this.getContrastEnhancementFromSldContrastEnhancement(i)), t; } /** * Get the geostyler-style ChannelSelection from a SLD ChannelSelection. * * @param sldChannelSelection The SLD ChannelSelection */ getChannelSelectionFromSldChannelSelection(e) { let n; const t = d(e, "RedChannel"), i = d(e, "BlueChannel"), s = d(e, "GreenChannel"), c = d(e, "GrayChannel"); if (c && t && i && s) throw new Error(this.translate("channelSelectionParseFailedRGBAndGrayscaleMutuallyExclusive")); if (c) n = { grayChannel: this.getChannelFromSldChannel(c) }; else if (t && s && i) { const a = this.getChannelFromSldChannel(t), o = this.getChannelFromSldChannel(i), r = this.getChannelFromSldChannel(s); n = { redChannel: a, blueChannel: o, greenChannel: r }; } else throw new Error(this.translate("channelSelectionParseFailedRGBChannelsUndefined")); return n; } /** * The writeStyle implementation of the geostyler-style StyleParser interface. * It reads a geostyler-style and returns a Promise. * The Promise itself resolves with a SLD string. * * @param geoStylerStyle A geostyler-style. * @return The Promise resolving with the SLD as a string. */ writeStyle(e) { return new Promise((n) => { const t = this.checkForUnsupportedProperties(e); try { const i = this.geoStylerStyleToSldObject(e), s = this.builder.build(i); n({ output: s, unsupportedProperties: t, warnings: t && ["Your style contains unsupportedProperties!"] }); } catch (i) { n({ errors: [i] }); } }); } /** * Get the correct tagName in dependency to the configured sldVersion. * * @param tagName * @returns The tagName as used by the configured sldVersion */ getTagName(e) { return ["Filter"].includes(e) ? e : e === "CssParameter" ? this.sldVersion === "1.1.0" ? "se:SvgParameter" : "CssParameter" : this.sldVersion === "1.1.0" ? `se:${e}` : e; } /** * Get the SLD Object (readable with fast-xml-parser) from a geostyler-style * * @param geoStylerStyle A geostyler-style. * @return The object representation of a SLD Style (readable with fast-xml-parser) */ geoStylerStyleToSldObject(e) { const n = this.getSldRulesFromRules(e.rules); n.forEach((r) => { const h = d(r, this.getTagName("Rule")), l = V(h, "Filter").at(0); l && (l[":@"] = { "@_xmlns": "http://www.opengis.net/ogc" }); }); const t = [ ...n ], i = this.getTagName("Name"), s = this.getTagName("Title"), c = this.getTagName("FeatureTypeStyle"), a = { "@_version": this.sldVersion, "@_xsi:schemaLocation": "http://www.opengis.net/sld StyledLayerDescriptor.xsd", "@_xmlns": "http://www.opengis.net/sld", "@_xmlns:ogc": "http://www.opengis.net/ogc", "@_xmlns:xlink": "http://www.w3.org/1999/xlink", "@_xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", "@_xmlns:se": "http://www.opengis.net/se" }, o = []; return o.push({ [i]: [{ "#text": e.name || "" }] }), this.sldVersion === "1.0.0" && o.push({ [s]: [{ "#text": e.name || "" }] }), o.push({ [c]: t }), [{ "?xml": [{ "#text": "" }], ":@": { "@_version": "1.0", "@_encoding": "UTF-8", "@_standalone": "yes" } }, { StyledLayerDescriptor: [{ NamedLayer: [{ [i]: [{ "#text": e.name || "" }] }, { UserStyle: o }] }], ":@": a }]; } /** * Get the SLD Object (readable with fast-xml-parser) from a geostyler-style Rule. * * @param rules An array of geostyler-style Rules. * @return The object representation of a SLD Rule (readable with fast-xml-parser) */ getSldRulesFromRules(e) { const n = this.getTagName("Name"), t = this.getTagName("Filter"), i = this.getTagName("Rule"), s = this.getTagName("MinScaleDenominator"), c = this.getTagName("MaxScaleDenominator"); return e.map((a) => { const o = { [i]: [] }; if (a.name && o[i].push({ [n]: [{ "#text": a.name }] }), a.filter) { const l = this.getSldFilterFromFilter(a.filter); o[i].push({ [t]: l }); } if (a.scaleDenominator) { const { min: l, max: p } = a.scaleDenominator; l && Number.isFinite(l) && o[i].push({ [s]: [{ "#text": l }] }), p && Number.isFinite(p) && o[i].push({ [c]: [{ "#text": p }] }); } const r = this.getSldSymbolizersFromSymbolizers(a.symbolizers); let h = []; return r.length > 0 && (h = Object.keys(r[0])), h.forEach((l) => { r[0][l].length === 0 && delete r[0][l]; }), r.length > 0 && h.length !== 0 && (o[i] = [ ...o[i], ...r ]), o; }); } /** * Get the SLD Object (readable with fast-xml-parser) from a geostyler-style ComparisonFilter. * * @param comparisonFilter A geostyler-style ComparisonFilter. * @return The object representation of a SLD Filter Expression with a * comparison operator (readable with fast-xml-parser) */ getSldComparisonFilterFromComparisonFilter(e) { const n = [], t = e[0], i = e[1], s = e[2], c = j(U, t), a = c.length > 1 && s === null ? c[1] : c[0], o = `ogc:${a}`, r = "ogc:Function", h = "ogc:PropertyName", l = "ogc:Literal"; if (P(i) || P(s)) { const p = a.replace("PropertyIs", ""), m = p.charAt(0).toLowerCase() + p.slice(1), y = P(i) ? T(i) : i, F = P(s) ? T(s) : s, f = []; return P(i) ? f.unshift(Array.isArray(y) ? y == null ? void 0 : y[0] : y) : f.unshift({ [l]: [{ "#text": y }] }), P(s) ? f.push(Array.isArray(F) ? F == null ? void 0 : F[0] : F) : f.push({ [l]: [{ "#text": F }] }), [{ [r]: f, ":@": { "@_name": m } }]; } if (a === "PropertyIsNull") n.push({ [o]: [{ [h]: [{ "#text": i }] }] }); else if (a === "PropertyIsLike") n.push({ [o]: [{ [h]: [{ "#text": i }] }, { [l]: [{ "#text": s }] }], ":@": { "@_wildCard": "*", "@_singleChar": ".", "@_escape": "!" } }); else if (a === "PropertyIsBetween") { const p = e; n.push({ [o]: [{ [h]: [{ "#text": i }] }, { "ogc:LowerBoundary": [{ [l]: [{ "#text": p[2] }] }] }, { "ogc:UpperBoundary": [{ [l]: [{ "#text": p[3] }] }] }] }); } else n.push({ [o]: [{ [h]: [{ "#text": i }] }, { [l]: [{ "#text": s }] }] }); return n; } /** * Get the SLD Object (readable with fast-xml-parser) from a geostyler-style Filter. * * @param filter A geostyler-style Filter. * @return The object representation of a SLD Filter Expression (readable with fast-xml-parser) */ getSldFilterFromFilter(e) { let n = []; if (Z(e)) n = this.getSldComparisonFilterFromComparisonFilter(e); else if (ee(e)) n.push({ "ogc:Not": this.getSldFilterFromFilter(e[1]) }); else if (te(e)) { const [ t, ...i ] = e, c = j(I, t)[0], a = i.map((o) => this.getSldFilterFromFilter(o)[0]); n.push({ [`ogc:${c}`]: a }); } return n; } /** * Checks on presence of the pseudo-property 'uom' inserted by function 'addUomEntry', removes it and inserts * an uom-attribute. Do it only for SLD 1.1.0, ignore it otherwise. */ moveUomEntryToAttributes(e, n) { const t = n[n.length - 1].uom; t && (this.sldVersion === "1.1.0" && (e[":@"] = { "@_uom": t }), n.pop()); } /** * Checks Distance-Unit used by given symbolizer and inserts a pseudo-property 'uom' if required, because * we only have a property-array returned by the getSldXXXSymbolizerFromXXXSymbolizer-functions. * Later, we will move to an attribute within function 'moveUomEntryToAttributes' */ addUomEntry(e, n) { n === "m" && e.push({ uom: q }), n === "px" && e.push({ uom: z }); } /** * Checks for an 'uom'-attribute and returns the distance-unit to be used for interpreting the * units of the symbolizer. */ getDistanceUnit(e) { if (!e) return; const n = v(e, "uom"); if (n) { if (n === q) return "m"; if (n === z) return "px"; } } /** * Get the SLD Object (readable with fast-xml-parser) from geostyler-style Symbolizers. * * @param symbolizers A geostyler-style Symbolizer array. * @return The object representation of a SLD Symbolizer (readable with fast-xml-parser) */ getSldSymbolizersFromSymbolizers(e) { const n = [], t = this.getTagName("PointSymbolizer"), i = this.getTagName("TextSymbolizer"), s = this.getTagName("LineSymbolizer"), c = this.getTagName("PolygonSymbolizer"), a = this.getTagName("RasterSymbolizer"); return e.forEach((o) => { const r = {}; let h; switch (o.kind) { case "Mark": h = this.getSldPointSymbolizerFromMarkSymbolizer(o), r[t] = h; break; case "Icon": h = this.getSldPointSymbolizerFromIconSymbolizer(o), r[t] = h; break; case "Text": h = this.getSldTextSymbolizerFromTextSymbolizer(o), r[i] = h; break; case "Line": h = this.getSldLineSymbolizerFromLineSymbolizer(o), r[s] = h; break; case "Fill": h = this.getSldPolygonSymbolizerFromFillSymbolizer(o), r[c] = h; break; case "Raster": h = this.getSldRasterSymbolizerFromRasterSymbolizer(o), r[a] = h; break; } r && h && this.moveUomEntryToAttributes(r, h), n.push(r); }), n; } /** * Get the SLD Displacement Object (readable with fast-xml-parser) from a geostyler-style offset. * In SLD positive y values mean a displacement to the top. * In geostyler-style positive y values mean a displacement to the bottom. * Thus the y value is inverted. * * @param offset A geostyler-style offset as a tuple of two numbers or two functions. * @returns The object representation of a SLD Displacement (readable with fast-xml-parser) */ getDisplacementFromOffset(e) { const n = this.getTagName("Displacement"), t = this.getTagName("DisplacementX"), i = this.getTagName("DisplacementY"), s = P(e[0]) ? T(e[0]) : [{ "#text": e[0].toString() }], c = P(e[1]) ? T(e[1]) : [{ "#text": (e[1] * -1).toString() }]; return { [n]: [{ [t]: s }, { [i]: c }] }; } /** * Get the SLD Object (readable with fast-xml-parser) from a geostyler-style MarkSymbolizer. * * @param markSymbolizer A geostyler-style MarkSymbolizer. * @return The object representation of a SLD PointSymbolizer with a Mark */ getSldPointSymbolizerFromMarkSymbolizer(e) { var F; const n = this.getTagName("WellKnownName"), t = this.getTagName("CssParameter"), i = this.getTagName("Fill"), s = this.getTagName("Mark"), c = this.getTagName("Stroke"), a = this.getTagName("Opacity"), o = this.getTagName("Rotation"), r = this.getTagName("Size"), h = this.getTagName("Graphic"), l = K.test(e.wellKnownName), p = [{ [n]: [{ "#text": l ? e.wellKnownName : e.wellKnownName.toLowerCase() }] }]; if (e.color || !u(e.fillOpacity)) { const f = []; if (e.color) if (P(e.color)) { const g = T(e.color); f.push({ [t]: g, ":@": { "@_name": "fill" } }); } else f.push({ [t]: [{ "#text": e.color }], ":@": { "@_name": "fill" } }); if (!u(e.fillOpacity)) if (P(e.fillOpacity)) { const g = T(e.fillOpacity); f.push({ [t]: g, ":@": { "@_name": "fill-opacity" } }); } else f.push({ [t]: [{ "#text": e.fillOpacity }], ":@": { "@_name": "fill-opacity" } }); p.push({ [i]: f }); } if (e.strokeColor || Number.isFinite(e.strokeWidth) || Number.isFinite(e.strokeOpacity)) { const f = []; if (e.strokeColor) if (P(e.strokeColor)) { const g = T(e.strokeColor); f.push({ [t]: g, ":@": { "@_name": "stroke" } }); } else f.push({ [t]: [{ "#text": e.strokeColor }], ":@": { "@_name": "stroke" } }); if (!u(e.strokeWidth)) if (P(e.strokeWidth)) { const g = T(e.strokeWidth); f.push({ [t]: g, ":@": { "@_name": "stroke-width" } }); } else f.push({ [t]: [{ "#text": e.strokeWidth }], ":@": { "@_name": "stroke-width" } }); if (!u(e.strokeOpacity)) if (P(e.strokeOpacity)) { const g = T(e.strokeOpacity); f.push({ [t]: g, ":@": { "@_name": "stroke-opacity" } }); } else f.push({ [t]: [{ "#text": e.strokeOpacity }], ":@": { "@_name": "stroke-opacity" } }); if (!u(e.strokeDasharray)) if (P(e.strokeDasharray)) { const g = T(e.strokeDasharray); f.push({ [t]: g, ":@": { "@_nam