Giant-Ass Image Viewer 2

January 23, 2006

GSV2 I am proud to announce that the next generation of the Giant Ass Image Viewer has been released to the world! For those of you who are not familiar with the original program, the Giant-Ass Image Viewer (GSV) is a javascript driven user interface for viewing large, high-resolution images, behaving very similarly to the Google Map interface. This interface allows a browser client to pan and zoom an image that is much larger than the screen, fetching tiled sections on demand. As one edge of the image moves off the screen in one direction, new portions of the image appear on the opposing side. Using this type of interface allows the image being viewed to theoretically be infinite. As such, the taxation on the network connection is minimized tremendously by the fact that only portions of the image are needed at any given time.

This next version offers a host of new features that were absent in the first cut, in addition to a new code design. (I want to add that the original program is an excellent script and the additions in no way dimish the accomplishments of the author. So before continuing on, hats off to Michal Migurski).

After hacking on GSV for a while, I got to the point where the code was starting to get tied in knots as I tried to weave in new features. Before continuing on, I decided that it would benefit tremendously from a new design, so I didn't waste a minute before rewriting the code from the ground up as a javascript object. The program now has a very clean and readable design that is quite extensible. It makes use of the observer pattern as well, that notifies listeners of events such as move and zoom. This pattern allows other programs to track its status.

So what else is different? GSV 2 adds a significant boost in performance by using more concise logic and throttling the response to mouse move events. It also fixes rounding errors in the tile placement that was causing artifacts, takes advantage of image preloading and caching, caps the maximum zoom level, disables pan events for areas outside of boundary, recenters on a double click of the mouse, smooth scrolls when repositioning, displays and optional loading image while fetching an uncached tile, fits the viewport to the size of the window, and uses a cross-browser grabbing mouse cursor, amidst other changes. Finally, the workhorse of the program, the python tilemaker script, has also been rewritten to be more informative and flexible.

I will be contributing the code back to the original author, Michal Migurski, so the location of the project page is undecided at this point. However, I have made a demo available in the cooker, which offers a NASA view of the Mars venturer, Spirit.

Posted at 10:25 AM in Javascript | Permalink Icon Permalink

36 Comments from the Peanut Gallery

1 | Posted by Jason on January 23, 2006 at 02:16 PM EST

Wow, nice job. Works great. I find the gears a bit distracting, but I'm sure that's a config option ;)

2 | Posted by Dan Allen on January 23, 2006 at 02:25 PM EST

Hey, thanks! Yeah, the gears are more of a demonstration for what can be done. It would be interesting to see what someone comes up with to use in place of them...like a loading indicator or something. What is more interesting is monitoring when they do not show up, which means that the images are being pulled from cache correctly.

3 | Posted by Andy on February 06, 2006 at 10:53 AM EST

Hi

I'm not particularly hot when it comes to javascript - when you talk about caching in GSV2, is it just the GSV script that caches tile images, or does the browser's cache still play a role? i.e. if the user returns another day, will GSV2 get its tile images from the browser's cache (assuming the user hasn't cleared their temp files) or will it always go to the server? I have an app in mind, but am concernced about bandwidth limits that hosting co's put in place.

Other than that I'm very impressed. Do you have a sample anywhere on the web? I only found the GSV 1.0 antarctica page (from this, and looking through GSV2.js, managed to get my own page up and running).

BTW, what browsers is GSV2 compatible with?

Regards

Andy

4 | Posted by Dan Allen on February 06, 2006 at 11:08 AM EST

GSV2 implements caching simply by "preloading" the images. It is a fairly standard trick, and has been done for years. If the browser is smart, it will load the images out of the cache on subsequent visits, unless the tiles have changed. This technique will alleviate bandwidth significantly.

The sample is mentioned in the blog entry, but I will add it here for convenience:

--> GSV2 sample page

GSV2 supports Mozilla (Firefox), IE6, Opera, Konqueror and maybe Safari (hasn't been tested).

5 | Posted by Andy on February 07, 2006 at 07:57 AM EST

Hi

Thanks for your previous reply.

Just a quick suggestion: it would be nice if GSV2 could not only raise zoom and move events, but also click events. Nicer still if it could return the mouse coordinates relative to the entire image, not just the viewer. I've made some mods to do this if interested.

Andy

6 | Posted by SR on February 23, 2006 at 04:12 PM EST

Instead of tileDir argument GSV constructor should take function or even better, an object titleUrlProvider with assebleUrl() method.

  function GSV(viewer, tileDir, tileSize, maxZoomLevel, initialZoomLevel)

function TileUrlProvider() { this.assembleUrl = function(xIndex, yIndex, zoomLevel) { return "assets/gfx/blank.gif"; }; } function DemoUrlProvider() { this.assembleUrl = function(xIndex, yIndex, zoomLevel) { var tileLocationPrefix = 'http://www.mojavelinux.com/cooker/demos/gsv/spirit-summit-tiles'; return tileLocationPrefix + '/' + GSV.TILE_FILENAME_PREFIX + zoomLevel + '-' + xIndex + '-' + yIndex + '.' + GSV.TILE_FILE_EXTENSION + (GSV.REVISION_FLAG ? '?r=' + GSV.REVISION_FLAG : ''); }; } DemoUrlProvider.prototype = new TileUrlProvider;

Then you would write

  var demoUrlProvider = new DemoUrlProvider();
  new GSV(viewer, demoUrlProvider, tileSize, maxZoomLevel, initialZoomLevel);

7 | Posted by David on February 25, 2006 at 02:22 PM EST

This is fantastic and exactly what I was looking for! A couple of questions though if anyone can help...

1) Is there any way of restricting the vertical drag, so that's it's only possible to drag the image left and right, not up and down at all?

2) Very newbie question but - How do I run a python script, and is that the only way of making the tiles for gsv?

Thank you and great work!

8 | Posted by Dan Allen on February 26, 2006 at 04:21 AM EST

I have just put up a project page for GSV2, which have renamed to GSIV. You can find it by clicking on "projects" in the main menu. I have implemented some of the requested features (namely the TileUrlProvider) and a fix for the scroll offset on the page when sending events. I will soon put up some documentation for getting started on this page as well.

Enjoy!

9 | Posted by SR on March 06, 2006 at 07:56 AM EST

I think there is a bug in GSIV in the first line below

if (options.tileUrlProvider && typeof options.tileUrlProvider == 'function') {
    this.tileUrlProvider = options.tileUrlProvider;
}

You should probably check if typeof tileUrlProvider is an object. Alternatively you could check if tileUrlProvider has assembleUrl() method. But maybe the most suitable check would be something like this:

instanceOf(tileUrlProvider, GSIV.TileUrlProvider);

where instanceOf() is defined like this:

function instanceOf(object, constructor) {
    while (object != null) {
        if (object == constructor.prototype) {
            return true;
        }
        object = object.__proto__;
    }
    return false;
}

10 | Posted by SR on March 06, 2006 at 09:17 AM EST

I forgot to mention GSIV needs to be internationalized.

The following lines

alert('Cannot zoom out past the current level.');
alert('Cannot zoom in beyond the current level.');

should be replaced with something like this:

GSV.MSG_CANNOT_ZOOM_PAST = 'Cannot zoom out past the current level.';
GSV.MSG_CANNOT_ZOOM_BEYOND = 'Cannot zoom in beyond the current level.';

alert(GSV.MSG_CANNOT_ZOOM_PAST); alert(GSV.MSG_CANNOT_ZOOM_BEYOND);

One more thing I forgot to mention. ;) Great job. Thank you very much for giving us GSIV, object oriented version of really cool GSV functionality.

11 | Posted by mike on March 12, 2006 at 10:01 PM EST

Hi,

First off great upgrade from GSV1, which was great too.

I'm no Javascript guru and I'm struggling to get markers for points of interest (like google markers) to show up on GSV2 pulled from a php/mysql setup. Each marker is diff color too.

Any links would be great. Making my own custom map! =)

Thanks,

Mike

12 | Posted by Dan Allen on March 17, 2006 at 01:35 AM EST

Okay folks, I made a new release and added a Quick Reference page, which is available on the GSIV project page. Enjoy!

13 | Posted by bamf on March 22, 2006 at 01:09 AM EST

There's two features in zoomify that make it very nice: 1) The ability to use low-res images until the high-res ones load (so you don't get black tiles in between) and 2) non-power of two zoom levels.

14 | Posted by Italy on March 27, 2006 at 04:45 AM EST

Hallo from Italy, I'm very impressed about this script.great work! In the future versions is difficult to add hotspot feature to the image? I think about add some sensible points to the image using html imagemaps It is only an idea.

15 | Posted by SR on March 28, 2006 at 03:25 AM EST

(a) Advanced feature idea/request: multiple layers

It would be nice to have support for additional (transparent) layers. Imagine Google's satellite pictures in first layer, city map in second transparent layer and locations of interests (for example skiing centers) in third transparent layer.

(b) I think documentation in gsiv.js file should reflect the new constructor. Change var viewerBean = new GSIV(element, 'tiles', 256, 3, 1); to something like this: var viewerBean = new GSIV(element, options);

(c) Is this stil true for 1.0.1 in gsiv.js ;) GSIV.PROJECT_VERSION = '1.0.0';

(d) I should probably provide more detailed explanation about the following but ...maybe there should be a little more clear separation of model (zoom, position and maybe viewer location and size), view and controller.

I had some integration problems (because of API) achieving "fi to window" and implementing new controls (like Google-like slider). Maybe there should exist method like:

    setZoom(zoomLevel);
    setPosition(x, y);
in addition to
    zoom(direction);
    recenter(coord, abs);

Should controls (even built-in) be put outside of gsiv.js?

Well, for (d) even I currently don't know what would be the best solution.

16 | Posted by Jamie on April 04, 2006 at 11:00 AM EST

Hi All,

I've been playing around with GSIV 1.0.1 and am managing to understand the majority of it!

I'm having a bit of fun figuring out how to get it to work properly with a set of image tiles that only have one 'zoom' level. For some reason, the entire image is only displayed when I have the zoom level set to '5'. I think this all stems from my lack of understanding of how the code figures out the total size of the image it's dealing with. Can anyone provide an explanation of how it does this, and figures out the initial zoom? I'd be extremely grateful.

- Jamie

17 | Posted by Eric on April 13, 2006 at 01:26 PM EST

Great work!

One suggestion though, instanceOf function uses the __proto__ property. This property is a Netscape only feature.

That whole function can be replace with the instanceof operator that is available in NN6+ & IE5+ (I don't know about the other browsers)

So your test of a custom URL handler would look like:

if (typeof options.tileUrlProvider != 'undefined' && (options.tileUrlProvider instanceof GSIV.TileUrlProvider)) { this.tileUrlProvider = options.tileUrlProvider; }

18 | Posted by gregor on July 10, 2006 at 01:45 PM EST

Great work!

I have one question. Cached images can get very big. So client start to act really slow. Is there a way to set max. cache size?

19 | Posted by dd on August 02, 2006 at 06:22 AM EST

(a) Advanced feature idea/request: multiple layers

It would be nice to have support for additional (transparent) layers. Imagine Google's satellite pictures in first layer, city map in second transparent layer and locations of interests (for example skiing centers) in third transparent layer.

Is this possible with multiple viewers?

20 | Posted by Frank Best on September 12, 2006 at 11:30 AM EST

Lovely job! Appears to be the answer to what I've been looking for! However.... I'm using IE7 RC1 and the drag function doesn't work. On 1 of my other machines running IE6 it works fine. Is there a setting that I can change in IE7 or maybe some other problem? Thanks.

21 | Posted by Mateescu Marcel on October 04, 2006 at 05:03 AM EST

Frank, this works in IE7, I'll test it in IE6 soon:

Replace in the HTML file the statement <div class="surface"><!-- --></div>

with <table class="surface" style="z-index:10"><tr><td>&nbsp;</td></tr><!-- --></table>

22 | Posted by greg on October 09, 2006 at 02:33 PM EST

It works in IE6!

There is another solution: In gsiv.css file where you have:

_background: url(../gfx/blank.gif) no-repeat center center; This works with IE6 but does not with IE7. You have to replace _background with _background-image!

23 | Posted by gregor on October 18, 2006 at 04:01 AM EST

Did anyone noticed a blank tile effect?

When you drag a map, sometimes happens that tile is not loaded, you just have blank. It looks like that onload event on tile image is not fireing.

Anyone having the same problem? Suggestion?

24 | Posted by ed park on October 22, 2006 at 05:58 PM EST

Nice package overall! Not the easiest package to work with, but very nicely done.

Gregor-- at the expense of speed, you can fix the missing tile problem you're speaking of by commenting out the following:

if (!tileImg.parentNode) { tile.element = this.well.appendChild(tileImg); }

Sometimes, it appears that there's a race condition in there that prevents tiles from having parentNodes, which positions them oddly and makes it appear that they are missing. This "solution" will end up using more browser memory and perform slightly worse, but is otherwise harmless and seems to work well.

25 | Posted by Tom on November 03, 2006 at 06:00 AM EST

Really nice script! One question: how to implement a MovedListener/ZoomedListener? Any example? Thanks

26 | Posted by Donny on November 25, 2006 at 10:35 PM EST

I want to add a zoom box function. Basically create a box on the image and the image should zoom into that area. I'm thinking that its going to have to check to see if there's other zoom levels and figure out which one to use depending on the size of the box. Than if there isn't a level it just magnifies the tile. Does anyone have any ideas on how I would do that? Any thooughts would be very helpful.

Donny

27 | Posted by vishal on January 23, 2007 at 06:28 AM EST

Hey can anyone give me the working example with code...i tried putting the same code from view source file of "http://mike.teczno.com/giant/pan/" page but it doesnt work for me....as i believe behaviour.js file is not there to download.....

can anyone help me please...

tx in advance...

28 | Posted by Shaun on April 04, 2007 at 04:29 AM EST

Hi,

Does this code work in IE7 at all?

Shaun

29 | Posted by Christoph Taubmann on April 27, 2007 at 05:45 PM EST

hello

trying to adapt this code to accept Tiles produced by Zoomify i have done some calculations of the overlapping Tiles per Zoom-level. Done this i realized that commenting img.style.width = this.tileSize + 'px'; img.style.height = this.tileSize + 'px'; in "createPrototype" will do the job (no stretched Pics anymore). But the dragging-handler and the positioning of the whole image is a problem: so i have to know the correct Dimensions of the (not quadratic) pics again for using this tiling-method. For further functionalities like building Panoramas and positioning of markers on the pic i think the calculation of the real sizes of the tiles (per zoom level) is neccessary. so here are my questions :) assuming i have two arrays (per zoom-levels the exact width+heights == rows+cols ) where should i try to rebuild the grid?

there is a comment in positionTiles i dont understand: "// tile may have moved out of view from the left // if so, consider the tile coming into view from the right" ??

i am interested to use this great tool as a map-engine. so something like markers (and recalculation of their position/scale) and the calculation of the "real" position according to the big-pic (x/y > utm > lat/lon) is neccessary. also i am interested to buils something like endless panoramic viewing (360°).

thanks chris

30 | Posted by Christoph Taubmann on April 27, 2007 at 06:04 PM EST

the reason why i prefer the Zoomify tiling Method is: cutting down the image is done without adding background to the image. so the filesize is smaller and you can use it for panoramas.

if you dont want to use zoomify EZ for the Tiling (they have strange Licences) you can do the job with python as well (look at sourceforge).

I am using NetPbm for the tiling....

chris

31 | Posted by mike on May 29, 2007 at 11:02 AM EST

hey, great work...

I've made some changes & problems ..!! (this is life)

1- Image Size :

