Restrict Pan outside WMS extent in OpenLayers3

前端 未结 3 955
无人共我
无人共我 2020-12-29 08:28

I have rectangle WMS of small area and want to restrict panning outside WMS extends, so there aren\'t white or black area outside the map visible at all. Adding exten

相关标签:
3条回答
  • 2020-12-29 08:41

    Here's a more robust implementation that should work really well in any case. It's written in ES6, and requires isEqual method (from lodash or anything else ...)

    const extent = [-357823.2365, 6037008.6939, 1313632.3628, 7230727.3772];
    const view = this.olMap.getView();
    
    const modifyValues = {};
    
    // Trick to forbid panning outside extent
    let constrainPan = (e) => {
      const type = e.type;
      const newValue = e.target.get(e.key);
      const oldValue = e.oldValue;
    
      if (isEqual(oldValue, newValue)) {
        // Do nothing when event doesn't change the value
        return;
      }
    
      if (isEqual(modifyValues[type], newValue)) {
        // Break possible infinite loop
        delete modifyValues[type];
        return;
      }
    
      if (type === 'change:resolution' && newValue < oldValue) {
        // Always allow zoom-in.
        return;
      }
    
      const visibleExtent = view.calculateExtent(this.olMap.getSize());
      const intersection = ol.extent.getIntersection(visibleExtent, extent);
      const modify = !isEqual(intersection, visibleExtent);
    
      if (modify) {
        if (type === 'change:center') {
          const newCenter = newValue.slice(0);
    
          if (ol.extent.getWidth(visibleExtent) !== ol.extent.getWidth(intersection)) {
            newCenter[0] = oldValue[0];
          }
    
          if (ol.extent.getHeight(visibleExtent) !== ol.extent.getHeight(intersection)) {
            newCenter[1] = oldValue[1];
          }
    
          modifyValues[type] = newCenter;
          view.setCenter(newCenter);
        } else if (type === 'change:resolution') {
          modifyValues[type] = oldValue;
          view.setResolution(oldValue);
        }
      }
    };
    view.on('change:resolution', constrainPan);
    view.on('change:center', constrainPan);
    
    0 讨论(0)
  • 2020-12-29 08:59

    This is an extension to @tremby answer, but to long for a comment.

    First of all, his solution works really well for me, but it was called way to often. Therefore I wrapped it in a debounce function.

    So

    view.on('change:resolution', constrainPan);
    view.on('change:center', constrainPan);
    

    becomes

    var dConstrainPan = debounce(constrainPan);
    view.on('change:resolution', dConstrainPan);
    view.on('change:center', dConstrainPan);
    

    This will result in a slight flicker, when moving outside the bounding box, bot zooming/ moving works without delay.

    Still not perfect but a useful improvement from my point of view.

    Debounce code:

    // Returns a function, that, as long as it continues to be invoked, will not
    // be triggered. The function will be called after it stops being called for
    // N milliseconds. If `immediate` is passed, trigger the function on the
    // leading edge, instead of the trailing.
    function debounce(func, wait, immediate) {
        var timeout;
        return function() {
            var context = this, args = arguments;
            var later = function() {
                timeout = null;
                if (!immediate) func.apply(context, args);
            };
            var callNow = immediate && !timeout;
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
            if (callNow) func.apply(context, args);
        };
    };
    

    Soruce: https://davidwalsh.name/javascript-debounce-function , in underscore.js

    0 讨论(0)
  • 2020-12-29 09:01

    Here's my solution. I wrote it just now, and so it is not extensively tested. It would probably break if you start rotating the map, for example, and it may be glitchy if you zoom out too far.

    var constrainPan = function() {
        var visible = view.calculateExtent(map.getSize());
        var centre = view.getCenter();
        var delta;
        var adjust = false;
        if ((delta = extent[0] - visible[0]) > 0) {
            adjust = true;
            centre[0] += delta;
        } else if ((delta = extent[2] - visible[2]) < 0) {
            adjust = true;
            centre[0] += delta;
        }
        if ((delta = extent[1] - visible[1]) > 0) {
            adjust = true;
            centre[1] += delta;
        } else if ((delta = extent[3] - visible[3]) < 0) {
            adjust = true;
            centre[1] += delta;
        }
        if (adjust) {
            view.setCenter(centre);
        }
    };
    view.on('change:resolution', constrainPan);
    view.on('change:center', constrainPan);
    

    This expects the variables map, view (with obvious meanings) and extent (the xmin, ymin, xmax, ymax you want to be visible) to be available.

    0 讨论(0)
提交回复
热议问题