Skip to content

Instantly share code, notes, and snippets.

@richstoner
Last active December 21, 2015 15:58
Show Gist options
  • Save richstoner/6329896 to your computer and use it in GitHub Desktop.
Save richstoner/6329896 to your computer and use it in GitHub Desktop.
This shows the code snippets of the difference between openseadragon annotations and leaflet. Original code: preleaf.js, aperiotilesource.js, load_openseadragon.js New leaflet code: postleaf.js, connectivity_layer.js, load_leaflet.js
// hacked together aperio support
(function( $ ){
/**
* A client implementation of the Aperio ImageServer Format
*
* @class
* @extends OpenSeadragon.TileSource
* @param {Number|Object} width - the pixel width of the image or the idiomatic
* options object which is used instead of positional arguments.
* @param {Number} height
* @param {Number} tileSize
* @param {String} tilesUrl
*/
$.AperioTileSource = function( width, height, tileSize, tilesUrl ) {
var options;
if( $.isPlainObject( width ) ){
options = width;
}else{
options = {
width: arguments[0],
height: arguments[1],
tileSize: arguments[2],
tilesUrl: arguments[3]
};
}
if( !options.width || !options.height || !options.tilesUrl ){
throw new Error('width, height, and tilesUrl parameters are required.');
}
if( !options.tileSize ){
options.tileSize = 256;
}
console.log(options);
// Initialization stuff
var imageSize = new $.Point(options.width, options.height);
var tiles = new $.Point(
Math.ceil(options.width / options.tileSize),
Math.ceil(options.height / options.tileSize)
);
// Create an arrays showing tier dimensions (in pixels and tiles)
// A tier is essentially a zoom level
this.tierSizeInTiles = [tiles];
this.tierImageSize = [imageSize];
while (imageSize.x > options.tileSize ||
imageSize.y > options.tileSize ) {
imageSize = new $.Point(
Math.floor( imageSize.x / 2 ),
Math.floor( imageSize.y / 2 )
);
tiles = new $.Point(
Math.ceil( imageSize.x / options.tileSize ),
Math.ceil( imageSize.y / options.tileSize )
);
this.tierSizeInTiles.push( tiles );
this.tierImageSize.push( imageSize );
}
this.tierSizeInTiles.reverse();
this.tierImageSize.reverse();
this.numberOfTiers = this.tierSizeInTiles.length;
options.maxLevel = this.numberOfTiers;
// In order to calculate the TileGroup, we need to have an index of the
// number of tiles in each tier
this.tileCountUpToTier = [0];
for (var i = 1; i < this.numberOfTiers; i++) {
this.tileCountUpToTier.push(
this.tierSizeInTiles[i-1].x * this.tierSizeInTiles[i-1].y +
this.tileCountUpToTier[i-1]
);
}
$.TileSource.apply( this, [ options ] );
};
$.extend( $.AperioTileSource.prototype, $.TileSource.prototype, {
/**
* Determine if the data and/or url imply the image service is supported by
* this tile source.
* @function
* @name OpenSeadragon.AperioTileSource.prototype.supports
* @param {Object|Array} data
* @param {String} optional - url
*/
supports: function( data, url ){
return (
data.type &&
"zoomify" == data.type
) || (
data.documentElement &&
"IMAGE_PROPERTIES" == data.documentElement.tagName
);
},
/**
*
* @function
* @name OpenSeadragon.AperioTileSource.prototype.configure
* @param {Object|XMLDocument} data - the raw configuration
* @param {String} url - the url the data was retreived from if any.
* @return {Object} options - A dictionary of keyword arguments sufficient
* to configure this tile source via it's constructor.
*/
configure: function( data, url ){
if( !$.isPlainObject(data) ){
options = configureFromXml( this, data );
} else {
options = configureFromObject( this, data );
}
var tilesurl = url || data.tilesUrl;
service = tilesurl.split('/');
service.pop(); //ImageProperties.json or ImageProperties.xml, or "" if slash-terminated
service = service.join('/') + "/";
if( 'http' !== tilesurl.substring( 0, 4 ) ){
host = location.protocol + '//' + location.host;
service = host + service;
}
options.tilesUrl = service;
return options;
},
// getLevelScale: function( level ) {
// var levelScale = NaN;
// if ( level >= this.minLevel && level <= this.maxLevel ){
// levelScale =
// this.levels[ level ].width /
// this.levels[ this.maxLevel ].width;
// }
// return levelScale;
// },
/**
* Responsible for retreiving the url which will return an image for the
* region speified by the given x, y, and level components.
* @function
* @name OpenSeadragon.AperioTileSource.prototype.getTileUrl
* @param {Number} level - z index
* @param {Number} x
* @param {Number} y
* @throws {Error}
*/
getTileUrl: function( level, x, y ){
//zoomify implementation
// level = Math.max(0, level - 1);
// var tileIndex = x + y * this.tierSizeInTiles[level].x + this.tileCountUpToTier[level];
// var tileGroup = Math.floor( (tileIndex) / 256 );
// var urlstring = "TileGroup" + tileGroup + "/" + level + "-" + x + "-" + y + ".jpg";
// return this.tilesUrl + urlstring;
// aperio via leaflet implementation
// var tileGroup, x, y, z;
// x = tilePoint.x;
// y = tilePoint.y;
// z = this._getZoomForUrl();
// tileGroup = this._tileGroupNumber(x, y, z);
// var scale = Math.pow(2, this.numOfTiers() - z - 1);
// var x_pos = x * 256
// var y_pos = y * 256
// var tileurl = this.baseUrl + "?" + x_pos + "+" + y_pos + "+" + 256 + "+" + 256 + "+" + scale + "+" + "80+s";
// aperio for openseadragon
var x_pos = x * this.tileSize;
var y_pos = y * this.tileSize;
var scale = Math.pow(2, this.numberOfTiers - level);
var urlString = "?" + x_pos + "+" + y_pos + "+" + this.tileSize + "+" + this.tileSize + "+" + scale + "+" + "80+s";
// console.log(urlString);
return this.tilesUrl + urlString;
}
});
/**
* @private
* @inner
* @function
*
<IMAGE_PROPERTIES WIDTH="5569" HEIGHT="7938" NUMTILES="945" NUMIMAGES="1" VERSION="1.8" TILESIZE="256"/>
*/
function configureFromXML( tileSource, xmlDoc ){
//parse the xml
if ( !xmlDoc || !xmlDoc.documentElement ) {
throw new Error( $.getString( "Errors.Xml" ) );
}
var root = xmlDoc.documentElement,
rootName = root.tagName,
configuration = null;
if ( rootName == "IMAGE_PROPERTIES" ) {
try {
configuration = {
"width": root.getAttribute("WIDTH"),
"height": root.getAttribute("HEIGHT"),
"tileSize": root.getAttribute("TILESIZE")
};
return configureFromObject( tileSource, configuration );
} catch ( e ) {
throw (e instanceof Error) ?
e :
new Error( $.getString("Errors.Aperio") );
}
}
throw new Error( $.getString( "Errors.Aperio" ) );
}
/**
* @private
* @inner
* @function
*
{
"width" : 6000,
"height" : 4000,
"tileSize" : 256
}
*/
function configureFromObject( tileSource, configuration ){
return configuration;
}
}( OpenSeadragon ));
// code for openseadragon aperio layer
// Generated by CoffeeScript 1.3.3
(function() {
var AperioLayer,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
AperioLayer = (function(_super) {
__extends(AperioLayer, _super);
'Implement AperioLayer layer for Cloudmade\'s Leaflet library';
function AperioLayer(url, imageProperties, options) {
var i, t;
if (options == null) {
options = {};
}
this.options = options;
console.log(this.options);
this.baseUrl = url;
if (!((imageProperties.width != null) && (imageProperties.height != null))) {
throw 'width and height must be defined';
}
this.imageProperties = {
width: imageProperties.width,
height: imageProperties.height,
tilesize: imageProperties.tilesize || 256
};
t = this.numOfTiers();
this._tiers = (function() {
var _i, _results;
_results = [];
for (i = _i = 1; 1 <= t ? _i <= t : _i >= t; i = 1 <= t ? ++_i : --_i) {
_results.push(Math.ceil(this.imageProperties.width / this.imageProperties.tilesize / Math.pow(2, t - i)) * Math.ceil(this.imageProperties.height / this.imageProperties.tilesize / Math.pow(2, t - i)));
}
return _results;
}).call(this);
this.options.minZoom = 0;
this.options.maxZoom = t - 1;
}
AperioLayer.prototype._getTierForResolution = function(resolution) {
var lambda;
lambda = function(x, r) {
if (r < resolution) {
return lambda(x + 1, r * 2);
}
return x;
};
return this.numOfTiers() - lambda(0, 1) - 1;
};
AperioLayer.prototype._getSizeForTier = function(tier) {
var r;
r = Math.pow(2, this.numOfTiers() - tier - 1);
return [Math.ceil(this.imageProperties.width / r), Math.ceil(this.imageProperties.height / r)];
};
AperioLayer.prototype.onAdd = function(map) {
var layerSize, offset, r, size, tier;
AperioLayer.__super__.onAdd.call(this, map);
size = map.getSize();
r = Math.max(Math.ceil(this.imageProperties.width / size.x), Math.ceil(this.imageProperties.height / size.y));
tier = this._getTierForResolution(r);
layerSize = this._getSizeForTier(tier);
offset = [(size.x - layerSize[0]) / 2, (size.y - layerSize[1]) / 2];
window.ll = map.options.crs.pointToLatLng(new L.Point(size.x / 2 - offset[0], size.y / 2 - offset[1]), tier);
return map.setView(ll, tier);
};
AperioLayer.prototype._createTile = function() {
var tile;
tile = document.createElement('img');
tile.setAttribute('class', 'leaflet-tile');
tile.onselectstart = tile.onmousemove = L.Util.falseFn;
return tile;
};
AperioLayer.prototype._addTilesFromCenterOut = function(bounds) {
var center, fragment, i, j, point, queue, tilesToLoad, _i, _j, _k, _ref, _ref1, _ref2, _ref3;
queue = [];
center = bounds.getCenter();
for (j = _i = _ref = bounds.min.y, _ref1 = bounds.max.y; _ref <= _ref1 ? _i <= _ref1 : _i >= _ref1; j = _ref <= _ref1 ? ++_i : --_i) {
for (i = _j = _ref2 = bounds.min.x, _ref3 = bounds.max.x; _ref2 <= _ref3 ? _j <= _ref3 : _j >= _ref3; i = _ref2 <= _ref3 ? ++_j : --_j) {
point = new L.Point(i, j);
if (this._tileShouldBeLoaded(point)) {
queue.push(point);
}
}
}
tilesToLoad = queue.length;
if (tilesToLoad === 0) {
return;
}
queue.sort(function(a, b) {
return a.distanceTo(center) - b.distanceTo(center);
});
fragment = document.createDocumentFragment();
if (!this._tilesToLoad) {
this.fire('loading');
}
this._tilesToLoad += tilesToLoad;
for (i = _k = 0; 0 <= tilesToLoad ? _k < tilesToLoad : _k > tilesToLoad; i = 0 <= tilesToLoad ? ++_k : --_k) {
this._addTile(queue[i], fragment);
}
return this._container.appendChild(fragment);
};
AperioLayer.prototype._reset = function() {
return AperioLayer.__super__._reset.call(this);
};
AperioLayer.prototype._tileShouldBeLoaded = function(point) {
var limitX, limitY, tier;
if (point.x >= 0 && point.y >= 0) {
tier = this._getZoomForUrl();
limitX = Math.pow(2, this.numOfTiers() - tier - 1) * this.imageProperties.tilesize * point.x;
limitY = Math.pow(2, this.numOfTiers() - tier - 1) * this.imageProperties.tilesize * point.y;
return limitX <= this.imageProperties.width && limitY <= this.imageProperties.height;
}
return false;
};
AperioLayer.prototype._getTilePos = function(tilePoint) {
var origin;
origin = this._map.getPixelOrigin();
return tilePoint.multiplyBy(this.imageProperties.tilesize).subtract(origin);
};
AperioLayer.prototype._update = function(e) {
var bounds, nwTilePoint, seTilePoint, tileBounds, tileSize, zoom;
if (!(this._map != null)) {
return;
}
bounds = this._map.getPixelBounds();
zoom = this._map.getZoom();
tileSize = this.imageProperties.tilesize;
if (zoom > this.options.maxZoom || zoom < this.options.minZoom) {
return;
}
nwTilePoint = new L.Point(Math.floor(bounds.min.x / tileSize), Math.floor(bounds.min.y / tileSize));
seTilePoint = new L.Point(Math.floor(bounds.max.x / tileSize), Math.floor(bounds.max.y / tileSize));
tileBounds = new L.Bounds(nwTilePoint, seTilePoint);
this._addTilesFromCenterOut(tileBounds);
if (this.options.unloadInvisibleTiles || this.options.reuseTiles) {
return this._removeOtherTiles(tileBounds);
}
};
AperioLayer.prototype.numOfTiers = function() {
var a, i;
if (!(this._numOfTiers != null)) {
i = 0;
a = Math.ceil(Math.max(this.imageProperties.width, this.imageProperties.height) / this.imageProperties.tilesize);
while (a > 1) {
i += 1;
a = Math.ceil(a / 2);
}
this._numOfTiers = i + 1;
}
return this._numOfTiers;
};
AperioLayer.prototype._tileGroupNumber = function(x, y, tier) {
var height, i, idx, numOfTiers, tileSize, width, _i;
numOfTiers = this.numOfTiers();
width = this.imageProperties.width;
height = this.imageProperties.height;
tileSize = this.imageProperties.tilesize;
idx = 0;
for (i = _i = 0; 0 <= tier ? _i < tier : _i > tier; i = 0 <= tier ? ++_i : --_i) {
idx += this._tiers[i];
}
idx += y * Math.ceil(this.imageProperties.width / this.imageProperties.tilesize / Math.pow(2, numOfTiers - tier - 1));
idx += x;
return Math.floor(idx / 256);
};
AperioLayer.prototype._getZoomForUrl = function() {
var zoom;
if (this.imageProperties != null) {
zoom = this._map.getZoom();
return zoom;
}
return 0;
};
AperioLayer.prototype.getTileUrl = function(tilePoint, zoom) {
var tileGroup, x, y, z;
x = tilePoint.x;
y = tilePoint.y;
z = this._getZoomForUrl();
tileGroup = this._tileGroupNumber(x, y, z);
var scale = Math.pow(2, this.numOfTiers() - z - 1);
var x_pos = x * this.imageProperties.tilesize;
var y_pos = y * this.imageProperties.tilesize;
var tileurl = this.baseUrl + "?" + x_pos + "+" + y_pos + "+" + this.imageProperties.tilesize + "+" + this.imageProperties.tilesize + "+" + scale + "+" + "80+s";
// return this.baseUrl + ("TileGroup" + tileGroup + "/" + z + "-" + x + "-" + y + ".jpg" + rangeString);
return tileurl;
};
return AperioLayer;
})(L.TileLayer);
window.AperioLayer = AperioLayer;
}).call(this);
if(annotation_type == 'freehand'){
var points = new Array();
var annotation_points = $(annotationXML).find('point');
$(annotation_points).each(function(index, point){
var x_base = parseInt($(point).find('x').text());
var y_base = parseInt($(point).find('y').text());
// converts hamamatsu annotation to aperio coordinate system
var new_point = self.convertPointToAperio(x_base, y_base);
// if valid point, push point in leaflet coordinate system (latlng) to point array (via native point unproject)
if (new_point) {
points.push(_activeViewer.unproject(new L.Point(new_point.x, new_point.y), _activeViewer.getMaxZoom()));
}
});
// native overlays!
var polygon = L.polygon(points, {
color : "#0000FF",
weight : 2,
opacity : 0.5,
fillOpacity: 0.0
})
// .addTo(_activeViewer);
// uncomment the above line to load the annotation immediately into the map
all_annotations.push(polygon);
// after exiting this scope, push all_annotations to a master annotation object
}
if(annotation_type == 'freehand'){
console.log('processing' + annotation_type)
var points = new Array();
var annotation_points = $(annotationXML).find('point');
$(annotation_points).each(function(index, point){
console.log(point);
var x_base = parseInt($(point).find('x').text());
var y_base = parseInt($(point).find('y').text());
var new_point = self.convertPointToAperio(x_base, y_base);
if (new_point) {
console.log(new_point);
points.push(new Seadragon.Point(new_point.x, new_point.y));
}
});
var svg = new OpenSeajax.Shapes.Polygon(points);
svg.getElement().attr({"stroke": lineColor, "stroke-width": "10px"});
svg.getElement().attr({"fill": "none"});
svg.attachTo(_activeViewer);
_annotations.push({path: svg, index: id });
console.log(points);
}
//Leaflet loading aperio metadata
var metadata_url = _basePath + '?INFO3';
var self = this;
$.ajax({
url: metadata_url,
success: function (data) {
var metadata_array = data.split('|');
_imageMetaData = {
width: parseInt(metadata_array[0]),
height: parseInt(metadata_array[1]),
tilex: parseInt(metadata_array[3]),
tiley: parseInt(metadata_array[4]),
appmag: metadata_array[7],
mpp: metadata_array[8]
}
// ((1./x+1./y)/2)*self.GetSourceLens()*10**7
// var conversionfactor = ((1.0/_imageMetaData.width) + (1.0/_imageMetaData.height)) * (20.) * Math.pow(10,7)
image_size = {width:_imageMetaData.width, height:_imageMetaData.height };
var aperioImageSource = new AperioLayer(_basePath, image_size, {tilesize:_imageMetaData.tilex*2});
var miniMap = new L.Control.MiniMap(aperioImageSource,{
toggleDisplay:true
}).addTo(_activeViewer);
_activeViewer.addLayer(aperioImageSource);
_annotationLayerGroup = L.layerGroup();
_activeViewer.addLayer(_annotationLayerGroup);
// example info3 response:
// ["55296", "38912", "1", "256", "256", "", "NanoZoomer Digital Pathology Image", "AppMag = 20", "MPP = 0.4526↵"]
// now load annotations (from Hamamatsu ndpa source) after short delay
self.loadNDPA();
}
});
// Openseadragon loading aperio metadata
var metadata_url = _basePath + '?INFO3';
var self = this;
$.ajax({
url: metadata_url,
success: function (data) {
var metadata_array = data.split('|');
_imageMetaData = {
width: parseInt(metadata_array[0]),
height: parseInt(metadata_array[1]),
tilex: parseInt(metadata_array[3]),
tiley: parseInt(metadata_array[4]),
appmag: metadata_array[7],
mpp: metadata_array[8]
}
// example info3 response:
// ["55296", "38912", "1", "256", "256", "", "NanoZoomer Digital Pathology Image", "AppMag = 20", "MPP = 0.4526↵"]
var aperioTileSource = new OpenSeadragon.AperioTileSource(_imageMetaData.width, _imageMetaData.height, _imageMetaData.tilex, _basePath);
aperioTileSource.minLevel = 3;
_activeViewer.open(aperioTileSource);
// now load annotations from Aperio xml after short delay
self.loadAperioAnnotations();
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment