Edit

Icon and Label Scale

vector80 style25 icon9 label4 scale4

Image -0.00 π      Text
     
π π

Example of scaling icons and labels.

Icons and labels can be scaled in both dimensions if required. A negative value will flip the image or text around its anchor point (reversed text is not suitable for line placement).

main.js
import Feature from 'ol/Feature.js';
import Map from 'ol/Map.js';
import Point from 'ol/geom/Point.js';
import View from 'ol/View.js';
import {Circle, Fill, Icon, Stroke, Style, Text} from 'ol/style.js';
import {OGCMapTile, Vector as VectorSource} from 'ol/source.js';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer.js';

const iconFeature = new Feature({
  geometry: new Point([0, 0]),
});

const iconStyle = new Style({
  image: new Icon({
    anchor: [0.5, 1],
    src: 'data/world.png',
  }),
  text: new Text({
    text: 'World\nText',
    font: 'bold 30px Calibri,sans-serif',
    fill: new Fill({
      color: 'black',
    }),
    stroke: new Stroke({
      color: 'white',
      width: 2,
    }),
  }),
});

const pointStyle = new Style({
  image: new Circle({
    radius: 7,
    fill: new Fill({
      color: 'black',
    }),
    stroke: new Stroke({
      color: 'white',
      width: 2,
    }),
  }),
});

iconFeature.setStyle([pointStyle, iconStyle]);

const vectorSource = new VectorSource({
  features: [iconFeature],
});

const vectorLayer = new VectorLayer({
  source: vectorSource,
});

const rasterLayer = new TileLayer({
  source: new OGCMapTile({
    url: 'https://maps.gnosis.earth/ogcapi/collections/NaturalEarth:raster:HYP_HR_SR_OB_DR/map/tiles/WebMercatorQuad',
    crossOrigin: '',
  }),
});

const map = new Map({
  layers: [rasterLayer, vectorLayer],
  target: 'map',
  view: new View({
    center: [0, 0],
    zoom: 3,
  }),
});

const textAlignments = ['left', 'center', 'right'];
const textBaselines = ['top', 'middle', 'bottom'];
const controls = {};
const controlIds = [
  'rotation',
  'rotateWithView',
  'scaleX',
  'scaleY',
  'anchorX',
  'anchorY',
  'displacementX',
  'displacementY',
  'textRotation',
  'textRotateWithView',
  'textScaleX',
  'textScaleY',
  'textAlign',
  'textBaseline',
  'textOffsetX',
  'textOffsetY',
];
controlIds.forEach(function (id) {
  const control = document.getElementById(id);
  const output = document.getElementById(id + 'Out');
  function setOutput() {
    const value = parseFloat(control.value);
    if (control.type === 'checkbox') {
      output.innerText = control.checked;
    } else if (id === 'textAlign') {
      output.innerText = textAlignments[value];
    } else if (id === 'textBaseline') {
      output.innerText = textBaselines[value];
    } else {
      output.innerText = control.step.startsWith('0.')
        ? value.toFixed(2)
        : value;
    }
  }
  control.addEventListener('input', function () {
    setOutput();
    updateStyle();
  });
  setOutput();
  controls[id] = control;
});

function updateStyle() {
  iconStyle
    .getImage()
    .setRotation(parseFloat(controls['rotation'].value) * Math.PI);

  iconStyle.getImage().setRotateWithView(controls['rotateWithView'].checked);

  iconStyle
    .getImage()
    .setScale([
      parseFloat(controls['scaleX'].value),
      parseFloat(controls['scaleY'].value),
    ]);

  iconStyle
    .getImage()
    .setAnchor([
      parseFloat(controls['anchorX'].value),
      parseFloat(controls['anchorY'].value),
    ]);

  iconStyle
    .getImage()
    .setDisplacement([
      parseFloat(controls['displacementX'].value),
      parseFloat(controls['displacementY'].value),
    ]);

  iconStyle
    .getText()
    .setRotation(parseFloat(controls['textRotation'].value) * Math.PI);

  iconStyle.getText().setRotateWithView(controls['textRotateWithView'].checked);

  iconStyle
    .getText()
    .setScale([
      parseFloat(controls['textScaleX'].value),
      parseFloat(controls['textScaleY'].value),
    ]);

  iconStyle
    .getText()
    .setTextAlign(textAlignments[parseFloat(controls['textAlign'].value)]);

  iconStyle
    .getText()
    .setTextBaseline(textBaselines[parseFloat(controls['textBaseline'].value)]);

  iconStyle.getText().setOffsetX(parseFloat(controls['textOffsetX'].value));
  iconStyle.getText().setOffsetY(parseFloat(controls['textOffsetY'].value));

  iconFeature.changed();
}
updateStyle();

