diff --git a/src/ol/format/KML.js b/src/ol/format/KML.js
index 0b2badd950..46d4674911 100644
--- a/src/ol/format/KML.js
+++ b/src/ol/format/KML.js
@@ -378,6 +378,24 @@ function createStyleDefaults() {
*/
let TEXTAREA;
+/**
+ * A function that takes a url `{string}` and returns a url `{string}`.
+ * Might be used to change an icon path or to substitute a
+ * data url obtained from a KMZ array buffer.
+ *
+ * @typedef {function(string):string} IconUrlFunction
+ * @api
+ */
+
+/**
+ * Function that returns a url unchanged.
+ * @param {string} href Input url.
+ * @return {string} Output url.
+ */
+function defaultIconUrlFunction(href) {
+ return href;
+}
+
/**
* @typedef {Object} Options
* @property {boolean} [extractStyles=true] Extract styles from the KML.
@@ -387,6 +405,8 @@ let TEXTAREA;
* @property {boolean} [writeStyles=true] Write styles into KML.
* @property {null|string} [crossOrigin='anonymous'] The `crossOrigin` attribute for loaded images. Note that you must provide a
* `crossOrigin` value if you want to access pixel data with the Canvas renderer.
+ * @property {IconUrlFunction} [iconUrlFunction] Function that takes a url string and returns a url string.
+ * Might be used to change an icon path or to substitute a data url obtained from a KMZ array buffer.
*/
/**
@@ -462,6 +482,13 @@ class KML extends XMLFeature {
*/
this.crossOrigin_ =
options.crossOrigin !== undefined ? options.crossOrigin : 'anonymous';
+
+ /**
+ * @type {IconUrlFunction}
+ */
+ this.iconUrlFunction_ = options.iconUrlFunction
+ ? options.iconUrlFunction
+ : defaultIconUrlFunction;
}
/**
@@ -1282,7 +1309,7 @@ function iconStyleParser(node, objectStack) {
rotation: rotation,
scale: scale,
size: size,
- src: src,
+ src: this.iconUrlFunction_(src),
color: color,
});
styleObject['imageStyle'] = imageStyle;
diff --git a/test/spec/ol/format/kml.test.js b/test/spec/ol/format/kml.test.js
index 205b292cb7..8df3ea6e99 100644
--- a/test/spec/ol/format/kml.test.js
+++ b/test/spec/ol/format/kml.test.js
@@ -2154,6 +2154,52 @@ describe('ol.format.KML', function () {
expect(style.getZIndex()).to.be(undefined);
});
+ it("can read a feature's IconStyle and apply an iconUrlFunction", function () {
+ format = new KML({
+ iconUrlFunction: function (href) {
+ return href.replace(/^http:/, 'https:');
+ },
+ });
+ const text =
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '';
+ const fs = format.readFeatures(text);
+ expect(fs).to.have.length(1);
+ const f = fs[0];
+ expect(f).to.be.an(Feature);
+ const styleFunction = f.getStyleFunction();
+ expect(styleFunction).not.to.be(undefined);
+ const styleArray = styleFunction(f, 0);
+ expect(styleArray).to.be.an(Array);
+ expect(styleArray).to.have.length(1);
+ const style = styleArray[0];
+ expect(style).to.be.an(Style);
+ expect(style.getFill()).to.be(getDefaultFillStyle());
+ expect(style.getStroke()).to.be(getDefaultStrokeStyle());
+ const imageStyle = style.getImage();
+ expect(imageStyle).to.be.an(Icon);
+ expect(new URL(imageStyle.getSrc()).href).to.eql(
+ new URL('https://foo.png').href
+ );
+ expect(imageStyle.getAnchor()).to.be(null);
+ expect(imageStyle.getOrigin()).to.be(null);
+ expect(imageStyle.getRotation()).to.eql(0);
+ expect(imageStyle.getSize()).to.be(null);
+ expect(imageStyle.getScale()).to.be(1);
+ expect(imageStyle.getImage().crossOrigin).to.eql('anonymous');
+ expect(style.getText()).to.be(getDefaultTextStyle());
+ expect(style.getZIndex()).to.be(undefined);
+ });
+
it("can read a IconStyle's hotspot", function () {
const text =
'' +