Skip to content

Commit

Permalink
px-map now prevents zooming outside the supported bounds of any child…
Browse files Browse the repository at this point in the history
…ren layers (#142)

* px-map now prevents zooming outside the supported bounds of any children layers

* Added `unclampZoomToLayers` prop to disable zoom clamping if needed
  • Loading branch information
evanjd authored Dec 31, 2018
1 parent e97b518 commit 322e3cf
Show file tree
Hide file tree
Showing 3 changed files with 209 additions and 9 deletions.
96 changes: 88 additions & 8 deletions px-map-behavior-root.es6.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
(function() {
'use strict';

/****************************************************************************
* CONSTANTS
****************************************************************************/

const MIN_SUPPORTED_ZOOM_LEVEL = 0;
const MAX_SUPPORTED_ZOOM_LEVEL = 18;

/****************************************************************************
* BEHAVIORS
****************************************************************************/
Expand Down Expand Up @@ -233,7 +240,7 @@
*/
_getZoomLevelForFit(bounds, fitSetting, map) {
if (fitSetting === 'min') {
return map.getMinZoom() || 0;
return map.getMinZoom() || MIN_SUPPORTED_ZOOM_LEVEL;
}
if (fitSetting === 'max') {
return map.getBoundsZoom(bounds);
Expand Down Expand Up @@ -356,6 +363,38 @@
observer: 'shouldUpdateInst'
},

/**
* If set, respects the configured minimum and maximum zoom levels even if they fall outside
* the supported bounds of any children layers.
*
* @type {Boolean}
*/
unclampZoomToLayers: {
type: Boolean,
value: false,
observer: 'shouldUpdateInst'
},

/**
* Tracks the highest minimum zoom level supported by all children layers.
*
* @type {Number}
*/
_clampedMinZoom: {
type: Number,
value: MIN_SUPPORTED_ZOOM_LEVEL
},

/**
* Tracks the lowest maximum zoom level supported by all children layers.
*
* @type {Number}
*/
_clampedMaxZoom: {
type: Number,
value: MAX_SUPPORTED_ZOOM_LEVEL
},

/**
* Restricts the user from moving the map outside of a specific geographic
* boundary. The user will be bounced back if they attempt to pan outside the view.
Expand Down Expand Up @@ -531,14 +570,19 @@
this.scopeSubtree(this.$.map, true);
}

// Bind custom events for the map intance. Events will be unbound automatically.
// Bind custom events for the map instance. Events will be unbound automatically.
const mapMoveFn = this._handleMapMove.bind(this);
const zoomStartFn = this._handleZoomStart.bind(this);
const zoomEndFn = this._handleZoomEnd.bind(this);
const addLayerFn = (evt) => {
if (evt.layer && evt.layer.options)
this._clampZoomForLayer(evt.layer.options.minZoom, evt.layer.options.maxZoom);
}
this.bindEvents({
'moveend' : mapMoveFn,
'zoomstart' : zoomStartFn,
'zoomend' : zoomEndFn
'zoomend' : zoomEndFn,
'layeradd' : addLayerFn
});
},

Expand All @@ -559,8 +603,8 @@
options.crs = this.crs || L.CRS.EPSG3857;
options.center = [this.lat, this.lng];
options.zoom = this.zoom;
options.minZoom = this.minZoom || 0;
options.maxZoom = this.maxZoom || 18;
options.minZoom = this.minZoom || MIN_SUPPORTED_ZOOM_LEVEL;
options.maxZoom = this.maxZoom || MAX_SUPPORTED_ZOOM_LEVEL;
options.maxBounds = this.maxBounds || undefined;

options.dragging = !this.disableDragging;
Expand All @@ -569,6 +613,7 @@
options.doubleClickZoom = !this.disableDoubleClickZoom;
options.attributionControl = !this.disableAttribution;
options.attributionPrefix = this.attributionPrefix;
options.unclampZoomToLayers = this.unclampZoomToLayers;

return options;
},
Expand All @@ -582,13 +627,30 @@
}

if (lastOptions.maxZoom !== nextOptions.maxZoom && !isNaN(nextOptions.maxZoom)) {
this.setMaxZoom(nextOptions.maxZoom);
this.elementInst.setMaxZoom(Math.min(nextOptions.maxZoom, this._clampedMaxZoom));
}
if (lastOptions.minZoom !== nextOptions.minZoom && !isNaN(nextOptions.minZoom)) {
this.setMinZoom(nextOptions.minZoom);
this.elementInst.setMinZoom(Math.max(nextOptions.minZoom, this._clampedMinZoom));
}
if (lastOptions.maxBounds !== nextOptions.maxBounds && !isNaN(nextOptions.maxBounds)) {
this.setMaxBounds(nextOptions.maxBounds);
this.elementInst.setMaxBounds(nextOptions.maxBounds);
}
if (lastOptions.unclampZoomToLayers !== nextOptions.unclampZoomToLayers) {
if (nextOptions.unclampZoomToLayers) {
// Reset clamped values
this._clampedMinZoom = MIN_SUPPORTED_ZOOM_LEVEL;
this._clampedMaxZoom = MAX_SUPPORTED_ZOOM_LEVEL;
// Revert min/max zoom level to user specified values
this.elementInst.setMinZoom(nextOptions.minZoom || MIN_SUPPORTED_ZOOM_LEVEL);
this.elementInst.setMaxZoom(nextOptions.maxZoom || MAX_SUPPORTED_ZOOM_LEVEL);
} else {
this.elementInst.eachLayer(layer => {
if (layer.options)
this._clampZoomForLayer(layer.options.minZoom, layer.options.maxZoom);
});
}

this.elementInst.options.unclampZoomToLayers = nextOptions.unclampZoomToLayers;
}

if (!lastOptions.dragging && nextOptions.dragging) {
Expand Down Expand Up @@ -634,6 +696,24 @@
this.elementInst.invalidateSize();
},

/**
* Overrides the configured min/max zoom levels if they fall outside
* the supported bounds for a layer.
*/
_clampZoomForLayer(minZoom, maxZoom) {
if (this.unclampZoomToLayers) return;

if (minZoom > this.elementInst.options.minZoom) {
this.elementInst.setMinZoom(minZoom);
this._clampedMinZoom = minZoom;
}

if (maxZoom < this.elementInst.options.maxZoom) {
this.elementInst.setMaxZoom(maxZoom);
this._clampedMaxZoom = maxZoom;
}
},

/**
* Called when the `lat`, `lng`, or `zoom` properties are set or updated.
* Sets the map view to the new values.
Expand Down
6 changes: 6 additions & 0 deletions test/px-map-root-fixture.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@
</template>
</test-fixture>

<test-fixture id="DisableZoomClamps">
<template>
<px-map style="width:100px; height:100px;" unclamp-zoom-to-layers></px-map>
</template>
</test-fixture>

<!-- load test script -->
<script src="px-map-root-tests.js"></script>
</body>
Expand Down
116 changes: 115 additions & 1 deletion test/px-map-root-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,120 @@ describe('Basic px-map without options', function () {
});
});

describe('Basic px-map with zoom clamping enabled', function () {
var mapEl;

beforeEach(function(done) {
mapEl = fixture('BasicMapFixture');

flushAndRender(() => {
mapEl.elementInst.fire('layeradd', {
layer: {
options: {
minZoom: 3,
maxZoom: 6
}
}
});
done();
});
});

it('clamps zoom to min/max supported zoom bounds when adding layers', function() {
expect(mapEl.elementInst.options.minZoom).to.be.equal(3);
expect(mapEl.elementInst.options.maxZoom).to.be.equal(6);
});

it('respects zoom clamps when setting new minimum zoom level', function(done) {
mapEl.minZoom = 1;

flushAndRender(() => {
expect(mapEl.elementInst.options.minZoom).to.be.equal(3);
done();
});
});

it('respects zoom clamps when setting new maximum zoom level', function(done) {
mapEl.maxZoom = 7;

flushAndRender(() => {
expect(mapEl.elementInst.options.maxZoom).to.be.equal(6);
done();
});
});

it('allows changes to minZoom if within clamp bounds', function(done) {
mapEl.minZoom = 4;

flushAndRender(() => {
expect(mapEl.elementInst.options.minZoom).to.be.equal(4);
done();
});
});

it('allows changes to maxZoom if within clamp bounds', function(done) {
mapEl.maxZoom = 5;

flushAndRender(() => {
expect(mapEl.elementInst.options.maxZoom).to.be.equal(5);
done();
});
});

it('reverts to original zoom bounds when clamping is disabled', function(done) {
mapEl.unclampZoomToLayers = true;

flushAndRender(() => {
expect(mapEl.elementInst.options.minZoom).to.be.equal(0);
expect(mapEl.elementInst.options.maxZoom).to.be.equal(18);
done();
});
});
})

describe('Basic px-map with zoom clamping disabled', function () {
var mapEl;

beforeEach(function(done) {
mapEl = fixture('DisableZoomClamps');

flushAndRender(() => {
mapEl.elementInst.fire('layeradd', {
layer: {
options: {
minZoom: 3,
maxZoom: 6
}
}
});
done();
}, 3);
});

it('does not clamp zoom when new layers are added', function() {
expect(mapEl.elementInst.options.minZoom).to.be.equal(0);
expect(mapEl.elementInst.options.maxZoom).to.be.equal(18);
});

it('ignores zoom clamps when setting new minimum zoom level', function(done) {
mapEl.minZoom = 1;

flushAndRender(() => {
expect(mapEl.elementInst.options.minZoom).to.be.equal(1);
done();
});
});

it('ignores zoom clamps when setting new maximum zoom level', function(done) {
mapEl.maxZoom = 7;

flushAndRender(() => {
expect(mapEl.elementInst.options.maxZoom).to.be.equal(7);
done();
});
});
})

describe('Basic px-map with all zooming disabled', function () {
var mapEl;

Expand Down Expand Up @@ -397,7 +511,7 @@ describe('px-map with fit-to-markers enabled', function() {
expect(callWithMax).to.eql(11); // should equal map.getBoundsZoom()
});

it('corretly sets its view', function() {
it('correctly sets its view', function() {
var CENTER = [1,2];
var ZOOM = 7;

Expand Down

0 comments on commit 322e3cf

Please sign in to comment.