/**
* @module GSVPANO
*/
var eventEmitter = require('event-emitter');
eventEmitter.alloff = require('event-emitter/all-off');
/**
* One single Panoramic item
* @class Pano
* @author juampi92
* @extends {EventEmitter}
* @constructor
* @param {Object} params
* @param {Hash} params.id
* @param {Number} params.rotation (on degrees)
* @param {Number} params.pitch
* @param {Google.Maps.LatLng} params.location
* @param {String} params.copyright
* @param {Date} params.imageDate
* @param {Number} params.zoom
* @example
* var pano = new GSVPANO.Pano({
* id: panoId,
* rotation: rotation,
* pitch: pitch,
* location: location,
* imageDate: imageDate,
* copyright: copyright,
* zoom: zoom
* });
*/
var Pano = function(params) {
eventEmitter(this);
var _params = params || {};
/**
* @attribute id
* @type {Hash}
*/
this.id = params.id;
/**
* @attribute rotation
* @type {Number}
*/
this.setRotation(params.rotation || 0);
/**
* @attribute pitch
* @type {Number}
*/
this.pitch = params.pitch;
/**
* @attribute location
* @type {Google.Maps.LatLng}
*/
this.location = params.location;
/**
* @attribute imageDate
* @type {Date}
*/
this.imageDate = params.imageDate;
/**
* @attribute copyright
* @type {String}
*/
this.copyright = params.copyright;
/**
* @attribute zoom
* @type {Number}
*/
this.zoom = parseInt(params.zoom);
/**
* @attribute canvas
* @type {Canvas Element}
* @default null
*/
this.canvas = null;
/**
* @attribute _ctx
* @type {Canvas 2d Context}
* @default null
*/
this._ctx = null;
/**
* @attribute _loaded
* @type {Boolean}
*/
this._loaded = false;
};
/**
* Saves rotation. Input in degrees
* @method setRotation
* @param {Number} deg
* @chainable
*/
Pano.prototype.setRotation = function(deg) {
this.rotation = deg * Math.PI / 180.0;
return this;
};
/**
* @method initCanvas
* @private
*/
Pano.prototype.initCanvas = function() {
this.canvas = document.createElement('canvas');
this._ctx = this.canvas.getContext('2d');
var w = 416 * Math.pow(2, this.zoom),
h = (416 * Math.pow(2, this.zoom - 1));
this.canvas.width = w;
this.canvas.height = h;
};
/**
* Progress notification
* @event progress
* @param {Number} p
* @chainable
* @example
* pano.on('progress', function(p) {
* console.log('Pano download progress: ' + p + '%');
* });
*/
/**
* Complete notification
* @event complete
* @param {Pano} pano
* @chainable
* @example
* pano.on('complete', function(p) {
* console.log('Pano completed progress: ' + p + '%');
* });
*/
/**
* Will fire 'callback' when completed
* @method compose
* @param {Hash} panoId
* @chainable
* @example
* var pano = new Pano(...);
* pano.compose();
*/
Pano.prototype.compose = function() {
this.initCanvas();
var w,
h = Math.pow(2, this.zoom - 1),
url, x, y;
switch (this.zoom) {
case 5:
w = 26;
h = 13;
break;
case 4:
w = 13;
h = 7;
break;
case 3:
w = 7;
break;
default:
w = Math.pow(2, this.zoom);
}
this._count = 0;
this._total = w * h;
// Get the tiles
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
this.createImage(x, y);
}
}
return this;
};
/**
* Creates an Image with the appropiate load callback
* @method createImage
* @param {Number} x
* @param {Number} y
* @private
*/
Pano.prototype.createImage = function(x, y) {
var url = GSVPANO._url + '&panoid=' + this.id + '&zoom=' + this.zoom + '&x=' + x + '&y=' + y + '&' + Date.now(),
img = new Image();
img.addEventListener('load', this.composeFromTile.bind(this, x, y, img));
img.crossOrigin = '';
img.src = url;
};
/**
* @method composeFromTile
* @param {Number} x
* @param {Number} y
* @param {Image} texture
* @private
*/
Pano.prototype.composeFromTile = function(x, y, texture) {
if (this.cancelled) {
return;
}
// Complete this section of the frame
this._ctx.drawImage(texture, x * 512, y * 512);
this._count++;
var p = Math.round(this._count * 100 / this._total);
this.emit('progress', p);
// If finished
if (this._count === this._total) {
// Done loading
this._loaded = true;
// Trigger complete event
this.emit('complete', this);
// Remove all events
eventEmitter.alloff(this);
}
};
/**
* Cancels the load of the Pano (the images may keep loading)
* @method cancel
*/
Pano.prototype.cancel = function() {
// Remove all events
eventEmitter.alloff(this);
this.cancelled = true;
};
module.exports = Pano;