Skip to content

TileManager zoom hysteresis bypasses maxZoom constraint #1286

@jarda-manana

Description

@jarda-manana

Description

When the map's zoom level exceeds a tile layer's configured maxZoom, the zoom hysteresis logic in TileManager.update() can override the clamped tileZoom value, causing tile requests at zoom levels beyond maxZoom.

Steps to reproduce

  1. Create a BitmapTileLayer with a tile source that has maxZoom = 7
  2. Set the map position to zoom level 8 or higher
  3. Observe that tile requests are made at zoom 8 instead of being clamped to 7

Root cause

In TileManager.update(), mPrevZoomlevel is initialized from the unclamped pos.zoomLevel:

  if (mNewTiles == null || mNewTiles.tiles.length == 0) {
      mPrevZoomlevel = pos.zoomLevel;  // unclamped — can exceed mMaxZoom
      init();
  }

  int tileZoom = clamp(pos.zoomLevel, mMinZoom, mMaxZoom);  // correctly clamped

  // Hysteresis logic:
  int zoomDiff = tileZoom - mPrevZoomlevel;  // e.g., 7 - 8 = -1
  if (zoomDiff == -1) {
      if (scaleDiv > mLevelDownThreshold) {
          tileZoom = mPrevZoomlevel;  // reverts to 8, bypassing maxZoom
      }
  }
  mPrevZoomlevel = tileZoom;  // persists the unclamped value

The hysteresis interprets the clamped-vs-unclamped difference as a "zoom out" and holds the previous (unclamped) zoom level. This persists on subsequent updates because mPrevZoomlevel remains above mMaxZoom.

Suggested fix

Clamp mPrevZoomlevel during initialization:

  if (mNewTiles == null || mNewTiles.tiles.length == 0) {
      mPrevZoomlevel = clamp(pos.zoomLevel, mMinZoom, mMaxZoom);
      init();
  }

Or apply the clamp after the hysteresis adjustment:

  // after the threshold logic block:
  tileZoom = clamp(tileZoom, mMinZoom, mMaxZoom);
  mPrevZoomlevel = tileZoom;

Impact

Any BitmapTileLayer (or other tile layer) with a maxZoom lower than the map's current zoom level will request tiles beyond its configured maximum. This causes unnecessary HTTP requests to non-existent tile endpoints and potential rendering issues.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions