Symbols with WebGL

Using symbols in an atlas with WebGL.

When using symbol styles with WebGL, OpenLayers would render the symbol on a temporary image and would create a WebGL texture for each image. For a better performance, it is recommended to use atlas images (similar to image sprites with CSS), so that the number of textures is reduced. OpenLayers provides an AtlasManager, which when passed to the constructor of a symbol style, will create atlases for the symbols.

<!DOCTYPE html>
<html>
  <head>
    <title>Symbols with WebGL</title>
    <link rel="stylesheet" href="https://openlayers.org/en/v5.3.0/css/ol.css" type="text/css">
    <!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
    <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL"></script>

  </head>
  <body>
    <div id="map" class="map"></div>
    <script>
      import Feature from 'ol/Feature.js';
      import Map from 'ol/WebGLMap.js';
      import View from 'ol/View.js';
      import Point from 'ol/geom/Point.js';
      import VectorLayer from 'ol/layer/Vector.js';
      import VectorSource from 'ol/source/Vector.js';
      import {AtlasManager, Circle as CircleStyle, Fill, RegularShape, Stroke, Style} from 'ol/style.js';

      var atlasManager = new AtlasManager({
        // we increase the initial size so that all symbols fit into
        // a single atlas image
        initialSize: 512
      });

      var symbolInfo = [{
        opacity: 1.0,
        scale: 1.0,
        fillColor: 'rgba(255, 153, 0, 0.4)',
        strokeColor: 'rgba(255, 204, 0, 0.2)'
      }, {
        opacity: 0.75,
        scale: 1.25,
        fillColor: 'rgba(70, 80, 224, 0.4)',
        strokeColor: 'rgba(12, 21, 138, 0.2)'
      }, {
        opacity: 0.5,
        scale: 1.5,
        fillColor: 'rgba(66, 150, 79, 0.4)',
        strokeColor: 'rgba(20, 99, 32, 0.2)'
      }, {
        opacity: 1.0,
        scale: 1.0,
        fillColor: 'rgba(176, 61, 35, 0.4)',
        strokeColor: 'rgba(145, 43, 20, 0.2)'
      }];

      var radiuses = [3, 6, 9, 15, 19, 25];
      var symbolCount = symbolInfo.length * radiuses.length * 2;
      var symbols = [];
      var i, j;
      for (i = 0; i < symbolInfo.length; ++i) {
        var info = symbolInfo[i];
        for (j = 0; j < radiuses.length; ++j) {
          // circle symbol
          symbols.push(new CircleStyle({
            opacity: info.opacity,
            scale: info.scale,
            radius: radiuses[j],
            fill: new Fill({
              color: info.fillColor
            }),
            stroke: new Stroke({
              color: info.strokeColor,
              width: 1
            }),
            // by passing the atlas manager to the symbol,
            // the symbol will be added to an atlas
            atlasManager: atlasManager
          }));

          // star symbol
          symbols.push(new RegularShape({
            points: 8,
            opacity: info.opacity,
            scale: info.scale,
            radius: radiuses[j],
            radius2: radiuses[j] * 0.7,
            angle: 1.4,
            fill: new Fill({
              color: info.fillColor
            }),
            stroke: new Stroke({
              color: info.strokeColor,
              width: 1
            }),
            atlasManager: atlasManager
          }));
        }
      }

      var featureCount = 50000;
      var features = new Array(featureCount);
      var feature, geometry;
      var e = 25000000;
      for (i = 0; i < featureCount; ++i) {
        geometry = new Point(
          [2 * e * Math.random() - e, 2 * e * Math.random() - e]);
        feature = new Feature(geometry);
        feature.setStyle(
          new Style({
            image: symbols[i % symbolCount]
          })
        );
        features[i] = feature;
      }

      var vectorSource = new VectorSource({
        features: features
      });
      var vector = new VectorLayer({
        source: vectorSource
      });

      var map = new Map({
        layers: [vector],
        target: document.getElementById('map'),
        view: new View({
          center: [0, 0],
          zoom: 4
        })
      });
    </script>
  </body>
</html>