2

I am trying to display a high resolution image in browser using openlayers 5. I found an example on how to use zoomify to create image tiles and render it using openlayers map. But I am unable to use it for my own image. I am completely new to this. The question I ask may be very trivial. Please bear my ignorance.

Example code - This is the example from openlayers website. I am trying to do the same with this image. high resolution image I tried replacing the zoomifyUrl and iipUrl with my image url but it didn't work.

import Map from 'ol/Map.js';
import View from 'ol/View.js';
import TileLayer from 'ol/layer/Tile.js';
import Zoomify from 'ol/source/Zoomify.js';

var imgWidth = 799;
var imgHeight = 586;

var zoomifyUrl = 'https://live.staticflickr.com/8173/7993440342_5d9c68faec_c.jpg';
var iipUrl = 'https://live.staticflickr.com/8173/7993440342_5d9c68faec_c.jpg' + '&JTL={z},{tileIndex}';

var layer = new TileLayer({
  source: new Zoomify({
    url: zoomifyUrl,
    size: [imgWidth, imgHeight],
    crossOrigin: 'anonymous'
  })
});

var extent = [0, -imgHeight, imgWidth, 0];

var map = new Map({
  layers: [layer],
  target: 'map',
  view: new View({
    // adjust zoom levels to those provided by the source
    resolutions: layer.getSource().getTileGrid().getResolutions(),
    // constrain the center: center cannot be set outside this extent
    extent: extent
  })
});
map.getView().fit(extent);

var control = document.getElementById('zoomifyProtocol');
control.addEventListener('change', function(event) {
  var value = event.currentTarget.value;
  if (value === 'iip') {
    layer.setSource(new Zoomify({
      url: iipUrl,
      size: [imgWidth, imgHeight],
      crossOrigin: 'anonymous'
    }));
  } else if (value === 'zoomify') {
    layer.setSource(new Zoomify({
      url: zoomifyUrl,
      size: [imgWidth, imgHeight],
      crossOrigin: 'anonymous'
    }));
  }

});

I want to achieve something like the demo in openseadragon website. After making the above code change, I get a a grid with a portion of image repeated. final image

troglodyte07
  • 3,598
  • 10
  • 42
  • 66

2 Answers2

3

To use Zoomify you need a server which supports the image served as tiles. The url you are using is a static image on flickr which OpenLayers would need to process as ImageStatic. Here is the code using the highest resolution image from flickr

  var extent = [0, 0, 10000, 7328];
  var resolutions = [64, 32, 16, 8, 4, 2, 1];

  var layer = new ol.layer.Image({
    source: new ol.source.ImageStatic({
      url: 'https://live.staticflickr.com/8173/7993440342_6a1f281898_o_d.jpg',
      imageExtent: extent
    })
  });

  var map = new ol.Map({
    layers: [layer],
    target: 'map',
    view: new ol.View({
      // adjust zoom levels to those provided by the source
      resolutions: resolutions,
      // constrain the center: center cannot be set outside this extent
      extent: extent
    })
  });
  map.getView().fit(extent);
