Ограничить панорамирование вне области WMS в OpenLayers3

У меня есть прямоугольник WMS небольшой области и вы хотите ограничить панорамирование вне WMS extends, так что на карте нет видимой белой или черной области. Добавление extent в View не работает для меня и в документации об этой опции написано

Степень, которая ограничивает центр, другими словами, центр не может быть установленным вне этой степени.

Но поскольку я понимаю это, если центр находится в области протяженности, но в самом углу он покажет белую область вне этой степени, но я не хочу видеть белую область вообще.

Можно ли добиться этого с помощью OL3?

Ответы

Ответ 1

Вот мое решение. Я написал это только сейчас, и поэтому он не подвергается обширной проверке. Вероятно, он сломается, если вы начнете вращать карту, например, и это может быть проблематично, если вы уменьшите слишком далеко.

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);

Здесь ожидаются переменные map, view (с очевидными значениями) и extent (xmin, ymin, xmax, ymax, которые вы хотите видеть).

Ответ 2

Здесь более надежная реализация, которая должна работать очень хорошо в любом случае. Он написан на ES6 и требует метода isEqual (от lodash или всего остального...)

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);

Ответ 3

Это расширение для ответа @tremby, но для комментария.

Прежде всего, его решение работает очень хорошо для меня, но его часто называют способом. Поэтому я завернул его в функцию debounce.

So

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

становится

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

Это приведет к небольшому мерцанию, когда вы перемещаетесь за пределы ограничивающего прямоугольника, масштабирование/перемещение бота без задержки.

Все еще не идеальное, но полезное улучшение с моей точки зрения.

Код ошибки:

// 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);
    };
};

Сорвали: https://davidwalsh.name/javascript-debounce-function, в underscore.js