full size image at any level don't have to be square, and level 0 doesn't have be 1 tile , i also i removed level 0 ( i didn't need it )..

i first set the org. image size at level 1 : ex. : GSV.MinW = 2500; GSV.MinH = 450; GSV.fullSizeW = 0; GSV.fullSizeH = 0;

then at functions " init" ,"zoom" i call this func to calc. the full size calcFullSize : function() { this.fullSizeW = GSV.MinW * Math.pow(2, this.zoomLevel -1 ); this.fullSizeH = GSV.MinH * Math.pow(2, this.zoomLevel -1 ); }

at" init" these lines are changed : this.x = Math.floor((this.fullSizeW - this.width) * -GSV.INITIAL_PAN.x); this.y = Math.floor((this.fullSizeH - this.height) * -GSV.INITIAL_PAN.y);

- when cutting the org image i just cut tiles at tile size or less (tiles at edges..) , instead of padding tiles with black... , and then i remove these lines from "createPrototype" to display images that has (width or height larger than tilesize ) correctly.. img.style.width = this.tileSize + 'px'; img.style.height = this.tileSize + 'px';

also changed this function : pointExceedsBoundaries : function(coords){ return (coords.x (this.fullSizeW + this.x) || coords.y > (this.fullSizeH + this.y)); }

i commented this line in "assignTileImage" (why? i don't know..) , without doing that nothing shows...!!!! if (high || left || low || right) { useBlankImage = true;}

2- move snap :

- i tried to add some snapping on moving the map to prevent getting out of org. image

so added this function canMove : function(motion) { var newMotion = { 'x' : 0, 'y' : 0 }; if( motion.x >0 ) { if( 0 - this.x >= motion.x ) newMotion.x = motion.x; else newMotion.x = 0 - this.x; } if(motion.x { if( ( -this.x + this.width) - this.fullSizeW else newMotion.x = (-this.x + this.width) - this.fullSizeW; } if( motion.y >0 ) { if( 0 - this.y >= motion.y ) newMotion.y = motion.y; else newMotion.y = 0 - this.y; } if(motion.y { if( ( -this.y + this.height) - this.fullSizeH else newMotion.y = (-this.y + this.height) - this.fullSizeH; } return newMotion; }

and call it at "positionTiles" : if (!motion) motion = { 'x' : 0, 'y' : 0 }; else motion = this.canMove(motion);

Problems:

- move snap works 50% good, sometimes when u drag the mouse up and down , or right and left , snapping won't work well.. and black areas appear , ANY ideas ?? - i don't see keyboard events works on 2nd version ? - what about a mouse scroll zooming

,thanks

,Mike

32 | Posted by neubrain on July 15, 2007 at 03:31 AM EST

AJAX implementation of Zoomify viewer!

The Brain Maps AJAX-Zoomify viewer may be used with your own Zoomify images. It is much faster than Flash-based versions. Try it yourself. Future versions will enable you to add overlays to brain maps (including markers and polylines) and display shadowed "info windows". The Brain Maps API is a free service, available for any web site that is free to consumers.

link: http://brainmaps.org/index.php?p=brain-maps-api

33 | Posted by Dan Allen on March 01, 2009 at 10:57 PM EST

For those following this thread, you may be interested to know that I have renamed this project to PanoJS and set it up as a project on Google Code. Feel free to join the project and contribute your ideas.

http://code.google.com/p/panojs/

34 | Posted by Eliyahu on February 03, 2010 at 10:45 AM EST

Hi all. Disclaimer: I am not a coding guru, just a desperate end-user looking for a way to map hotspots to a full-sized image that will work together with a pan and zoom script. I assume this requires some kind of proportional mapping. Is such a thing possible? Thanks.

35 | Posted by Renmiri on June 04, 2011 at 09:21 AM EST

I love the GSV script, took me out of a bind allowing me to assemble a map dynamically, with tiles from the web at large. Needed it for a texture project i had. Still having some troubles with the zoom (and want the pan disabled since it is for a perfectly lined up texture map) so i just gutted the script till it left the map alone.

You can check the results at the link!

http://www.virtualsoil.com/minecraft/mine_terrain.html

36 | Posted by Dan Allen on June 06, 2011 at 11:24 AM EST

For those following this thread, I am once again happy to announce that PanoJS (formally GSIV) now has a new home with its new maintainer.

The project is being carried on my Dmitry Fedorov:

PanoJS project page

The source is now available in a github repository:

PanoJS Github repository

Fork away!