/* * Leaflet-IIIF 0.0.5 * IIIF Viewer for Leaflet * by Jack Reed, @mejackreed */ L.TileLayer.Iiif = L.TileLayer.extend({ options: { continuousWorld: true, tileSize: 256, updateWhenIdle: true }, initialize: function(url, options) { options = L.setOptions(this, options); this._infoDeferred = new $.Deferred(); this._infoUrl = url; this._baseUrl = this._templateUrl(); this._getInfo(); }, getTileUrl: function(coords) { var _this = this, x = coords.x, y = (coords.y), zoom = _this._map.getZoom(), scale = Math.pow(2, _this.maxZoom - zoom), tileBaseSize = _this.options.tileSize * scale, minx = (x * tileBaseSize), miny = (y * tileBaseSize), maxx = Math.min(minx + tileBaseSize, _this.x), maxy = Math.min(miny + tileBaseSize, _this.y); return L.Util.template(this._baseUrl, L.extend({ format: 'jpg', quality: _this.quality, region: [minx, miny, (maxx - minx), (maxy - miny)].join(','), rotation: 0, size: 'pct:' + (100 / scale) }, this.options)); }, onAdd: function(map) { var _this = this; // Wait for deferred to complete $.when(_this._infoDeferred).done(function() { // Find best zoom level and center map var initialZoom = _this._getInitialZoom(map.getSize()), imageSize = _this._imageSizes[initialZoom], sw = map.options.crs.pointToLatLng(L.point(0, imageSize.y), initialZoom), ne = map.options.crs.pointToLatLng(L.point(imageSize.x, 0), initialZoom), bounds = L.latLngBounds(sw, ne); map.fitBounds(bounds, true); // Set maxZoom for map map._layersMaxZoom = _this.maxZoom; // Call add TileLayer L.TileLayer.prototype.onAdd.call(_this, map); // Reset tile sizes to handle non 256x256 IIIF tiles _this.on('tileload', function(tile, url) { var height = tile.tile.naturalHeight, width = tile.tile.naturalWidth; // No need to resize if tile is 256 x 256 if (height === 256 && width === 256) return; tile.tile.style.width = width + 'px'; tile.tile.style.height = height + 'px'; }); }); }, _getInfo: function() { var _this = this; // Look for a way to do this without jQuery $.getJSON(_this._infoUrl) .done(function(data) { _this.y = data.height; _this.x = data.width; var profile, tierSizes = [], imageSizes = [], scale, width_, height_, tilesX_, tilesY_; // Set quality based off of IIIF version if (data.profile instanceof Array) { profile = data.profile[0]; }else { profile = data.profile; } switch (profile) { case 'http://library.stanford.edu/iiif/image-api/compliance.html#level1': _this.quality = 100; break; case 'http://library.stanford.edu/iiif/image-api/1.1/compliance.html': _this.quality = 'native'; break; case 'http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level1': _this.quality = 'native'; break; case 'http://iiif.io/api/image/2/level2.json': _this.quality = 'default'; break; case 'http://iiif.io/api/image/2/level1.json': _this.quality = 'default'; break; case 'http://iiif.io/api/image/2/level0.json': _this.quality = 'default'; break; } ceilLog2 = function(x) { return Math.ceil(Math.log(x) / Math.LN2); }; // Calculates maxZoom for the layer _this.maxZoom = Math.max(ceilLog2(_this.x / _this.options.tileSize), ceilLog2(_this.y / _this.options.tileSize)); for (var i = 0; i <= _this.maxZoom; i++) { scale = Math.pow(2, _this.maxZoom - i); width_ = Math.ceil(_this.x / scale); height_ = Math.ceil(_this.y / scale); tilesX_ = Math.ceil(width_ / _this.options.tileSize); tilesY_ = Math.ceil(height_ / _this.options.tileSize); tierSizes.push([tilesX_, tilesY_]); imageSizes.push(L.point(width_,height_)); } _this._tierSizes = tierSizes; _this._imageSizes = imageSizes; // Resolved Deferred to initiate tilelayer load _this._infoDeferred.resolve(); }); }, _infoToBaseUrl: function() { return this._infoUrl.replace('info.json', ''); }, _templateUrl: function() { return this._infoToBaseUrl() + '{region}/{size}/{rotation}/{quality}.{format}'; }, _tileShouldBeLoaded: function(coords) { var _this = this, zoom = _this._map.getZoom(), sizes = _this._tierSizes[zoom], x = coords.x, y = (coords.y); if (!sizes) return false; if (x < 0 || sizes[0] <= x || y < 0 || sizes[1] <= y) { return false; }else { return true; } }, _getInitialZoom: function (mapSize) { var _this = this, tolerance = 0.8, imageSize; for (var i = _this.maxZoom; i >= 0; i--) { imageSize = this._imageSizes[i]; if (imageSize.x * tolerance < mapSize.x && imageSize.y * tolerance < mapSize.y) { return i; } } // return a default zoom return 2; } }); L.tileLayer.iiif = function(url, options) { return new L.TileLayer.Iiif(url, options); };