diff --git a/examples/style-renderer.css b/examples/style-renderer.css
new file mode 100644
index 0000000000..5f29be1fe5
--- /dev/null
+++ b/examples/style-renderer.css
@@ -0,0 +1,3 @@
+#map {
+ background: #75d0f0;
+}
diff --git a/examples/style-renderer.html b/examples/style-renderer.html
new file mode 100644
index 0000000000..63593eca0f
--- /dev/null
+++ b/examples/style-renderer.html
@@ -0,0 +1,10 @@
+---
+layout: example.html
+title: Style renderer
+shortdesc: Example of a style with a custom renderer.
+docs: >
+ A custom style renderer and a clip path are used to render the flag of a country as fill for the country shape.
+tags: "vector, geojson, style, custom, renderer"
+---
+
+
diff --git a/examples/style-renderer.js b/examples/style-renderer.js
new file mode 100644
index 0000000000..1cbe71ba0e
--- /dev/null
+++ b/examples/style-renderer.js
@@ -0,0 +1,75 @@
+import GeoJSON from '../src/ol/format/GeoJSON.js';
+import Map from '../src/ol/Map.js';
+import VectorLayer from '../src/ol/layer/Vector.js';
+import VectorSource from '../src/ol/source/Vector.js';
+import View from '../src/ol/View.js';
+import {Fill, Stroke, Style} from '../src/ol/style.js';
+import {getBottomLeft, getHeight, getWidth} from '../src/ol/extent.js';
+import {toContext} from '../src/ol/render.js';
+
+const fill = new Fill();
+const stroke = new Stroke({
+ color: 'rgba(255,255,255,0.8)',
+ width: 2,
+});
+const style = new Style({
+ renderer: function (pixelCoordinates, state) {
+ const context = state.context;
+ const geometry = state.geometry.clone();
+ geometry.setCoordinates(pixelCoordinates);
+ const extent = geometry.getExtent();
+ const width = getWidth(extent);
+ const height = getHeight(extent);
+ const flag = state.feature.get('flag');
+ if (!flag || height < 1 || width < 1) {
+ return;
+ }
+
+ // Stitch out country shape from the blue canvas
+ context.save();
+ const renderContext = toContext(context, {
+ pixelRatio: 1,
+ });
+ renderContext.setFillStrokeStyle(fill, stroke);
+ renderContext.drawGeometry(geometry);
+ context.clip();
+
+ // Fill transparent country with the flag image
+ const bottomLeft = getBottomLeft(extent);
+ const left = bottomLeft[0];
+ const bottom = bottomLeft[1];
+ context.drawImage(flag, 2, 12, 60, 40, left, bottom, width, height);
+ context.restore();
+ },
+});
+
+const vectorLayer = new VectorLayer({
+ source: new VectorSource({
+ url:
+ 'https://openlayersbook.github.io/openlayers_book_samples/assets/data/countries.geojson',
+ format: new GeoJSON(),
+ }),
+ style: style,
+});
+
+// Load country flags and set them as `flag` attribute on the country feature
+vectorLayer.getSource().on('addfeature', function (event) {
+ const feature = event.feature;
+ const img = new Image();
+ img.onload = function () {
+ feature.set('flag', img);
+ };
+ img.src =
+ 'https://www.countryflags.io/' +
+ feature.get('iso_a2').toLowerCase() +
+ '/flat/64.png';
+});
+
+new Map({
+ layers: [vectorLayer],
+ target: 'map',
+ view: new View({
+ center: [0, 0],
+ zoom: 1,
+ }),
+});
diff --git a/examples/vector-label-decluttering.html b/examples/vector-label-decluttering.html
index 19ae8ab16a..c453b3a2d2 100644
--- a/examples/vector-label-decluttering.html
+++ b/examples/vector-label-decluttering.html
@@ -1,11 +1,11 @@
---
layout: example.html
title: Vector Label Decluttering
-shortdesc: Label decluttering with a custom renderer.
+shortdesc: Label decluttering on polygons.
resources:
- https://cdn.polyfill.io/v2/polyfill.min.js?features=Set"
docs: >
- Decluttering is used to avoid overlapping labels. The `overflow: true` setting on the text style makes it so labels that do not fit within the bounds of a polygon are also included.
+ Decluttering is used to avoid overlapping labels. The `overflow: true` setting on the text style makes it so labels that do not fit within the bounds of a polygon are also considered for decluttering.
tags: "vector, decluttering, labels"
---