Very much like the Style Render example https://openlayers.org/en/latest/examples/style-renderer.html except that instead of using flag images you would create a canvas image (big enough for the largest feature) filled with your pattern and use that. In the example the flags can be seen to move relative to the country outlines as they are panned partially out of the view. That can be prevented by setting a large renderBuffer
on the layer. https://codesandbox.io/s/style-renderer-forked-tvnq7r?file=/main.js
Working example:
import GeoJSON from 'ol/format/GeoJSON.js';
import Map from 'ol/Map.js';
import VectorLayer from 'ol/layer/Vector.js';
import VectorSource from 'ol/source/Vector.js';
import View from 'ol/View.js';
import {Fill, Stroke, Style} from 'ol/style.js';
import {fromLonLat} from 'ol/proj.js';
import {getBottomLeft, getHeight, getWidth} from 'ol/extent.js';
import {toContext} from 'ol/render.js';
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const pattern = (function () {
canvas.width = 8;
canvas.height = 8;
// white background
context.fillStyle = 'white';
context.fillRect(0, 0, canvas.width, canvas.height);
// outer circle
context.fillStyle = 'rgba(102, 0, 102, 0.5)';
context.beginPath();
context.arc(4, 4, 3, 0, 2 * Math.PI);
context.fill();
// inner circle
context.fillStyle = 'rgb(55, 0, 170)';
context.beginPath();
context.arc(4, 4, 1.5, 0, 2 * Math.PI);
context.fill();
return context.createPattern(canvas, 'repeat');
})();
const flag = document.createElement('canvas');
const flagContext = flag.getContext('2d');
flag.width = 2000;
flag.height = 2000;
flagContext.fillStyle = pattern;
flagContext.fillRect(0, 0, flag.width, flag.height);
const fill = new Fill();
const stroke = new Stroke({
color: 'black',
width: 2,
});
let scale;
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);
if (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.imageSmoothingEnabled = false;
context.drawImage(
flag,
0,
0,
width * scale,
height * scale,
left,
bottom,
width,
height
);
context.restore();
},
});
const vectorLayer = new VectorLayer({
source: new VectorSource({
url: 'https://openlayers.org/data/vector/us-states.json',
format: new GeoJSON(),
}),
style: style,
renderBuffer: 1e10,
});
const map = new Map({
layers: [vectorLayer],
target: 'map',
view: new View({
center: fromLonLat([-100, 38.5]),
zoom: 4,
}),
});
const view = map.getView();
const setScale = function () {
scale = view.getResolution() * 0.00008;
};
view.on('change:resolution', setScale);
setScale();