html, body, .map {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<div id="map" class="map"></div>

Alternatively you could link to the url being used by OpenSeadragon. Sttrangely the zoom levels run from 8 to 14, with one tile at level 8 and a 55 x 41 grid at level 14 where the tiles in the rightmost column are 206px wide and in the bottom row 41px high. Zoomify can be used but a custom tile url function is needed to add 8 to the zoom level expected by OpenLayers.

  var imgWidth = 54*256 + 206;
  var imgHeight = 40*256 + 41;

  var zoomifyUrl = 'https://openseadragon.github.io/example-images/duomo/duomo_files/{z}/{x}_{y}.jpg';

  var layer = new ol.layer.Tile({
    source: new ol.source.Zoomify({
      url: zoomifyUrl,
      size: [imgWidth, imgHeight],
      crossOrigin: 'anonymous'
    })
  });

  layer.getSource().setTileUrlFunction(function(tileCoord) {
    return zoomifyUrl.replace(
      '{z}', tileCoord[0]+8
    ).replace(
      '{x}', tileCoord[1]
    ).replace(
      '{y}', -(tileCoord[2]+1)
    );
  });

  var extent = [0, -imgHeight, imgWidth, 0];

  var map = new ol.Map({
    layers: [layer],
    target: 'map',
    view: new ol.View({
      // adjust zoom levels to those provided by the source
      resolutions: layer.getSource().getTileGrid().getResolutions(),
      // constrain the center: center cannot be set outside this extent
      extent: extent
    })
  });
  map.getView().fit(extent);
html, body, .map {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<div id="map" class="map"></div>

Looking at the result from that attempt it is apparent some tiles are only 255px instead of the standard 256 which is causing white lines to appear on the output. I added a custom tile load function to stretch 255px widths or heights to 256 (but it must not stretch tiles at the right and bottom edges which can be less than 255px).

  var imgWidth = 54*256 + 206;
  var imgHeight = 40*256 + 41;

  var zoomifyUrl = 'https://openseadragon.github.io/example-images/duomo/duomo_files/{z}/{x}_{y}.jpg';

  var layer = new ol.layer.Tile({
    source: new ol.source.Zoomify({
      url: zoomifyUrl,
      size: [imgWidth, imgHeight],
      crossOrigin: 'anonymous'
    })
  });

  layer.getSource().setTileUrlFunction(function(tileCoord) {
    return zoomifyUrl.replace(
      '{z}', tileCoord[0]+8
    ).replace(
      '{x}', tileCoord[1]
    ).replace(
      '{y}', -(tileCoord[2]+1)
    );
  });

  var tileSize = ol.size.toSize(layer.getSource().getTileGrid().getTileSize(0));

  layer.getSource().setTileLoadFunction(function(imageTile, src) {
    var img = document.createElement('img');
    img.onload = function() {
      var width = img.naturalWidth >= tileSize[0]-1 ? tileSize[0] : img.naturalWidth;
      var height = img.naturalHeight >= tileSize[1]-1 ? tileSize[1] : img.naturalHeight;
      var canvas = document.createElement('canvas');
      canvas.width = width;
      canvas.height = height;
      var ctx = canvas.getContext('2d');
      ctx.drawImage(img, 0, 0, width, height);
      imageTile.getImage().src = canvas.toDataURL();
    };
    img.crossOrigin = 'anonymous';
    img.src = src;
  });

  var extent = [0, -imgHeight, imgWidth, 0];

  var map = new ol.Map({
    layers: [layer],
    target: 'map',
    view: new ol.View({
      // adjust zoom levels to those provided by the source
      resolutions: layer.getSource().getTileGrid().getResolutions(),
      // constrain the center: center cannot be set outside this extent
      extent: extent
    })
  });
  map.getView().fit(extent);
html, body, .map {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<div id="map" class="map"></div>

Using a dzi XML parser:

var map = new ol.Map({
    target: 'map',
    logo: false
});

var layer = dzi.loadUrl(
    'https://openseadragon.github.io/example-images/duomo/duomo.dzi',
    { attributions: 'Image &copy 2012, <a href="https://www.flickr.com/photos/projectese/" target="_blank">Dario Morelli</a>' }
);

layer.on('change:source', function(evt) {
    map.setView(
      new ol.View({
        resolutions: layer.getSource().getTileGrid().getResolutions(),
        extent: layer.getExtent()
      })
    );
    map.getView().fit(layer.getExtent(), { size: map.getSize() });
});

map.addLayer(layer);
html, body, .map {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<script>

dzi = function() {

function loadUrl ( url,
                   opt_options  // attributions (defaults to undefined), crossOrigin (defaults to 'anonymous')
) {

    var options = opt_options || {};
    var crossOrigin = options.crossOrigin === undefined ? 'anonymous' : options.crossOrigin;

    var layer = new ol.layer.Tile();

    var last = url.lastIndexOf('.');
    var path = url.slice(0, last);

    var xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.onload = function() {

        var parser = new DOMParser();
        var xmlDoc = parser.parseFromString(xhr.responseText,'text/xml');

        var elements = xmlDoc.getElementsByTagName('Image');
        var tileSize = Number(elements[0].getAttribute('TileSize'));
        var format = elements[0].getAttribute('Format');
        var width = Number(elements[0].getElementsByTagName('Size')[0].getAttribute('Width'));
        var height = Number(elements[0].getElementsByTagName('Size')[0].getAttribute('Height'));
        var url = path + '_files/{z}/{x}_{y}.' + format;

        var source = new ol.source.Zoomify({
            attributions: options.attributions,
            url: url,
            size: [width, height],
            tileSize: tileSize,
            crossOrigin: crossOrigin
        });

        var offset = Math.ceil(Math.log(tileSize)/Math.LN2);

        source.setTileUrlFunction(function(tileCoord) {
            return url.replace(
                '{z}', tileCoord[0] + offset
            ).replace(
                '{x}', tileCoord[1]
            ).replace(
                '{y}', -(tileCoord[2]+1)
            );
        });

        layer.setExtent([0, -height, width, 0]);
        layer.setSource(source);

    }
    xhr.send();
    return layer;

}

return {
   "loadUrl" : loadUrl
}

} ();

</script>
<div id="map" class="map"></div>
Mike
  • 16,042
  • 2
  • 14
  • 30
  • Hi. Thank you so much for the answer. That helped a lot. Can you please tell me where you found the zoomify source for that image and the related metadata file? Also, is there a way to use deep zoom image tiles with openlayers? – troglodyte07 Jun 27 '19 at 08:32
  • The tile urls can be seen in the browser console sources tab https://i.stack.imgur.com/w7eaC.png OpenLayers doesn't have any built-in parsing of dzi definition files but the tile grids they use seem to be compatible with OpenLayers. – Mike Jun 27 '19 at 08:52
  • But how did you know how many rows and column are there in the last level? And the tile size? The tile size is usually specified in the xml file which I'm not able to find in the sources tab. And the number of images in a level seem to keep increasing as I move through the image in that zoom level. Is that what u did? Or is there anyway else? – troglodyte07 Jun 27 '19 at 09:49
  • I zoomed in on the bottom right corner of the image to ensure the url with the maximum x and y values was loaded, then opened it directly to get the dimensions. I hadn't been aware that it was using a dzi file, but examining the network tab when the page is loaded reveals it is https://openseadragon.github.io/example-images/duomo/duomo.dzi – Mike Jun 27 '19 at 10:40
  • 1
    That file uses smaller dimensions than I had used because the nominal tile size is smaller (although larger tiles are clearly being served if 254 were specified in the OpenLayers source any overflow or overlap would be ignored). If desired the dzi XML could be loaded by XHR and parsed to build an OpenLayers source definition. The unusual (for OpenLayers) single tile at zoom level 8 is presumably because level 0 is a single pixel, so the offset could change depending on the tile size and would need to be calculated. – Mike Jun 27 '19 at 10:40
  • @Mike how do we deepzoom further into the images. Right now it is restricting the zoom level based on image resolution – Ganesh Rathinavel Oct 22 '19 at 06:25
  • Updates: I have figured out the deepzoom using Zoomify: https://stackoverflow.com/questions/58498434/deepzoom-into-openlayers-images-using-zoomify/58500085#58500085 – Ganesh Rathinavel Oct 22 '19 at 08:33
  • @Mike How did you calculate resolutions? I nee to understand logic behind [64, 32, 16, 8, 4, 2, 1] – Akshay Sep 20 '21 at 09:17
  • ! shows the static image at its native size, 2 at 1/2 size, etc. – Mike Sep 20 '21 at 09:36
0

The above answer has two issues:

  • lack of support for deepzoom in zoomify
  • white lines in between the tiles.

I was able to figure it out. Answer: Deepzoom into OpenLayers images using zoomify

Ganesh Rathinavel
  • 1,243
  • 4
  • 17
  • 38