openlayers 交互过程中resolution的确定 小记

群里有个人问了个奇怪的问题,发现自己对resolution尚未理解透彻,先查看了一个例子,再查看源码

https://openlayers.org/en/latest/examples/interaction-options.html?

这个例子中有一句:By default, pinch-zoom and wheel/trackpad zoom interactions can leave the map at fractional zoom levels. If instead you want to constrain wheel/trackpad zooming to integer zoom levels, set constrainResolution: true;谷歌翻译

默认情况下,捏拉缩放和滚轮/触控板缩放交互可以使地图保持小数缩放级别。 如果您想要将轮/轨迹板缩放到整数缩放级别,请设置constrainResolution:true

保持小数级别?这个与自己平时对zoom的认识有些冲突了,我原以为zoom都是整数缩放的,跟踪一下源码

function handleEvent(mapBrowserEvent) {
  if (!this.condition_(mapBrowserEvent)) {
    return true;
  }
  const type = mapBrowserEvent.type;
  if (type !== EventType.WHEEL && type !== EventType.MOUSEWHEEL) {
    return true;
  }

  mapBrowserEvent.preventDefault();

  const map = mapBrowserEvent.map;
  const wheelEvent = /** @type {WheelEvent} */ (mapBrowserEvent.originalEvent);

  if (this.useAnchor_) {
    this.lastAnchor_ = mapBrowserEvent.coordinate;
  }

  // Delta normalisation inspired by
  // https://github.com/mapbox/mapbox-gl-js/blob/001c7b9/js/ui/handler/scroll_zoom.js
  let delta;
  if (mapBrowserEvent.type == EventType.WHEEL) {
    delta = wheelEvent.deltaY;
    if (FIREFOX &&
        wheelEvent.deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
      delta /= DEVICE_PIXEL_RATIO;
    }
    if (wheelEvent.deltaMode === WheelEvent.DOM_DELTA_LINE) {
      delta *= 40;
    }
  } else if (mapBrowserEvent.type == EventType.MOUSEWHEEL) {
    delta = -wheelEvent.wheelDeltaY;
    if (SAFARI) {
      delta /= 3;
    }
  }
.
.
.
}

这个函数首先计算了滚轮的滚动距离,函数的最后实际处理了鼠标滚动事件,多点触控缩放另说。

this.timeoutId_ = setTimeout(this.handleWheelZoom_.bind(this, map), timeLeft);
 handleWheelZoom_(map) {
    const view = map.getView();
    if (view.getAnimating()) {
      view.cancelAnimations();
    }
    const maxDelta = MAX_DELTA;
    const delta = clamp(this.delta_, -maxDelta, maxDelta);
    zoomByDelta(view, -delta, this.lastAnchor_, this.duration_);
    this.mode_ = undefined;
    this.delta_ = 0;
    this.lastAnchor_ = null;
    this.startTime_ = undefined;
    this.timeoutId_ = undefined;
  }

按我的理解这个clamp函数将距离转换为了zoom的变化数,要么是-1,要么是1,

而zoomByDetla中利用View.js 中的,计算了在resolutions未设置或已设置的情况下,resolution的选择。

export function zoomByDelta(view, delta, opt_anchor, opt_duration) {
  const currentResolution = view.getResolution();
  let resolution = view.constrainResolution(currentResolution, delta, 0);

  if (resolution !== undefined) {
    const resolutions = view.getResolutions();
    resolution = clamp(
      resolution,
      view.getMinResolution() || resolutions[resolutions.length - 1],
      view.getMaxResolution() || resolutions[0]);
  }

  // If we have a constraint on center, we need to change the anchor so that the
  // new center is within the extent. We first calculate the new center, apply
  // the constraint to it, and then calculate back the anchor
  if (opt_anchor && resolution !== undefined && resolution !== currentResolution) {
    const currentCenter = view.getCenter();
    let center = view.calculateCenterZoom(resolution, opt_anchor);
    center = view.constrainCenter(center);

    opt_anchor = [
      (resolution * currentCenter[0] - currentResolution * center[0]) /
          (resolution - currentResolution),
      (resolution * currentCenter[1] - currentResolution * center[1]) /
          (resolution - currentResolution)
    ];
  }

  zoomWithoutConstraints(view, resolution, opt_anchor, opt_duration);
}

 

这个函数计算了缩放后的分辨率。

view.constrainResolution(currentResolution, delta, 0);

这两个函数分别计算了有无设置resolutions时缩放的分辨率,

有则:createSnapToResolutions(resolutions);
无则:createSnapToPower(zoomFactor, maxResolution, maxZoom - minZoom);
export function createSnapToPower(power, maxResolution, opt_maxLevel) {
  return (
    /**
     * @param {number|undefined} resolution Resolution.
     * @param {number} delta Delta.
     * @param {number} direction Direction.
     * @return {number|undefined} Resolution.
     */
    function(resolution, delta, direction) {
      if (resolution !== undefined) {
        const offset = -direction / 2 + 0.5;
        const oldLevel = Math.floor(
          Math.log(maxResolution / resolution) / Math.log(power) + offset);
        let newLevel = Math.max(oldLevel + delta, 0);
        if (opt_maxLevel !== undefined) {
          newLevel = Math.min(newLevel, opt_maxLevel);
        }
        return maxResolution / Math.pow(power, newLevel);
      } else {
        return undefined;
      }
    });
}

可以看到,首先根据最大层级(固定常量28目前ol的设定),及缩放前的分辨率计算出缩放前的旧分辨率,然后增加一层,根据增加的一层重新计算了分辨率,感觉这样很绕,不知为何。

而若已经设定了resolutions数组,则吸附到最近的一个分辨率上,

export function createSnapToResolutions(resolutions) {
  return (
    /**
     * @param {number|undefined} resolution Resolution.
     * @param {number} delta Delta.
     * @param {number} direction Direction.
     * @return {number|undefined} Resolution.
     */
    function(resolution, delta, direction) {
      if (resolution !== undefined) {
        let z = linearFindNearest(resolutions, resolution, direction);
        z = clamp(z + delta, 0, resolutions.length - 1);
        const index = Math.floor(z);
        if (z != index && index < resolutions.length - 1) {
          const power = resolutions[index] / resolutions[index + 1];
          return resolutions[index] / Math.pow(power, z - index);
        } else {
          return resolutions[index];
        }
      } else {
        return undefined;
      }
    }
  );
}

所以,根据源码查看,在缩放过程中,不会出现对应一个zoom带小数的对应的分辨率的,不知道文章开始时的例子为何说会存在保持缩放小数级别的?不解!

而view若未设定强制指定缩放到整数级,fit函数实际上是可以重新计算resolution的

 // calculate resolution
    let resolution = this.getResolutionForExtent(
      [minRotX, minRotY, maxRotX, maxRotY],
      [size[0] - padding[1] - padding[3], size[1] - padding[0] - padding[2]]);
    resolution = isNaN(resolution) ? minResolution :
      Math.max(resolution, minResolution);
    if (constrainResolution) {
      let constrainedResolution = this.constrainResolution(resolution, 0, 0);
      if (!nearest && constrainedResolution < resolution) {
        constrainedResolution = this.constrainResolution(
          constrainedResolution, -1, 0);
      }
      resolution = constrainedResolution;
    }

分辨率计算,

getResolutionForExtent(extent, opt_size) {
    const size = opt_size || this.getSizeFromViewport_();
    const xResolution = getWidth(extent) / size[0];
    const yResolution = getHeight(extent) / size[1];
    return Math.max(xResolution, yResolution);
  }

 

你可能感兴趣的:(gis)