geostyler-openlayers-parser
Version:
GeoStyler Style Parser implementation for OpenLayers styles
944 lines (943 loc) • 34.1 kB
JavaScript
import { parseFont as L } from "css-font-parser";
import { isGeoStylerBooleanFunction as b, isGeoStylerFunction as d, isSprite as k, isIconSymbolizer as x, isMarkSymbolizer as R, isGeoStylerStringFunction as E } from "geostyler-style";
import M from "ol/ImageState.js";
import P from "ol/geom/Point.js";
import T from "ol/style/Style.js";
import W from "ol/style/Image.js";
import A from "ol/style/Stroke.js";
import K from "ol/style/Text.js";
import z from "ol/style/Circle.js";
import I from "ol/style/Fill.js";
import D from "ol/style/Icon.js";
import G from "ol/style/RegularShape.js";
import _ from "ol/geom/LineString.js";
import U from "ol/geom/MultiLineString.js";
import j from "ol/geom/Polygon.js";
import H from "ol/geom/MultiPolygon.js";
import { METERS_PER_UNIT as Y } from "ol/proj/Units.js";
import a, { DEGREES_TO_RADIANS as C } from "./Util/OlStyleUtil.js";
import { isPointDefinedAsSvg as B, getPointSvg as X, cleanWellKnownName as $ } from "./Util/OlSvgPoints.js";
import { toContext as J } from "ol/render.js";
import V from "ol/Feature.js";
import { fillPatternSvg as q } from "./Util/OlSvgPatterns.js";
import { getDecodedSvg as Q, getSvgProperties as Z, getEncodedSvg as tt, LINE_WELLKNOWNNAMES as et, NOFILL_WELLKNOWNNAMES as ot, drawSvgToCanvas as rt } from "./Util/OlSvgUtil.js";
import v from "./Util/OlGraphicStrokeUtil.js";
class It {
/**
* The name of the OlStyleParser.
*/
static title = "OpenLayers Style Parser";
unsupportedProperties = {
Symbolizer: {
MarkSymbolizer: {
avoidEdges: "none",
blur: "none",
offsetAnchor: "none",
pitchAlignment: "none",
pitchScale: "none"
},
FillSymbolizer: {
antialias: "none",
opacity: {
support: "none",
info: "Use fillOpacity instead."
}
},
IconSymbolizer: {
allowOverlap: "none",
anchor: "none",
avoidEdges: "none",
color: "none",
haloBlur: "none",
haloColor: "none",
haloWidth: "none",
keepUpright: "none",
offsetAnchor: "none",
size: {
support: "partial",
info: "Will set/get the width of the ol Icon."
},
optional: "none",
padding: "none",
pitchAlignment: "none",
rotationAlignment: "none",
textFit: "none",
textFitPadding: "none"
},
LineSymbolizer: {
blur: "none",
gapWidth: "none",
gradient: "none",
miterLimit: "none",
roundLimit: "none",
spacing: "none",
graphicFill: "none",
perpendicularOffset: "none"
},
RasterSymbolizer: "none",
TextSymbolizer: {
anchor: "none",
placement: {
support: "partial",
info: "point and line supported. line-center will be mapped to line."
}
}
},
Function: {
double2bool: {
support: "none",
info: "Always returns false"
},
atan2: {
support: "none",
info: "Currently returns the first argument"
},
rint: {
support: "none",
info: "Currently returns the first argument"
},
numberFormat: {
support: "none",
info: "Currently returns the first argument"
},
strAbbreviate: {
support: "none",
info: "Currently returns the first argument"
}
}
};
title = "OpenLayers Style Parser";
olIconStyleCache = {};
OlStyleConstructor = T;
OlStyleImageConstructor = W;
OlStyleFillConstructor = I;
OlStyleStrokeConstructor = A;
OlStyleTextConstructor = K;
OlStyleCircleConstructor = z;
OlStyleIconConstructor = D;
OlStyleRegularshapeConstructor = G;
OlLineStringContructor = _;
OlMultiLineStringConstructor = U;
OlPolygonConstructor = j;
OlMultiPolygonConstructor = H;
OlPointConstructor = P;
constructor(t) {
t && (this.OlStyleConstructor = t.style.Style, this.OlStyleImageConstructor = t.style.Image, this.OlStyleFillConstructor = t.style.Fill, this.OlStyleStrokeConstructor = t.style.Stroke, this.OlStyleTextConstructor = t.style.Text, this.OlStyleCircleConstructor = t.style.Circle, this.OlStyleIconConstructor = t.style.Icon, this.OlStyleRegularshapeConstructor = t.style.RegularShape, this.OlLineStringContructor = t.geom.LineString, this.OlMultiLineStringConstructor = t.geom.MultiLineString, this.OlPolygonConstructor = t.geom.Polygon, this.OlMultiPolygonConstructor = t.geom.MultiPolygon, this.OlPointConstructor = t.geom.Point);
}
isOlParserStyleFct = (t) => typeof t == "function";
/**
* Get the GeoStyler-Style PointSymbolizer from an OpenLayers Style object.
*
* @param olStyle The OpenLayers Style object
* @return The GeoStyler-Style PointSymbolizer
*/
getPointSymbolizerFromOlStyle(t) {
let o;
if (t.getImage() instanceof this.OlStyleCircleConstructor) {
const e = t.getImage(), r = e.getFill(), n = e.getStroke(), s = e.getDisplacement();
o = {
kind: "Mark",
wellKnownName: "circle",
color: r ? a.getHexColor(r.getColor()) : void 0,
opacity: e.getOpacity() !== 1 ? e.getOpacity() : void 0,
fillOpacity: r ? a.getOpacity(r.getColor()) : void 0,
radius: e.getRadius() !== 0 ? e.getRadius() : 5,
strokeColor: n ? n.getColor() : void 0,
strokeOpacity: n ? a.getOpacity(n.getColor()) : void 0,
strokeWidth: n ? n.getWidth() : void 0,
offset: s[0] || s[1] ? s : void 0
};
} else if (t.getImage() instanceof this.OlStyleRegularshapeConstructor) {
const e = t.getImage(), r = e.getFill(), n = e.getStroke(), s = e.getRadius(), i = e.getRadius2(), l = e.getPoints(), g = e.getAngle(), u = e.getDisplacement(), c = {
kind: "Mark",
color: r ? a.getHexColor(r.getColor()) : void 0,
opacity: e.getOpacity() !== 1 ? e.getOpacity() : void 0,
fillOpacity: r ? a.getOpacity(r.getColor()) : void 0,
strokeColor: n ? n.getColor() : void 0,
strokeOpacity: n ? a.getOpacity(n.getColor()) : void 0,
strokeWidth: n ? n.getWidth() : void 0,
radius: s !== 0 ? s : 5,
// Rotation in openlayers is radians while we use degree
rotate: e.getRotation() / C,
offset: u[0] || u[1] ? u : void 0
};
switch (l) {
case 2:
switch (g) {
case 0:
c.wellKnownName = "shape://vertline";
break;
case Math.PI / 2:
c.wellKnownName = "shape://horline";
break;
case Math.PI / 4:
c.wellKnownName = "shape://slash";
break;
case 2 * Math.PI - Math.PI / 4:
c.wellKnownName = "shape://backslash";
break;
}
break;
case 3:
switch (g) {
case 0:
c.wellKnownName = "triangle";
break;
case Math.PI / 2:
c.wellKnownName = "shape://carrow";
break;
}
break;
case 4:
Number.isFinite(i) ? e.getAngle() === 0 ? e.getRadius2() === 0 ? c.wellKnownName = "shape://plus" : c.wellKnownName = "star_diamond" : c.wellKnownName = "x" : e.getAngle() === 0 ? c.wellKnownName = "diamond" : c.wellKnownName = "square";
break;
case 5:
Number.isFinite(i) ? c.wellKnownName = "star" : c.wellKnownName = "pentagon";
break;
case 6:
c.wellKnownName = "hexagon";
break;
case 8:
c.wellKnownName = "octagon";
break;
case 10:
c.wellKnownName = "decagon";
break;
default:
throw new Error("Could not parse OlStyle.");
}
o = c;
} else if (t.getText() instanceof this.OlStyleTextConstructor) {
const e = t.getText(), r = e.getFill(), n = e.getStroke(), s = e.getRotation();
let i = e.getText() || "a";
const l = e.getFont() || "10px sans-serif", g = a.getFontNameFromOlFont(l), u = a.getSizeFromOlFont(l), c = [e.getOffsetX(), e.getOffsetY()];
Array.isArray(i) && (i = i[0]), o = {
kind: "Mark",
wellKnownName: `ttf://${g}#0x${i.charCodeAt(0).toString(16)}`,
color: r ? a.getHexColor(r.getColor()) : void 0,
opacity: r ? a.getOpacity(r.getColor()) : void 0,
strokeColor: n ? n.getColor() : void 0,
strokeOpacity: n ? a.getOpacity(n.getColor()) : void 0,
strokeWidth: n ? n.getWidth() : void 0,
radius: u !== 0 ? u : 5,
// Rotation in openlayers is radians while we use degree
rotate: s ? s / C : 0,
offset: c[0] || c[1] ? c : void 0
};
} else {
const e = t.getImage(), r = e.getDisplacement(), n = e.getWidth(), s = e.getRotation() / C, i = e.getOpacity(), l = e.getSrc();
let g;
l && l.startsWith("data:image/svg+xml") ? g = Q(l) : l && l.trim().startsWith("<svg") && (g = l.trim()), g ? o = {
...Z(g),
...a.checkOpacity(i) && { opacity: i },
...s && { rotate: s },
...(r[0] || r[1]) && { offset: r }
} : o = {
kind: "Icon",
image: this.getImageFromIconStyle(e),
size: n,
...a.checkOpacity(i) && { opacity: i },
...s && { rotate: s },
...(r[0] || r[1]) && { offset: r }
};
}
return o;
}
/**
*
* @param olIconStyle An ol style Icon representation
* @returns A string or Sprite configuration
*/
getImageFromIconStyle(t) {
const o = t.getSize();
if (Array.isArray(o)) {
const e = t.offset_, r = t.offsetOrigin_;
if (r && r !== "top-left")
throw new Error(`Offset origin ${r} not supported`);
return {
source: t.getSrc(),
position: e,
size: o
};
} else
return t.getSrc() ? t.getSrc() : void 0;
}
/**
* Get the GeoStyler-Style LineSymbolizer from an OpenLayers Style object.
*
* @param olStyle The OpenLayers Style object
* @return The GeoStyler-Style LineSymbolizer
*/
getLineSymbolizerFromOlStyle(t) {
const o = t.getStroke(), e = o ? o.getLineDash() : void 0;
return {
kind: "Line",
color: o ? a.getHexColor(o.getColor()) : void 0,
opacity: o ? a.getOpacity(o.getColor()) : void 0,
width: o ? o.getWidth() : void 0,
cap: o ? o.getLineCap() : "butt",
join: o ? o.getLineJoin() : "miter",
dasharray: e || void 0,
dashOffset: o ? o.getLineDashOffset() : void 0
};
}
/**
* Get the GeoStyler-Style FillSymbolizer from an OpenLayers Style object.
*
* PolygonSymbolizer Stroke is just partially supported.
*
* @param olStyle The OpenLayers Style object
* @return The GeoStyler-Style FillSymbolizer
*/
getFillSymbolizerFromOlStyle(t) {
const o = t.getFill(), e = t.getStroke(), r = e ? e.getLineDash() : void 0, n = {
kind: "Fill"
};
return o && (n.color = a.getHexColor(o.getColor())), o && (n.fillOpacity = a.getOpacity(o.getColor())), e && (n.outlineColor = a.getHexColor(e.getColor())), r && (n.outlineDasharray = r), e && (n.outlineOpacity = a.getOpacity(e.getColor())), e && e.getWidth() && (n.outlineWidth = e.getWidth()), n;
}
/**
* Get the GeoStyler-Style TextSymbolizer from an OpenLayers Style object.
*
*
* @param olStyle The OpenLayers Style object
* @return The GeoStyler-Style TextSymbolizer
*/
getTextSymbolizerFromOlStyle(t) {
const o = t.getText();
if (!o)
throw new Error("Could not get text from olStyle.");
const e = o.getFill(), r = o.getStroke(), n = o.getOffsetX(), s = o.getOffsetY(), i = o.getFont(), l = o.getRotation(), g = o.getOverflow() ? o.getOverflow() : void 0, u = o.getPlacement(), c = o.getRepeat(), f = o.getText(), y = Array.isArray(f) ? f[0] : f;
let S = 1 / 0, h, O, F;
if (i) {
const p = L(i);
p["font-weight"] && (O = p["font-weight"]), p["font-size"] && (S = parseInt(p["font-size"], 10)), p["font-family"] && (h = p["font-family"]?.map((m) => m.includes(" ") ? '"' + m + '"' : m)), p["font-style"] && (F = p["font-style"]);
}
return {
kind: "Text",
label: y,
placement: u,
repeat: c,
allowOverlap: g,
color: e ? a.getHexColor(e.getColor()) : void 0,
size: isFinite(S) ? S : void 0,
font: h,
fontWeight: O || void 0,
fontStyle: F || void 0,
offset: n !== void 0 && s !== void 0 ? [n, s] : [0, 0],
haloColor: r && r.getColor() ? a.getHexColor(r.getColor()) : void 0,
haloWidth: r ? r.getWidth() : void 0,
rotate: l !== void 0 ? l / C : void 0
};
}
/**
* Get the GeoStyler-Style Symbolizer from an OpenLayers Style object.
*
* @param olStyles The OpenLayers Style object
* @return The GeoStyler-Style Symbolizer array
*/
getSymbolizersFromOlStyle(t) {
const o = [];
return t.forEach((e) => {
let r;
switch (this.getStyleTypeFromOlStyle(e)) {
case "Point":
e.getText() && !a.getIsMarkSymbolizerFont(e.getText().getFont()) ? r = this.getTextSymbolizerFromOlStyle(e) : r = this.getPointSymbolizerFromOlStyle(e);
break;
case "Line":
r = this.getLineSymbolizerFromOlStyle(e);
break;
case "Fill":
r = this.getFillSymbolizerFromOlStyle(e);
break;
default:
throw new Error("Failed to parse SymbolizerKind from OpenLayers Style");
}
o.push(r);
}), o;
}
/**
* Get the GeoStyler-Style Rule from an OpenLayers Style object.
*
* @param olStyles The OpenLayers Style object
* @return The GeoStyler-Style Rule
*/
getRuleFromOlStyle(t) {
let o;
const e = "OL Style Rule 0";
return Array.isArray(t) ? o = this.getSymbolizersFromOlStyle(t) : o = this.getSymbolizersFromOlStyle([t]), {
name: e,
symbolizers: o
};
}
/**
* Get the GeoStyler-Style Symbolizer from an OpenLayers Style object.
*
* @param olStyle The OpenLayers Style object
* @return The GeoStyler-Style Symbolizer
*/
getStyleTypeFromOlStyle(t) {
let o;
if (t.getImage() instanceof this.OlStyleImageConstructor)
o = "Point";
else if (t.getText() instanceof this.OlStyleTextConstructor)
o = "Point";
else if (t.getFill() instanceof this.OlStyleFillConstructor)
o = "Fill";
else if (t.getStroke() && !t.getFill())
o = "Line";
else
throw new Error("StyleType could not be detected");
return o;
}
/**
* Get the GeoStyler-Style Style from an OpenLayers Style object.
*
* @param olStyle The OpenLayers Style object
* @return The GeoStyler-Style Style
*/
olStyleToGeoStylerStyle(t) {
const o = "OL Style", e = this.getRuleFromOlStyle(t);
return {
name: o,
rules: [e]
};
}
/**
* The readStyle implementation of the GeoStyler-Style StyleParser interface.
* It reads an OpenLayers Style, an array of OpenLayers Styles or an olParserStyleFct and returns a Promise.
*
* The Promise itself resolves with a GeoStyler-Style Style.
*
* @param olStyle The style to be parsed
* @return The Promise resolving with the GeoStyler-Style Style
*/
readStyle(t) {
return new Promise((o) => {
try {
if (this.isOlParserStyleFct(t))
o({
output: t.__geoStylerStyle
});
else {
t = t;
const e = this.olStyleToGeoStylerStyle(t), r = this.checkForUnsupportedProperties(e);
o({
output: e,
unsupportedProperties: r
});
}
} catch (e) {
o({
errors: [e]
});
}
});
}
/**
* The writeStyle implementation of the GeoStyler-Style StyleParser interface.
* It reads a GeoStyler-Style Style and returns a Promise.
* The Promise itself resolves one of three types
*
* 1. OlStyle if input Style consists of
* one rule with one symbolizer, no filter, no scaleDenominator, no TextSymbolizer
* 2. OlStyle[] if input Style consists of
* one rule with multiple symbolizers, no filter, no scaleDenominator, no TextSymbolizer
* 3. OlParserStyleFct for everything else
*
* @param geoStylerStyle A GeoStyler-Style Style.
* @return The Promise resolving with one of above mentioned style types.
*/
writeStyle(t) {
return new Promise((o) => {
const e = structuredClone(t), r = this.checkForUnsupportedProperties(e);
try {
const n = this.getOlStyleTypeFromGeoStylerStyle(e);
o({
output: n,
unsupportedProperties: r,
warnings: r && ["Your style contains unsupportedProperties!"]
});
} catch (n) {
o({
errors: [n]
});
}
});
}
checkForUnsupportedProperties(t) {
const o = (r) => r[0].toUpperCase() + r.slice(1), e = {};
if (t.rules.forEach((r) => {
r.symbolizers.forEach((n) => {
const s = o(`${n.kind}Symbolizer`), i = this.unsupportedProperties?.Symbolizer?.[s];
i && (typeof i == "string" ? (e.Symbolizer || (e.Symbolizer = {}), e.Symbolizer[s] = i) : Object.keys(n).forEach((l) => {
i[l] && (e.Symbolizer || (e.Symbolizer = {}), e.Symbolizer[s] || (e.Symbolizer[s] = {}), e.Symbolizer[s][l] = i[l]);
}));
});
}), Object.keys(e).length > 0)
return e;
}
/**
* Decides which OlStyleType should be returned depending on given geoStylerStyle.
* Three OlStyleTypes are possible:
*
* 1. OlStyle if input Style consists of
* one rule with one symbolizer, no filter, no scaleDenominator, no TextSymbolizer
* 2. OlStyle[] if input Style consists of
* one rule with multiple symbolizers, no filter, no scaleDenominator, no TextSymbolizer
* 3. OlParserStyleFct for everything else
*
* @param geoStylerStyle A GeoStyler-Style Style
*/
getOlStyleTypeFromGeoStylerStyle(t) {
const o = t.rules;
if (o.length === 1) {
const r = t?.rules?.[0]?.filter !== void 0, n = t?.rules?.[0]?.scaleDenominator?.min !== void 0, s = t?.rules?.[0]?.scaleDenominator?.max !== void 0, i = !!(n || s), l = a.containsGeoStylerFunctions(t), g = v.containsGraphicStroke(t), u = t.rules[0].symbolizers.length, c = o[0].symbolizers.some((y) => y.kind === "Text"), f = o[0].symbolizers.some((y) => y.kind === "Icon" && typeof y.image == "string" && y.image.includes("{{"));
return !r && !i && !c && !f && !l && !g ? u === 1 ? this.geoStylerStyleToOlStyle(t) : this.geoStylerStyleToOlStyleArray(t) : this.geoStylerStyleToOlParserStyleFct(t);
} else
return this.geoStylerStyleToOlParserStyleFct(t);
}
/**
* Parses the first symbolizer of the first rule of a GeoStyler-Style Style.
*
* @param geoStylerStyle GeoStyler-Style Style
* @return An OpenLayers Style Object
*/
geoStylerStyleToOlStyle(t) {
const e = t.rules[0].symbolizers[0];
return this.getOlSymbolizerFromSymbolizer(e);
}
/**
* Parses all symbolizers of the first rule of a GeoStyler-Style Style.
*
* @param geoStylerStyle GeoStyler-Style Style
* @return An array of OpenLayers Style Objects
*/
geoStylerStyleToOlStyleArray(t) {
const o = t.rules[0], e = [];
return o.symbolizers.forEach((r) => {
const n = this.getOlSymbolizerFromSymbolizer(r);
e.push(n);
}), e;
}
/**
* Get the OpenLayers Style object from an GeoStyler-Style Style
*
* @param geoStylerStyle A GeoStyler-Style Style.
* @return An OlParserStyleFct
*/
geoStylerStyleToOlParserStyleFct(t) {
const o = structuredClone(t.rules), r = (n, s) => {
let i = !1;
const l = [], g = 25.4 / 0.28, u = Y.m, f = s * u * 39.37 * g;
return o.forEach((y) => {
let S = y?.scaleDenominator?.min, h = y?.scaleDenominator?.max, O = !0;
(S || h) && (S = d(S) ? a.evaluateFunction(S, n) : S, h = d(h) ? a.evaluateFunction(h, n) : h, S && f < S && (O = !1), h && f >= h && (O = !1));
let F = !1;
if (!y.filter)
F = !0;
else
try {
F = this.geoStylerFilterToOlParserFilter(n, y.filter);
} catch {
F = !1;
}
if (O && F) {
if (y.elseRule && i)
return;
i = !0, y.symbolizers.forEach((p) => {
p.visibility === !1 && l.push(null), b(p.visibility) && (a.evaluateFunction(p.visibility, n) || l.push(null));
const w = this.getOlSymbolizerFromSymbolizer(p, n, s);
if (typeof w == "function") {
const m = w(n, s);
l.push(m);
} else Array.isArray(w) ? w.forEach((m) => {
if (typeof m == "function") {
const N = m(n, s);
l.push(N);
} else
l.push(m);
}) : l.push(w);
});
}
}), l;
};
return r.__geoStylerStyle = t, r;
}
/**
* Checks if a feature matches given filter expression(s)
* @param feature ol.Feature
* @param filter Filter
* @return boolean true if feature matches filter expression
*/
geoStylerFilterToOlParserFilter(t, o) {
const e = {
"&&": !0,
"||": !0,
"!": !0
};
let r = !0;
if (b(o))
return a.evaluateBooleanFunction(o, t);
if (o === !0 || o === !1)
return o;
const n = o[0];
let s = !1;
e[n] && (s = !0);
try {
if (s) {
let i, l;
switch (o[0]) {
case "&&":
i = !0, l = o.slice(1), l.forEach((g) => {
this.geoStylerFilterToOlParserFilter(t, g) || (i = !1);
}), r = i;
break;
case "||":
i = !1, l = o.slice(1), l.forEach((g) => {
this.geoStylerFilterToOlParserFilter(t, g) && (i = !0);
}), r = i;
break;
case "!":
r = !this.geoStylerFilterToOlParserFilter(t, o[1]);
break;
default:
throw new Error("Cannot parse Filter. Unknown combination or negation operator.");
}
} else {
let i;
d(o[1]) ? i = a.evaluateFunction(o[1], t) : i = t.get(o[1]);
let l;
switch (d(o[2]) ? l = a.evaluateFunction(o[2], t) : l = o[2], o[0]) {
case "==":
r = "" + i == "" + l;
break;
case "*=":
typeof l == "string" && typeof i == "string" && (l.length > i.length ? r = !1 : r = i.indexOf(l) !== -1);
break;
case "!=":
r = "" + i != "" + l;
break;
case "<":
r = Number(i) < Number(l);
break;
case "<=":
r = Number(i) <= Number(l);
break;
case ">":
r = Number(i) > Number(l);
break;
case ">=":
r = Number(i) >= Number(l);
break;
default:
throw new Error("Cannot parse Filter. Unknown comparison operator.");
}
}
} catch {
throw new Error("Cannot parse Filter. Invalid structure.");
}
return r;
}
/**
* Get the OpenLayers Style object or an OL StyleFunction from an
* GeoStyler-Style Symbolizer.
*
* @param symbolizer A GeoStyler-Style Symbolizer.
* @return The OpenLayers Style object or a StyleFunction
*/
getOlSymbolizerFromSymbolizer(t, o, e) {
let r;
switch (t = structuredClone(t), t.kind) {
case "Mark":
r = this.getOlPointSymbolizerFromMarkSymbolizer(t, o);
break;
case "Icon":
r = this.getOlIconSymbolizerFromIconSymbolizer(t, o);
break;
case "Text":
r = this.getOlTextSymbolizerFromTextSymbolizer(t, o);
break;
case "Line":
r = this.getOlLineSymbolizerFromLineSymbolizer(t, o, e);
break;
case "Fill":
r = this.getOlPolygonSymbolizerFromFillSymbolizer(t, o);
break;
default: {
const n = new this.OlStyleFillConstructor({
color: "rgba(255,255,255,0.4)"
}), s = new this.OlStyleStrokeConstructor({
color: "#3399CC",
width: 1.25
});
r = new this.OlStyleConstructor({
image: new this.OlStyleCircleConstructor({
fill: n,
stroke: s,
radius: 5
}),
fill: n,
stroke: s
});
break;
}
}
return r;
}
/**
* Get the OL Style object from an GeoStyler-Style MarkSymbolizer.
*
* @param markSymbolizer A GeoStyler-Style MarkSymbolizer.
* @return The OL Style object
*/
getOlPointSymbolizerFromMarkSymbolizer(t, o) {
for (const c of Object.keys(t))
d(t[c]) && (t[c] = a.evaluateFunction(t[c], o));
const e = t.strokeColor, r = t.strokeOpacity, n = t.strokeWidth, s = t.color, i = t.fillOpacity, l = Number(t.rotate) * C, g = t.offset;
let u;
if (B(t.wellKnownName)) {
const c = t.opacity, f = X(t);
u = new this.OlStyleConstructor({
image: new this.OlStyleIconConstructor({
crossOrigin: "anonymous",
...g && { displacement: g },
...a.checkOpacity(c) && { opacity: c },
...l && { rotation: l },
scale: 1,
src: tt(f)
})
});
} else if (a.getIsFontGlyphBased(t)) {
const c = e && a.checkOpacity(r) ? a.getRgbaColor(e, r) : e, f = s && a.checkOpacity(i) ? a.getRgbaColor(s, i) : s, y = new this.OlStyleStrokeConstructor({
...c && { color: c },
...n && { width: n }
}), S = new this.OlStyleFillConstructor({
...f && { color: f }
});
u = new this.OlStyleConstructor({
text: new this.OlStyleTextConstructor({
text: a.getCharacterForMarkSymbolizer(t),
font: a.getTextFontForMarkSymbolizer(t),
...S && { fill: S },
...g && { offsetX: g[0] },
...g && { offsetY: g[1] },
...y && { stroke: y },
...l && { rotation: l }
})
});
} else
throw new Error("MarkSymbolizer cannot be parsed. Unsupported WellKnownName.");
return u;
}
/**
* Get the OL Style object from an GeoStyler-Style IconSymbolizer.
*
* @param symbolizer A GeoStyler-Style IconSymbolizer.
* @return The OL Style object
*/
getOlIconSymbolizerFromIconSymbolizer(t, o) {
for (const l of Object.keys(t))
d(t[l]) && (t[l] = a.evaluateFunction(t[l], o));
const e = {
src: k(t.image) ? t.image.source : t.image,
crossOrigin: "anonymous",
opacity: t.opacity,
width: t.size,
// Rotation in openlayers is radians while we use degree
rotation: typeof t.rotate == "number" ? t.rotate * C : void 0,
displacement: t.offset,
size: k(t.image) ? t.image.size : void 0,
offset: k(t.image) ? t.image.position : void 0
}, r = "\\{\\{", n = "\\}\\}", s = new RegExp(r + ".*?" + n, "g");
return (typeof t.image == "string" ? t.image.match(s) : null) ? (g) => {
let u = a.resolveAttributeTemplate(g, t.image, "");
u || (u = t.image + "");
let c;
return this.olIconStyleCache[u] ? (c = this.olIconStyleCache[u], e.rotation !== void 0 && c.setRotation(e.rotation), e.opacity !== void 0 && c.setOpacity(e.opacity)) : (c = new this.OlStyleIconConstructor({
...e,
src: u
// order is important
}), this.olIconStyleCache[u] = c), new this.OlStyleConstructor({
image: c
});
} : new this.OlStyleConstructor({
image: new this.OlStyleIconConstructor({
...e
})
});
}
/**
* Get the OL Style object from an GeoStyler-Style LineSymbolizer.
*
* @param symbolizer A GeoStyler-Style LineSymbolizer.
* @return The OL Style object
*/
getOlLineSymbolizerFromLineSymbolizer(t, o, e) {
if (t.graphicStroke)
return this.getOlGraphicStrokeFromGraphicStroke(t, o, e);
for (const i of Object.keys(t))
d(t[i]) && (t[i] = a.evaluateFunction(t[i], o));
const r = t.color, n = t.opacity, s = r && n !== null && n !== void 0 ? a.getRgbaColor(r, n) : r;
return new this.OlStyleConstructor({
stroke: new this.OlStyleStrokeConstructor({
color: s,
width: t.width,
lineCap: t.cap,
lineJoin: t.join,
lineDash: t.dasharray,
lineDashOffset: t.dashOffset
})
});
}
getOlGraphicStrokeFromGraphicStroke(t, o, e) {
const r = v.getLineStringsFromGeometry(o.getGeometry(), {
LineString: this.OlLineStringContructor,
MultiLineString: this.OlMultiLineStringConstructor,
Polygon: this.OlPolygonConstructor,
MultiPolygon: this.OlMultiPolygonConstructor
}), n = t.graphicStroke, s = this.getSymbolSizeFromGraphicStroke(n, o);
if (s <= 0)
return console.warn("Symbol size must be greater than zero for graphic stroke. No graphic will be drawn."), [];
const i = n.rotate, l = d(i) ? a.evaluateFunction(i, o) : i ?? 0, g = t.dasharray, u = t.dashOffset || 0, c = d(u) ? a.evaluateFunction(u, o) : u, f = (y) => this.getOlSymbolizerFromSymbolizer(y, o, e);
return r.flatMap(
(y) => v.processLineStringGraphicStroke(
y,
s,
e,
g,
c,
l,
n,
f,
this.OlPointConstructor
)
);
}
/**
* Get the size of a symbol from graphicStroke.
* @param graphicStroke The graphicStroke from the LineSymbolizer.
* @param feat The feature.
* @returns The size of the symbol in pixels.
*/
getSymbolSizeFromGraphicStroke(t, o) {
let e = 0;
if (t.kind === "Mark") {
const r = t.radius;
if (d(r) ? e = a.evaluateFunction(r, o) * 2 : e = (r ?? 0) * 2, e <= 0)
return e;
const n = t.strokeWidth;
d(n) ? e += a.evaluateFunction(n, o) : e += n ?? 0;
} else if (t.kind === "Icon") {
const r = t.size;
d(r) ? e = a.evaluateFunction(r, o) : e = r ?? 0;
}
return e;
}
/**
* Get the OL Style object from an GeoStyler-Style FillSymbolizer.
*
* @param symbolizer A GeoStyler-Style FillSymbolizer.
* @return The OL Style object
*/
getOlPolygonSymbolizerFromFillSymbolizer(t, o) {
for (const f of Object.keys(t))
d(t[f]) && (t[f] = a.evaluateFunction(t[f], o));
const e = t.color, r = t.fillOpacity, n = e && Number.isFinite(r) ? a.getRgbaColor(e, r) : e;
let s = e ? new this.OlStyleFillConstructor({ color: n }) : void 0;
const i = t.outlineColor, l = t.outlineOpacity, g = i && Number.isFinite(l) ? a.getRgbaColor(i, l) : i, u = i || t.outlineWidth ? new this.OlStyleStrokeConstructor({
color: g,
width: t.outlineWidth,
lineDash: t.outlineDasharray
}) : void 0, c = new T({
fill: s,
stroke: u
});
if (t.graphicFill) {
const f = this.getOlPatternFromGraphicFill(t.graphicFill);
s || (s = new I({})), f && s.setColor(f), c.setFill(s);
}
return c;
}
/**
* Get the pattern for a graphicFill.
*
* This creates a CanvasPattern based on the
* properties of the given PointSymbolizer. Currently,
* only IconSymbolizer and MarkSymbolizer are supported.
*
* @param graphicFill The Symbolizer that holds the pattern config.
* @returns The created CanvasPattern, or null.
*/
getOlPatternFromGraphicFill(t) {
if (!x(t) && !R(t))
return null;
const o = document.createElement("canvas"), e = o.getContext("2d");
let r, n = [8, 8], s = 1.5, i = 1;
if (x(t)) {
const h = this.getOlIconSymbolizerFromIconSymbolizer(t), O = h?.getImage();
if (O?.load(), O?.getImageState() !== M.LOADED)
return null;
n = h.getImage().getSize(), r = n.map((m) => m * s), o.width = r[0], o.height = r[1];
const F = J(e, {
size: r,
pixelRatio: 1
}), p = r.map((m) => m / 2), w = new V(new P(p));
return F.drawFeature(w, h), e.createPattern(o, "repeat");
}
const l = q(t), g = et.includes(String(t.wellKnownName)), u = $(t.wellKnownName), c = !t.strokeWidth && ot.includes(u), f = u.startsWith("dense"), y = t.rotate || 0;
(g || c || f) && (y / C % 1 !== 0 && (i = Math.sin(y * C)), s = 1);
const S = (t.radius ?? 4) * 2;
return n = [S, S], r = n.map((h) => h * s * i), o.width = r[0], o.height = r[1], rt(l, e, r[0], y), e.createPattern(o, "repeat");
}
/**
* Get the OL StyleFunction object from an GeoStyler-Style TextSymbolizer.
*
* @param {TextSymbolizer} textSymbolizer A GeoStyler-Style TextSymbolizer.
* @return {object} The OL StyleFunction
*/
getOlTextSymbolizerFromTextSymbolizer(t, o) {
for (const h of Object.keys(t))
d(t[h]) && (t[h] = a.evaluateFunction(t[h], o));
const e = t.color;
let r = t.placement;
r || (r = "point"), r === "line-center" && (r = "line");
const n = t.opacity, s = e && Number.isFinite(n) ? a.getRgbaColor(e, n) : e, i = t.haloColor, l = t.haloWidth, g = i && Number.isFinite(n) ? a.getRgbaColor(i, n) : i, u = {
font: a.getTextFont(t),
fill: new this.OlStyleFillConstructor({
color: s
}),
stroke: new this.OlStyleStrokeConstructor({
color: g,
width: l || 0
}),
overflow: t.allowOverlap,
offsetX: t.offset ? t.offset[0] : 0,
offsetY: t.offset ? t.offset[1] : 0,
rotation: typeof t.rotate == "number" ? t.rotate * C : void 0,
placement: r,
repeat: t.repeat
// TODO check why props match
// textAlign: symbolizer.pitchAlignment,
// textBaseline: symbolizer.anchor
}, c = "\\{\\{", f = "\\}\\}", y = new RegExp(c + ".*?" + f, "g");
let S;
return E(t.label) || (S = t.label ? t.label.match(y) : null), S ? (O) => {
const F = new this.OlStyleTextConstructor({
text: a.resolveAttributeTemplate(O, t.label, ""),
...u
});
return new this.OlStyleConstructor({
text: F
});
} : new this.OlStyleConstructor({
text: new this.OlStyleTextConstructor({
text: t.label,
...u
})
});
}
}
export {
It as OlStyleParser,
It as default
};