Layer Spy

View a portion of one layer over another

Layer rendering can be manipulated in precompose and postcompose event listeners. These listeners get an event with a reference to the Canvas rendering context. In this example, the precompose listener sets a clipping mask around the most recent mouse position, giving you a spyglass effect for viewing one layer over another.

Move around the map to see the effect. Use the ↑ up and ↓ down arrow keys to adjust the spyglass size.

<!DOCTYPE html>
<html>
  <head>
    <title>Layer Spy</title>
    <link rel="stylesheet" href="https://openlayers.org/en/v4.6.4/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>
    <script src="https://openlayers.org/en/v4.6.4/build/ol.js"></script>
  </head>
  <body>
    <div id="map" class="map"></div>
    <script>
      var key = 'Your Bing Maps Key from http://www.bingmapsportal.com/ here';

      var roads = new ol.layer.Tile({
        source: new ol.source.BingMaps({key: key, imagerySet: 'Road'})
      });

      var imagery = new ol.layer.Tile({
        source: new ol.source.BingMaps({key: key, imagerySet: 'Aerial'})
      });

      var container = document.getElementById('map');

      var map = new ol.Map({
        layers: [roads, imagery],
        target: container,
        view: new ol.View({
          center: ol.proj.fromLonLat([-109, 46.5]),
          zoom: 6
        })
      });

      var radius = 75;
      document.addEventListener('keydown', function(evt) {
        if (evt.which === 38) {
          radius = Math.min(radius + 5, 150);
          map.render();
          evt.preventDefault();
        } else if (evt.which === 40) {
          radius = Math.max(radius - 5, 25);
          map.render();
          evt.preventDefault();
        }
      });

      // get the pixel position with every move
      var mousePosition = null;

      container.addEventListener('mousemove', function(event) {
        mousePosition = map.getEventPixel(event);
        map.render();
      });

      container.addEventListener('mouseout', function() {
        mousePosition = null;
        map.render();
      });

      // before rendering the layer, do some clipping
      imagery.on('precompose', function(event) {
        var ctx = event.context;
        var pixelRatio = event.frameState.pixelRatio;
        ctx.save();
        ctx.beginPath();
        if (mousePosition) {
          // only show a circle around the mouse
          ctx.arc(mousePosition[0] * pixelRatio, mousePosition[1] * pixelRatio,
              radius * pixelRatio, 0, 2 * Math.PI);
          ctx.lineWidth = 5 * pixelRatio;
          ctx.strokeStyle = 'rgba(0,0,0,0.5)';
          ctx.stroke();
        }
        ctx.clip();
      });

      // after rendering the layer, restore the canvas context
      imagery.on('postcompose', function(event) {
        var ctx = event.context;
        ctx.restore();
      });
    </script>
  </body>
</html>