// change mouse cursor when over marker
map.on('pointermove', function (e) {
  const hit = map.hasFeatureAtPixel(e.pixel);
  map.getTargetElement().style.cursor = hit ? 'pointer' : '';
});
index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Icon and Label Scale</title>
    <link rel="stylesheet" href="node_modules/ol/ol.css">
    <style>
      .map {
        width: 100%;
        height: 400px;
      }
    </style>
  </head>
  <body>
    <div id="map" class="map"></div>
    <table class="controls">
      <tr>
        <th>
          Image
        </th>
        <th></th>
        <th style="visibility: hidden;">-0.00 π</th>
        <th>&nbsp;&nbsp;&nbsp;&nbsp;</th>
        <th>
          Text
        </th>
        <th></th>
        <th></th>
      </tr>
      <tr>
        <td><label for="rotateWithView">Rotate with view</label>&nbsp;&nbsp;</td>
        <td>
          <input id="rotateWithView" type="checkbox" />
        </td>
        <td><span id="rotateWithViewOut"></span></td>
        <td></td>
        <td><label for="textRotateWithView">Rotate with view</label>&nbsp;&nbsp;</td>
        <td>
          <input id="textRotateWithView" type="checkbox" />
        </td>
        <td><span id="textRotateWithViewOut"></span></td>
      </tr>
      <tr>
        <td><label for="rotation">Rotation</label></td>
        <td>
          <input
            id="rotation"
            type="range"
            min="-1"
            max="1"
            step="0.05"
            value="0.25"
          />
        </td>
        <td><span id="rotationOut"></span> π</td>
        <td></td>
        <td><label for="textRotation">Rotation</label></td>
        <td>
          <input
            id="textRotation"
            type="range"
            min="-1"
            max="1"
            step="0.05"
            value="0.25"
          />
        </td>
        <td><span id="textRotationOut"></span> π</td>
      </tr>
      <tr>
        <td><label for="scaleX">Scale X</label></td>
        <td>
          <input
            id="scaleX"
            type="range"
            min="-2"
            max="2"
            step="0.1"
            value="1"
          />
        </td>
        <td><span id="scaleXOut"></span></td>
        <td></td>
        <td><label for="textScaleX">Scale X</label></td>
        <td>
          <input
            id="textScaleX"
            type="range"
            min="-2"
            max="2"
            step="0.1"
            value="1"
          />
        </td>
        <td><span id="textScaleXOut"></span></td>
      </tr>
      <tr>
        <td><label for="scaleY">Scale Y</label></td>
        <td>
          <input
            id="scaleY"
            type="range"
            min="-2"
            max="2"
            step="0.1"
            value="1"
          />
        </td>
        <td><span id="scaleYOut"></span></td>
        <td></td>
        <td><label for="textScaleY">Scale Y</label></td>
        <td>
          <input
            id="textScaleY"
            type="range"
            min="-2"
            max="2"
            step="0.1"
            value="1"
          />
        </td>
        <td><span id="textScaleYOut"></span></td>
      </tr>
      <tr>
        <td><label for="anchorX">Anchor X</label></td>
        <td>
          <input
            id="anchorX"
            type="range"
            min="0"
            max="1"
            step="0.01"
            value="0.5"
          />
        </td>
        <td><span id="anchorXOut"></span></td>
        <td></td>
        <td><label for="textAlign">Align</label></td>
        <td>
          <input
            id="textAlign"
            type="range"
            min="0"
            max="2"
            step="1"
            value="1"
          />
        </td>
        <td><span id="textAlignOut"></span></td>
      </tr>
      <tr>
        <td><label for="anchorY">Anchor Y</label></td>
        <td>
          <input
            id="anchorY"
            type="range"
            min="0"
            max="1"
            step="0.01"
            value="1"
          />
        </td>
        <td><span id="anchorYOut"></span></td>
        <td></td>
        <td><label for="textBaseline">Baseline</label></td>
        <td>
          <input
            id="textBaseline"
            type="range"
            min="0"
            max="2"
            step="1"
            value="0"
          />
        </td>
        <td><span id="textBaselineOut"></span></td>
      </tr>
      <tr>
        <td><label for="displacementX">Displacement X</label></td>
        <td>
          <input
            id="displacementX"
            type="range"
            min="-100"
            max="100"
            value="0"
          />
        </td>
        <td><span id="displacementXOut"></span></td>
        <td></td>
        <td><label for="textOffsetX">Offset X</label></td>
        <td>
          <input id="textOffsetX" type="range" min="-100" max="100" value="0" />
        </td>
        <td><span id="textOffsetXOut"></span></td>
      </tr>
      <tr>
        <td><label for="displacementY">Displacement Y</label></td>
        <td>
          <input
            id="displacementY"
            type="range"
            min="-100"
            max="100"
            value="0"
          />
        </td>
        <td><span id="displacementYOut"></span></td>
        <td></td>
        <td><label for="textOffsetY">Offset Y</label></td>
        <td>
          <input id="textOffsetY" type="range" min="-100" max="100" value="0" />
        </td>
        <td><span id="textOffsetYOut"></span></td>
      </tr>
    </table>

    <script type="module" src="main.js"></script>
  </body>
</html>
package.json
{
  "name": "icon-scale",
  "dependencies": {
    "ol": "10.3.1"
  },
  "devDependencies": {
    "vite": "^3.2.3"
  },
  "scripts": {
    "start": "vite",
    "build": "vite build"
  }
}