diff --git a/examples/iiif-example.js b/examples/iiif-example.js
index ca13cea..2a85544 100644
--- a/examples/iiif-example.js
+++ b/examples/iiif-example.js
@@ -7,9 +7,7 @@ map = L.map('map', {
});
stanfordMlk = L.tileLayer.iiif('https://stacks.stanford.edu/image/iiif/hg676jb4964%2F0380_796-44/info.json', {
- attribution: 'Martin Luther King Jr. & Joan Baez march to integrate schools, Grenada, MS, 1966',
- maxZoom: 5,
- tileSize: 512
+ attribution: 'Martin Luther King Jr. & Joan Baez march to integrate schools, Grenada, MS, 1966'
}).addTo(map);
princetonMap = L.tileLayer.iiif('http://libimages.princeton.edu/loris2/pudl0076%2Fmap_pownall%2F00000001.jp2/info.json', {
diff --git a/leaflet-iiif.js b/leaflet-iiif.js
index 9545957..470011b 100644
--- a/leaflet-iiif.js
+++ b/leaflet-iiif.js
@@ -69,6 +69,25 @@ L.TileLayer.Iiif = L.TileLayer.extend({
// Set maxZoom for map
map._layersMaxZoom = _this.maxZoom;
+ // Set minZoom and minNativeZoom based on how the imageSizes match up
+ var smallestImage = _this._imageSizes[0];
+ var mapSize = _this._map.getSize();
+ var newMinZoom = 0;
+ // Loop back through 5 times to see if a better fit can be found.
+ for (var i = 1; i <= 5; i++) {
+ if (smallestImage.x > mapSize.x || smallestImage.y > mapSize.y) {
+ smallestImage = smallestImage.divideBy(2);
+ _this._imageSizes.unshift(smallestImage);
+ newMinZoom = -i;
+ } else {
+ break;
+ }
+ }
+ _this.options.minZoom = newMinZoom;
+ _this.options.minNativeZoom = newMinZoom;
+ _this._prev_map_layersMinZoom = _this._map._layersMinZoom;
+ _this._map._layersMinZoom = newMinZoom;
+
// Call add TileLayer
L.TileLayer.prototype.onAdd.call(_this, map);
@@ -98,6 +117,8 @@ L.TileLayer.Iiif = L.TileLayer.extend({
onRemove: function(map) {
var _this = this;
+ map._layersMinZoom = _this._prev_map_layersMinZoom;
+
// Remove maxBounds set for this image
if(_this.options.setMaxBounds) {
map.setMaxBounds(null);
@@ -112,7 +133,8 @@ L.TileLayer.Iiif = L.TileLayer.extend({
// Find best zoom level and center map
var initialZoom = _this._getInitialZoom(_this._map.getSize());
- var imageSize = _this._imageSizes[initialZoom];
+ var offset = _this._imageSizes.length - 1 - _this.options.maxNativeZoom;
+ var imageSize = _this._imageSizes[initialZoom + offset];
var sw = _this._map.options.crs.pointToLatLng(L.point(0, imageSize.y), initialZoom);
var ne = _this._map.options.crs.pointToLatLng(L.point(imageSize.x, 0), initialZoom);
var bounds = L.latLngBounds(sw, ne);
@@ -177,6 +199,7 @@ L.TileLayer.Iiif = L.TileLayer.extend({
// Calculates maximum native zoom for the layer
_this.maxNativeZoom = Math.max(ceilLog2(_this.x / _this.options.tileSize),
ceilLog2(_this.y / _this.options.tileSize));
+ _this.options.maxNativeZoom = _this.maxNativeZoom;
// Enable zooming further than native if maxZoom option supplied
if (_this._customMaxZoom && _this.options.maxZoom > _this.maxNativeZoom) {
@@ -236,11 +259,15 @@ L.TileLayer.Iiif = L.TileLayer.extend({
return this._infoToBaseUrl() + '{region}/{size}/{rotation}/{quality}.{format}';
},
_isValidTile: function(coords) {
- var _this = this,
- zoom = _this._getZoomForUrl(),
- sizes = _this._tierSizes[zoom],
- x = coords.x,
- y = (coords.y);
+ var tileBounds = this._tileCoordsToBounds(coords);
+ var _this = this;
+ var zoom = _this._getZoomForUrl();
+ var sizes = _this._tierSizes[zoom];
+ var x = coords.x;
+ var y = coords.y;
+ if (zoom < 0 && x >= 0 && y >= 0) {
+ return true;
+ }
if (!sizes) return false;
if (x < 0 || sizes[0] <= x || y < 0 || sizes[1] <= y) {
@@ -250,14 +277,15 @@ L.TileLayer.Iiif = L.TileLayer.extend({
}
},
_getInitialZoom: function (mapSize) {
- var _this = this,
- tolerance = 0.8,
- imageSize;
-
- for (var i = _this.maxNativeZoom; i >= 0; i--) {
- imageSize = this._imageSizes[i];
+ var _this = this;
+ var tolerance = 0.8;
+ var imageSize;
+ // Calculate an offset between the zoom levels and the array accessors
+ var offset = _this._imageSizes.length - 1 - _this.options.maxNativeZoom;
+ for (var i = _this._imageSizes.length - 1; i >= 0; i--) {
+ imageSize = _this._imageSizes[i];
if (imageSize.x * tolerance < mapSize.x && imageSize.y * tolerance < mapSize.y) {
- return i;
+ return i - offset;
}
}
// return a default zoom
diff --git a/spec/LTileLayerIiifSpec.js b/spec/LTileLayerIiifSpec.js
index 139d26f..5fc4cea 100644
--- a/spec/LTileLayerIiifSpec.js
+++ b/spec/LTileLayerIiifSpec.js
@@ -28,6 +28,36 @@ describe('L.TileLayer.Iiif', function() {
it('initializes the map', function(){
expect(typeof (map)).toEqual('object');
});
+
+ describe('onAdd', function() {
+ beforeEach(function() {
+ iiifLayer = iiifLayerFactory();
+ });
+
+ afterEach(function() {
+ iiifLayer.off('load');
+ });
+
+ it('with a fitable tileSize', function(done) {
+ map.addLayer(iiifLayer);
+ iiifLayer.on('load', function() {
+ expect(iiifLayer.options.minZoom).toBe(0);
+ expect(iiifLayer.options.minNativeZoom).toBe(0);
+ done();
+ });
+ });
+
+ it('with a large tileSize tries to best fit size by setting minNativeZoom and minZoom', function(done) {
+ var largeTileSize = L.tileLayer.iiif('http://localhost:9876/base/fixtures/cantaloupe/info.json');
+ map.addLayer(largeTileSize);
+ largeTileSize.on('load', function() {
+ expect(largeTileSize.options.minZoom).toBe(-2);
+ expect(largeTileSize.options.minNativeZoom).toBe(-2);
+ expect(largeTileSize._prev_map_layersMinZoom).toBe(0)
+ done();
+ });
+ });
+ });
describe('generated tile urls', function() {
var iiifLayer;
@@ -81,6 +111,17 @@ describe('L.TileLayer.Iiif', function() {
});
});
+ it('with a large tile size', function(done) {
+ var largeTileSize = L.tileLayer.iiif('http://localhost:9876/base/fixtures/cantaloupe/info.json');
+ map.addLayer(largeTileSize);
+ largeTileSize.on('load', function() {
+ expect(largeTileSize.options.fitBounds).toBe(true);
+ expect(map.getBounds().getSouthWest().toString()).toBe('LatLng(-1956, -592)');
+ expect(map.getBounds().getNorthEast().toString()).toBe('LatLng(444, 2608)');
+ done();
+ });
+ });
+
it('can be configured not to be on', function(done) {
var iiifLayerNoFitBounds = iiifLayerFactory({ fitBounds: false });
map.addLayer(iiifLayerNoFitBounds);
diff --git a/spec/fixtures/cantaloupe/info.json b/spec/fixtures/cantaloupe/info.json
new file mode 100644
index 0000000..b04bf39
--- /dev/null
+++ b/spec/fixtures/cantaloupe/info.json
@@ -0,0 +1,83 @@
+{
+ "@context": "http://iiif.io/api/image/2/context.json",
+ "@id": "http://127.0.0.1:8182/iiif/2/IMG_1707.JPG",
+ "protocol": "http://iiif.io/api/image",
+ "width": 4032,
+ "height": 3024,
+ "sizes": [
+ {
+ "width": 126,
+ "height": 95
+ },
+ {
+ "width": 252,
+ "height": 189
+ },
+ {
+ "width": 504,
+ "height": 378
+ },
+ {
+ "width": 1008,
+ "height": 756
+ },
+ {
+ "width": 2016,
+ "height": 1512
+ }
+ ],
+ "tiles": [
+ {
+ "width": 2016,
+ "height": 1512,
+ "scaleFactors": [
+ 1,
+ 2,
+ 4,
+ 8,
+ 16,
+ 32
+ ]
+ }
+ ],
+ "profile": [
+ "http://iiif.io/api/image/2/level2.json",
+ {
+ "formats": [
+ "tif",
+ "jpg",
+ "gif",
+ "png"
+ ],
+ "maxArea": 400000000,
+ "qualities": [
+ "bitonal",
+ "default",
+ "gray",
+ "color"
+ ],
+ "supports": [
+ "sizeByW",
+ "regionByPx",
+ "sizeByWhListed",
+ "cors",
+ "regionSquare",
+ "sizeByDistortedWh",
+ "sizeAboveFull",
+ "canonicalLinkHeader",
+ "sizeByConfinedWh",
+ "sizeByPct",
+ "jsonldMediaType",
+ "regionByPct",
+ "sizeByH",
+ "rotationArbitrary",
+ "baseUriRedirect",
+ "rotationBy90s",
+ "profileLinkHeader",
+ "sizeByForcedWh",
+ "sizeByWh",
+ "mirroring"
+ ]
+ }
+ ]
+}