3

I want any method or help to be able to flip (vertically and/or horizontally) the feature's icon without using the rotation option in the icon style since I am using it already to rotate the icon also. I am using [email protected].

Here is how I create the icon style:

var style = new ol_style_Icon({
src: 'url',
size: .....
rotation: .....
});

1 Answer 1

4

Similar to different vertical and horizontal scaling I think it can only be done in canvas at present (an old pull request for scaling https://github.com/openlayers/openlayers/pull/6100 now conflicts with more recent changes). In this function a negative scale would flip in the image:

function createIconStyle(url, scaleX, scaleY, styleOptions, iconOptions) {

    var style = new ol.style.Style(styleOptions);

    var img = new Image();
    img.crossOrigin = 'anonymous';
    img.onload = function(){

        var x = Math.round(img.width * scaleX);
        var y = Math.round(img.height * scaleY);
        var canvas = document.createElement('canvas');
        canvas.width = Math.abs(x);
        canvas.height = Math.abs(y);
        var ctx = canvas.getContext('2d');

        ctx.translate(Math.abs(Math.min(0,x)), Math.abs(Math.min(0,y)));
        ctx.scale(x<0 ? -1 : 1, y<0 ? -1 : 1);

        ctx.drawImage(img, 0, 0, Math.abs(x), Math.abs(y));
        var dataUrl = canvas.toDataURL();
        canvas.remove();
        iconOptions = iconOptions || {};
        iconOptions.src = dataUrl;
        style.setImage(new ol.style.Icon(iconOptions));

    };
    img.src = url;
    return style;

}

The function could be used to create styles for layers and features and predefined styles for use in a style function, but won't work correctly if called directly in a style function (except for creating cached styles) due to the asynchronous load of the image. The canvas operation means the image must be same origin or CORS enabled. e.g.

feature.setStyle( createIconStyle('url', -1, 1, {}, {rotation: angle}) );

If instead of passing the url you passed pre-loaded image object the method could be used in a style function:

function createIconStyleFromImage(img, scaleX, scaleY, styleOptions, iconOptions) {

    var style = new ol.style.Style(styleOptions);

    var x = Math.round(img.width * scaleX);
    var y = Math.round(img.height * scaleY);
    var canvas = document.createElement('canvas');
    canvas.width = Math.abs(x);
    canvas.height = Math.abs(y);
    var ctx = canvas.getContext('2d');

    ctx.translate(Math.abs(Math.min(0,x)), Math.abs(Math.min(0,y)));
    ctx.scale(x<0 ? -1 : 1, y<0 ? -1 : 1);

    ctx.drawImage(img, 0, 0, Math.abs(x), Math.abs(y));
    var dataUrl = canvas.toDataURL();
    canvas.remove();
    iconOptions = iconOptions || {};
    iconOptions.src = dataUrl;
    style.setImage(new ol.style.Icon(iconOptions));

    return style;

}

Example of caching flipped images

var imageCache = {};

function createIconStyle(url, scaleX, scaleY, styleOptions, iconOptions) {

    var style = new ol.style.Style(styleOptions);
    iconOptions = iconOptions || {};

    var dataUrl = imageCache[url + ',' + scaleX + ',' + scaleY];
    if (dataUrl) {

        iconOptions.src = dataUrl;
        style.setImage(new ol.style.Icon(iconOptions));

    } else {

        var img = document.createElement('img');
        img.crossOrigin = 'anonymous';
        img.onload = function() {

            var x = Math.round(img.width * scaleX);
            var y = Math.round(img.height * scaleY);
            var canvas = document.createElement('canvas');
            canvas.width = Math.abs(x);
            canvas.height = Math.abs(y);
            var ctx = canvas.getContext('2d');

            ctx.translate(Math.abs(Math.min(0,x)), Math.abs(Math.min(0,y)));
            ctx.scale(x<0 ? -1 : 1, y<0 ? -1 : 1);

            ctx.drawImage(img, 0, 0, Math.abs(x), Math.abs(y));
            var dataUrl = canvas.toDataURL();
            imageCache[url + ',' + scaleX + ',' + scaleY] = dataUrl;
            canvas.remove();

            iconOptions.src = dataUrl;
            style.setImage(new ol.style.Icon(iconOptions));

            // maybe force an immediate re-render?
            //map.renderSync();

        };
        img.src = url;

    }
    return style;

}
7
  • I will try it. thx in advance.
    – abd0991
    Commented May 11, 2019 at 15:33
  • Is there a different method to do the job using the style function? @Mike
    – abd0991
    Commented Jun 12, 2019 at 16:52
  • 1
    The results from style functions are used immediately, so by the time the url loaded it would be too late. If you preloaded all the urls you were going to use before creating the map you could use the image objects instead of urls and dynamically set the scales in style functions.
    – Mike
    Commented Jun 12, 2019 at 17:14
  • thanks my friend, one last question: is there another method to flip the image without using the canvas ? like a CSS trick or something like that ? Because I have to use a style function and preloading the images is not a right choice for my project.
    – abd0991
    Commented Jun 13, 2019 at 11:37
  • 1
    CSS would only work on displayed image objects, OpenLayers doesn't display icon images directly they are rendered to map canvas. The best you could get is to use a caching style function, either caching flipped images or complete styles, so once loaded they don't need reloading. But new styles might not appear at the first render attempt - maybe calling map.renderSync() after completing a new style could fix that?
    – Mike
    Commented Jun 15, 2019 at 23:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.