3

I want to add a border color to the icons in OpenLayers. I also, want to be able to change the color of this border depending on the attribute of the marker. Is there any easy way to do this? The code below allows you to click the icon and make a border color appear. However, I want the color to be their from the beginning and not depend on the clicks. Also, I want it to change depending on the variable. If anyone has any idea please let me know.

class MapComponent implements OnInit {

  map:Map;
  iconVectorSource: VectorSource;
  iconVectorLayer: VectorLayer;

  iconArr: Feature[] = [];

  constructor() { }

  ngOnInit() {

  this.map = new Map({
    target: 'map',
    view: new View({
      projection: 'EPSG:4326',  
      center: [114.190750, 22.339914],
      zoom: 12,
      minZoom: 12,
    }),

    layers:[
      new TileLayer({
        source: new OSM()
      }),
    ]
  });
  this.addIcons();
  this.addSelect();

  }
  public addIcons(){
    let icon1 = new Feature({
      geometry: new Point([114.190750, 22.339914]),
      imgSrc: 'assets/purple.png',
    });

    icon1.set('style',this.createStyle(icon1.get('imgSrc'), undefined));

    this.iconArr.push(icon1);
    this.iconVectorSource = new VectorSource({
      features: this.iconArr,
      wrapX: false,
    });
    //creates a layer and use the source in the layer
    this.iconVectorLayer = new VectorLayer({
      source: this.iconVectorSource,
      style: function(feature){
        return feature.get('style');
      },
    });
    this.map.addLayer(this.iconVectorLayer);

  }

  public addSelect(){
    var select = new Select({
      style: function(feature) {
        var image = feature.get('style').getImage().getImage();

          var canvas = document.createElement('canvas');
          var ctx = canvas.getContext('2d');
          var activeColor = "red"; //set border color
          var dArr = [-1,-1, 0,-1, 1,-1, -1,0, 1,0, -1,1, 0,1, 1,1], // offset array
              s = 2,  // thickness scale
              i = 0,  // iterator
              x = 2,  // final x position
              y = 2;  // final y position

          //set new canvas dimentions adjusted for border
          canvas.width = image.width + s + s;
          canvas.height = image.height + s + s;

          // draw images at offsets from the array scaled by s
          for(; i < dArr.length; i += 2)
          ctx.drawImage(image, x + dArr[i]*s, y + dArr[i+1]*s);

          // fill with color
          ctx.globalCompositeOperation = "source-in";
          ctx.fillStyle = activeColor;
          ctx.fillRect(0,0,canvas.width, canvas.height);

          // draw original image in normal mode
          ctx.globalCompositeOperation = "source-over";
          ctx.drawImage(image, x, y,image.width, image.height);

          //create new openlayers icon style from canvas
          return new Style({
              image: new Icon(/** @type {olx.style.IconOptions} */ ({
              crossOrigin: 'anonymous',
              src: undefined,
              img: canvas,
              imgSize: canvas ? [canvas.width, canvas.height] : undefined
            }))
          });

      }
    });
    this.map.addInteraction(select);
  }

  public createStyle(src, img) {
    return new Style({
      image: new Icon(/** @type {olx.style.IconOptions} */ ({
        crossOrigin: 'anonymous',
        src: src,
        img: img,
        imgSize: img ? [img.width, img.height] : undefined
      }))
    });
  }

}

1 Answer 1

0
+50

If you are not changing style on clicks you wouldn't need a Select interaction. Move the activeColor definition outside the function (you will have some other code which changes it) then inside the layer style function check if the color has changed (the feature will need to store color as well as style) and change the style to match the new color if necessary.

class MapComponent implements OnInit {

  map:Map;
  iconVectorSource: VectorSource;
  iconVectorLayer: VectorLayer;
  activeColor: string;

  iconArr: Feature[] = [];

  constructor() { }

  ngOnInit() {

  this.map = new Map({
    target: 'map',
    view: new View({
      projection: 'EPSG:4326',  
      center: [114.190750, 22.339914],
      zoom: 12,
      minZoom: 12,
    }),

    layers:[
      new TileLayer({
        source: new OSM()
      }),
    ]
  });
  this.activeColor = 'red'; //set border color
  this.addIcons();

  }
  public addIcons(){
    let icon1 = new Feature({
      geometry: new Point([114.190750, 22.339914]),
      imgSrc: 'assets/purple.png',
    });

    icon1.set('style',this.createStyle(icon1.get('imgSrc'), undefined));

    this.iconArr.push(icon1);
    this.iconVectorSource = new VectorSource({
      features: this.iconArr,
      wrapX: false,
    });
    //creates a layer and use the source in the layer
    this.iconVectorLayer = new VectorLayer({
      source: this.iconVectorSource,
      style: function(feature) {
        if (feature.get('color') !== this.activeColor) {

          var image = feature.get('style').getImage().getImage();

          var canvas = document.createElement('canvas');
          var ctx = canvas.getContext('2d');
          var dArr = [-1,-1, 0,-1, 1,-1, -1,0, 1,0, -1,1, 0,1, 1,1], // offset array
              s = 2,  // thickness scale
              i = 0,  // iterator
              x = 2,  // final x position
              y = 2;  // final y position

          //set new canvas dimentions adjusted for border
          canvas.width = image.width + s + s;
          canvas.height = image.height + s + s;

          // draw images at offsets from the array scaled by s
          for(; i < dArr.length; i += 2)
          ctx.drawImage(image, x + dArr[i]*s, y + dArr[i+1]*s);

          // fill with color
          ctx.globalCompositeOperation = "source-in";
          ctx.fillStyle = this.activeColor;
          ctx.fillRect(0,0,canvas.width, canvas.height);

          // draw original image in normal mode
          ctx.globalCompositeOperation = "source-over";
          ctx.drawImage(image, x, y,image.width, image.height);

          //create new openlayers icon style from canvas
          var newStyle = new Style({
              image: new Icon(/** @type {olx.style.IconOptions} */ ({
              crossOrigin: 'anonymous',
              src: undefined,
              img: canvas,
              imgSize: canvas ? [canvas.width, canvas.height] : undefined
            }))
          });

          feature.set('style', newStyle);
          feature.set('color', this.activeColor);

        }
        return feature.get('style');
      },
    });
    this.map.addLayer(this.iconVectorLayer);
  }

  public createStyle(src, img) {
    return new Style({
      image: new Icon(/** @type {olx.style.IconOptions} */ ({
        crossOrigin: 'anonymous',
        src: src,
        img: img,
        imgSize: img ? [img.width, img.height] : undefined
      }))
    });
  }

}
14
  • Your code does not seem to be working, the icon is not in the place where it is supposed to be. Nothing in that position. if i just use style: function(feature){ return feature.get('style') } this shows an icon but when i add the code above no icon can be seen on the map. Commented Jan 21, 2020 at 2:19
  • Also, different icon have different border color not a fixed color (this.activecolor) Commented Jan 21, 2020 at 9:15
  • You said you wanted the color to change depending on the variable. If you have code which changes the value of activecolor the color will change.
    – Mike
    Commented Jan 21, 2020 at 9:39
  • Here is working code codesandbox.io/s/icon-negative-mpi4m There were two problems (1) you cannot update until the original icon has loaded and (2) you must save the origin icon, otherwise your code will draw a new border outside the earlier border.
    – Mike
    Commented Jan 21, 2020 at 10:46
  • Thank you very much for the code. There is just one last thing, i will be updating the icon's border depending on what i get from server. This means that different icon will have different border but with this code all the icons will have the same border. How do i solve this issue ? Commented Jan 22, 2020 at 6:18

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.