Edit

Draw lines rendered with WebGL

draw14 vector76 webgl17 line1 dash1

Cap type  
Join type  
12
10
0
0

Example showing how stroke styling options are represented with the WebGL renderer

This example lets you tweak the stroke styling options dynamically to see how they affect line rendering. The line string showing by default can be modified and new ones can be drawn by single-clicking on the map.

main.js
import GeoJSON from 'ol/format/GeoJSON.js';
import Layer from 'ol/layer/Layer.js';
import Map from 'ol/Map.js';
import View from 'ol/View.js';
import WebGLVectorLayerRenderer from 'ol/renderer/webgl/VectorLayer.js';
import {Draw, Modify, Snap} from 'ol/interaction.js';
import {OSM, Vector as VectorSource} from 'ol/source.js';
import {Tile as TileLayer} from 'ol/layer.js';
import {fromLonLat} from 'ol/proj.js';

const style = {
  variables: {
    width: 12,
    offset: 0,
    capType: 'butt',
    joinType: 'miter',
    miterLimit: 10, // ratio
    dashLength1: 25,
    dashLength2: 15,
    dashLength3: 15,
    dashLength4: 15,
    dashOffset: 0,
  },
  'stroke-width': ['var', 'width'],
  'stroke-color': 'rgba(24,86,34,0.7)',
  'stroke-line-dash': [
    ['var', 'dashLength1'],
    ['var', 'dashLength2'],
    ['var', 'dashLength3'],
    ['var', 'dashLength4'],
  ],
  'stroke-line-dash-offset': ['var', 'dashOffset'],
  'stroke-offset': ['var', 'offset'],
  'stroke-line-cap': ['var', 'capType'],
  'stroke-line-join': ['var', 'joinType'],
  'stroke-miter-limit': ['var', 'miterLimit'],
};

class WebGLLayer extends Layer {
  createRenderer() {
    return new WebGLVectorLayerRenderer(this, {
      className: this.getClassName(),
      style,
    });
  }
}

const osm = new TileLayer({
  source: new OSM(),
});

const source = new VectorSource({
  url: 'data/geojson/switzerland.geojson',
  format: new GeoJSON(),
});
const vector = new WebGLLayer({
  source: source,
});

const map = new Map({
  layers: [osm, vector],
  target: 'map',
  view: new View({
    center: fromLonLat([8.43, 46.82]),
    zoom: 7,
  }),
});

const modify = new Modify({source: source});
map.addInteraction(modify);

let draw, snap; // global so we can remove them later

function addInteractions() {
  draw = new Draw({
    source: source,
    type: 'LineString',
  });
  map.addInteraction(draw);
  snap = new Snap({source: source});
  map.addInteraction(snap);
}

addInteractions();

const inputListener = (event) => {
  const variables = style.variables;
  const variableName = event.target.name;
  if (event.target.type === 'radio') {
    variables[variableName] = event.target.value;
  } else {
    variables[variableName] = parseFloat(event.target.value);
  }
  const valueSpan = document.getElementById(`value-${variableName}`);
  if (valueSpan) {
    valueSpan.textContent = variables[variableName];
  }
  map.render();
};
document
  .querySelectorAll('input.uniform')
  .forEach((input) => input.addEventListener('input', inputListener));
index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Draw lines rendered with WebGL</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>
    <form>
      <div class="form-group">Cap type &nbsp;
        <label class="radio-inline">
          <input type="radio" class="uniform" name="capType" value="butt" checked/> <code>butt</code>&nbsp;
        </label>
        <label class="radio-inline">
          <input type="radio" class="uniform" name="capType" value="round"/> <code>round</code>&nbsp;
        </label>
        <label class="radio-inline">
          <input type="radio" class="uniform" name="capType" value="square"/> <code>square</code>&nbsp;
        </label>
      </div>
      <div class="form-group">Join type &nbsp;
        <label class="radio-inline">
          <input type="radio" class="uniform" name="joinType" value="miter" checked/> <code>miter</code>&nbsp;
        </label>
        <label class="radio-inline">
          <input type="radio" class="uniform" name="joinType" value="round"/> <code>round</code>&nbsp;
        </label>
        <label class="radio-inline">
          <input type="radio" class="uniform" name="joinType" value="bevel"/> <code>bevel</code>&nbsp;
        </label>
      </div>
      <div class="form-group">
        <label>
          Line width (pixels)
          <input type="range" class="uniform" name="width" min="1" max="40" value="8">
        </label>
        <span id="value-width">12</span>
      </div>
      <div class="form-group">
        <label>
          Miter limit (ratio)
          <input type="range" class="uniform" name="miterLimit" min="1" max="20" value="10" step="0.1">
        </label>
        <span id="value-miterLimit">10</span>
      </div>
      <div class="form-group">
        <label>
          Line offset (pixels)
          <input type="range" class="uniform" name="offset" min="-40" max="40" value="0">
        </label>
        <span id="value-offset">0</span>
      </div>
      <div class="form-group">
        <label>
          Line dash pattern (px)
          <input type="number" class="uniform" name="dashLength1" min="0" max="100" value="25">
          <input type="number" class="uniform" name="dashLength2" min="0" max="100" value="15">
          <input type="number" class="uniform" name="dashLength3" min="0" max="100" value="15">
          <input type="number" class="uniform" name="dashLength4" min="0" max="100" value="15">
        </label>
      </div>
      <label>
        Line dash offset (pixels)
        <input type="range" class="uniform" name="dashOffset" min="-200" max="200" value="0">
      </label>
      <span id="value-dashOffset">0</span>
    </form>

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