Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
blocks:app-note:deepzoom [2021-08-27 20:59] – [Deep Zoom] mattiasblocks:app-note:deepzoom [2026-02-19 07:34] (current) – Added note about the new Deep Zoom block type admin
Line 1: Line 1:
-======Deep Zoom======+=====Deep Zoom=====
  
 +:!: **IMPORTANT**: This application note is obsolete. It has been superseded by the Deep Zoom block type introduced in Blocks 7.5, which is now the recommended method for accomplishing this functionality. That block also lets you combine deep zoom with other blocks appearing at selectable zoom depths as well as navigation to/from the Deep Zoom block.
 +----
  
-Blocks has at time of writing this note not any native feature for curating and publich deep zoom imagery This application note shows how a web block can be used to present deep zoom images using few lines of custom html and content curated with a 3rd party application.+The term "deep zoom" refers to the ability of displaying extremely high resolution images in a way that allows you to zoom into the image using pinch and pan gestures on a touch screen. This can be used to show, for example, a large map where you can zoom in to read fine print or see other details. It's based on [[https://en.wikipedia.org/wiki/Pyramid_(image_processing)|pyramid representation]] of the original image, dividing it up into multiple layers of increasing resolution. This arrangement makes it possible to handle images that are too large to be managed as is.
  
 +This application note shows how a Web Block can be used to present such deep zoom images. It's based on a small amount of custom html and an image that has been preprocessed using a command line program to create a pyramid representation. This is all embedded into the block itself, as a self contained unit that can be imported to any Blocks server
  
-====Try deep zoom==== 
  
-Download {{ :blocks:app-note:deep-zoom:deepzoommap.zip |this block,}} import it to your Blocks system using the import feature in the editor. This is a minimalistic self-contained custom web block that contains the components required to run the example.+====Using the deep zoom block====
  
-{{:blocks:app-note:deep-zoom:deepzoommapoutput.jpg?800|}}+To try out a deep zoom image on your own Blocks server, do as follows:
  
-If you rather try it out on a visitor spot, you may want to {{ :blocks:app-note:deep-zoom:deepzoommapvisitor.zip |use this block}} that is made in portrait and uses more appropriate resolution to make the panning and zooming smooth on mobile devices+  * Download {{ :blocks:app-note:deep-zoom:deepzoommap.zip |this block}} (but do not unpack the ZIP file). 
 +  * Import it into your Blocks server using the Import Block. command on the Block meny. 
 +  * Drag the resulting block onto a Display Spot, preferably one connected to a touch screen (for testing, you can also use a mouse with a scroll wheel). 
 +  * Pinch (or use the scroll wheel) to zoom into the image. 
 + 
 +{{:blocks:app-note:deep-zoom:deepzoommapoutput.jpg?800|}}
  
 +Here's {{ :blocks:app-note:deep-zoom:deepzoommapvisitor.zip |another similar block}}, optimized for use on a mobile device in vertical orientation. Import it as described above, then drag it onto a Visitor Spot, accessed using a mobile phone. 
  
-====How does it work==== 
  
 +====Inside the deep zoom====
  
-This block has a custom component the is stored inside the example blocks folderThis makes it self-contained and can be distributed via the editor +The two blocks mentioned above bundles a custom web page with the pyramidal representation of the imageNormally, you wouldn't open a ZIP file intended to be imported into BlocksBut if you want to learn more about how this block is structured, go ahead and unzip the //deepzoommap.zip// file downloaded aboveThis is what you'll find inside:
-Open the zip file in finder/explorer and look at the internals+
  
-You should see something like this:  
 {{:blocks:app-note:deep-zoom:blockstructure.jpg?600|}} {{:blocks:app-note:deep-zoom:blockstructure.jpg?600|}}
  
-Where the files //Spec.pixiMeta,json// and the directory// media// is standard blocks components. Our //custom// directory is the interesting bit here+The //Spec.pixi// and //Meta,json// files as well as the //media// directory are standard Blocks stuff, while the //custom// directory contains the additional pieces used to render the deep zoom image using a Web BlockThis is what's inside: 
  
 {{:blocks:app-note:deep-zoom:customdir.jpg?600|}} {{:blocks:app-note:deep-zoom:customdir.jpg?600|}}
  
- //Index.html// this is what we reference to in the web block.+The //index.html// file is the HTML code used by the Web Block. The web block references this HTML code using a //relative URL//, beginning with a '~' character. This lets HTML content (as well as custom CSS files) to be embedded inside the block itself, allowing the block and its custom parts to be distributed and imported as a single unit.
  
-//Openseadragon.min.js// is the js library used to create the deep zoom functionality.+Looking more closely at the files in the //custom// directory, you'll see an //openseadragon.min.js// file, which is the javascript that presents and manages the deep zoom image. The //image// folder contains images used to show navigation buttons, if enabled – not the deep zoom images. Those are inside the //out// folder
  
- //Image// folder is images used by the library if navigation buttons is enable in the html file.+{{:blocks:app-note:deep-zoom:outdir.jpg?600|}}
  
-//Out// is where we find our deep zoom media. The media has been processed into a //dzi// package. +Inside the //out// folder, you'll find a ".dzi" file containing various internal information describing the deep zoom image, as well as directory containing the pyramid representation of the deep zoom image. Note that both the ".dzi" file and the directory are prefixed with the name of the deep zoom imagehere "hr"The method used to create the content of the //out// directory is covered below.
- This is a pyramid format often used by ie. digital maps where it is beneficial to zoom large distancesIt is simply a method to avoid loading a single very large file, instead they are shopped up in a grid and there are several grids stacked on top of each other to create the zoom levels. This creates kind of a pyramidwhere the top is the first image you see in the viewerThis way the viewer can automatically show just a few of the images at any given time in order to conserve loading time, memory use and to make sure the viewer is capable to handle any user interaction with heigh framerate+
  
-A dzi package has two components, a description file, in our example it is hr.dzi and a folder prefixed with the same name where all the processed images is stored. +In case you're interested in the inner workings of the web page used to show the deep zoom imagetake look in the //index.html// file:
-{{:blocks:app-note:deep-zoom:outdir.jpg?600|}}+
  
-How to create such package is covered later in this article. 
- 
-The code inside the //index.html// file is simple. 
 <code> <code>
 <!DOCTYPE html> <!DOCTYPE html>
 <html lang="en"> <html lang="en">
-<head> +  <head> 
- <meta charset="UTF-8"> +    <meta charset="UTF-8"> 
- <title>Title</title> +    <title>Title</title> 
- <style> +    <style> 
- html, body { +      html, body { 
- margin:0; +        margin:0; 
- padding: 0; +        padding: 0; 
- +      
- #openseadragon1 { +      #openseadragon1 { 
- width: 1920px; height: 1080px; +        width: 1920px; height: 1080px; 
- +      
- </style> +    </style> 
-</head> +  </head> 
-<body> +  <body> 
-<div id="openseadragon1"></div> +    <div id="openseadragon1"></div> 
-<script src="openseadragon.min.js"></script> +    <script src="openseadragon.min.js"></script> 
-<script type="text/javascript"> +    <script type="text/javascript"> 
- +      var viewer = OpenSeadragon({ 
-    var viewer = OpenSeadragon({+      
         id: "openseadragon1",         id: "openseadragon1",
         prefixUrl: "images/",         prefixUrl: "images/",
         tileSources: "out/hr.dzi",         tileSources: "out/hr.dzi",
- showNavigationControl: false +        showNavigationControl: false 
- }); +         
- +      }); 
-</script> +    </script> 
-</body>+  </body>
 </html> </html>
 </code> </code>
    
-At the top we got the usual HTML document header followed by a head element with some inline styling+Most of it is just standard HTML stuff, along with with some CSS inside the style tags. The size of the deep zoom image viewer is specified inside the //#openseadragon1// CSS selector shown above 
  
-The size of the seadragon viewer is specified here.  +The small amount of javascript code inside the //script// tag initializes the viewer, passing it various options, such as //tileSources// indicating where the image pyramid data is to be found and  //showNavigationControl//, here set to //false// to not show navigation control buttons
  
-<code> +If you have access to the file system of your Blocks server (e.g., you're running Blocks locally on your laptop), you can locate these files under public/block/<group-name>/deepzoommap/custom/index.html, in case you want to try any [[https://openseadragon.github.io/docs/|other options]]. Just open the //index.html// file with a plain text editor and hack away. Reload the page showing the deep zoom block in your browser to see the changes. [[https://openseadragon.github.io/|OpenSeadragon]] is the open source project that powers this deep zoom function.
- #openseadragon1 { +
- width1920px; height1080px; +
-+
-</code>+
  
-The the html element where the viewer is displayed is here. 
  
-Finally we got the script part where the first line references the external library and the rest initialises the viewer with its settings.+====Preparing high resolution images for deep zoom====
  
-<code> +A Java-based program is used to preprocess for deep zoom useThis program requires a recent version of Java installed on your computer. If you're not sure if this is available, do as follows:
-<code><div id="openseadragon1"></div></code> +
-<script type="text/javascript"> +
- +
-    var viewer = OpenSeadragon({ +
-        id: "openseadragon1", +
-        prefixUrl: "images/", +
-        tileSources: "out/hr.dzi", +
- showNavigationControl: false +
- }); +
- +
-</script> +
-</code> +
- +
-The full list of options to experiment with is available [[https://openseadragon.github.io/docs/OpenSeadragon.html#.Options|here]]. +
- +
- +
- +
- +
- +
- +
- +
- +
- +
- +
- +
-====Prepare the images for deep zoom====+
  
-A computer with java installed and the java default path enabled in the environment variables.  +  * Open a terminal window
-This can be tested by open a terminal and type java and hit enter. If java is present and the path is in environment variables, java should start and a list of available options is presented in the terminal+  * Type //java -version// and press enter.
-We have used the java application Pyramido-cli to prepare the deep zoom content+
  
-The tool can be downloaded {{:blocks:app-note:deep-zoom:pyramiddzitool.zip|here}}. +If this command isn't recognized, you need to [[https://adoptopenjdk.net|download and install]] Java before proceedingIf a version number is shown, make sure it's version 8 or later. You should also make sure the JAVA_HOME enviroment variable is set on the computer (at least on windows). If it is not you cannot just type java in the terminal, you will have to use the full path to java everytime you use it.  Use google to see how to do that 
-Credits to the employees at National Institute of Standards and Technology for writing and sharing the program.+
  
-Unpack the zipfile om your hard drive.   +Then go ahead and {{:blocks:app-note:deep-zoom:pyramiddzitool.zip|download this program}}This is an open source (public domain) command line program, kindly provided by the National Institute of Standards and TechnologyMore details can be found [[https://github.com/usnistgov/pyramidio|here]].
- Inside you will find a high-resolution example file The tool support both jpg and pngIt is important that the colour format is RGB 8bitIf it is not, the image must be converted by open it and change the settings in a photo editor+
  
-Browse to the folder where the tool is located.+Extract the //pyramiddzitool.zip// file. Inside, you'll find a high resolution example file. The tool supports JPEG and PNG image files containing 8-bit-per-component RGB data. If you run into trouble processing an image of your own, you may need to open, convery and re-save the image using an image editing program such as Photoshop or Gimp
  
-Inside the folder you can find a //process.bat// file to use with windows and //process.sh// file to use with unix based computers such as mac and linux.  The scripts are just one-liner with the command line instruction below to process the example file hr.jpg and put the output in the out folder. Double click the script to start the process.+Two script files are included, showing how to use the command line program; //process.bat// for Windows and //process.sh// for MacOS and Linux.  These scripts are just one-liners containing the command for processing the example //hr.jpg// file, generating the pyramid image data in the //out// folder. Run the script from a command line window, after navigating to its location, to start the process. Alternatively, copy the content of the script file and run it as is in the terminal. This is what's inside the scripts:
  
-As an alternative a terminal can be opened in the tool’s directory. Run the following command in the terminal window.  
 <code> java -Xms1G -jar pyramidio-cli.jar --tileSize 510 -i hr.jpg -o out</code> <code> java -Xms1G -jar pyramidio-cli.jar --tileSize 510 -i hr.jpg -o out</code>
-To select another source file just copy the file to the same directory as the tool and change hr.jpg in the command to the name of your new file. The result will end up in the //out// directory prefixed with the source file name.  
  
-Copy the resulting file and folder from inside the //out// directory to your deep zoom block'//custom/out// directory.+Be patient, it takes a minute or so. You will se this message in the terminal while the process is running: 
 +<code> INFO: Making directory archiver for out </code> 
 +To process another image, just copy the file to the same directory as the tool and change //hr.jpg// in the command to the name of your new file. The result will end up in the //out// directory, prefixed with the source image file name. Avoid using spaces or non-ASCII characters in the file name
  
-The file name of the new dzi source must be specified in the custom blocks //index.html// file. Find the line specifying the //tileSource// and change to your new .dzi file name+Copy the resulting file and folder from inside the //out// directory into your deep zoom block'//custom/out// directory.
  
 +The file name of your new ".dzi" file must be specified in the custom blocks' //index.html// file. Find the line specifying the //tileSource// and change it accordingly. 
  
- ====A block example using multiple dzi source files==== 
  
-This example uses a modified version of the custom index.html file that accepts query parameters. This make is possible to control the tileSource form by sending query parameters. A query parameter is the options that can be sent to a website using a ? to tell the webpage that there are query parameters to follow. One can use multiple query parameters separated by &+====Using multiple deep zoom images====
  
-The example block can be downloaded [[https://int.pixilab.se/outgoing/permanent/wiki/DeepZoom.zip|from here]]. +The next [[https://int.pixilab.se/outgoing/permanent/wiki/DeepZoom.zip|example block]] uses a slightly modified version of the //index.html// file accepting [[https://en.wikipedia.org/wiki/Query_string|query parameters]] controlling the tileSource option in the script. The block structure used here is as follows:
  
-The block is a composition at top-level. Inside a couple of small compositions that builds our menu. The book that acts as an overlay that loads the webpage controlled by the buttons.  +  * A Composition at the top-level.  
-The buttons trigs two actions each, one that set parameter to a string, the value is the path to the tileSource. Ie. //out/hr.dzi// and the other changes the overlay book to page2 to expose the web block with the zoom content. +  * A number of smaller Compositions for building a simple menu.  
-When the user closes with the close button the menu is exposed, and a new selection can be made by the user.  +  * A Book for showing the image selected on the menu. 
 +  
 +The menu buttons each have two actionsone setting Spot Parameter subsequently used to control the //tileSource//the other locating //page2// in the Book, exposing a Web Block showing the deep zoom content. 
 +The close button returns to the menu.  
  
 {{:blocks:app-note:deep-zoom:multisourceblock.jpg?800|}} {{:blocks:app-note:deep-zoom:multisourceblock.jpg?800|}}
  
-The web block uses a useful feature that enables us to pass the value of Blocks parameters on to the custom web page as query parameterIn this case we pass the tileSource parameter value we set with the buttons. Multiple parameters value can be passed with this method separated with a comma.+The Web Block uses feature allowing you to pass Spot Parameters to custom web page as query parametersHere we pass the //tileSource// parameter set by the buttons. Multiple Spot Parameters can be passed along by listing their names, comma separated.
  
 {{:blocks:app-note:deep-zoom:webblockconfig.jpg?800|}} {{:blocks:app-note:deep-zoom:webblockconfig.jpg?800|}}
  
  
-====Query parameters code example====+===Using query parameters from custom HTML content=== 
 + 
 +Below you can see how you can use query parameters from within custom HTML code. This is similar to the example shown earlier, but adds some code to extract and use query parameters, here for setting a number of deep zoom options. Additional options can be added as query parameters, as needed.
  
-This example can also be useful to demonstrate the code required to use query parameters in pages. Compare to the simple example from the beginning of this application note. 
 <code> <code>
 <!DOCTYPE html> <!DOCTYPE html>
 <html lang="en"> <html lang="en">
-<head> +  <head> 
- <meta charset="UTF-8"> +    <meta charset="UTF-8"> 
- <title>Title</title> +    <title>Title</title> 
- <style> +    <style> 
- html, body { +      html, body { 
- margin:0; +        margin:0; 
- padding: 0; +        padding: 0; 
- +      
- #openseadragon1 { +      #openseadragon1 { 
- width: 800px; height: 800px; +        width: 800px; height: 800px; 
- +      
- </style> +    </style> 
-</head> +  </head> 
-<body> +  <body> 
-<div id="openseadragon1"></div> +    <div id="openseadragon1"></div> 
-<script src="openseadragon.min.js"></script> +    <script src="openseadragon.min.js"></script> 
-<script type="text/javascript"> +    <script type="text/javascript"> 
- +     
-/** +      /** 
- Get the set of query params specified in URL, building it if not yet done. +         Get the set of query params specified in URL, building it if not yet done. 
- */ +         Returned as a dictionary with key/value pairs. 
-function getQueryParams() { +       */ 
- if (!queryParams) { +      function getQueryParams() { 
- const query = window.location.search.substring(1); +        if (!queryParams) { 
- const vars = query.split("&"); +          const query = window.location.search.substring(1); 
- const qParams = {}; +          const vars = query.split("&"); 
- if (vars.length > 0 && vars[0].length > 0) { // Got some fish +          const qParams = {}; 
- for (var i = 0; i < vars.length; i++) { +          if (vars.length > 0 && vars[0].length > 0) {  // Got some fish 
- const pair = vars[i].split('='); +            for (var i = 0; i < vars.length; i++) { 
- var value = pair[1]; +              const pair = vars[i].split('='); 
- value = value.split('?')[0]; // Discard any garbage added by SSSP v2 +              var value = pair[1]; 
- qParams[pair[0]] = value; +              value = value.split('?')[0];  // Discard any garbage added by SSSP v2 
- +              qParams[pair[0]] = value; 
- +            
- queryParams = qParams; +          
-+          queryParams = qParams; 
- return queryParams; +        
-+        return queryParams; 
-var queryParams; // Use ONLY through getQueryParams +      
- +      var queryParams;  // Query params cached here by getQueryParams 
- +    
- +       
-    var viewer = OpenSeadragon({+      var viewer = OpenSeadragon({
         id: "openseadragon1",         id: "openseadragon1",
         prefixUrl: "images/",         prefixUrl: "images/",
         tileSources: getQueryParams()['tileSources'] || "out/hr.dzi",         tileSources: getQueryParams()['tileSources'] || "out/hr.dzi",
- minZoomLevel: getQueryParams()['minZoomLevel'], +        minZoomLevel: getQueryParams()['minZoomLevel'], 
- maxZoomLevel: getQueryParams()['maxZoomLevel'], +        maxZoomLevel: getQueryParams()['maxZoomLevel'], 
- minZoomImageRatio: getQueryParams()['minZoomImageRatio'] || 1, +        minZoomImageRatio: getQueryParams()['minZoomImageRatio'] || 1, 
- maxZoomPixelRatio: getQueryParams()['maxZoomPixelRatio'] || 1, +        maxZoomPixelRatio: getQueryParams()['maxZoomPixelRatio'] || 1, 
- visibilityRatio: getQueryParams()['visibilityRatio'] || 1, +        visibilityRatio: getQueryParams()['visibilityRatio'] || 1, 
- showNavigationControl: false +        showNavigationControl: false 
- }); +      }); 
- +     
- +    </script> 
- +  </body>
- +
-</script> +
-</body>+
 </html> </html>
 </code> </code>
    
-