|
/*!
|
|
Virtual Sky
|
|
(c) Stuart Lowe, Las Cumbres Observatory Global Telescope
|
|
A browser planetarium using HTML5's <canvas>.
|
|
*/
|
|
/*
|
|
USAGE:
|
|
<!--[if lt IE 9]><script src="http://lcogt.net/virtualsky/embed/excanvas.js" type="text/javascript"></script><![endif]-->
|
|
<script src="http://lcogt.net/virtualsky/embed/jquery-1.7.1.min.js" type="text/javascript"></script>
|
|
<script src="http://lcogt.net/virtualsky/embed/virtualsky.js" type="text/javascript"></script>
|
|
<script type="text/javascript">
|
|
<!--
|
|
$(document).ready(function(){
|
|
planetarium = $.virtualsky({id:'starmapper',projection:'polar'}); // Assumes you want to draw this to a <div> with the id 'starmapper'
|
|
});
|
|
// -->
|
|
</script>
|
|
|
|
OPTIONS (default values in brackets):
|
|
id ('starmap') - The ID for the HTML element where you want the sky inserted
|
|
projection ('polar') - The projection type as 'polar', 'stereo', 'lambert', 'equirectangular', or 'ortho'
|
|
width (500) - Set the width of the sky unless you've set the width of the element
|
|
height (250) - Set the height of the sky unless you've set the height of the element
|
|
planets - either an object containing an array of planets or a JSON file
|
|
magnitude (5) - the magnitude limit of displayed stars
|
|
longitude (53.0) - the longitude of the observer
|
|
latitude (-2.5) - the latitude of the observer
|
|
clock (now) - a Javascript Date() object with the starting date/time
|
|
background ('rgba(0,0,0,0)') - the background colour
|
|
transparent (false) - make the sky background transparent
|
|
color ('rgb(255,255,255)') - the text colour
|
|
az (180) - an azimuthal offset with 0 = north and 90 = east
|
|
ra (0 <= x < 360) - the RA for the centre of the view in gnomic projection
|
|
dec (-90 < x < 90) - the Declination for the centre of the view in gnomic projection
|
|
negative (false) - invert the default colours i.e. to black on white
|
|
ecliptic (false) - show the Ecliptic line
|
|
meridian (false) - show the Meridian line
|
|
gradient (true) - reduce the brightness of stars near the horizon
|
|
cardinalpoints (true) - show/hide the N/E/S/W labels
|
|
constellations (false) - show/hide the constellation lines
|
|
constellationlabels (false) - show/hide the constellation labels
|
|
constellationboundaries (false) - show/hide the constellation boundaries (IAU)
|
|
showstars (true) - show/hide the stars
|
|
showstarlabels (false) - show/hide the star labels for brightest stars
|
|
showplanets (true) - show/hide the planets
|
|
showplanetlabels (true) - show/hide the planet labels
|
|
showorbits (false) - show/hide the orbits of the planets
|
|
showgalaxy (false) - show/hide an outline of the plane of the Milky Way
|
|
showdate (true) - show/hide the date and time
|
|
showposition (true) - show/hide the latitude/longitude
|
|
ground (false) - show/hide the local ground (for full sky projections)
|
|
keyboard (true) - allow keyboard controls
|
|
mouse (true) - allow mouse controls
|
|
gridlines_az (false) - show/hide the azimuth/elevation grid lines
|
|
gridlines_eq (false) - show/hide the RA/Dec grid lines
|
|
gridlines_gal (false) - show/hide the Galactic Coordinate grid lines
|
|
gridstep (30) - the size of the grid step when showing grid lines
|
|
live (false) - update the display in real time
|
|
fontsize - set the font size in pixels if you want to over-ride the auto sizing
|
|
fontfamily - set the font family using a CSS style font-family string otherwise it inherits from the container element
|
|
objects - a semi-colon-separated string of recognized object names to display e.g. "M1;M42;Horsehead Nebula" (requires internet connection)
|
|
*/
|
|
(function ($) {
|
|
|
|
/*@cc_on
|
|
// Fix for IE's inability to handle arguments to setTimeout/setInterval
|
|
// From http://webreflection.blogspot.com/2007/06/simple-settimeout-setinterval-extra.html
|
|
(function(f){
|
|
window.setTimeout =f(window.setTimeout);
|
|
window.setInterval =f(window.setInterval);
|
|
})(function(f){return function(c,t){var a=[].slice.call(arguments,2);return f(function(){c.apply(this,a)},t)}});
|
|
@*/
|
|
// Define a shortcut for checking variable types
|
|
function is(a,b){return typeof a===b;}
|
|
|
|
// Get the URL query string and parse it
|
|
$.query = function() {
|
|
var r = {length:0};
|
|
var q = location.search;
|
|
if(q && q != '#'){
|
|
// remove the leading ? and trailing &
|
|
q = q.replace(/^\?/,'').replace(/\&$/,'');
|
|
jQuery.each(q.split('&'), function(){
|
|
var key = this.split('=')[0];
|
|
var val = this.split('=')[1];
|
|
// convert floats
|
|
if(/^-?[0-9.]+$/.test(val)) val = parseFloat(val);
|
|
if(val == "true") val = true;
|
|
if(val == "false") val = false;
|
|
if(/^\?[0-9\.]+$/.test(val)) val = parseFloat(val); // convert floats
|
|
r[key] = val;
|
|
});
|
|
}
|
|
return r;
|
|
};
|
|
|
|
$.extend($.fn.addTouch = function(){
|
|
// Adapted from http://code.google.com/p/rsslounge/source/browse/trunk/public/javascript/addtouch.js?spec=svn115&r=115
|
|
this.each(function(i,el){
|
|
// Pass the original event object because the jQuery event object
|
|
// is normalized to w3c specs and does not provide the TouchList.
|
|
$(el).bind('touchstart touchmove touchend touchcancel touchdbltap',function(){ handleTouch(event); });
|
|
});
|
|
var handleTouch = function(event){
|
|
event.preventDefault();
|
|
|
|
var simulatedEvent;
|
|
var touches = event.changedTouches,
|
|
first = touches[0],
|
|
type = '';
|
|
switch(event.type){
|
|
case 'touchstart':
|
|
type = ['mousedown','click'];
|
|
break;
|
|
case 'touchmove':
|
|
type = ['mousemove'];
|
|
break;
|
|
case 'touchend':
|
|
type = ['mouseup'];
|
|
break;
|
|
case 'touchdbltap':
|
|
type = ['dblclick'];
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
for(var i = 0; i < type.length; i++){
|
|
simulatedEvent = document.createEvent('MouseEvent');
|
|
simulatedEvent.initMouseEvent(type[i], true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY, false, false, false, false, 0/*left*/, null);
|
|
first.target.dispatchEvent(simulatedEvent);
|
|
}
|
|
};
|
|
});
|
|
/*! Copyright (c) 2013 Brandon Aaron (http://brandonaaron.net)
|
|
* Licensed under the MIT License (LICENSE.txt).
|
|
*
|
|
* Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
|
|
* Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
|
|
* Thanks to: Seamus Leahy for adding deltaX and deltaY
|
|
*
|
|
* Version: 3.1.3
|
|
*
|
|
* Requires: 1.2.2+
|
|
*/
|
|
(function (factory) {
|
|
if (typeof define==='function' && define.amd){
|
|
// AMD. Register as an anonymous module.
|
|
define(['jquery'], factory);
|
|
} else if (typeof exports==='object') {
|
|
// Node/CommonJS style for Browserify
|
|
module.exports = factory;
|
|
} else {
|
|
// Browser globals
|
|
factory(jQuery);
|
|
}
|
|
}(function ($) {
|
|
|
|
var toFix = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'];
|
|
var toBind = 'onwheel' in document || document.documentMode >= 9 ? ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'];
|
|
var lowestDelta, lowestDeltaXY;
|
|
|
|
if ( $.event.fixHooks ) {
|
|
for ( var i = toFix.length; i; ) {
|
|
$.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks;
|
|
}
|
|
}
|
|
|
|
$.event.special.mousewheel = {
|
|
setup: function() {
|
|
if ( this.addEventListener ) {
|
|
for ( var i = toBind.length; i; ) {
|
|
this.addEventListener( toBind[--i], handler, false );
|
|
}
|
|
} else {
|
|
this.onmousewheel = handler;
|
|
}
|
|
},
|
|
|
|
teardown: function() {
|
|
if ( this.removeEventListener ) {
|
|
for ( var i = toBind.length; i; ) {
|
|
this.removeEventListener( toBind[--i], handler, false );
|
|
}
|
|
} else {
|
|
this.onmousewheel = null;
|
|
}
|
|
}
|
|
};
|
|
|
|
$.fn.extend({
|
|
mousewheel: function(fn) {
|
|
return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
|
|
},
|
|
|
|
unmousewheel: function(fn) {
|
|
return this.unbind("mousewheel", fn);
|
|
}
|
|
});
|
|
|
|
|
|
function handler(event) {
|
|
var orgEvent = event || window.event,
|
|
args = [].slice.call(arguments, 1),
|
|
delta = 0,
|
|
deltaX = 0,
|
|
deltaY = 0,
|
|
absDelta = 0,
|
|
absDeltaXY = 0,
|
|
fn;
|
|
event = $.event.fix(orgEvent);
|
|
event.type = "mousewheel";
|
|
|
|
// Old school scrollwheel delta
|
|
if (orgEvent.wheelDelta) {delta = orgEvent.wheelDelta;}
|
|
if (orgEvent.detail) {delta = orgEvent.detail * -1;}
|
|
|
|
// New school wheel delta (wheel event)
|
|
if (orgEvent.deltaY){
|
|
deltaY = orgEvent.deltaY * -1;
|
|
delta = deltaY;
|
|
}
|
|
if (orgEvent.deltaX){
|
|
deltaX = orgEvent.deltaX;
|
|
delta = deltaX * -1;
|
|
}
|
|
|
|
// Webkit
|
|
if (orgEvent.wheelDeltaY!==undefined ) { deltaY = orgEvent.wheelDeltaY; }
|
|
if (orgEvent.wheelDeltaX!==undefined ) { deltaX = orgEvent.wheelDeltaX * -1; }
|
|
|
|
// Look for lowest delta to normalize the delta values
|
|
absDelta = Math.abs(delta);
|
|
if (!lowestDelta || absDelta < lowestDelta ) { lowestDelta = absDelta; }
|
|
absDeltaXY = Math.max(Math.abs(deltaY), Math.abs(deltaX));
|
|
if (!lowestDeltaXY || absDeltaXY < lowestDeltaXY ) { lowestDeltaXY = absDeltaXY; }
|
|
|
|
// Get a whole value for the deltas
|
|
fn = delta > 0 ? 'floor' : 'ceil';
|
|
delta = Math[fn](delta / lowestDelta);
|
|
deltaX = Math[fn](deltaX / lowestDeltaXY);
|
|
deltaY = Math[fn](deltaY / lowestDeltaXY);
|
|
|
|
// Add event and delta to the front of the arguments
|
|
args.unshift(event, delta, deltaX, deltaY);
|
|
|
|
return ($.event.dispatch || $.event.handle).apply(this, args);
|
|
}
|
|
|
|
}));
|
|
/*! VirtualSky */
|
|
function VirtualSky(input){
|
|
|
|
this.version = "0.6.7";
|
|
|
|
this.ie = false;
|
|
this.excanvas = (typeof G_vmlCanvasManager != 'undefined') ? true : false;
|
|
/*@cc_on
|
|
this.ie = true
|
|
@*/
|
|
|
|
this.q = $.query(); // Query string
|
|
this.dir = $('script[src*=virtualsky]').attr('src').match(/^.*\//); // the JS file path
|
|
this.dir = this.dir && this.dir[0] || ""; // set dir to match or ""
|
|
this.langurl = this.dir + "lang/%LANG%.json"; // The location of the language files
|
|
|
|
this.id = ''; // The ID of the canvas/div tag - if none given it won't display
|
|
this.gradient = true; // Show the sky gradient
|
|
this.magnitude = 5; // Limit for stellar magnitude
|
|
this.background = "rgba(0,0,0,0)"; // Default background colour is transparent
|
|
this.color = ""; // Default background colour is chosen automatically
|
|
this.wide = 0; // Default width if not set in the <canvas> <div> or input argument
|
|
this.tall = 0;
|
|
|
|
// Constants
|
|
this.d2r = Math.PI/180;
|
|
this.r2d = 180.0/Math.PI;
|
|
|
|
// Set location on the Earth
|
|
this.setLongitude(-119.86286);
|
|
this.setLatitude(34.4326);
|
|
|
|
// Toggles
|
|
this.spin = false;
|
|
this.cardinalpoints = true; // Display N, E, S and W.
|
|
this.constellation = { lines: false, boundaries: false, labels: false }; // Display constellations
|
|
this.meteorshowers = false; // Display meteor shower radiants
|
|
this.negative = false; // Invert colours to make it better for printing
|
|
this.showgalaxy = false; // Display the Milky Way
|
|
this.showstars = true; // Display current positions of the stars
|
|
this.showstarlabels = false; // Display names for named stars
|
|
this.showplanets = true; // Display current positions of the planets
|
|
this.showplanetlabels = true; // Display names for planets
|
|
this.showorbits = false; // Display the orbital paths of the planets
|
|
this.showdate = true; // Display the current date
|
|
this.showposition = true; // Display the longitude/latitude
|
|
this.scalestars = 1; // A scale factor by which to increase the star sizes
|
|
this.ground = false;
|
|
this.grid = { az: false, eq: false, gal: false, step: 30 }; // Display grids
|
|
this.ecliptic = false; // Display the Ecliptic
|
|
this.meridian = false; // Display the Meridian
|
|
this.keyboard = true; // Allow keyboard controls
|
|
this.mouse = true; // Allow mouse controls
|
|
this.islive = false; // Update the sky in real time
|
|
this.fullscreen = false; // Should it take up the full browser window
|
|
this.transparent = false; // Show the sky background or not
|
|
this.fps = 10; // Number of frames per second when animating
|
|
this.credit = (location.host == "lco.global" && location.href.indexOf("/embed") < 0) ? false : true;
|
|
this.callback = { geo:'', mouseenter:'', mouseout:'' };
|
|
this.keys = new Array();
|
|
this.base = "";
|
|
this.az_step = 0;
|
|
this.az_off = 0;
|
|
this.ra_off = 0;
|
|
this.dc_off = 0;
|
|
this.fov = 30;
|
|
this.plugins = [];
|
|
this.calendarevents = [];
|
|
this.events = {}; // Let's add some default events
|
|
|
|
// Projections
|
|
this.projections = {
|
|
'polar': {
|
|
title: 'Polar projection',
|
|
azel2xy: function(az,el,w,h){
|
|
var radius = h/2;
|
|
var r = radius*((Math.PI/2)-el)/(Math.PI/2);
|
|
return {x:(w/2-r*Math.sin(az)),y:(radius-r*Math.cos(az)),el:el};
|
|
},
|
|
polartype: true,
|
|
atmos: true
|
|
},
|
|
'fisheye':{
|
|
title: 'Fisheye polar projection',
|
|
azel2xy: function(az,el,w,h){
|
|
var radius = h/2;
|
|
var r = radius*Math.sin(((Math.PI/2)-el)/2)/0.70710678; // the field of view is bigger than 180 degrees
|
|
return {x:(w/2-r*Math.sin(az)),y:(radius-r*Math.cos(az)),el:el};
|
|
},
|
|
polartype:true,
|
|
atmos: true
|
|
},
|
|
'ortho':{
|
|
title: 'Orthographic polar projection',
|
|
azel2xy: function(az,el,w,h){
|
|
var radius = h/2;
|
|
var r = radius*Math.cos(el);
|
|
return {x:(w/2-r*Math.sin(az)),y:(radius-r*Math.cos(az)),el:el};
|
|
},
|
|
polartype:true,
|
|
atmos: true
|
|
},
|
|
'stereo': {
|
|
title: 'Stereographic projection',
|
|
azel2xy: function(az,el,w,h){
|
|
var f = 0.42;
|
|
var sinel1 = 0;
|
|
var cosel1 = 1;
|
|
var cosaz = Math.cos((az-Math.PI));
|
|
var sinaz = Math.sin((az-Math.PI));
|
|
var sinel = Math.sin(el);
|
|
var cosel = Math.cos(el);
|
|
var k = 2/(1+sinel1*sinel+cosel1*cosel*cosaz);
|
|
return {x:(w/2+f*k*h*cosel*sinaz),y:(h-f*k*h*(cosel1*sinel-sinel1*cosel*cosaz)),el:el};
|
|
},
|
|
atmos: true
|
|
},
|
|
'lambert':{
|
|
title: 'Lambert projection',
|
|
azel2xy: function(az,el,w,h){
|
|
var cosaz = Math.cos((az-Math.PI));
|
|
var sinaz = Math.sin((az-Math.PI));
|
|
var sinel = Math.sin(el);
|
|
var cosel = Math.cos(el);
|
|
var k = Math.sqrt(2/(1+cosel*cosaz));
|
|
return {x:(w/2+0.6*h*k*cosel*sinaz),y:(h-0.6*h*k*(sinel)),el:el};
|
|
},
|
|
atmos: true
|
|
},
|
|
'gnomic': {
|
|
title: 'Gnomic projection',
|
|
azel2xy: function(az,el){
|
|
if(el >= 0){
|
|
var pos = this.azel2radec(az,el);
|
|
return this.radec2xy(pos.ra*this.d2r,pos.dec*this.d2r,[el,az]);
|
|
}else{
|
|
return { x: -1, y: -1, el: el };
|
|
}
|
|
},
|
|
radec2xy: function(ra,dec,coords){
|
|
|
|
var fov, cd, cd0, sd, sd0, dA, A, F, scale, twopi;
|
|
|
|
// Only want to project the sky around the map centre
|
|
if(Math.abs(dec-this.dc_off) > this.maxangle) return {x:-1,y:-1,el:-1};
|
|
var ang = this.greatCircle(this.ra_off,this.dc_off,ra,dec);
|
|
if(ang > this.maxangle) return {x:-1,y:-1,el:-1};
|
|
|
|
if(!coords) coords = this.coord2horizon(ra, dec);
|
|
|
|
// Should we show things below the horizon?
|
|
if(this.ground && coords[0] < -1e-6) return {x:-1, y:-1, el:coords[0]*this.r2d};
|
|
|
|
// number of pixels per degree in the map
|
|
scale = this.tall/this.fov;
|
|
|
|
cd = Math.cos(dec);
|
|
cd0 = Math.cos(this.dc_off);
|
|
sd = Math.sin(dec);
|
|
sd0 = Math.sin(this.dc_off);
|
|
|
|
dA = ra-this.ra_off;
|
|
dA = inrangeAz(dA);
|
|
|
|
A = cd*Math.cos(dA);
|
|
F = scale*this.r2d/(sd0*sd + A*cd0);
|
|
|
|
return {x:(this.wide/2)-F*cd*Math.sin(dA),y:(this.tall/2) -F*(cd0*sd - A*sd0),el:coords[0]*this.r2d};
|
|
},
|
|
draw: function(){
|
|
if(!this.transparent){
|
|
this.ctx.fillStyle = (this.hasGradient()) ? "rgba(0,15,30, 1)" : ((this.negative) ? this.col.white : this.col.black);
|
|
this.ctx.fillRect(0,0,this.wide,this.tall);
|
|
this.ctx.fill();
|
|
}
|
|
},
|
|
isVisible: function(el){
|
|
return true;
|
|
},
|
|
atmos: false,
|
|
fullsky: true
|
|
},
|
|
'equirectangular':{
|
|
title: 'Equirectangular projection',
|
|
azel2xy: function(az,el,w,h){
|
|
while(az < 0) az += 2*Math.PI;
|
|
az = (az)%(Math.PI*2);
|
|
return {x:(((az-Math.PI)/(Math.PI/2))*h + w/2),y:(h-(el/(Math.PI/2))*h),el:el};
|
|
},
|
|
maxb: 90,
|
|
atmos: true
|
|
},
|
|
'mollweide':{
|
|
title: 'Mollweide projection',
|
|
radec2xy: function(ra,dec){
|
|
var dtheta, x, y, coords, sign, outside, normra;
|
|
var thetap = Math.abs(dec);
|
|
var pisindec = Math.PI*Math.sin(Math.abs(dec));
|
|
// Now iterate to correct answer
|
|
for(var i = 0; i < 20 ; i++){
|
|
dtheta = -(thetap + Math.sin(thetap) - pisindec)/(1+Math.cos(thetap));
|
|
thetap += dtheta;
|
|
if(dtheta < 1e-4) break;
|
|
}
|
|
normra = (ra+this.d2r*this.az_off)%(2*Math.PI) - Math.PI;
|
|
outside = false;
|
|
x = -(2/Math.PI)*(normra)*Math.cos(thetap/2)*this.tall/2 + this.wide/2;
|
|
if(x > this.wide) outside = true;
|
|
sign = (dec >= 0) ? 1 : -1;
|
|
y = -sign*Math.sin(thetap/2)*this.tall/2 + this.tall/2;
|
|
coords = this.coord2horizon(ra, dec);
|
|
return {x:(outside ? -100 : x%this.wide),y:y,el:coords[0]*this.r2d};
|
|
},
|
|
draw: function(){
|
|
var c = this.ctx;
|
|
c.moveTo(this.wide/2,this.tall/2);
|
|
c.beginPath();
|
|
var x = this.wide/2-this.tall;
|
|
var y = 0;
|
|
var w = this.tall*2;
|
|
var h = this.tall;
|
|
var kappa = 0.5522848;
|
|
var ox = (w / 2) * kappa; // control point offset horizontal
|
|
var oy = (h / 2) * kappa; // control point offset vertical
|
|
var xe = x + w; // x-end
|
|
var ye = y + h; // y-end
|
|
var xm = x + w / 2; // x-middle
|
|
var ym = y + h / 2; // y-middle
|
|
c.moveTo(x, ym);
|
|
c.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
|
|
c.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
|
|
c.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
|
|
c.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
|
|
c.closePath();
|
|
if(!this.transparent){
|
|
c.fillStyle = (this.hasGradient()) ? "rgba(0,15,30, 1)" : ((this.negative) ? this.col.white : this.col.black);
|
|
c.fill();
|
|
}
|
|
},
|
|
altlabeltext:true,
|
|
fullsky:true,
|
|
atmos: false
|
|
},
|
|
'planechart':{
|
|
title: 'Planechart projection',
|
|
radec2xy: function(ra,dec){
|
|
ra = inrangeAz(ra);
|
|
var normra = (ra+this.d2r*this.az_off)%(2*Math.PI)-Math.PI;
|
|
var x = -(normra/(2*Math.PI))*this.tall*2 + this.wide/2;
|
|
var y = -(dec/Math.PI)*this.tall+ this.tall/2;
|
|
var coords = this.coord2horizon(ra, dec);
|
|
return {x:x,y:y,el:coords[0]*this.r2d};
|
|
},
|
|
draw: function(){
|
|
if(!this.transparent){
|
|
this.ctx.fillStyle = (this.hasGradient()) ? "rgba(0,15,30, 1)" : ((this.negative) ? this.col.white : this.col.black);
|
|
this.ctx.fillRect((this.wide/2) - (this.tall),0,this.tall*2,this.tall);
|
|
this.ctx.fill();
|
|
}
|
|
},
|
|
fullsky:true,
|
|
atmos: false
|
|
}
|
|
};
|
|
|
|
// Data for stars < mag 4.5 or that are a vertex for a constellation line - 20 kB [id, mag, right ascension, declination]
|
|
// index with Hipparcos number
|
|
this.stars = this.convertStarsToRadians([[677,2.1,2.097,29.09],[746,2.3,2.295,59.15],[765,3.9,2.353,-45.75],
|
|
[1067,2.8,3.309,15.18],[1562,3.6,4.857,-8.82],[1599,4.2,5.018,-64.87],[1645,5.4,5.149,8.19],
|
|
[2021,2.8,6.438,-77.25],[2072,3.9,6.551,-43.68],[2081,2.4,6.571,-42.31],[2484,4.4,7.886,-62.96],
|
|
[2920,3.7,9.243,53.9],[3092,3.3,9.832,30.86],[3179,2.2,10.127,56.54],[3419,2,10.897,-17.99],
|
|
[3760,5.9,12.073,7.3],[3821,3.5,12.276,57.82],[3881,4.5,12.454,41.08],[4427,2.1,14.177,60.72],
|
|
[4436,3.9,14.188,38.5],[4577,4.3,14.652,-29.36],[4889,5.5,15.705,31.8],[4906,4.3,15.736,7.89],
|
|
[5165,3.3,16.521,-46.72],[5348,3.9,17.096,-55.25],[5364,3.5,17.147,-10.18],[5447,2.1,17.433,35.62],
|
|
[5742,4.7,18.437,24.58],[6193,4.7,19.867,27.26],[6537,3.6,21.006,-8.18],[6686,2.7,21.454,60.24],
|
|
[6867,3.4,22.091,-43.32],[7007,4.8,22.546,6.14],[7083,3.9,22.813,-49.07],[7097,3.6,22.871,15.35],
|
|
[7588,0.5,24.429,-57.24],[7607,3.6,24.498,48.63],[7884,4.5,25.358,5.49],[8102,3.5,26.017,-15.94],
|
|
[8198,4.3,26.348,9.16],[8645,3.7,27.865,-10.34],[8796,3.4,28.27,29.58],[8832,3.9,28.383,19.29],
|
|
[8833,4.6,28.389,3.19],[8837,4.4,28.411,-46.3],[8886,3.4,28.599,63.67],[8903,2.6,28.66,20.81],
|
|
[9007,3.7,28.989,-51.61],[9236,2.9,29.692,-61.57],[9347,4,30.001,-21.08],[9487,3.8,30.512,2.76],
|
|
[9598,4,30.859,72.42],[9640,2.1,30.975,42.33],[9884,2,31.793,23.46],[10064,3,32.386,34.99],
|
|
[10324,4.4,33.25,8.85],[10559,5.3,33.985,33.36],[10602,3.6,34.127,-51.51],[10826,6.5,34.837,-2.98],
|
|
[11001,4.1,35.437,-68.66],[11345,4.9,36.488,-12.29],[11407,4.2,36.746,-47.7],[11484,4.3,37.04,8.46],
|
|
[11767,2,37.955,89.26],[11783,4.7,38.022,-15.24],[12093,4.9,38.969,5.59],[12387,4.1,39.871,0.33],
|
|
[12390,4.8,39.891,-11.87],[12394,4.1,39.897,-68.27],[12413,4.7,39.95,-42.89],[12484,5.2,40.165,-54.55],
|
|
[12486,4.1,40.167,-39.86],[12706,3.5,40.825,3.24],[12770,4.2,41.031,-13.86],[12828,4.3,41.236,10.11],
|
|
[12843,4.5,41.276,-18.57],[13147,4.5,42.273,-32.41],[13209,3.6,42.496,27.26],[13254,4.2,42.646,38.32],
|
|
[13268,3.8,42.674,55.9],[13531,3.9,43.564,52.76],[13701,3.9,44.107,-8.9],[13847,2.9,44.565,-40.3],
|
|
[13954,4.7,44.929,8.91],[14135,2.5,45.57,4.09],[14146,4.1,45.598,-23.62],[14240,5.1,45.903,-59.74],
|
|
[14328,2.9,46.199,53.51],[14354,3.3,46.294,38.84],[14576,2.1,47.042,40.96],[14668,3.8,47.374,44.86],
|
|
[14879,3.8,48.019,-28.99],[15197,4.8,48.958,-8.82],[15474,3.7,49.879,-21.76],[15510,4.3,49.982,-43.07],
|
|
[15863,1.8,51.081,49.86],[15900,3.6,51.203,9.03],[16083,3.7,51.792,9.73],[16228,4.2,52.267,59.94],
|
|
[16537,3.7,53.233,-9.46],[16611,4.3,53.447,-21.63],[17358,3,55.731,47.79],[17378,3.5,55.812,-9.76],
|
|
[17440,3.8,56.05,-64.81],[17448,3.8,56.08,32.29],[17499,3.7,56.219,24.11],[17529,3.8,56.298,42.58],
|
|
[17573,3.9,56.457,24.37],[17651,4.2,56.712,-23.25],[17678,3.3,56.81,-74.24],[17702,2.9,56.871,24.11],
|
|
[17797,4.3,57.149,-37.62],[17847,3.6,57.291,24.05],[17874,4.2,57.364,-36.2],[17959,4.6,57.59,71.33],
|
|
[18246,2.8,58.533,31.88],[18505,5,59.356,63.07],[18532,2.9,59.463,40.01],[18543,3,59.507,-13.51],
|
|
[18597,4.6,59.686,-61.4],[18614,4,59.741,35.79],[18724,3.4,60.17,12.49],[18907,3.9,60.789,5.99],
|
|
[19343,4,62.165,47.71],[19747,3.9,63.5,-42.29],[19780,3.3,63.606,-62.47],[19893,4.3,64.007,-51.49],
|
|
[19921,4.4,64.121,-59.3],[20042,3.5,64.474,-33.8],[20205,3.6,64.948,15.63],[20455,3.8,65.734,17.54],
|
|
[20535,4,66.009,-34.02],[20648,4.3,66.372,17.93],[20885,3.8,67.144,15.96],[20889,3.5,67.154,19.18],
|
|
[20894,3.4,67.166,15.87],[21060,5.1,67.709,-44.95],[21281,3.3,68.499,-55.04],[21393,3.8,68.888,-30.56],
|
|
[21421,0.9,68.98,16.51],[21444,3.9,69.08,-3.35],[21594,3.9,69.545,-14.3],[21770,4.4,70.14,-41.86],
|
|
[21861,5,70.515,-37.14],[21881,4.3,70.561,22.96],[21949,5.5,70.767,-70.93],[22109,4,71.376,-3.25],
|
|
[22449,3.2,72.46,6.96],[22509,4.3,72.653,8.9],[22549,3.7,72.802,5.61],[22701,4.4,73.224,-5.45],
|
|
[22730,5.3,73.345,2.51],[22783,4.3,73.513,66.34],[22797,3.7,73.563,2.44],[22845,4.6,73.724,10.15],
|
|
[23015,2.7,74.248,33.17],[23123,4.5,74.637,1.71],[23416,3,75.492,43.82],[23453,3.7,75.62,41.08],
|
|
[23685,3.2,76.365,-22.37],[23767,3.2,76.629,41.23],[23875,2.8,76.962,-5.09],[23972,4.3,77.287,-8.75],
|
|
[24244,4.5,78.075,-11.87],[24305,3.3,78.233,-16.21],[24327,4.4,78.308,-12.94],[24436,0.2,78.634,-8.2],
|
|
[24608,0.1,79.172,46],[24674,3.6,79.402,-6.84],[24845,4.3,79.894,-13.18],[24873,5.3,79.996,-12.32],
|
|
[25110,5.1,80.64,79.23],[120412],[25281,3.4,81.119,-2.4],[25336,1.6,81.283,6.35],
|
|
[25428,1.6,81.573,28.61],[25606,2.8,82.061,-20.76],[25859,3.9,82.803,-35.47],[25918,5.2,82.971,-76.34],
|
|
[25930,2.3,83.002,-0.3],[25985,2.6,83.183,-17.82],[26069,3.8,83.406,-62.49],[26207,3.4,83.784,9.93],
|
|
[26241,2.8,83.858,-5.91],[26311,1.7,84.053,-1.2],[26451,3,84.411,21.14],[26549,3.8,84.687,-2.6],
|
|
[26634,2.6,84.912,-34.07],[26727,1.7,85.19,-1.94],[27072,3.6,86.116,-22.45],[27100,4.3,86.193,-65.74],
|
|
[27288,3.5,86.739,-14.82],[27321,3.9,86.821,-51.07],[27366,2.1,86.939,-9.67],[27530,4.5,87.457,-56.17],
|
|
[27628,3.1,87.74,-35.77],[27654,3.8,87.83,-20.88],[27673,4,87.872,39.15],[27890,4.7,88.525,-63.09],
|
|
[27913,4.4,88.596,20.28],[27989,0.5,88.793,7.41],[28103,3.7,89.101,-14.17],[28199,4.4,89.384,-35.28],
|
|
[28328,4,89.787,-42.82],[28358,3.7,89.882,54.28],[28360,1.9,89.882,44.95],[28380,2.6,89.93,37.21],
|
|
[28614,4.1,90.596,9.65],[28691,5.1,90.864,19.69],[28734,4.2,91.03,23.26],[28910,4.7,91.539,-14.94],
|
|
[29038,4.4,91.893,14.77],[29151,5.7,92.241,2.5],[29426,4.5,92.985,14.21],[29651,4,93.714,-6.27],
|
|
[29655,3.3,93.719,22.51],[29807,4.4,94.138,-35.14],[30060,4.4,94.906,59.01],[30122,3,95.078,-30.06],
|
|
[30277,3.9,95.528,-33.44],[30324,2,95.675,-17.96],[30343,2.9,95.74,22.51],[30419,4.4,95.942,4.59],
|
|
[30438,-0.6,95.988,-52.7],[30867,3.8,97.204,-7.03],[30883,4.1,97.241,20.21],[31416,4.5,98.764,-22.96],
|
|
[31592,4,99.171,-19.26],[31681,1.9,99.428,16.4],[31685,3.2,99.44,-43.2],[32246,3.1,100.983,25.13],
|
|
[32349,-1.4,101.287,-16.72],[32362,3.4,101.322,12.9],[32607,3.2,102.048,-61.94],[32759,3.5,102.46,-32.51],
|
|
[32768,2.9,102.484,-50.61],[33018,3.6,103.197,33.96],[33152,3.9,103.533,-24.18],[33160,4.1,103.547,-12.04],
|
|
[33165,6.7,103.554,-23.93],[33347,4.4,104.034,-17.05],[33449,4.3,104.319,58.42],[33579,1.5,104.656,-28.97],
|
|
[33856,3.5,105.43,-27.93],[33977,3,105.756,-23.83],[34045,4.1,105.94,-15.63],[34088,4,106.027,20.57],
|
|
[34444,1.8,107.098,-26.39],[34481,3.8,107.187,-70.5],[34693,4.4,107.785,30.25],[34769,4.2,107.966,-0.49],
|
|
[35037,4,108.703,-26.77],[35228,4,109.208,-67.96],[35264,2.7,109.286,-37.1],[35350,3.6,109.523,16.54],
|
|
[35550,3.5,110.031,21.98],[35904,2.5,111.024,-29.3],[36046,3.8,111.432,27.8],[36145,4.6,111.679,49.21],
|
|
[36188,2.9,111.788,8.29],[36377,3.3,112.308,-43.3],[36850,1.6,113.649,31.89],[36962,4.1,113.981,26.9],
|
|
[37229,3.8,114.708,-26.8],[37279,0.4,114.825,5.22],[37447,3.9,115.312,-9.55],[37504,3.9,115.455,-72.61],
|
|
[37677,3.9,115.952,-28.95],[37740,3.6,116.112,24.4],[37819,3.6,116.314,-37.97],[37826,1.2,116.329,28.03],
|
|
[38146,5.3,117.257,-24.91],[38170,3.3,117.324,-24.86],[38414,3.7,118.054,-40.58],[38827,3.5,119.195,-52.98],
|
|
[39429,2.2,120.896,-40],[39757,2.8,121.886,-24.3],[39794,4.3,121.982,-68.62],[39863,4.4,122.149,-2.98],
|
|
[39953,1.8,122.383,-47.34],[40526,3.5,124.129,9.19],[40702,4,124.631,-76.92],[40843,5.1,125.016,27.22],
|
|
[41037,1.9,125.628,-59.51],[41075,4.3,125.709,43.19],[41307,3.9,126.415,-3.91],[41312,3.8,126.434,-66.14],
|
|
[41704,3.4,127.566,60.72],[42313,4.1,129.414,5.7],[42402,4.5,129.689,3.34],[42515,4,130.026,-35.31],
|
|
[42536,3.6,130.073,-52.92],[42568,4.3,130.154,-59.76],[42570,3.8,130.157,-46.65],[42799,4.3,130.806,3.4],
|
|
[42806,4.7,130.821,21.47],[42828,3.7,130.898,-33.19],[42911,3.9,131.171,18.15],[42913,1.9,131.176,-54.71],
|
|
[43023,3.9,131.507,-46.04],[43103,4,131.674,28.76],[43109,3.4,131.694,6.42],[43234,4.3,132.108,5.84],
|
|
[43409,4,132.633,-27.71],[43783,3.8,133.762,-60.64],[43813,3.1,133.848,5.95],[44066,4.3,134.622,11.86],
|
|
[44127,3.1,134.802,48.04],[44248,4,135.16,41.78],[44382,4,135.612,-66.4],[44471,3.6,135.906,47.16],
|
|
[44511,3.8,136.039,-47.1],[44700,4.6,136.632,38.45],[44816,2.2,136.999,-43.43],[45080,3.4,137.742,-58.97],
|
|
[45101,4,137.82,-62.32],[45238,1.7,138.3,-69.72],[45336,3.9,138.591,2.31],[45556,2.2,139.273,-59.28],
|
|
[45688,3.8,139.711,36.8],[45860,3.1,140.264,34.39],[45941,2.5,140.528,-55.01],[46390,2,141.897,-8.66],
|
|
[46509,4.6,142.287,-2.77],[46651,3.6,142.675,-40.47],[46701,3.2,142.805,-57.03],[46733,3.6,142.882,63.06],
|
|
[46776,4.5,142.996,-1.18],[46853,3.2,143.214,51.68],[46952,4.5,143.556,36.4],[47431,3.9,144.964,-1.14],
|
|
[47508,3.5,145.288,9.89],[47854,3.7,146.312,-62.51],[47908,3,146.463,23.77],[48002,2.9,146.776,-65.07],
|
|
[48319,3.8,147.747,59.04],[48356,4.1,147.87,-14.85],[48402,4.5,148.026,54.06],[48455,3.9,148.191,26.01],
|
|
[48774,3.5,149.216,-54.57],[48926,5.2,149.718,-35.89],[49583,3.5,151.833,16.76],[49593,4.5,151.857,35.24],
|
|
[49641,4.5,151.985,-0.37],[49669,1.4,152.093,11.97],[49841,3.6,152.647,-12.35],[50099,3.3,153.434,-70.04],
|
|
[50191,3.9,153.684,-42.12],[50335,3.4,154.173,23.42],[50371,3.4,154.271,-61.33],[50372,3.5,154.274,42.91],
|
|
[50583,2,154.993,19.84],[50801,3.1,155.582,41.5],[50954,4,156.099,-74.03],[51069,3.8,156.523,-16.84],
|
|
[51172,4.3,156.788,-31.07],[51232,3.8,156.97,-58.74],[51233,4.2,156.971,36.71],[51437,5.1,157.573,-0.64],
|
|
[51576,3.3,158.006,-61.69],[51624,3.8,158.203,9.31],[51839,4.1,158.867,-78.61],[51986,3.8,159.326,-48.23],
|
|
[52419,2.7,160.739,-64.39],[52468,4.6,160.885,-60.57],[52727,2.7,161.692,-49.42],[52943,3.1,162.406,-16.19],
|
|
[53229,3.8,163.328,34.21],[53253,3.8,163.374,-58.85],[53740,4.1,164.944,-18.3],[53910,2.3,165.46,56.38],
|
|
[54061,1.8,165.932,61.75],[54463,3.9,167.147,-58.98],[54539,3,167.416,44.5],[54682,4.5,167.915,-22.83],
|
|
[54872,2.6,168.527,20.52],[54879,3.3,168.56,15.43],[55203,3.8],[55219,3.5,169.62,33.09],
|
|
[55282,3.6,169.835,-14.78],[55425,3.9,170.252,-54.49],[55687,4.8,171.152,-10.86],[55705,4.1,171.221,-17.68],
|
|
[56211,3.8,172.851,69.33],[56343,3.5,173.25,-31.86],[56480,4.6,173.69,-54.26],[56561,3.1,173.945,-63.02],
|
|
[56633,4.7,174.17,-9.8],[57283,4.7,176.191,-18.35],[57363,3.6,176.402,-66.73],[57380,4,176.465,6.53],
|
|
[57399,3.7,176.513,47.78],[57632,2.1,177.265,14.57],[57757,3.6,177.674,1.76],[57936,4.3,178.227,-33.91],
|
|
[58001,2.4,178.458,53.69],[58188,5.2,179.004,-17.15],[59196,2.6,182.09,-50.72],[59199,4,182.103,-24.73],
|
|
[59316,3,182.531,-22.62],[59449,4,182.913,-52.37],[59747,2.8,183.786,-58.75],[59774,3.3,183.857,57.03],
|
|
[59803,2.6,183.952,-17.54],[60000,4.2,184.587,-79.31],[60030,5.9,184.668,-0.79],[60129,3.9,184.976,-0.67],
|
|
[60260,3.6,185.34,-60.4],[60718,0.8,186.65,-63.1],[60742,4.3,186.734,28.27],[60823,3.9,187.01,-50.23],
|
|
[60965,2.9,187.466,-16.52],[61084,1.6,187.791,-57.11],[61174,4.3,188.018,-16.2],[61199,3.8,188.117,-72.13],
|
|
[61281,3.9,188.371,69.79],[61317,4.2,188.436,41.36],[61359,2.6,188.597,-23.4],[61585,2.7,189.296,-69.14],
|
|
[61622,3.9,189.426,-48.54],[61932,2.2,190.379,-48.96],[61941,2.7,190.415,-1.45],[62322,3,191.57,-68.11],
|
|
[62434,1.3,191.93,-59.69],[62956,1.8,193.507,55.96],[63090,3.4,193.901,3.4],[63125,2.9,194.007,38.32],
|
|
[63608,2.9,195.544,10.96],[63613,3.6,195.568,-71.55],[64166,4.9,197.264,-23.12],[64241,4.3,197.497,17.53],
|
|
[64394,4.2,197.968,27.88],[64962,3,199.73,-23.17],[65109,2.8,200.149,-36.71],[65378,2.2,200.981,54.93],
|
|
[65474,1,201.298,-11.16],[65477,4,201.306,54.99],[65936,3.9,202.761,-39.41],[66249,3.4,203.673,-0.6],
|
|
[66657,2.3,204.972,-53.47],[67301,1.9,206.885,49.31],[67459,4,207.369,15.8],[67464,3.4,207.376,-41.69],
|
|
[67472,3.5,207.404,-42.47],[67927,2.7,208.671,18.4],[68002,2.5,208.885,-47.29],[68245,3.8,209.568,-42.1],
|
|
[68282,3.9,209.67,-44.8],[68520,4.2,210.412,1.54],[68702,0.6,210.956,-60.37],[68756,3.7,211.097,64.38],
|
|
[68895,3.3,211.593,-26.68],[68933,2.1,211.671,-36.37],[69427,4.2,213.224,-10.27],[69673,-0.1,213.915,19.18],
|
|
[69701,4.1,214.004,-6],[69996,3.5,214.851,-46.06],[70576,4.3,216.545,-45.38],[70638,4.3,216.73,-83.67],
|
|
[71053,3.6,217.957,30.37],[71075,3,218.019,38.31],[71352,2.3,218.877,-42.16],[71536,4,219.472,-49.43],
|
|
[71681,1.4,219.896,-60.84],[71683,-0,219.902,-60.83],[71795,3.8,220.287,13.73],[71860,2.3,220.482,-47.39],
|
|
[71908,3.2,220.627,-64.98],[71957,3.9,220.765,-5.66],[72105,2.4,221.247,27.07],[72220,3.7,221.562,1.89],
|
|
[72370,3.8,221.965,-79.04],[72607,2.1,222.676,74.16],[72622,2.8,222.72,-16.04],[73273,2.7,224.633,-43.13],
|
|
[73334,3.1,224.79,-42.1],[73555,3.5,225.487,40.39],[73714,3.3,226.018,-25.28],[73807,3.9,226.28,-47.05],
|
|
[74376,3.9,227.984,-48.74],[74395,3.4,228.071,-52.1],[74666,3.5,228.876,33.31],[74785,2.6,229.252,-9.38],
|
|
[74824,4.1,229.379,-58.8],[74946,2.9,229.727,-68.68],[75097,3,230.182,71.83],[75141,3.2,230.343,-40.65],
|
|
[75177,3.6,230.452,-36.26],[75264,3.4,230.67,-44.69],[75323,4.5,230.844,-59.32],[75458,3.3,231.232,58.97],
|
|
[75695,3.7,231.957,29.11],[76127,4.1,233.232,31.36],[76267,2.2,233.672,26.71],[76276,3.8,233.701,10.54],
|
|
[76297,2.8,233.785,-41.17],[76333,3.9,233.882,-14.79],[76470,3.6,234.256,-28.14],[76552,4.3,234.513,-42.57],
|
|
[76600,3.7,234.664,-29.78],[76952,3.8,235.686,26.3],[77055,4.3,236.015,77.79],[77070,2.6,236.067,6.43],
|
|
[77233,3.6,236.547,15.42],[77450,4.1,237.185,18.14],[77512,4.6,237.399,26.07],[77516,3.5,237.405,-3.43],
|
|
[77622,3.7,237.704,4.48],[77634,4,237.74,-33.63],[77760,4.6,238.169,42.45],[77853,4.1,238.456,-16.73],
|
|
[77952,2.8,238.786,-63.43],[78072,3.9,239.113,15.66],[78104,3.9,239.221,-29.21],[78159,4.1,239.397,26.88],
|
|
[78265,2.9,239.713,-26.11],[78384,3.4,240.031,-38.4],[78401,2.3,240.083,-22.62],[78493,5,240.361,29.85],
|
|
[78527,4,240.472,58.57],[78639,4.7,240.804,-49.23],[78820,2.6,241.359,-19.81],[78933,3.9,241.702,-20.67],
|
|
[78970,5.7,241.818,-36.76],[79509,5,243.37,-54.63],[79593,2.7,243.586,-3.69],[79664,3.9,243.859,-63.69],
|
|
[79822,5,244.376,75.76],[79882,3.2,244.58,-4.69],[79992,3.9,244.935,46.31],[80000,4,244.96,-50.16],
|
|
[80112,2.9,245.297,-25.59],[80170,3.7,245.48,19.15],[80331,2.7,245.998,61.51],[80582,4.5,246.796,-47.55],
|
|
[80763,1.1,247.352,-26.43],[80816,2.8,247.555,21.49],[80883,3.8,247.728,1.98],[81065,3.9,248.363,-78.9],
|
|
[81126,4.2,248.526,42.44],[81266,2.8,248.971,-28.22],[81377,2.5,249.29,-10.57],[81693,2.8,250.322,31.6],
|
|
[81833,3.5,250.724,38.92],[81852,4.2,250.769,-77.52],[82080,4.2,251.493,82.04],[82273,1.9,252.166,-69.03],
|
|
[82363,3.8,252.446,-59.04],[82396,2.3,252.541,-34.29],[82514,3,252.968,-38.05],[82545,3.6,253.084,-38.02],
|
|
[82671,4.7,253.499,-42.36],[82729,3.6,253.646,-42.36],[83000,3.2,254.417,9.38],[83081,3.1,254.655,-55.99],
|
|
[83207,3.9,255.072,30.93],[83895,3.2,257.197,65.71],[84012,2.4,257.595,-15.72],[84143,3.3,258.038,-43.24],
|
|
[84345,2.8,258.662,14.39],[84379,3.1,258.758,24.84],[84380,3.2,258.762,36.81],[84606,4.6,259.418,37.29],
|
|
[84880,4.3,260.207,-12.85],[84970,3.3,260.502,-25],[85112,4.2,260.921,37.15],[85258,2.8,261.325,-55.53],
|
|
[85267,3.3,261.349,-56.38],[85670,2.8,262.608,52.3],[85693,4.4,262.685,26.11],[85696,2.7,262.691,-37.3],
|
|
[85727,3.6,262.775,-60.68],[85755,4.8,262.854,-23.96],[85792,2.8,262.96,-49.88],[85822,4.3,263.054,86.59],
|
|
[85829,4.9,263.067,55.17],[85927,1.6,263.402,-37.1],[86032,2.1,263.734,12.56],[86228,1.9,264.33,-43],
|
|
[86263,3.5,264.397,-15.4],[86414,3.8,264.866,46.01],[86565,4.2,265.354,-12.88],[86670,2.4,265.622,-39.03],
|
|
[86742,2.8,265.868,4.57],[86929,3.6,266.433,-64.72],[86974,3.4,266.615,27.72],[87072,4.5,266.89,-27.83],
|
|
[87073,3,266.896,-40.13],[87108,3.8,266.973,2.71],[87261,3.2,267.465,-37.04],[87585,3.7,268.382,56.87],
|
|
[87808,3.9,269.063,37.25],[87833,2.2,269.152,51.49],[87933,3.7,269.441,29.25],[88048,3.3,269.757,-9.77],
|
|
[88192,3.9,270.161,2.93],[88635,3,271.452,-30.42],[88714,3.6,271.658,-50.09],[88771,3.7,271.837,9.56],
|
|
[88794,3.8,271.886,28.76],[88866,4.3,272.145,-63.67],[89341,3.8,273.441,-21.06],[89642,3.1,274.407,-36.76],
|
|
[89931,2.7,275.249,-29.83],[89937,3.5,275.264,72.73],[89962,3.2,275.328,-2.9],[90098,4.3,275.807,-61.49],
|
|
[90139,3.9,275.925,21.77],[90185,1.8,276.043,-34.38],[90422,3.5,276.743,-45.97],[90496,2.8,276.993,-25.42],
|
|
[90568,4.1,277.208,-49.07],[90595,4.7,277.299,-14.57],[90887,5.2,278.089,-39.7],[91117,3.9,278.802,-8.24],
|
|
[91262,0,279.235,38.78],[91792,4,280.759,-71.43],[91875,5.1,280.946,-38.32],[91971,4.3,281.193,37.61],
|
|
[92041,3.2,281.414,-26.99],[92175,4.2,281.794,-4.75],[92202,5.4,281.871,-5.71],[92420,3.5,282.52,33.36],
|
|
[92609,4.2,283.054,-62.19],[92791,4.2,283.626,36.9],[92814,5.1,283.68,-15.6],[92855,2,283.816,-26.3],
|
|
[92946,4.6,284.055,4.2],[92953,5.3,284.071,-42.71],[92989,5.4,284.169,-37.34],[93015,4.4,284.238,-67.23],
|
|
[93085,3.5,284.433,-21.11],[93174,4.8,284.681,-37.11],[93194,3.3,284.736,32.69],[93244,4,284.906,15.07],
|
|
[93506,2.6,285.653,-29.88],[93542,4.7,285.779,-42.1],[93683,3.8,286.171,-21.74],[93747,3,286.353,13.86],
|
|
[93805,3.4,286.562,-4.88],[93825,4.2,286.605,-37.06],[93864,3.3,286.735,-27.67],[94005,4.6,287.087,-40.5],
|
|
[94114,4.1,287.368,-37.9],[94141,2.9,287.441,-21.02],[94160,4.1,287.507,-39.34],[94376,3.1,288.139,67.66],
|
|
[94779,3.8,289.276,53.37],[94820,4.9,289.409,-18.95],[95168,3.9,290.418,-17.85],[95241,4,290.66,-44.46],
|
|
[95294,4.3,290.805,-44.8],[95347,4,290.972,-40.62],[95501,3.4,291.375,3.11],[95771,4.4,292.176,24.66],
|
|
[95853,3.8,292.426,51.73],[95947,3,292.68,27.96],[96406,5.6,294.007,-24.72],[96757,4.4,295.024,18.01],
|
|
[96837,4.4,295.262,17.48],[97165,2.9,296.244,45.13],[97278,2.7,296.565,10.61],[97365,3.7,296.847,18.53],
|
|
[97433,3.8,297.043,70.27],[97649,0.8,297.696,8.87],[97804,3.9,298.118,1.01],[98032,4.1,298.815,-41.87],
|
|
[98036,3.7,298.828,6.41],[98110,3.9,299.077,35.08],[98337,3.5,299.689,19.49],[98412,4.4,299.934,-35.28],
|
|
[98495,4,300.148,-72.91],[98543,4.7,300.275,27.75],[98688,4.4,300.665,-27.71],[98920,5.1,301.29,19.99],
|
|
[99240,3.5,302.182,-66.18],[99473,3.2,302.826,-0.82],[99675,3.8,303.408,46.74],[99848,4,303.868,47.71],
|
|
[100064,3.6,304.514,-12.54],[100345,3,305.253,-14.78],[100453,2.2,305.557,40.26],[100751,1.9,306.412,-56.74],
|
|
[101421,4,308.303,11.3],[101769,3.6,309.387,14.6],[101772,3.1,309.392,-47.29],[101958,3.8,309.91,15.91],
|
|
[102098,1.3,310.358,45.28],[102281,4.4,310.865,15.07],[102395,3.4,311.24,-66.2],[102422,3.4,311.322,61.84],
|
|
[102485,4.1,311.524,-25.27],[102488,2.5,311.553,33.97],[102532,4.3,311.665,16.12],[102618,3.8,311.919,-9.5],
|
|
[102831,4.9,312.492,-33.78],[102978,4.1,312.955,-26.92],[103227,3.7,313.703,-58.45],[103413,3.9,314.293,41.17],
|
|
[103738,4.7,315.323,-32.26],[104060,3.7,316.233,43.93],[104139,4.1,316.487,-17.23],[104521,4.7,317.585,10.13],
|
|
[104732,3.2,318.234,30.23],[104858,4.5,318.62,10.01],[104887,3.7,318.698,38.05],[104987,3.9,318.956,5.25],
|
|
[105140,4.7,319.485,-32.17],[105199,2.5,319.645,62.59],[105319,4.4,319.967,-53.45],[105515,4.3,320.562,-16.83],
|
|
[105570,5.2,320.723,6.81],[105858,4.2,321.611,-65.37],[105881,3.8,321.667,-22.41],[106032,3.2,322.165,70.56],
|
|
[106278,2.9,322.89,-5.57],[106481,4,323.495,45.59],[106985,3.7,325.023,-16.66],[107089,3.7,325.369,-77.39],
|
|
[107310,4.5,326.036,28.74],[107315,2.4,326.046,9.88],[107354,4.1,326.161,25.65],[107556,2.9,326.76,-16.13],
|
|
[107608,5,326.934,-30.9],[108085,3,328.482,-37.36],[108661,5.4,330.209,-28.45],[109074,3,331.446,-0.32],
|
|
[109111,4.5,331.529,-39.54],[109139,4.3,331.609,-13.87],[109176,3.8,331.753,25.35],[109268,1.7,332.058,-46.96],
|
|
[109352,5.6,332.307,33.17],[109422,4.9,332.537,-32.55],[109427,3.5,332.55,6.2],[109492,3.4,332.714,58.2],
|
|
[109937,4.1,333.992,37.75],[110003,4.2,334.208,-7.78],[110130,2.9,334.625,-60.26],[110395,3.9,335.414,-1.39],
|
|
[110538,4.4,335.89,52.23],[110609,4.5,336.129,49.48],[110960,3.6,337.208,-0.02],[110997,4,337.317,-43.5],
|
|
[111022,4.3,337.383,47.71],[111104,4.5,337.622,43.12],[111123,4.8,337.662,-10.68],[111169,3.8,337.823,50.28],
|
|
[111188,4.3,337.876,-32.35],[111497,4,338.839,-0.12],[111954,4.2,340.164,-27.04],[112029,3.4,340.366,10.83],
|
|
[112122,2.1,340.667,-46.88],[112158,2.9,340.751,30.22],[112405,4.1,341.515,-81.38],[112440,4,341.633,23.57],
|
|
[112447,4.2,341.673,12.17],[112623,3.5,342.139,-51.32],[112716,4,342.398,-13.59],[112724,3.5,342.42,66.2],
|
|
[112748,3.5,342.501,24.6],[112961,3.7,343.154,-7.58],[113136,3.3,343.663,-15.82],[113246,4.2,343.987,-32.54],
|
|
[113368,1.2,344.413,-29.62],[113638,4.1,345.22,-52.75],[113726,3.6,345.48,42.33],[113881,2.4,345.944,28.08],
|
|
[113963,2.5,346.19,15.21],[114131,4.3,346.72,-43.52],[114341,3.7,347.362,-21.17],[114421,3.9,347.59,-45.25],
|
|
[114855,4.2,348.973,-9.09],[114971,3.7,349.291,3.28],[114996,4,349.357,-58.24],[115102,4.4,349.706,-32.53],
|
|
[115438,4,350.743,-20.1],[115738,5,351.733,1.26],[115830,4.3,351.992,6.38],[116231,4.4,353.243,-37.82],
|
|
[116584,3.8,354.391,46.46],[116727,3.2,354.837,77.63],[116771,4.1,354.988,5.63],[116928,4.5,355.512,1.78],
|
|
[118268,4,359.828,6.86]]);
|
|
|
|
// Data for star names to display (if showstarlabels is set to true) - indexed by Hipparcos number
|
|
this.starnames = {};
|
|
|
|
// Identify the default base directory
|
|
this.dir = $('script[src*=virtualsky]').attr('src').match(/^.*\//); // the JS file path
|
|
this.dir = this.dir && this.dir[0] || ""; // set dir to match or ""
|
|
|
|
// Define extra files (JSON/JS)
|
|
this.file = {
|
|
stars: this.dir+"stars.json", // Data for faint stars - 54 kB
|
|
lines: this.dir+"lines_latin.json", // Data for constellation lines - 12 kB
|
|
boundaries: this.dir+"boundaries.json", // Data for constellation boundaries - 20 kB
|
|
showers: this.dir+"showers.json", // Data for meteor showers - 4 kB
|
|
galaxy: this.dir+"galaxy.json", // Data for milky way - 12 kB
|
|
planets: this.dir+"virtualsky-planets.js" // Plugin for planet ephemeris - 12kB
|
|
}
|
|
|
|
this.hipparcos = {}; // Define our star catalogue
|
|
this.updateClock(new Date()); // Define the 'current' time
|
|
this.fullsky = false; // Are we showing the entire sky?
|
|
|
|
// Define the colours that we will use
|
|
this.colours = {
|
|
'normal' : {
|
|
'txt' : "rgb(255,255,255)",
|
|
'black':"rgb(0,0,0)",
|
|
'white':"rgb(255,255,255)",
|
|
'grey':"rgb(100,100,100)",
|
|
'stars':'rgb(255,255,255)',
|
|
'sun':'rgb(255,215,0)',
|
|
'moon':'rgb(150,150,150)',
|
|
'cardinal':'rgba(163,228,255, 1)',
|
|
'constellation':"rgba(180,180,255,0.8)",
|
|
'constellationboundary':"rgba(255,255,100,0.6)",
|
|
'showers':"rgba(100,255,100,0.8)",
|
|
'galaxy':"rgba(100,200,255,0.5)",
|
|
'az':"rgba(100,100,255,0.4)",
|
|
'eq':"rgba(255,100,100,0.4)",
|
|
'ec':'rgba(255,0,0,0.4)',
|
|
'gal':'rgba(100,200,255,0.4)',
|
|
'meridian':'rgba(25,255,0,0.4)',
|
|
'pointers':'rgb(200,200,200)'
|
|
},
|
|
'negative':{
|
|
'txt' : "rgb(0,0,0)",
|
|
'black':"rgb(0,0,0)",
|
|
'white':"rgb(255,255,255)",
|
|
'grey':"rgb(100,100,100)",
|
|
'stars':'rgb(0,0,0)',
|
|
'sun':'rgb(0,0,0)',
|
|
'moon':'rgb(0,0,0)',
|
|
'cardinal':'rgba(0,0,0,1)',
|
|
'constellation':"rgba(0,0,0,0.8)",
|
|
'constellationboundary':"rgba(0,0,0,0.6)",
|
|
"showers":"rgba(0,0,0,0.8)",
|
|
'galaxy':"rgba(0,0,0,0.5)",
|
|
'az':"rgba(0,0,255,0.6)",
|
|
'eq':"rgba(255,100,100,0.8)",
|
|
'ec':'rgba(255,0,0,0.6)',
|
|
'gal':'rgba(100,200,255,0.8)',
|
|
'meridian':'rgba(0,255,0,0.6)'
|
|
}
|
|
};
|
|
|
|
// Keep a copy of the inputs
|
|
this.input = input;
|
|
|
|
// Overwrite our defaults with input values
|
|
this.init(input);
|
|
|
|
// Country codes at http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
|
|
this.language = (typeof this.q.lang==="string") ? this.q.lang : (typeof this.setlang==="string" ? this.setlang : (navigator) ? (navigator.userLanguage||navigator.systemLanguage||navigator.language||browser.language) : "");
|
|
var fromqs = (typeof this.q.lang==="string" || typeof this.setlang==="string");
|
|
this.langs = {
|
|
'ar': { "language": {"name": "العربية","alignment": "right" } },
|
|
'cs': { "language": {"name": "Čeština","alignment": "left" } },
|
|
'en': { "language": {"name": "English","alignment": "left" } },
|
|
'es': { "language": {"name": "Español","alignment": "left" } },
|
|
'fr': { "language": {"name": "Français","alignment": "left" } },
|
|
'it': { "language": {"name": "Italiano","alignment": "left" } },
|
|
'pt': { "language": {"name": "Português","alignment": "left" } },
|
|
}; // The contents of the language will be loaded from the JSON language file
|
|
this.lang = this.langs['en']; // default
|
|
|
|
if(typeof this.polartype=="undefined") this.selectProjection('polar'); // Set the default projection
|
|
|
|
// Update the colours
|
|
this.updateColours();
|
|
|
|
// Load the language file
|
|
this.loadLanguage(this.language,'',fromqs);
|
|
|
|
// Define some VirtualSky styles
|
|
var v,a,b,r,s,p,k,c;
|
|
v = '.virtualsky';
|
|
a = '#f0f0f0';
|
|
b = '#fcfcfc';
|
|
k = 'background';
|
|
c = k+'-color';
|
|
p = 'padding';
|
|
bs = 'box-shadow:0px 0px 20px rgba(255,255,255,0.5);';
|
|
function br(i){ return 'border-radius:'+i+';-moz-border-radius:'+i+';-webkit-border-radius:'+i+';';}
|
|
r = br('0.5em');
|
|
s = br('3px');
|
|
$('<style type="text/css">'+
|
|
v+'_help { '+p+':10px;'+c+':white;'+r+'} '+
|
|
v+'_help ul { list-style:none;margin:0px;'+p+':0px; } '+
|
|
v+'_infobox { '+c+':'+a+';color:black;'+p+':5px;'+r+bs+'} '+
|
|
v+'_infobox img {} '+
|
|
v+'_infocredit {color:white;float:left;font-size:0.8em;'+p+':5px;position:absolute;} '+
|
|
v+'form { position:absolute;z-index:20;display:block;overflow:hidden;'+c+':#ddd;'+p+':10px;'+bs+r+' } '+
|
|
v+'_dismiss { float:right;'+p+': 0 5px 0 5px;margin:0px;font-weight:bold;cursor:pointer;color:black;margin-right:-5px;margin-top:-5px; } '+
|
|
v+'form input,'+v+'form .divider { display:inline-block;font-size:1em;text-align:center;margin-right:2px; } '+v+'form .divider { margin-top: 5px; '+p+': 2px;} '+
|
|
v+'_help_key:active{ '+k+':#e9e9e9; } '+
|
|
v+'_help_key:hover{ border-color: #b0b0b0; } '+
|
|
v+'_help_key { cursor:pointer;display:inline-block;text-align:center;'+
|
|
k+':'+a+';'+k+':-moz-linear-gradient(top,'+a+','+b+');'+
|
|
k+':-webkit-gradient(linear,center top,center bottom,from('+a+'),to('+b+'));'+
|
|
s+'-webkit-'+k+'-clip:'+p+'-box;-moz-'+k+'-clip:'+p+';'+k+'-clip:'+
|
|
p+'-box;color:#303030;border:1px solid #e0e0e0;border-bottom-width:2px;white-space:nowrap;font-family:monospace'+
|
|
';'+p+':1px 6px;font-size:1.1em;}</style>').appendTo("head");
|
|
|
|
this.pointers = []; // Define an empty list of pointers/markers
|
|
|
|
// Internal variables
|
|
this.dragging = false;
|
|
this.x = "";
|
|
this.y = "";
|
|
this.theta = 0;
|
|
this.skygrad;
|
|
this.infobox = "virtualsky_infobox";
|
|
this.container = '';
|
|
this.times = this.astronomicalTimes();
|
|
if(this.id) this.createSky();
|
|
|
|
// Find out where the Sun and Moon are
|
|
p = this.moonPos(this.times.JD);
|
|
this.moon = p.moon;
|
|
this.sun = p.sun;
|
|
|
|
if(this.islive) interval = window.setInterval(function(sky){ sky.setClock('now'); },1000,this);
|
|
|
|
return this;
|
|
}
|
|
|
|
VirtualSky.prototype.init = function(d){
|
|
if(!d) return this;
|
|
var q = location.search;
|
|
|
|
if(q && q != '#'){
|
|
var bits = q.replace(/^\?|\&$/g,'').split('&'); // remove the leading ? and trailing &
|
|
var key,val;
|
|
for(var i = 0; i < bits.length ; i++){
|
|
key = bits[i].split('=')[0], val = bits[i].split('=')[1];
|
|
// convert floats
|
|
if(/^-?[0-9.]+$/.test(val)) val = parseFloat(val);
|
|
if(val == "true") val = true;
|
|
if(val == "false") val = false;
|
|
// apply only first key occurency
|
|
if(d[key]===undefined) d[key] = val;
|
|
}
|
|
}
|
|
var n = "number";
|
|
var s = "string";
|
|
var b = "boolean";
|
|
var o = "object";
|
|
var f = "function";
|
|
|
|
|
|
// Overwrite defaults with variables passed to the function
|
|
// directly mapped variables
|
|
var pairs = {
|
|
id: s,
|
|
gradient: b,
|
|
cardinalpoints: b,
|
|
negative: b,
|
|
meteorshowers: b,
|
|
showstars: b,
|
|
scalestars: n,
|
|
showstarlabels: b,
|
|
starnames: o,
|
|
showplanets: b,
|
|
showplanetlabels: b,
|
|
showorbits: b,
|
|
showgalaxy: b,
|
|
showdate: b,
|
|
showposition: b,
|
|
keyboard: b,
|
|
mouse: b,
|
|
ground: b,
|
|
ecliptic: b,
|
|
meridian: b,
|
|
magnitude: n,
|
|
clock: o,
|
|
background: s,
|
|
color: s,
|
|
fov: n,
|
|
objects: s,
|
|
base: s,
|
|
fullscreen: b,
|
|
credit: b,
|
|
transparent: b,
|
|
plugins: o,
|
|
lang: s
|
|
}
|
|
for(key in pairs)
|
|
if(is(d[key], pairs[key]))
|
|
this[key] = d[key];
|
|
|
|
// Undirectly paired values
|
|
if(is(d.projection,s)) this.selectProjection(d.projection);
|
|
if(is(d.constellations,b)) this.constellation.lines = d.constellations;
|
|
if(is(d.constellationboundaries,b)) this.constellation.boundaries = d.constellationboundaries;
|
|
if(is(d.constellationlabels,b)) this.constellation.labels = d.constellationlabels;
|
|
if(is(d.gridlines_az,b)) this.grid.az = d.gridlines_az;
|
|
if(is(d.gridlines_eq,b)) this.grid.eq = d.gridlines_eq;
|
|
if(is(d.gridlines_gal,b)) this.grid.gal = d.gridlines_gal;
|
|
if(is(d.gridstep,n)) this.grid.step = d.gridstep;
|
|
if(is(d.longitude,n)) this.setLongitude(d.longitude);
|
|
if(is(d.latitude,n)) this.setLatitude(d.latitude);
|
|
if(is(d.clock,s)) this.updateClock(new Date(d.clock.replace(/%20/g,' ')));
|
|
if(is(d.az,n)) this.az_off = (d.az%360)-180;
|
|
if(is(d.ra,n)) this.setRA(d.ra);
|
|
if(is(d.dec,n)) this.setDec(d.dec);
|
|
if(is(d.planets,s)) this.file.planets = d.planets;
|
|
if(is(d.planets,o)) this.planets = d.planets;
|
|
if(is(d.lines,s)) this.file.lines = d.lines;
|
|
if(is(d.lines,o)) this.lines = d.lines;
|
|
if(is(d.boundaries,s)) this.file.boundaries = d.boundaries;
|
|
if(is(d.boundaries,o)) this.boundaries = d.boundaries;
|
|
if(is(d.width,n)) this.wide = d.width;
|
|
if(is(d.height,n)) this.tall = d.height;
|
|
if(is(d.live,b)) this.islive = d.live;
|
|
if(is(d.lang,s) && d.lang.length==2) this.language = d.lang;
|
|
if(is(d.fontfamily,s)) this.fntfam = d.fontfamily.replace(/%20/g,' ');
|
|
if(is(d.fontsize,s)) this.fntsze = d.fontsize;
|
|
if(is(d.lang,s)) this.setlang = d.lang;
|
|
if(is(d.callback,o)){
|
|
if(is(d.callback.geo,f)) this.callback.geo = d.callback.geo;
|
|
if(is(d.callback.mouseenter,f)) this.callback.mouseenter = d.callback.mouseenter;
|
|
if(is(d.callback.mouseout,f)) this.callback.mouseout = d.callback.mouseout;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
// Load the specified language
|
|
// If it fails and this was the long variation of the language (e.g. "en-gb" or "zh-yue"), try the short version (e.g. "en" or "zh")
|
|
VirtualSky.prototype.loadLanguage = function(l,fn,fromquerystring){
|
|
l = l || this.language;
|
|
var lang = "";
|
|
if(this.langs[l]) lang = l;
|
|
if(!lang){
|
|
// Try loading a short version of the language code
|
|
l = (l.indexOf('-') > 0 ? l.substring(0,l.indexOf('-')) : l.substring(0,2));
|
|
if(fromquerystring){
|
|
// If it was set in the query string we try it
|
|
lang = l;
|
|
}else{
|
|
// If it was just from the browser settings, we'll limit to known translations
|
|
if(this.langs[l]) lang = l;
|
|
}
|
|
}
|
|
l = lang;
|
|
if(!l) l = "en"; // Use English as a default if we haven't got a language here
|
|
var url = this.langurl.replace('%LANG%',l);
|
|
this.loadJSON(
|
|
url,
|
|
function(data){
|
|
this.langcode = l;
|
|
this.langs[l] = data;
|
|
this.langs[l].loaded = true;
|
|
|
|
// Update any starnames
|
|
if(data.starnames) for(var n in data.starnames) this.starnames[n] = data.starnames[n];
|
|
|
|
this.changeLanguage(l);
|
|
if(typeof fn==="function") fn.call(this);
|
|
},
|
|
function(data){ },
|
|
function(e){
|
|
// If we tried to load the short version of the language and it failed, default to English
|
|
this.loadLanguage('en',fn);
|
|
}
|
|
);
|
|
return this;
|
|
}
|
|
// Change the active language
|
|
VirtualSky.prototype.changeLanguage = function(code,fn){
|
|
if(this.langs[code]){
|
|
if(!this.langs[code].loaded) this.loadLanguage(code,fn);
|
|
else {
|
|
this.lang = this.langs[code];
|
|
this.langcode = code;
|
|
this.draw();
|
|
if(typeof fn==="function") fn.call(this);
|
|
}
|
|
return this;
|
|
}
|
|
this.lang = this.langs['en'];
|
|
return this;
|
|
}
|
|
VirtualSky.prototype.htmlDecode = function(input){
|
|
if(!input) return "";
|
|
var e = document.createElement('div');
|
|
e.innerHTML = input;
|
|
return e.childNodes[0].nodeValue;
|
|
}
|
|
VirtualSky.prototype.getPhrase = function(key,key2){
|
|
if(key===undefined) return undefined;
|
|
if(key==="constellations"){
|
|
if(key2 && is(this.lang.constellations[key2],"string"))
|
|
return this.htmlDecode(this.lang.constellations[key2]);
|
|
}else if(key==="planets"){
|
|
if(this.lang.planets && this.lang.planets[key2]) return this.htmlDecode(this.lang.planets[key2]);
|
|
else return this.htmlDecode(this.lang[key2]);
|
|
}else return this.htmlDecode(this.lang[key]) || this.htmlDecode(this.langs['en'][key]) || "";
|
|
}
|
|
VirtualSky.prototype.resize = function(w,h){
|
|
if(!this.canvas) return;
|
|
if(!w || !h){
|
|
if(this.fullscreen){
|
|
this.canvas.css({'width':0,'height':0});
|
|
w = $(window).width();
|
|
h = $(window).height();
|
|
this.canvas.css({'width':w,'height':h});
|
|
$(document).css({'width':w,'height':h});
|
|
}else{
|
|
// We have to zap the width of the canvas to let it take the width of the container
|
|
this.canvas.css({'width':0,'height':0});
|
|
w = this.container.outerWidth();
|
|
h = this.container.outerHeight();
|
|
this.canvas.css({'width':w,'height':h});
|
|
}
|
|
}
|
|
if(w == this.wide && h == this.tall) return;
|
|
this.setWH(w,h);
|
|
this.positionCredit();
|
|
this.updateSkyGradient();
|
|
this.draw();
|
|
}
|
|
VirtualSky.prototype.setWH = function(w,h){
|
|
if(!w || !h) return;
|
|
this.c.width = w;
|
|
this.c.height = h;
|
|
this.wide = w;
|
|
this.tall = h;
|
|
this.changeFOV();
|
|
// Bug fix for IE 8 which sets a width of zero to a div within the <canvas>
|
|
if(this.ie && $.browser.version == 8) $('#'+this.idinner).find('div').css({'width':w,'height':h});
|
|
this.canvas.css({'width':w,'height':h});
|
|
}
|
|
VirtualSky.prototype.changeFOV = function(delta){
|
|
var fov = this.fov;
|
|
if(delta > 0) fov /= 1.2;
|
|
else if(delta < 0) fov *= 1.2;
|
|
return this.setFOV(fov);
|
|
}
|
|
VirtualSky.prototype.setFOV = function(fov){
|
|
if(fov > 60 || typeof fov!=="number") this.fov = 60;
|
|
else if(fov < 1) this.fov = 1;
|
|
else this.fov = fov;
|
|
this.maxangle = this.d2r*this.fov*Math.max(this.wide,this.tall)/this.tall;
|
|
this.maxangle = Math.min(this.maxangle,Math.PI/2)
|
|
return this;
|
|
}
|
|
// Some pseudo-jQuery
|
|
VirtualSky.prototype.hide = function(){ this.container.hide(); return this; }
|
|
VirtualSky.prototype.show = function(){ this.container.show(); return this; }
|
|
VirtualSky.prototype.toggle = function(){ this.container.toggle(); return this; }
|
|
// Our stars are stored in decimal degrees so we will convert them here
|
|
VirtualSky.prototype.convertStarsToRadians = function(stars){
|
|
for(var i = 0; i < stars.length; i++){
|
|
stars[i][2] *= this.d2r;
|
|
stars[i][3] *= this.d2r;
|
|
}
|
|
return stars;
|
|
}
|
|
VirtualSky.prototype.load = function(t,file,fn){
|
|
return this.loadJSON(file,function(data){
|
|
if(t=="stars"){ this.starsdeep = true; this.stars = this.stars.concat(this.convertStarsToRadians(data.stars));}
|
|
else{ this[t] = data[t]; }
|
|
this.draw();
|
|
this.trigger("loaded"+(t.charAt(0).toUpperCase() + t.slice(1)),{data:data});
|
|
},fn);
|
|
}
|
|
VirtualSky.prototype.loadJSON = function(file,callback,complete,error){
|
|
if(typeof file!=="string") return this;
|
|
var dt = file.match(/\.json$/i) ? "json" : "script";
|
|
if(dt=="script"){
|
|
// If we are loading an external script we need to make sure we initiate
|
|
// it first. To do that we will re-write the callback that was provided.
|
|
var tmp = callback;
|
|
callback = function(data){
|
|
// Initialize any plugins
|
|
for (var i = 0; i < this.plugins.length; ++i){
|
|
if(typeof this.plugins[i].init=="function") this.plugins[i].init.call(this);
|
|
}
|
|
tmp.call(this,data);
|
|
};
|
|
}
|
|
var config = {
|
|
dataType: dt,
|
|
url: this.base+file,
|
|
context: this,
|
|
success: callback,
|
|
complete: complete || function(){},
|
|
error: error || function(){}
|
|
};
|
|
if(dt=="json") config.jsonp = 'onJSONPLoad';
|
|
if(dt=="script") config.cache = true; // Use a cached version
|
|
$.ajax(config);
|
|
return this;
|
|
}
|
|
|
|
VirtualSky.prototype.createSky = function(){
|
|
this.container = $('#'+this.id);
|
|
this.container.addTouch();
|
|
this.times = this.astronomicalTimes();
|
|
|
|
if(this.fntfam) this.container.css({'font-family':this.fntfam});
|
|
if(this.fntsze) this.container.css({'font-size':this.fntsze});
|
|
|
|
if(this.container.length == 0){
|
|
// No appropriate container exists. So we'll make one.
|
|
$('body').append('<div id="'+this.id+'"></div>');
|
|
this.container = $('#'+this.id);
|
|
}
|
|
this.container.css('position','relative');
|
|
$(window).resize({me:this},function(e){ e.data.me.resize(); });
|
|
|
|
// Get the planet data
|
|
if(!this.planets) this.load('planets',this.file.planets);
|
|
|
|
// Get the constellation line data
|
|
if(!this.lines) this.load('lines',this.file.lines);
|
|
|
|
// Get the constellation line data
|
|
if(!this.boundaries) this.load('boundaries',this.file.boundaries);
|
|
|
|
// Get the meteor showers
|
|
if(!this.showers) this.load('showers',this.file.showers);
|
|
|
|
// Get the Milky Way
|
|
if(!this.galaxy) this.load('galaxy',this.file.galaxy);
|
|
|
|
// Get the faint star data
|
|
this.changeMagnitude(0);
|
|
|
|
// Add named objects to the display
|
|
if(this.objects){
|
|
// To stop lookUp being hammered, we'll only lookup a maximum of 5 objects
|
|
// If you need more objects (e.g. for the Messier catalogue) build a JSON
|
|
// file containing all the results one time only.
|
|
var ob = this.objects.split(';');
|
|
|
|
// Build the array of JSON requests
|
|
for(var o = 0; o < ob.length ; o++) ob[o] = ((ob[o].search(/\.json$/) >= 0) ? {'url':ob[o], 'src':'file', 'type':'json' } : {'url': 'http://www.strudel.org.uk/lookUP/json/?name='+ob[o],'src':'lookup','type':'jsonp'});
|
|
|
|
// Loop over the requests
|
|
var lookups = 0;
|
|
var ok = true;
|
|
for(var o = 0; o < ob.length ; o++){
|
|
if(ob[o].src == "lookup") lookups++;
|
|
if(lookups > 5) ok = false;
|
|
if(ok || ob[o].src != "lookup"){
|
|
$.ajax({ dataType: ob[o].type, url: ob[o].url, context: this, success: function(data){
|
|
// If we don't have a length property, we only have one result so make it an array
|
|
if(typeof data.length === "undefined") data = [data];
|
|
// Loop over the array of objects
|
|
for(var i = 0; i < data.length ; i++){
|
|
// The object needs an RA and Declination
|
|
if(data[i] && data[i].dec && data[i].ra){
|
|
this.addPointer({
|
|
ra: data[i].ra.decimal,
|
|
dec: data[i].dec.decimal,
|
|
label: data[i].target.name,
|
|
colour: this.col.pointers
|
|
});
|
|
}
|
|
// Update the sky with all the points we've added
|
|
this.draw();
|
|
}
|
|
}});
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the Javascript function has been passed a width/height
|
|
// those take precedence over the CSS-set values
|
|
if(this.wide > 0)
|
|
this.container.css('width',this.wide);
|
|
this.wide = this.container.width();
|
|
if(this.tall > 0)
|
|
this.container.css('height',this.tall);
|
|
this.tall = this.container.height();
|
|
|
|
// Add a <canvas> to it with the original ID
|
|
this.idinner = this.id+'_inner';
|
|
this.container.html('<canvas id="'+this.idinner+'" style="display:block;"></canvas>');
|
|
this.canvas = $('#'+this.idinner);
|
|
this.c = document.getElementById(this.idinner);
|
|
// For excanvas we need to initialise the newly created <canvas>
|
|
if(this.excanvas)
|
|
this.c = G_vmlCanvasManager.initElement(this.c);
|
|
|
|
if(this.c && this.c.getContext){
|
|
this.setWH(this.wide,this.tall);
|
|
var ctx = this.ctx = this.c.getContext('2d');
|
|
ctx.clearRect(0,0,this.wide,this.tall);
|
|
ctx.beginPath();
|
|
var fs = this.fontsize();
|
|
ctx.font = fs+"px Helvetica";
|
|
ctx.fillStyle = 'rgb(0,0,0)';
|
|
ctx.lineWidth = 1.5;
|
|
var loading = 'Loading sky...';
|
|
ctx.fillText(loading,(ctx.wide-ctx.measureText(loading).width)/2,(this.tall-fs)/2)
|
|
ctx.fill();
|
|
|
|
$("#"+this.idinner).on('click',{sky:this},function(e){
|
|
var x = e.pageX - $(this).offset().left - window.scrollX;
|
|
var y = e.pageY - $(this).offset().top - window.scrollY;
|
|
matched = e.data.sky.whichPointer(x,y);
|
|
e.data.sky.toggleInfoBox(matched);
|
|
if(matched >= 0) $(e.data.sky.canvas).css({cursor:'pointer'});
|
|
}).on('mousemove',{sky:this},function(e){
|
|
var s = e.data.sky;
|
|
// We don't need scrollX/scrollY as pageX/pageY seem to include this
|
|
var x = e.pageX - $(this).offset().left;
|
|
var y = e.pageY - $(this).offset().top;
|
|
var theta,f,dr;
|
|
if(s.mouse)
|
|
$(s.canvas).css({cursor:'move'});
|
|
if(s.dragging && s.mouse){
|
|
if(s.polartype){
|
|
theta = Math.atan2(y-s.tall/2,x-s.wide/2);
|
|
if(!s.theta) s.theta = theta;
|
|
s.az_off -= (s.theta-theta)*s.r2d;
|
|
s.theta = theta;
|
|
}else if(s.projection.id=="gnomic"){
|
|
f = 0.0015*(s.fov*s.d2r);
|
|
dr = 0;
|
|
if(typeof s.x=="number")
|
|
dr = Math.min(Math.abs(s.x-x)*f/(Math.cos(s.dc_off)),Math.PI/36);
|
|
if(typeof s.y=="number")
|
|
s.dc_off -= (s.y-y)*f;
|
|
s.ra_off -= (s.x-x > 0 ? 1 : -1)*dr;
|
|
s.dc_off = inrangeEl(s.dc_off);
|
|
}else{
|
|
if(typeof s.x=="number")
|
|
s.az_off += (s.x-x)/4;
|
|
}
|
|
s.az_off = s.az_off%360;
|
|
s.x = x;
|
|
s.y = y;
|
|
s.draw();
|
|
$(s.canvas).css({cursor:'-moz-grabbing'});
|
|
}else{
|
|
matched = s.whichPointer(x,y);
|
|
s.toggleInfoBox(matched);
|
|
}
|
|
}).on('mousedown',{sky:this},function(e){
|
|
e.data.sky.dragging = true;
|
|
}).on('mouseup',{sky:this},function(e){
|
|
var s = e.data.sky;
|
|
s.dragging = false;
|
|
s.x = "";
|
|
s.y = "";
|
|
s.theta = "";
|
|
}).on('mouseout',{sky:this},function(e){
|
|
var s = e.data.sky;
|
|
s.dragging = false;
|
|
s.mouseover = false;
|
|
s.x = "";
|
|
s.y = "";
|
|
if(typeof s.callback.mouseout=="function") s.callback.mouseout.call(s);
|
|
}).on('mouseenter',{sky:this},function(e){
|
|
var s = e.data.sky;
|
|
s.mouseover = true;
|
|
if(typeof s.callback.mouseenter=="function") s.callback.mouseenter.call(s);
|
|
}).on('mousewheel',{sky:this},function(e, delta) {
|
|
var s = e.data.sky;
|
|
if(s.mouse && s.projection.id=="gnomic"){
|
|
s.changeFOV(delta).draw();
|
|
return false;
|
|
}else return true;
|
|
});
|
|
$(document).bind('keypress',{sky:this},function(e){
|
|
if(!e) e = window.event;
|
|
var code = e.keyCode || e.charCode || e.which || 0;
|
|
e.data.sky.keypress(code,e);
|
|
});
|
|
}
|
|
|
|
this.registerKey('a',function(){ this.toggleAtmosphere(); },'atmos');
|
|
this.registerKey('g',function(){ this.toggleGround(); },'ground');
|
|
this.registerKey('h',function(){ this.cycleProjection(); },'projection');
|
|
this.registerKey('i',function(){ this.toggleNegative(); },'neg');
|
|
this.registerKey(',',function(){ this.toggleEcliptic(); },'ec');
|
|
this.registerKey(';',function(){ this.toggleMeridian(); },'meridian');
|
|
this.registerKey('e',function(){ this.toggleGridlinesEquatorial(); },'eq');
|
|
this.registerKey('z',function(){ this.toggleGridlinesAzimuthal(); },'az');
|
|
this.registerKey('m',function(){ this.toggleGridlinesGalactic(); },'gal');
|
|
this.registerKey('M',function(){ this.toggleGalaxy(); },'galaxy');
|
|
this.registerKey('q',function(){ this.toggleCardinalPoints(); },'cardinal');
|
|
this.registerKey('s',function(){ this.toggleStars(); },'stars');
|
|
this.registerKey('S',function(){ this.toggleStarLabels(); },'starlabels');
|
|
this.registerKey('u',function(){ this.togglePlanetLabels(); },'sollabels');
|
|
this.registerKey('p',function(){ this.togglePlanetHints(); },'sol');
|
|
this.registerKey('o',function(){ this.toggleOrbits(); },'orbits');
|
|
this.registerKey('c',function(){ this.toggleConstellationLines(); },'con');
|
|
this.registerKey('v',function(){ this.toggleConstellationLabels(); },'names');
|
|
this.registerKey('b',function(){ this.toggleConstellationBoundaries(); },'conbound');
|
|
this.registerKey('R',function(){ this.toggleMeteorShowers(); },'meteorshowers');
|
|
this.registerKey('1',function(){ this.toggleHelp(); });
|
|
this.registerKey('8',function(){ this.setClock('now').calendarUpdate(); },'reset');
|
|
this.registerKey('j',function(){ if(!this.islive) this.spinIt("down"); },'slow');
|
|
this.registerKey('k',function(){ this.spinIt(0) },'stop');
|
|
this.registerKey('l',function(){ if(!this.islive) this.spinIt("up"); },'fast');
|
|
this.registerKey('-',function(){ this.setClock(-86400).calendarUpdate(); },'subtractday');
|
|
this.registerKey('=',function(){ this.setClock(86400).calendarUpdate(); },'addday');
|
|
this.registerKey('[',function(){ this.setClock(-86400*7).calendarUpdate(); },'subtractweek');
|
|
this.registerKey(']',function(){ this.setClock(86400*7).calendarUpdate(); },'addweek');
|
|
this.registerKey(37,function(){ this.az_off -= 2; this.draw(); },'azleft'); // left
|
|
this.registerKey(39,function(){ this.az_off += 2; this.draw(); },'azright'); // right
|
|
this.registerKey(38,function(){ this.changeMagnitude(0.25); },'magup'); // up
|
|
this.registerKey(40,function(){ this.changeMagnitude(-0.25);},'magdown'); // down
|
|
this.registerKey(63,function(){ this.toggleHelp(); });
|
|
|
|
this.draw();
|
|
}
|
|
VirtualSky.prototype.changeMagnitude = function(m){
|
|
if(typeof m!=="number")
|
|
return this;
|
|
this.magnitude += m;
|
|
if(!this.starsdeep && this.magnitude > 4)
|
|
this.load('stars',this.file.stars);
|
|
else
|
|
this.draw();
|
|
return this;
|
|
}
|
|
VirtualSky.prototype.toggleHelp = function(){
|
|
var v = "virtualsky";
|
|
if($('.'+v+'_dismiss').length > 0) $('.'+v+'_dismiss').trigger('click');
|
|
else{
|
|
// Build the list of keyboard options
|
|
var o = '';
|
|
for(var i = 0; i < this.keys.length ; i++){
|
|
if(this.keys[i].txt)
|
|
o += '<li>'+
|
|
'<strong class="'+v+'_help_key '+v+'_'+this.keys[i].txt+'">'+this.keys[i].str+'</strong> → <a href="#" class="'+v+'_'+this.keys[i].txt+'" style="text-decoration:none;">'+this.getPhrase(this.keys[i].txt)+'</a>'+
|
|
'</li>'; }
|
|
$('<div class="'+v+'_help">'+
|
|
'<div class="'+v+'_dismiss" title="'+this.getPhrase('close')+'">×</div>'+
|
|
'<span>'+this.getPhrase('keyboard')+'</span>'+
|
|
'<div class="'+v+'_helpinner"><ul></ul></div>'+
|
|
'</div>').appendTo(this.container);
|
|
|
|
var hlp = $('.'+v+'_help');
|
|
var h = hlp.outerHeight();
|
|
|
|
// Add the keyboard option list
|
|
hlp.find('ul').html(o);
|
|
|
|
// Set the maximum height for the list and add a scroll bar if necessary
|
|
$('.'+v+'_helpinner').css({'overflow':'auto','max-height':(this.tall-h)+'px'});
|
|
|
|
// Add the events for each keyboard option
|
|
for(var i = 0; i < this.keys.length ; i++){
|
|
if(this.keys[i].txt)
|
|
$('.'+v+'_'+this.keys[i].txt)
|
|
.on('click',{fn:this.keys[i].fn,me:this},function(e){
|
|
e.preventDefault(); e.data.fn.call(e.data.me);
|
|
});
|
|
}
|
|
|
|
// Create a lightbox
|
|
this.lightbox($('.'+v+'_help'));
|
|
|
|
$('.'+v+'_help, .'+v+'_bg').on('mouseout',{sky:this},function(e){ e.data.sky.mouseover = false; }).on('mouseenter',{sky:this},function(e){ e.data.sky.mouseover = true; });
|
|
}
|
|
}
|
|
// Register keyboard commands and associated functions
|
|
VirtualSky.prototype.registerKey = function(charCode,fn,txt){
|
|
if(!is(fn,"function")) return this;
|
|
if(!is(charCode,"object")) charCode = [charCode];
|
|
var aok, ch, c, i, alt, str;
|
|
for(c = 0 ; c < charCode.length ; c++){
|
|
alt = false;
|
|
if(typeof charCode[c]=="string"){
|
|
if(charCode[c].indexOf('alt')==0){
|
|
str = charCode[c];
|
|
alt = true;
|
|
charCode[c] = charCode[c].substring(4);
|
|
}else{
|
|
str = charCode[c];
|
|
}
|
|
ch = charCode[c].charCodeAt(0);
|
|
}else{
|
|
ch = charCode[c];
|
|
var arrows = {37:"left",38:"up",39:"right",40:"down"};
|
|
str = this.getPhrase(arrows[ch]) || String.fromCharCode(ch);
|
|
}
|
|
aok = true;
|
|
for(i = 0 ; i < this.keys.length ; i++){ if(this.keys.charCode == ch && this.keys.altKey == alt) aok = false; }
|
|
if(aok){
|
|
this.keys.push({
|
|
'str': str,
|
|
'charCode': ch,
|
|
'char': String.fromCharCode(ch),
|
|
'fn': fn,
|
|
'txt': txt,
|
|
'altKey': alt
|
|
});
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
// Work out if the keypress has a function that needs to be called.
|
|
VirtualSky.prototype.keypress = function(charCode,event){
|
|
if(!event) event = { altKey: false };
|
|
if(this.mouseover && this.keyboard){
|
|
for(var i = 0 ; i < this.keys.length ; i++){
|
|
if(this.keys[i].charCode == charCode && event.altKey == this.keys[i].altKey){
|
|
this.keys[i].fn.call(this,{event:event});
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
VirtualSky.prototype.whichPointer = function(x,y){
|
|
for(var i = 0 ; i < this.pointers.length ; i++)
|
|
if(Math.abs(x-this.pointers[i].x) < 5 && Math.abs(y-this.pointers[i].y) < 5)
|
|
return i
|
|
|
|
return -1;
|
|
}
|
|
VirtualSky.prototype.toggleInfoBox = function(i){
|
|
if(this.pointers.length == 0 || i >= this.pointers.length || (i>=0 && !this.pointers[i].html))
|
|
return this;
|
|
|
|
if($('#'+this.id+'_'+this.infobox).length <= 0)
|
|
this.container.append('<div id="'+this.id+'_'+this.infobox+'" class="'+this.infobox+'" style="display:none;"></div>');
|
|
var el = $('#'+this.id+'_'+this.infobox);
|
|
if(i >= 0 && this.isVisible(this.pointers[i].el) && this.pointers[i].x > 0 && this.pointers[i].y > 0 && this.pointers[i].x < this.wide && this.pointers[i].y < this.tall){
|
|
var offset = this.container.position();
|
|
el.html(this.pointers[i].html);
|
|
var x = this.pointers[i].x - Math.round(el.outerWidth()/2);
|
|
var y = this.pointers[i].y - Math.round(el.outerHeight()/2);
|
|
el.css({'position':'absolute',left:x,top:y,'z-index':10}).fadeIn("fast");
|
|
}else el.hide();
|
|
}
|
|
// compute horizon coordinates from utc, ra, dec
|
|
// ra, dec in radians
|
|
// lat, lon in degrees
|
|
// results returned in hrz_altitude, hrz_azimuth
|
|
VirtualSky.prototype.coord2horizon = function(ra, dec){
|
|
var ha, alt, az, sd, sl, cl;
|
|
// compute hour angle in degrees
|
|
ha = (Math.PI*this.times.LST/12) - ra;
|
|
sd = Math.sin(dec);
|
|
sl = Math.sin(this.latitude);
|
|
cl = Math.cos(this.latitude);
|
|
// compute altitude in radians
|
|
alt = Math.asin(sd*sl + Math.cos(dec)*cl*Math.cos(ha));
|
|
// compute azimuth in radians
|
|
// divide by zero error at poles or if alt = 90 deg (so we should've already limited to 89.9999)
|
|
az = Math.acos((sd - Math.sin(alt)*sl)/(Math.cos(alt)*cl));
|
|
// choose hemisphere
|
|
if (Math.sin(ha) > 0) az = 2*Math.PI - az;
|
|
return [alt,az];
|
|
}
|
|
function inrangeAz(a,deg){
|
|
if(deg){
|
|
while(a < 0) a += 360;
|
|
while(a > 360) a -= 360;
|
|
}else{
|
|
var twopi = (2*Math.PI);
|
|
while(a < 0) a += twopi;
|
|
while(a > twopi) a -= twopi;
|
|
}
|
|
return a;
|
|
}
|
|
function inrangeEl(a,deg){
|
|
if(deg){
|
|
if(a >= 90) a = 89.99999;
|
|
if(a <= -90) a = -89.99999;
|
|
}else{
|
|
if(a >= Math.PI/2) a = (Math.PI/2)*0.999999;
|
|
if(a <= -Math.PI/2) a = (-Math.PI/2)*0.999999;
|
|
}
|
|
return a;
|
|
}
|
|
VirtualSky.prototype.selectProjection = function(proj){
|
|
if(this.projections[proj]){
|
|
this.projection = this.projections[proj];
|
|
this.projection.id = proj;
|
|
this.fullsky = this.projection.fullsky == true;
|
|
this.polartype = this.projection.polartype == true;
|
|
|
|
// Set coordinate transforms
|
|
|
|
// Convert AZ,EL -> X,Y
|
|
// Inputs: az (rad), el (rad), width (px), height (px)
|
|
if(typeof this.projection.azel2xy==="function")
|
|
this.azel2xy = this.projection.azel2xy;
|
|
else this.azel2xy = function(az,el,w,h){
|
|
if(!w) w = this.wide;
|
|
if(!h) h = this.tall;
|
|
if(az < 0) az += 360;
|
|
return {x:-1,y:-1,el:-1};
|
|
}
|
|
|
|
// Convert AZ,EL -> RA,Dec
|
|
// Inputs: az (rad), el (rad)
|
|
// Output: { ra: ra (deg), dec: dec (deg) }
|
|
if(typeof this.projection.azel2radec==="function")
|
|
this.azel2radec = this.projection.azel2radec;
|
|
else this.azel2radec = function(az,el){
|
|
var xt,yt,r,l;
|
|
l = this.latitude;
|
|
xt = Math.asin( Math.sin(el) * Math.sin(l) + Math.cos(el) * Math.cos(l) * Math.cos(az) );
|
|
r = ( Math.sin(el) - Math.sin(l) * Math.sin(xt) ) / ( Math.cos(l) * Math.cos(xt) );
|
|
if(r > 1) r = 1;
|
|
yt = Math.acos(r);
|
|
if(Math.sin(az) > 0.0) yt = Math.PI*2 - yt;
|
|
xt *= this.r2d;
|
|
yt *= this.r2d;
|
|
yt = (this.times.LST*15 - yt + 360)%360.0;
|
|
return { ra: yt, dec: xt }
|
|
}
|
|
|
|
if(this.ctx)
|
|
this.updateSkyGradient();
|
|
|
|
this.updateColours();
|
|
|
|
// Draw update label
|
|
if(this.container){
|
|
var w = this.container.width();
|
|
var h = this.container.height();
|
|
var s = (this.lang.projections && this.lang.projections[proj]) ? this.lang.projections[proj] : this.projections[proj].title;
|
|
if($('.'+this.id+'_projection').length > 0) $('.'+this.id+'_projection').remove();
|
|
this.container.append('<div class="'+this.id+'_projection">'+s+'</div>');
|
|
$('.'+this.id+'_projection')
|
|
.on('mouseover',{me:this},function(e){e.data.me.mouseover = true;})
|
|
.css({
|
|
position:'absolute',
|
|
padding:0,
|
|
width:w+'px',
|
|
top:0,left:0,
|
|
'text-align':'center',
|
|
'line-height':h+'px',
|
|
zIndex:20,
|
|
fontSize:'1.5em',
|
|
display:'block',
|
|
overflow:'hidden',
|
|
backgroundColor:'transparent',
|
|
color:(this.negative ? this.col.black : this.col.white)})
|
|
.delay(500).fadeOut(1000,function(){ $(this).remove(); });
|
|
}
|
|
}
|
|
}
|
|
// Cycle through the map projections
|
|
VirtualSky.prototype.cycleProjection = function(){
|
|
var usenext = false;
|
|
var proj = this.projection.id;
|
|
var i = 0;
|
|
var firstkey;
|
|
for(var key in this.projections){
|
|
if(i==0) firstkey = key;
|
|
if(usenext){
|
|
proj = key;
|
|
break;
|
|
}
|
|
if(key == this.projection.id) usenext = true;
|
|
i++;
|
|
}
|
|
if(proj == this.projection.id) proj = firstkey;
|
|
this.draw(proj);
|
|
}
|
|
// Update the sky colours
|
|
VirtualSky.prototype.updateColours = function(){
|
|
// We need to make a copy of the correct colour palette otherwise it'll overwrite it
|
|
this.col = $.extend(true, {}, ((this.negative) ? this.colours.negative : this.colours.normal));
|
|
if(this.color==""){
|
|
if((this.polartype || this.projection.altlabeltext))
|
|
this.col.txt = this.col.grey;
|
|
}else{
|
|
this.col.txt = this.color;
|
|
}
|
|
}
|
|
|
|
VirtualSky.prototype.isVisible = function(el){
|
|
if(typeof this.projection.isVisible==="function") return this.projection.isVisible.call(el);
|
|
if(!this.fullsky) return (el > 0);
|
|
else return (this.ground) ? (el > 0) : true;
|
|
}
|
|
VirtualSky.prototype.isPointBad = function(p){
|
|
return p.x==-1 && p.y==-1;
|
|
}
|
|
// Return a structure with the Julian Date, Local Sidereal Time and Greenwich Sidereal Time
|
|
VirtualSky.prototype.astronomicalTimes = function(clock,lon){
|
|
clock = clock || this.clock;
|
|
lon = lon || this.longitude*this.r2d;
|
|
var JD,JD0,S,T,T0,UT,A,GST,d,LST;
|
|
JD = this.getJD(clock);
|
|
JD0 = Math.floor(JD-0.5)+0.5;
|
|
S = JD0-2451545.0;
|
|
T = S/36525.0;
|
|
T0 = (6.697374558 + (2400.051336*T) + (0.000025862*T*T))%24;
|
|
if(T0 < 0) T0 += 24;
|
|
UT = (((clock.getUTCMilliseconds()/1000 + clock.getUTCSeconds())/60) + clock.getUTCMinutes())/60 + clock.getUTCHours();
|
|
A = UT*1.002737909;
|
|
T0 += A;
|
|
GST = T0%24;
|
|
if(GST < 0) GST += 24;
|
|
d = (GST + lon/15.0)/24.0;
|
|
d = d - Math.floor(d);
|
|
if(d < 0) d += 1;
|
|
LST = 24.0*d;
|
|
return { GST:GST, LST:LST, JD:JD };
|
|
}
|
|
// Uses algorithm defined in Practical Astronomy (4th ed) by Peter Duffet-Smith and Jonathan Zwart
|
|
VirtualSky.prototype.moonPos = function(JD,sun){
|
|
var d2r,JD,sun,lo,Po,No,i,e,l,Mm,N,C,Ev,sinMo,Ae,A3,Mprimem,Ec,A4,lprime,V,lprimeprime,Nprime,lppNp,sinlppNp,y,x,lm,Bm;
|
|
d2r = this.d2r;
|
|
JD = JD || this.times.JD;
|
|
sun = sun || this.sunPos(JD);
|
|
lo = 91.929336; // Moon's mean longitude at epoch 2010.0
|
|
Po = 130.143076; // mean longitude of the perigee at epoch
|
|
No = 291.682547; // mean longitude of the node at the epoch
|
|
i = 5.145396; // inclination of Moon's orbit
|
|
e = 0.0549; // eccentricity of the Moon's orbit
|
|
l = (13.1763966*sun.D + lo)%360;
|
|
if(l < 0) l += 360;
|
|
Mm = (l - 0.1114041*sun.D - Po)%360;
|
|
if(Mm < 0) Mm += 360;
|
|
N = (No - 0.0529539*sun.D)%360;
|
|
if(N < 0) N += 360;
|
|
C = l-sun.lon;
|
|
Ev = 1.2739*Math.sin((2*C-Mm)*d2r);
|
|
sinMo = Math.sin(sun.Mo*d2r);
|
|
Ae = 0.1858*sinMo;
|
|
A3 = 0.37*sinMo;
|
|
Mprimem = Mm + Ev -Ae - A3;
|
|
Ec = 6.2886*Math.sin(Mprimem*d2r);
|
|
A4 = 0.214*Math.sin(2*Mprimem*d2r);
|
|
lprime = l + Ev + Ec -Ae + A4;
|
|
V = 0.6583*Math.sin(2*(lprime-sun.lon)*d2r);
|
|
lprimeprime = lprime + V;
|
|
Nprime = N - 0.16*sinMo;
|
|
lppNp = (lprimeprime-Nprime)*d2r;
|
|
sinlppNp = Math.sin(lppNp);
|
|
y = sinlppNp*Math.cos(i*d2r);
|
|
x = Math.cos(lppNp);
|
|
lm = Math.atan2(y,x)/d2r + Nprime;
|
|
Bm = Math.asin(sinlppNp*Math.sin(i*d2r))/d2r;
|
|
if(lm > 360) lm -= 360;
|
|
return { moon: {lon:lm,lat:Bm}, sun:sun };
|
|
}
|
|
// Uses algorithm defined in Practical Astronomy (4th ed) by Peter Duffet-Smith and Jonathan Zwart
|
|
VirtualSky.prototype.sunPos = function(JD){
|
|
var D,eg,wg,e,N,Mo,v,lon,lat;
|
|
D = (JD-2455196.5); // Number of days since the epoch of 2010 January 0.0
|
|
// Calculated for epoch 2010.0. If T is the number of Julian centuries since 1900 January 0.5 = (JD-2415020.0)/36525
|
|
eg = 279.557208; // mean ecliptic longitude in degrees = (279.6966778 + 36000.76892*T + 0.0003025*T*T)%360;
|
|
wg = 283.112438; // longitude of the Sun at perigee in degrees = 281.2208444 + 1.719175*T + 0.000452778*T*T;
|
|
e = 0.016705; // eccentricity of the Sun-Earth orbit in degrees = 0.01675104 - 0.0000418*T - 0.000000126*T*T;
|
|
N = ((360/365.242191)*D)%360;
|
|
if(N < 0) N += 360;
|
|
Mo = (N + eg - wg)%360 // mean anomaly in degrees
|
|
if(Mo < 0) Mo += 360;
|
|
v = Mo + (360/Math.PI)*e*Math.sin(Mo*Math.PI/180);
|
|
lon = v + wg;
|
|
if(lon > 360) lon -= 360;
|
|
lat = 0;
|
|
return {lat:lat,lon:lon,Mo:Mo,D:D,N:N}
|
|
}
|
|
// Input is Julian Date
|
|
// Uses method defined in Practical Astronomy (4th ed) by Peter Duffet-Smith and Jonathan Zwart
|
|
VirtualSky.prototype.meanObliquity = function(JD){
|
|
if(!JD) JD = this.times.JD;
|
|
var T,T2,T3;
|
|
T = (JD-2451545.0)/36525 // centuries since 2451545.0 (2000 January 1.5)
|
|
T2 = T*T;
|
|
T3 = T2*T;
|
|
return (23.4392917 - 0.0130041667*T - 0.00000016667*T2 + 0.0000005027778*T3)*this.d2r;
|
|
}
|
|
// Take input in radians, decimal Sidereal Time and decimal latitude
|
|
// Uses method defined in Practical Astronomy (4th ed) by Peter Duffet-Smith and Jonathan Zwart
|
|
VirtualSky.prototype.ecliptic2azel = function(l,b,LST,lat){
|
|
if(!LST){
|
|
this.times = this.astronomicalTimes();
|
|
LST = this.times.LST;
|
|
}
|
|
if(!lat) lat = this.latitude
|
|
var sl,cl,sb,cb,v,e,ce,se,Cprime,s,ST,cST,sST,B,r,sphi,cphi,A,w,theta,psi;
|
|
sl = Math.sin(l);
|
|
cl = Math.cos(l);
|
|
sb = Math.sin(b);
|
|
cb = Math.cos(b);
|
|
v = [cl*cb,sl*cb,sb];
|
|
e = this.meanObliquity();
|
|
ce = Math.cos(e);
|
|
se = Math.sin(e);
|
|
Cprime = [[1.0,0.0,0.0],[0.0,ce,-se],[0.0,se,ce]];
|
|
s = this.vectorMultiply(Cprime,v);
|
|
ST = LST*15*this.d2r;
|
|
cST = Math.cos(ST);
|
|
sST = Math.sin(ST);
|
|
B = [[cST,sST,0],[sST,-cST,0],[0,0,1]];
|
|
r = this.vectorMultiply(B,s);
|
|
sphi = Math.sin(lat);
|
|
cphi = Math.cos(lat);
|
|
A = [[-sphi,0,cphi],[0,-1,0],[cphi,0,sphi]];
|
|
w = this.vectorMultiply(A,r);
|
|
theta = Math.atan2(w[1],w[0]);
|
|
psi = Math.asin(w[2]);
|
|
return {az:theta,el:psi};
|
|
}
|
|
// Convert from ecliptic l,b -> RA,Dec
|
|
// Inputs: l (rad), b (rad), Julian date
|
|
VirtualSky.prototype.ecliptic2radec = function(l,b,JD){
|
|
var e = this.meanObliquity();
|
|
var sl = Math.sin(l);
|
|
var cl = Math.cos(l);
|
|
var sb = Math.sin(b);
|
|
var cb = Math.cos(b);
|
|
var tb = Math.tan(b);
|
|
var se = Math.sin(e);
|
|
var ce = Math.cos(e);
|
|
ra = Math.atan2((sl*ce - tb*se),(cl));
|
|
dec = Math.asin(sb*ce+cb*se*sl);
|
|
// Make sure RA is positive
|
|
if(ra < 0) ra += Math.PI+Math.PI;
|
|
return { ra:ra, dec:dec };
|
|
}
|
|
// Convert Ecliptic coordinates to x,y position
|
|
// Inputs: l (rad), b (rad), local sidereal time
|
|
// Returns [x, y (,elevation)]
|
|
VirtualSky.prototype.ecliptic2xy = function(l,b,LST){
|
|
LST = LST || this.times.LST;
|
|
if(typeof this.projection.ecliptic2xy==="function") return this.projection.ecliptic2xy.call(this,l,b,LST);
|
|
else{
|
|
if(this.fullsky){
|
|
var pos = this.ecliptic2radec(l,b);
|
|
return this.radec2xy(pos.ra,pos.dec);
|
|
}else{
|
|
var pos = this.ecliptic2azel(l,b,LST);
|
|
var el = pos.el*this.r2d;
|
|
pos = this.azel2xy(pos.az-(this.az_off*this.d2r),pos.el,this.wide,this.tall);
|
|
pos.el = el;
|
|
return pos;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Convert RA,Dec -> X,Y
|
|
// Inputs: RA (rad), Dec (rad)
|
|
// Returns [x, y (,elevation)]
|
|
VirtualSky.prototype.radec2xy = function(ra,dec){
|
|
if(typeof this.projection.radec2xy==="function") return this.projection.radec2xy.call(this,ra,dec);
|
|
else{
|
|
var coords = this.coord2horizon(ra, dec);
|
|
// Only return coordinates above the horizon
|
|
if(coords[0] > 0){
|
|
var pos = this.azel2xy(coords[1]-(this.az_off*this.d2r),coords[0],this.wide,this.tall);
|
|
return {x:pos.x,y:pos.y,az:coords[1]*this.r2d,el:coords[0]*this.r2d};
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Dummy function - overwritten in selectProjection
|
|
// Convert AZ,EL -> X,Y
|
|
// Inputs: az (degrees), el (degrees), width (px), height (px)
|
|
// Output: { x: x, y: y }
|
|
VirtualSky.prototype.azel2xy = function(az,el,w,h){ return {x:-1,y:-1}; }
|
|
|
|
// Dummy functions - overwritten in selectProjection
|
|
// Convert AZ,EL -> RA,Dec
|
|
// Inputs: az (rad), el (rad)
|
|
// Output: { ra: ra (deg), dec: dec (deg) }
|
|
VirtualSky.prototype.azel2radec = function(az,el){ return { ra: 0, dec: 0 }; }
|
|
|
|
// Convert Galactic -> x,y
|
|
// Inputs: longitude (rad), latitude (rad)
|
|
VirtualSky.prototype.gal2xy = function(l,b){
|
|
var pos = this.gal2radec(l,b);
|
|
return this.radec2xy(pos[0],pos[1]);
|
|
}
|
|
|
|
// Convert Galactic -> J2000
|
|
// Inputs: longitude (rad), latitude (rad)
|
|
VirtualSky.prototype.gal2radec = function(l,b){
|
|
// Using SLALIB values
|
|
return this.Transform([l,b], [-0.054875539726, 0.494109453312, -0.867666135858, -0.873437108010, -0.444829589425, -0.198076386122, -0.483834985808, 0.746982251810, 0.455983795705],false);
|
|
}
|
|
|
|
// Input is a two element position (degrees) and rotation matrix
|
|
// Output is a two element position (degrees)
|
|
VirtualSky.prototype.Transform = function(p, rot, indeg){
|
|
if(indeg){
|
|
p[0] *= this.d2r;
|
|
p[1] *= this.d2r;
|
|
}
|
|
var cp1 = Math.cos(p[1]);
|
|
var m = [Math.cos(p[0])*cp1, Math.sin(p[0])*cp1, Math.sin(p[1])];
|
|
var s = [m[0]*rot[0] + m[1]*rot[1] + m[2]*rot[2], m[0]*rot[3] + m[1]*rot[4] + m[2]*rot[5], m[0]*rot[6] + m[1]*rot[7] + m[2]*rot[8] ];
|
|
var r = Math.sqrt(s[0]*s[0] + s[1]*s[1] + s[2]*s[2]);
|
|
var b = Math.asin(s[2]/r); // Declination in range -90 -> +90
|
|
var cb = Math.cos(b);
|
|
var a = Math.atan2(((s[1]/r)/cb),((s[0]/r)/cb));
|
|
if (a < 0) a += Math.PI*2;
|
|
if(indeg) return [a*this.r2d,b*this.r2d];
|
|
else return [a,b];
|
|
}
|
|
// Convert from B1875 to J2000
|
|
// Using B = 1900.0 + (JD − 2415020.31352) / 365.242198781 and p73 Practical Astronomy With A Calculator
|
|
VirtualSky.prototype.fk1tofk5 = function(a,b){
|
|
// Convert from B1875 -> J2000
|
|
return this.Transform([a,b], [0.9995358730015703, -0.02793693620138929, -0.012147682028606801, 0.027936935758478665, 0.9996096732234282, -0.00016976035344812515, 0.012147683047201562, -0.00016968744936278707, 0.9999261997781408]);
|
|
}
|
|
VirtualSky.prototype.vectorMultiply = function(A,B){
|
|
if(B.length > 0){
|
|
// 2D (3x3)x(3x3) or 1D (3x3)x(3x1)
|
|
if(B[0].length > 0) return [[(A[0][0]*B[0][0]+A[0][1]*B[1][0]+A[0][2]*B[2][0]),(A[0][0]*B[0][1]+A[0][1]*B[1][1]+A[0][2]*B[2][1]),(A[0][0]*B[0][2]+A[0][1]*B[1][2]+A[0][2]*B[2][2])],
|
|
[(A[1][0]*B[0][0]+A[1][1]*B[1][0]+A[1][2]*B[2][0]),(A[1][0]*B[0][1]+A[1][1]*B[1][1]+A[1][2]*B[2][1]),(A[1][0]*B[0][2]+A[1][1]*B[1][2]+A[1][2]*B[2][2])],
|
|
[(A[2][0]*B[0][0]+A[2][1]*B[1][0]+A[2][2]*B[2][0]),(A[2][0]*B[0][1]+A[2][1]*B[1][1]+A[2][2]*B[2][1]),(A[2][0]*B[0][2]+A[2][1]*B[1][2]+A[2][2]*B[2][2])]];
|
|
else return [(A[0][0]*B[0] + A[0][1]*B[1] + A[0][2]*B[2]),(A[1][0]*B[0] + A[1][1]*B[1] + A[1][2]*B[2]),(A[2][0]*B[0] + A[2][1]*B[1] + A[2][2]*B[2])];
|
|
}
|
|
}
|
|
VirtualSky.prototype.setFont = function(){ this.ctx.font = this.fontsize()+"px "+this.canvas.css('font-family'); }
|
|
VirtualSky.prototype.fontsize = function(){
|
|
if(this.fntsze) return parseInt(this.fntsze);
|
|
var m = Math.min(this.wide,this.tall);
|
|
return (m < 600) ? ((m < 500) ? ((m < 350) ? ((m < 300) ? ((m < 250) ? 9 : 10) : 11) : 12) : 14) : parseInt(this.container.css('font-size'));
|
|
}
|
|
VirtualSky.prototype.positionCredit = function(){
|
|
this.container.find('.'+this.id+'_credit').css({position:'absolute',top:parseFloat(this.tall)-5-this.fontsize(),left:5});
|
|
}
|
|
VirtualSky.prototype.updateSkyGradient = function(){
|
|
var s = null;
|
|
if(this.ctx && this.hasGradient()){
|
|
if(this.projection.polartype){
|
|
if(typeof this.ctx.createRadialGradient==="function"){
|
|
s = this.ctx.createRadialGradient(this.wide/2,this.tall/2,0,this.wide/2,this.tall/2,this.tall/2);
|
|
s.addColorStop(0, 'rgba(0,0,0,1)');
|
|
s.addColorStop(0.7, 'rgba(0,0,0,0.2)');
|
|
s.addColorStop(1, 'rgba(0,50,80,0.3)');
|
|
}
|
|
}else{
|
|
s = this.ctx.createLinearGradient(0,0,0,this.tall);
|
|
s.addColorStop(0.0, 'rgba(0,30,50,0.1)');
|
|
s.addColorStop(0.7, 'rgba(0,30,50,0.35)');
|
|
s.addColorStop(1, 'rgba(0,50,80,0.6)');
|
|
}
|
|
}
|
|
this.skygrad = s;
|
|
return this;
|
|
}
|
|
VirtualSky.prototype.draw = function(proj){
|
|
|
|
// Don't bother drawing anything if there is no physical area to draw on
|
|
if(this.wide <= 0 || this.tall <= 0) return this;
|
|
if(!(this.c && this.c.getContext)) return this;
|
|
|
|
if(proj !== undefined) this.selectProjection(proj);
|
|
var white = this.col.white;
|
|
var black = this.col.black;
|
|
|
|
// Shorthands
|
|
var c = this.ctx;
|
|
var d = this.container;
|
|
|
|
c.moveTo(0,0);
|
|
c.clearRect(0,0,this.wide,this.tall);
|
|
c.fillStyle = (this.polartype || this.fullsky) ? this.background : ((this.negative) ? white : black);
|
|
c.fillRect(0,0,this.wide,this.tall);
|
|
c.fill();
|
|
|
|
if(this.polartype){
|
|
c.moveTo(this.wide/2,this.tall/2);
|
|
c.closePath();
|
|
c.beginPath();
|
|
c.arc(this.wide/2,this.tall/2,-0.5+this.tall/2,0,Math.PI*2,true);
|
|
c.closePath();
|
|
if(!this.transparent){
|
|
c.fillStyle = (this.hasGradient()) ? "rgba(0,15,30, 1)" : ((this.negative) ? white : black);
|
|
c.fill();
|
|
}
|
|
c.lineWidth = 0.5;
|
|
c.strokeStyle = black;
|
|
c.stroke();
|
|
}else if(typeof this.projection.draw==="function") this.projection.draw.call(this);
|
|
|
|
if(this.hasGradient()){
|
|
if(this.skygrad===undefined){
|
|
this.updateSkyGradient();
|
|
}else{
|
|
c.beginPath();
|
|
c.fillStyle = this.skygrad;
|
|
// draw shapes
|
|
if(this.projection.polartype){ c.arc(this.wide/2,this.tall/2,this.tall/2,0,2*Math.PI,false); c.fill(); }
|
|
else c.fillRect(0,0,this.wide,this.tall);
|
|
c.closePath();
|
|
}
|
|
}
|
|
|
|
this.drawGridlines("az")
|
|
.drawGridlines("eq")
|
|
.drawGridlines("gal")
|
|
.drawGalaxy()
|
|
.drawConstellationLines()
|
|
.drawConstellationBoundaries()
|
|
.drawStars()
|
|
.drawEcliptic()
|
|
.drawMeridian()
|
|
.drawPlanets()
|
|
.drawMeteorShowers()
|
|
.drawCardinalPoints();
|
|
|
|
for(var i = 0; i < this.pointers.length ; i++) this.highlight(i);
|
|
|
|
var txtcolour = (this.color!="") ? (this.color) : this.col.txt;
|
|
var fontsize = this.fontsize();
|
|
|
|
c.fillStyle = txtcolour;
|
|
c.lineWidth = 1.5;
|
|
this.setFont();
|
|
|
|
// Time line
|
|
if(this.showdate){
|
|
var clockstring = this.clock.toDateString()+' '+this.clock.toLocaleTimeString();
|
|
var metric_clock = this.drawText(clockstring,5,5+fontsize);
|
|
}
|
|
|
|
// Position line
|
|
if(this.showposition){
|
|
var positionstring = Math.abs(this.latitude*this.r2d).toFixed(2) + ((this.latitude>0) ? this.getPhrase('N') : this.getPhrase('S')) + ', ' + Math.abs(this.longitude*this.r2d).toFixed(2) + ((this.longitude>0) ? this.getPhrase('E') : this.getPhrase('W'));
|
|
var metric_pos = this.drawText(positionstring,5,5+fontsize+fontsize);
|
|
}
|
|
|
|
// Credit line
|
|
if(this.credit){
|
|
var credit = this.getPhrase('power');
|
|
var metric_credit = this.drawText(credit,5,this.tall-5);
|
|
// Float a transparent link on top of the credit text
|
|
if(d.find('.'+this.id+'_credit').length == 0) d.append('<div class="'+this.id+'_credit"><a href="http://virtualsky.lco.global/" target="_parent" title="Las Cumbres Observatory Global Telescope">'+this.getPhrase('powered')+'</a></div>');
|
|
d.find('.'+this.id+'_credit').css({padding:0,zIndex:20,display:'block',overflow:'hidden',backgroundColor:'transparent'});
|
|
d.find('.'+this.id+'_credit a').css({display:'block',width:Math.ceil(metric_credit)+'px',height:fontsize+'px','font-size':fontsize+'px'});
|
|
this.positionCredit();
|
|
}
|
|
if(this.showhelp){
|
|
var helpstr = '?';
|
|
if(d.find('.'+this.id+'_help').length == 0)
|
|
d.append('<div class="'+this.id+'_help"><a href="#">'+helpstr+'</a></div>')
|
|
.find('.'+this.id+'_help')
|
|
.css({
|
|
position:'absolute',
|
|
padding:5,
|
|
zIndex:20,
|
|
display:'block',
|
|
overflow:'hidden',
|
|
backgroundColor:'transparent',
|
|
right:0,
|
|
top:0,
|
|
'font-size':fontsize
|
|
}).find('a').css({
|
|
'text-decoration':'none',
|
|
color:txtcolour
|
|
}).on('click',{me:this},function(e){ e.data.me.toggleHelp(); });
|
|
d.find('.'+this.id+'_help').css({'font-size':fontsize}).find('a').css({color:txtcolour});
|
|
}
|
|
if(this.container.find('.'+this.id+'_clock').length == 0) this.container.append('<div class="'+this.id+'_clock" title="'+this.getPhrase('datechange')+'">'+clockstring+'</div>');
|
|
var off = $('#'+this.idinner).position();
|
|
this.container.find('.'+this.id+'_clock').css({
|
|
position:'absolute',
|
|
padding:0,
|
|
width:metric_clock,
|
|
cursor:'pointer',
|
|
top:off.top+5,
|
|
left:off.left+5,
|
|
zIndex:20,
|
|
display:'block',
|
|
overflow:'hidden',
|
|
backgroundColor:'transparent',
|
|
fontSize:fontsize+'px',
|
|
color:'transparent'
|
|
}).bind('click',{sky:this},function(e){
|
|
var s = e.data.sky;
|
|
var id = s.id;
|
|
var hid = '#'+id;
|
|
var v = "virtualsky";
|
|
if($(hid+'_calendar').length == 0){
|
|
var off = $(hid).offset();
|
|
var w = 280;
|
|
var h = 50;
|
|
if(s.wide < w) w = s.wide;
|
|
s.container.append(
|
|
'<div id="'+id+'_calendar" class="'+v+'form">'+
|
|
'<div style="" id="'+id+'_calendar_close" class="'+v+'_dismiss" title="'+e.data.sky.getPhrase('close')+'">×</div>'+
|
|
'<div style="text-align:center;margin:2px;">'+e.data.sky.getPhrase('date')+'</div>'+
|
|
'<div style="text-align:center;">'+
|
|
'<input type="text" id="'+id+'_year" style="width:3.2em;" value="" />'+
|
|
'<div class="divider">/</div>'+
|
|
'<input type="text" id="'+id+'_month" style="width:1.6em;" value="" />'+
|
|
'<div class="divider">/</div>'+
|
|
'<input type="text" id="'+id+'_day" style="width:1.6em;" value="" />'+
|
|
'<div class="divider"> </div>'+
|
|
'<input type="text" id="'+id+'_hours" style="width:1.6em;" value="" />'+
|
|
'<div class="divider">:</div>'+
|
|
'<input type="text" id="'+id+'_mins" style="width:1.6em;" value="" />'+
|
|
'</div>'+
|
|
'</div>');
|
|
$(hid+'_calendar').css({width:w});
|
|
$(hid+'_calendar input').bind('change',{sky:s},function(e){
|
|
e.data.sky.updateClock(new Date(parseInt($('#'+id+'_year').val()), parseInt($('#'+id+'_month').val()-1), parseInt($('#'+id+'_day').val()), parseInt($('#'+id+'_hours').val()), parseInt($('#'+id+'_mins').val()), 0,0));
|
|
e.data.sky.calendarUpdate();
|
|
e.data.sky.draw();
|
|
});
|
|
}
|
|
s.lightbox($(hid+'_calendar'));
|
|
$(hid+'_year').val(s.clock.getFullYear());
|
|
$(hid+'_month').val(s.clock.getMonth()+1);
|
|
$(hid+'_day').val(s.clock.getDate());
|
|
$(hid+'_hours').val(s.clock.getHours());
|
|
$(hid+'_mins').val(s.clock.getMinutes());
|
|
});
|
|
|
|
if($('.'+this.id+'_position').length == 0) this.container.append('<div class="'+this.id+'_position" title="'+this.getPhrase('positionchange')+'">'+positionstring+'</div>');
|
|
var off = $('#'+this.idinner).position();
|
|
$('.'+this.id+'_position').css({
|
|
position:'absolute',
|
|
padding:0,
|
|
width:metric_pos,
|
|
cursor:'pointer',
|
|
top:off.top+5+fontsize,
|
|
left:off.left+5,
|
|
zIndex:20,
|
|
fontSize:fontsize+'px',
|
|
display:'block',
|
|
overflow:'hidden',
|
|
backgroundColor:'transparent',
|
|
fontSize:fontsize+'px',
|
|
color:'transparent'
|
|
}).bind('click',{sky:this},function(e){
|
|
var s = e.data.sky;
|
|
var id = s.id;
|
|
var hid = '#'+id;
|
|
var v = "virtualsky";
|
|
if($(hid+'_geo').length == 0){
|
|
var w = 310;
|
|
var narrow = '';
|
|
if(s.wide < w){
|
|
narrow = '<br style="clear:both;margin-top:20px;" />';
|
|
w = w/2;
|
|
}
|
|
s.container.append(
|
|
'<div id="'+id+'_geo" class="'+v+'form">'+
|
|
'<div id="'+id+'_geo_close" class="'+v+'_dismiss" title="'+s.getPhrase('close')+'">×</div>'+
|
|
'<div style="text-align:center;margin:2px;">'+s.getPhrase('position')+'</div>'+
|
|
'<div style="text-align:center;">'+
|
|
'<input type="text" id="'+id+'_lat" value="" style="padding-right:10px!important;">'+
|
|
'<div class="divider">'+s.getPhrase('N')+'</div>'+
|
|
narrow+'<input type="text" id="'+id+'_long" value="" />'+
|
|
'<div class="divider">'+s.getPhrase('E')+'</div>'+
|
|
'</div>'+
|
|
'</div>');
|
|
$(hid+'_geo').css({width:w,'align':'center'})
|
|
$(hid+'_geo input').css({width:'6em'});
|
|
$(hid+'_geo_close').bind('click',{sky:s},function(e){
|
|
e.data.sky.setGeo($(hid+'_lat').val()+','+$(hid+'_long').val()).setClock(0).draw();
|
|
});
|
|
}
|
|
s.lightbox($(hid+'_geo'));
|
|
$(hid+'_lat').val(s.latitude*s.r2d)
|
|
$(hid+'_long').val(s.longitude*s.r2d)
|
|
if(typeof s.callback.geo=="function") s.callback.geo.call(s);
|
|
});
|
|
|
|
return this;
|
|
}
|
|
|
|
VirtualSky.prototype.lightbox = function(lb){
|
|
if(!lb.length) return this;
|
|
function columize(){
|
|
// Make each li as wide as it needs to be so we can calculate the widest
|
|
lb.find('li').css({'display':'inline-block','margin-left':'0px','width':'auto'});
|
|
// Remove positioning so we can work out sizes
|
|
lb.find('ul').css({'width':'auto'});
|
|
lb.css({'position':'relative'});
|
|
w = lb.outerWidth();
|
|
var bar = 24;
|
|
var li = lb.find('ul li');
|
|
var mx = 1;
|
|
for(var i = 0 ; i < li.length; i++){
|
|
if(li.eq(i).width() > mx) mx = li.eq(i).width();
|
|
}
|
|
// If the list items are wider than the space we have we turn them
|
|
// into block items otherwise set their widths to the maximum width.
|
|
var n = Math.floor(w/(mx+bar));
|
|
if(n > 1){
|
|
if(n > 3) n = 3;
|
|
lb.find('li').css({'width':(mx)+'px','margin-left':Math.floor(bar/2)+'px'});
|
|
lb.find('li:nth-child('+n+'n+1)').css({'margin-left':'0px'});
|
|
}else{
|
|
lb.find('li').css({'display':'block','width':'auto'});
|
|
}
|
|
lb.find('ul').css({'width':'100%'}).parent().css({'width':Math.min(w-bar,(mx+bar/2)*n + bar)+'px'});
|
|
lb.css({'z-index': 100,'position': 'absolute'});
|
|
lb.css({'left':Math.floor((this.wide-lb.outerWidth())/2)+'px',top:((this.tall-lb.height())/2)+'px'});
|
|
}
|
|
columize.call(this);
|
|
var n = "virtualsky_bg";
|
|
if(this.container.find('.'+n).length == 0) this.container.append('<div class="'+n+'" style="position:absolute;z-index: 99;left:0px;top: 0px;right: 0px;bottom: 0px;background-color: rgba(0,0,0,0.7);"></div>')
|
|
var bg = this.container.find('.'+n).show();
|
|
lb.css({left:((this.wide-lb.outerWidth())/2)+'px',top:((this.tall-lb.outerHeight())/2)+'px'}).show();
|
|
this.container.find('.virtualsky_dismiss').click({lb:lb,bg:bg},function(e){ lb.remove(); bg.remove(); });
|
|
bg.click({lb:lb,bg:bg},function(e){ lb.hide(); bg.hide(); });
|
|
// Update lightbox when the screen is resized
|
|
$(window).resize({vs:this,fn:columize},function(e){ e.data.fn.call(e.data.vs); });
|
|
return this;
|
|
}
|
|
|
|
VirtualSky.prototype.drawStars = function(){
|
|
|
|
if(!this.showstars && !this.showstarlabels) return this;
|
|
var mag,i,j,p,d,atmos,fovf;
|
|
var c = this.ctx;
|
|
c.beginPath();
|
|
c.fillStyle = this.col.stars;
|
|
this.az_off = (this.az_off+360)%360;
|
|
atmos = this.hasAtmos();
|
|
fovf = Math.sqrt(30/this.fov);
|
|
var f = 1;
|
|
if(this.negative) f *= 1.4;
|
|
if(typeof this.scalestars==="number" && this.scalestars!=1) f *= this.scalestars;
|
|
if(this.projection.id==="gnomic") f *= fovf;
|
|
|
|
for(i = 0; i < this.stars.length; i++){
|
|
if(this.stars[i][1] < this.magnitude){
|
|
mag = this.stars[i][1];
|
|
p = this.radec2xy(this.stars[i][2], this.stars[i][3]);
|
|
if(this.isVisible(p.el) && !isNaN(p.x) && !this.isPointBad(p)){
|
|
d = 0.8*Math.max(3-mag/2.1, 0.5);
|
|
// Modify the 'size' of the star by how close to the horizon it is
|
|
// i.e. smaller when closer to the horizon
|
|
if(atmos) d *= Math.exp(-(90-p.el)*0.01);
|
|
d *= f;
|
|
c.moveTo(p.x+d,p.y);
|
|
if(this.showstars) c.arc(p.x,p.y,d,0,Math.PI*2,true);
|
|
if(this.showstarlabels && this.starnames[this.stars[i][0]]) this.drawLabel(p.x,p.y,d,"",this.htmlDecode(this.starnames[this.stars[i][0]]));
|
|
}
|
|
}
|
|
}
|
|
c.fill();
|
|
|
|
return this;
|
|
}
|
|
|
|
VirtualSky.prototype.hasAtmos = function(){
|
|
return (typeof this.projection.atmos==="boolean") ? (this.gradient ? this.projection.atmos : this.gradient) : this.gradient;
|
|
}
|
|
|
|
VirtualSky.prototype.hasGradient = function(){
|
|
return (this.hasAtmos() && !this.fullsky && !this.negative) ? true : false;
|
|
}
|
|
|
|
// When provided with an array of Julian dates, ra, dec, and magnitude this will interpolate to the nearest
|
|
// data = [jd_1, ra_1, dec_1, mag_1, jd_2, ra_2, dec_2, mag_2....]
|
|
VirtualSky.prototype.interpolate = function(jd,data){
|
|
var mindt = jd; // Arbitrary starting value in days
|
|
var mini = 0; // index where we find the minimum
|
|
for(var i = 0 ; i < data.length ; i+=4){
|
|
// Find the nearest point to now
|
|
var dt = (jd-data[i]);
|
|
if(Math.abs(dt) < Math.abs(mindt)){ mindt = dt; mini = i; }
|
|
}
|
|
var dra,ddec,dmag,pos_2,pos_1,fract;
|
|
if(mindt >= 0){
|
|
pos_2 = mini+1+4;
|
|
pos_1 = mini+1;
|
|
fract = mindt/Math.abs(data[pos_2-1]-data[pos_1-1]);
|
|
}else{
|
|
pos_2 = mini+1;
|
|
pos_1 = mini+1-4;
|
|
fract = (1+(mindt)/Math.abs(data[pos_2-1]-data[pos_1-1]));
|
|
}
|
|
// We don't want to attempt to find positions beyond the edges of the array
|
|
if(pos_2 > data.length || pos_1 < 0){
|
|
dra = data[mini+1];
|
|
ddec = data[mini+2];
|
|
dmag = data[mini+3];
|
|
}else{
|
|
dra = (Math.abs(data[pos_2]-data[pos_1]) > 180) ? (data[pos_1]+(data[pos_2]+360-data[pos_1])*fract)%360 : (data[pos_1]+(data[pos_2]-data[pos_1])*fract)%360;
|
|
ddec = data[pos_1+1]+(data[pos_2+1]-data[pos_1+1])*fract;
|
|
dmag = data[pos_1+2]+(data[pos_2+2]-data[pos_1+2])*fract;
|
|
}
|
|
return { ra: dra, dec:ddec, mag:dmag}
|
|
}
|
|
VirtualSky.prototype.drawPlanets = function(){
|
|
|
|
if(!this.showplanets && !this.showplanetlabels && !this.showorbits) return this;
|
|
if(!this.planets || this.planets.length <= 0) return this;
|
|
var ra,dec,mag,pos,p;
|
|
var c = this.ctx;
|
|
var oldjd = this.jd;
|
|
this.jd = this.times.JD;
|
|
|
|
var colour = this.col.grey;
|
|
var maxl = this.maxLine();
|
|
for(p = 0 ; p < this.planets.length ; p++){
|
|
// We'll allow 2 formats here:
|
|
// [Planet name,colour,ra,dec,mag] or [Planet name,colour,[jd_1, ra_1, dec_1, mag_1, jd_2, ra_2, dec_2, mag_2....]]
|
|
if(!this.planets[p]) continue;
|
|
if(this.planets[p].length == 3){
|
|
// Find nearest JD
|
|
if(this.planets[p][2].length%4 == 0){
|
|
if(this.jd > this.planets[p][2][0] && this.jd < this.planets[p][2][(this.planets[p][2].length-4)]){
|
|
var interp = this.interpolate(this.jd,this.planets[p][2]);
|
|
ra = interp.ra;
|
|
dec = interp.dec;
|
|
mag = interp.mag;
|
|
}else{
|
|
continue; // We don't have data for this planet so skip to the next
|
|
}
|
|
}
|
|
}else{
|
|
ra = this.planets[p][2];
|
|
dec = this.planets[p][3];
|
|
}
|
|
pos = this.radec2xy(ra*this.d2r,dec*this.d2r);
|
|
|
|
if(!this.negative) colour = this.planets[p][1];
|
|
if(typeof colour==="string") c.strokeStyle = colour;
|
|
|
|
if((this.showplanets || this.showplanetlabels) && this.isVisible(pos.el) && mag < this.magnitude && !this.isPointBad(pos)){
|
|
var d = 0;
|
|
if(mag !== undefined){
|
|
d = 0.8*Math.max(3-mag/2, 0.5);
|
|
if(this.hasAtmos()) d *= Math.exp(-((90-pos.el)*this.d2r)*0.6);
|
|
}
|
|
if(d < 1.5) d = 1.5;
|
|
this.drawPlanet(pos.x,pos.y,d,colour,this.planets[p][0]);
|
|
}
|
|
|
|
if(this.showorbits && mag < this.magnitude){
|
|
c.beginPath();
|
|
c.lineWidth = 0.5
|
|
this.setFont();
|
|
c.lineWidth = 1;
|
|
var previous = {x:-1,y:-1,el:-1};
|
|
for(i = 0 ; i < this.planets[p][2].length-4 ; i+=4){
|
|
var point = this.radec2xy(this.planets[p][2][i+1]*this.d2r, this.planets[p][2][i+2]*this.d2r);
|
|
if(previous.x > 0 && previous.y > 0 && this.isVisible(point.el)){
|
|
c.moveTo(previous.x,previous.y);
|
|
// Basic error checking: points behind us often have very long lines so we'll zap them
|
|
if(Math.abs(point.x-previous.x) < maxl){
|
|
c.lineTo(point.x,point.y);
|
|
}
|
|
}
|
|
previous = point;
|
|
}
|
|
c.stroke();
|
|
}
|
|
}
|
|
|
|
// Sun & Moon
|
|
if(this.showplanets || this.showplanetlabels){
|
|
|
|
// Only recalculate the Moon's ecliptic position if the time has changed
|
|
if(oldjd != this.jd){
|
|
var p = this.moonPos(this.jd);
|
|
this.moon = p.moon;
|
|
this.sun = p.sun;
|
|
}
|
|
var pos;
|
|
// Draw the Sun
|
|
pos = this.ecliptic2xy(this.sun.lon*this.d2r,this.sun.lat*this.d2r,this.times.LST);
|
|
if(this.isVisible(pos.el) && !this.isPointBad(pos)) this.drawPlanet(pos.x,pos.y,5,this.col.sun,"sun");
|
|
// Draw Moon last as it is closest
|
|
pos = this.ecliptic2xy(this.moon.lon*this.d2r,this.moon.lat*this.d2r,this.times.LST);
|
|
if(this.isVisible(pos.el) && !this.isPointBad(pos)) this.drawPlanet(pos.x,pos.y,5,this.col.moon,"moon");
|
|
|
|
}
|
|
return this;
|
|
}
|
|
VirtualSky.prototype.drawPlanet = function(x,y,d,colour,label){
|
|
var c = this.ctx;
|
|
c.beginPath();
|
|
c.fillStyle = colour;
|
|
c.strokeStyle = colour;
|
|
c.moveTo(x+d,y+d);
|
|
if(this.showplanets) c.arc(x,y,d,0,Math.PI*2,true);
|
|
label = this.getPhrase('planets',label);
|
|
if(this.showplanetlabels) this.drawLabel(x,y,d,colour,label);
|
|
c.fill();
|
|
return this;
|
|
}
|
|
VirtualSky.prototype.drawText = function(txt,x,y){
|
|
this.ctx.beginPath();
|
|
this.ctx.fillText(txt,x,y);
|
|
return this.ctx.measureText(txt).width;
|
|
}
|
|
// Helper function. You'll need to wrap it with a this.ctx.beginPath() and a this.ctx.fill();
|
|
VirtualSky.prototype.drawLabel = function(x,y,d,colour,label){
|
|
if(label===undefined) return this;
|
|
var c = this.ctx;
|
|
if(colour.length > 0) c.fillStyle = colour;
|
|
c.lineWidth = 1.5;
|
|
var xoff = d;
|
|
if((this.polartype) && c.measureText) xoff = -c.measureText(label).width-3
|
|
if((this.polartype) && x < this.wide/2) xoff = d;
|
|
c.fillText(label,x+xoff,y-(d+2))
|
|
return this;
|
|
}
|
|
VirtualSky.prototype.drawConstellationLines = function(colour){
|
|
if(!(this.constellation.lines || this.constellation.labels)) return this;
|
|
if(!colour) colour = this.col.constellation;
|
|
var x = this.ctx;
|
|
x.beginPath();
|
|
x.strokeStyle = colour;
|
|
x.fillStyle = colour;
|
|
x.lineWidth = 0.75
|
|
var fontsize = this.fontsize();
|
|
this.setFont();
|
|
if(typeof this.lines!=="object") return this;
|
|
var pos,posa,posb,a,b,idx,l;
|
|
var maxl = this.maxLine();
|
|
for(var c = 0; c < this.lines.length; c++){
|
|
if(this.constellation.lines){
|
|
for(l = 3; l < this.lines[c].length; l+=2){
|
|
a = -1;
|
|
b = -1;
|
|
idx1 = ''+this.lines[c][l]+'';
|
|
idx2 = ''+this.lines[c][l+1]+'';
|
|
if(!this.hipparcos[idx1]){
|
|
for(s = 0; s < this.stars.length; s++){
|
|
if(this.stars[s][0] == this.lines[c][l]){
|
|
this.hipparcos[idx1] = s;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(!this.hipparcos[idx2]){
|
|
for(s = 0; s < this.stars.length; s++){
|
|
if(this.stars[s][0] == this.lines[c][l+1]){
|
|
this.hipparcos[idx2] = s;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
a = this.hipparcos[idx1];
|
|
b = this.hipparcos[idx2];
|
|
if(a >= 0 && b >= 0 && a < this.stars.length && b < this.stars.length){
|
|
posa = this.radec2xy(this.stars[a][2], this.stars[a][3]);
|
|
posb = this.radec2xy(this.stars[b][2], this.stars[b][3]);
|
|
if(this.isVisible(posa.el) && this.isVisible(posb.el)){
|
|
if(!this.isPointBad(posa) && !this.isPointBad(posb)){
|
|
// Basic error checking: constellations behind us often have very long lines so we'll zap them
|
|
if(Math.abs(posa.x-posb.x) < maxl && Math.abs(posa.y-posb.y) < maxl){
|
|
x.moveTo(posa.x,posa.y);
|
|
x.lineTo(posb.x,posb.y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(this.constellation.labels){
|
|
pos = this.radec2xy(this.lines[c][1]*this.d2r,this.lines[c][2]*this.d2r);
|
|
if(this.isVisible(pos.el)){
|
|
label = this.getPhrase('constellations',this.lines[c][0]);
|
|
xoff = (x.measureText) ? -x.measureText(label).width/2 : 0;
|
|
x.fillText(label,pos.x+xoff,pos.y-fontsize/2)
|
|
x.fill();
|
|
}
|
|
}
|
|
}
|
|
x.stroke();
|
|
return this;
|
|
}
|
|
|
|
// Draw the boundaries of constellations
|
|
// Input: colour (e.g. "rgb(255,255,0)")
|
|
// We should have all the boundary points stored in this.boundaries. As many of the constellations
|
|
// will share boundaries we don't want to bother drawing lines that we've already done so we will
|
|
// keep a record of the lines we've drawn as we go. As some segments may be large on the sky we will
|
|
// interpolate a few points between so that boundaries follow the curvature of the projection better.
|
|
// As the boundaries are in FK1 we will calculate the J2000 positions once and keep them cached as
|
|
// this speeds up the re-drawing as the user moves the sky. We assume that the user's session << time
|
|
// between epochs.
|
|
VirtualSky.prototype.drawConstellationBoundaries = function(colour){
|
|
if(!this.constellation.boundaries) return this;
|
|
if(!colour) colour = this.col.constellationboundary;
|
|
this.ctx.beginPath();
|
|
this.ctx.strokeStyle = colour;
|
|
this.ctx.fillStyle = colour;
|
|
this.ctx.lineWidth = 0.75;
|
|
if(typeof this.boundaries!=="object") return this;
|
|
var posa, posb, a, b, l, c, d, atob,btoa, move, i, j, ra,dc,dra,ddc,b3;
|
|
// Keys defining a line in both directions
|
|
atob = "";
|
|
btoa = "";
|
|
var n = 5;
|
|
var maxl = this.maxLine(5);
|
|
// Create a holder for the constellation boundary points i.e. a cache of position calculations
|
|
if(!this.constellation.bpts) this.constellation.bpts = new Array(this.boundaries.length);
|
|
// We'll record which boundary lines we've already processed
|
|
var cbdone = [];
|
|
if(this.constellation.boundaries){
|
|
for(c = 0; c < this.boundaries.length; c++){
|
|
if(typeof this.boundaries!=="string" && c < this.boundaries.length){
|
|
|
|
if(this.constellation.bpts[c]){
|
|
// Use the old array
|
|
var points = this.constellation.bpts[c];
|
|
}else{
|
|
// Create a new array of points
|
|
var points = [];
|
|
for(l = 1; l < this.boundaries[c].length; l+=2){
|
|
b = [this.boundaries[c][l],this.boundaries[c][l+1]];
|
|
if(a){
|
|
atob = a[0]+','+a[1]+'-'+b[0]+','+b[1];
|
|
btoa = b[0]+','+b[1]+'-'+a[0]+','+a[1];
|
|
}
|
|
if(l > 1){
|
|
move = (cbdone[atob] || cbdone[btoa]);
|
|
ra = (b[0]-a[0])%360;
|
|
if(ra > 180) ra = ra-360;
|
|
if(ra < -180) ra = ra+360;
|
|
dc = (b[1]-a[1]);
|
|
|
|
// If we've already done this line we'll only calculate
|
|
// two points on the line otherwise we'll do 5
|
|
n = (move) ? 5 : 2;
|
|
if(ra/2 > n) n = parseInt(ra);
|
|
if(dc/2 > n) n = parseInt(dc);
|
|
|
|
dra = ra/n;
|
|
ddc = dc/n;
|
|
|
|
for(var i = 1; i <= n; i++){
|
|
ra = a[0]+(i*dra);
|
|
if(ra < 0) ra += 360;
|
|
dc = a[1]+(i*ddc);
|
|
// Convert to J2000
|
|
d = this.fk1tofk5(ra*this.d2r,dc*this.d2r);
|
|
points.push([d[0],d[1],move]);
|
|
}
|
|
}
|
|
// Mark this line as drawn
|
|
cbdone[atob] = true;
|
|
cbdone[btoa] = true;
|
|
a = b;
|
|
}
|
|
this.constellation.bpts[c] = points;
|
|
}
|
|
posa = null;
|
|
// Now loop over joining the points
|
|
for(i = 0; i <= points.length; i++){
|
|
j = (i == points.length) ? 0 : i;
|
|
posb = this.radec2xy(points[j][0],points[j][1]);
|
|
if(posa && this.isVisible(posa.el) && this.isVisible(posb.el) && points[j][2]){
|
|
if(!this.isPointBad(posa) && !this.isPointBad(posb)){
|
|
// Basic error checking: constellations behind us often have very long lines so we'll zap them
|
|
if(Math.abs(posa.x-posb.x) < maxl && Math.abs(posa.y-posb.y) < maxl){
|
|
this.ctx.moveTo(posa.x,posa.y);
|
|
this.ctx.lineTo(posb.x,posb.y);
|
|
}
|
|
}
|
|
}
|
|
posa = posb;
|
|
}
|
|
}
|
|
}
|
|
cbdone = [];
|
|
}
|
|
this.ctx.stroke();
|
|
return this;
|
|
}
|
|
VirtualSky.prototype.drawGalaxy = function(colour){
|
|
if(!this.galaxy || !this.showgalaxy) return this;
|
|
if(!colour) colour = this.col.galaxy;
|
|
this.ctx.beginPath();
|
|
this.ctx.strokeStyle = colour;
|
|
this.ctx.fillStyle = colour;
|
|
this.ctx.lineWidth = 1;
|
|
if(typeof this.boundaries!=="object") return this;
|
|
var p, pa, pb, i, c, old, maxl;
|
|
maxl = this.maxLine(5);
|
|
|
|
for(c = 0; c < this.galaxy.length; c++){
|
|
|
|
// We will convert all the galaxy outline coordinates to radians
|
|
if(!this.galaxyprocessed) for(i = 1; i < this.galaxy[c].length; i++) this.galaxy[c][i] *= this.d2r;
|
|
|
|
// Get a copy of the current shape
|
|
p = this.galaxy[c].slice(0);
|
|
|
|
// Get the colour (first element)
|
|
p.shift();
|
|
// Set the initial point to null
|
|
pa = null;
|
|
|
|
// Now loop over joining the points
|
|
for(i = 0; i < p.length; i+=2){
|
|
pb = this.radec2xy(p[i], p[i+1]);
|
|
if(pa){
|
|
// Basic error checking: if the line is very long we need to normalize to other side of sky
|
|
if(Math.abs(pa.x-pb.x) < maxl && Math.abs(pa.y-pb.y) < maxl){
|
|
this.ctx.moveTo(pa.x,pa.y);
|
|
this.ctx.lineTo(pb.x,pb.y);
|
|
}
|
|
}
|
|
pa = pb;
|
|
}
|
|
}
|
|
// We've converted the galaxy to radians
|
|
this.galaxyprocessed = true;
|
|
this.ctx.stroke();
|
|
return this;
|
|
}
|
|
VirtualSky.prototype.drawMeteorShowers = function(colour){
|
|
if(!this.meteorshowers || typeof this.showers==="string") return this;
|
|
if(!colour) colour = this.col.showers;
|
|
var shower, pos, label, xoff, c, d, p, start, end, dra, ddc, f;
|
|
c = this.ctx;
|
|
c.beginPath();
|
|
c.strokeStyle = colour;
|
|
c.fillStyle = colour;
|
|
c.lineWidth = 0.75;
|
|
var fs = this.fontsize();
|
|
this.setFont();
|
|
var y = this.clock.getFullYear();
|
|
for(var s in this.showers){
|
|
d = this.showers[s].date;
|
|
p = this.showers[s].pos;
|
|
start = new Date(y,d[0][0]-1,d[0][1]);
|
|
end = new Date(y,d[1][0]-1,d[1][1]);
|
|
if(start > end && this.clock < start) start = new Date(y-1,d[0][0]-1,d[0][1]);
|
|
if(this.clock > start && this.clock < end){
|
|
dra = (p[1][0]-p[0][0]);
|
|
ddc = (p[1][1]-p[0][1]);
|
|
f = (this.clock-start)/(end-start);
|
|
pos = this.radec2xy((this.showers[s].pos[0][0]+(dra*f))*this.d2r,(this.showers[s].pos[0][1]+(ddc*f))*this.d2r);
|
|
if(this.isVisible(pos.el)){
|
|
label = this.htmlDecode(this.showers[s].name);
|
|
xoff = (c.measureText) ? -c.measureText(label).width/2 : 0;
|
|
c.moveTo(pos.x+2,pos.y);
|
|
c.arc(pos.x,pos.y,2,0,Math.PI*2,true);
|
|
c.fillText(label,pos.x+xoff,pos.y-fs/2);
|
|
}
|
|
}
|
|
}
|
|
c.fill();
|
|
return this;
|
|
}
|
|
|
|
VirtualSky.prototype.drawEcliptic = function(colour){
|
|
if(!this.ecliptic) return this;
|
|
if(!colour || typeof colour!="string") colour = this.col.ec;
|
|
var c = this.ctx;
|
|
var step = 2*this.d2r;
|
|
c.beginPath();
|
|
c.strokeStyle = colour;
|
|
c.lineWidth = 3;
|
|
var maxl = this.maxLine();
|
|
|
|
var old = {x:-1,y:-1,moved:false};
|
|
for(var a = 0 ; a < Math.PI*2 ; a += step) old = joinpoint(this,"ec",a,0,old,maxl);
|
|
|
|
c.stroke();
|
|
return this;
|
|
}
|
|
|
|
VirtualSky.prototype.drawMeridian = function(colour){
|
|
if(!this.meridian) return this;
|
|
if(!colour || typeof colour!="string") colour = this.col.meridian;
|
|
var c = this.ctx;
|
|
var a, b;
|
|
var minb = 0;
|
|
var maxb = (typeof this.projection.maxb==="number") ? this.projection.maxb*this.d2r : Math.PI/2;
|
|
var step = 2*this.d2r;
|
|
var maxl = this.maxLine();
|
|
c.beginPath();
|
|
c.strokeStyle = colour;
|
|
c.lineWidth = 2;
|
|
|
|
var old = {x:-1,y:-1,moved:false};
|
|
for(b = minb, a = 0; b <= maxb ; b+= step) old = joinpoint(this,"az",Math.PI,b,old,maxl);
|
|
for(b = maxb, a = 0; b >= minb ; b-= step) old = joinpoint(this,"az",0,b,old,maxl);
|
|
|
|
c.stroke();
|
|
return this;
|
|
}
|
|
|
|
// type can be "az" or "eq"
|
|
VirtualSky.prototype.drawGridlines = function(type,step,colour){
|
|
if(!type || !this.grid[type]) return this;
|
|
if(typeof colour!=="string") colour = this.col[type];
|
|
if(typeof step!=="number") step = this.grid.step;
|
|
|
|
var maxb,minb,x,y,a,b,pos,c,oldx,oldy,bstep2,ra,dc;
|
|
c = this.ctx;
|
|
oldx = 0;
|
|
oldy = 0;
|
|
c.beginPath();
|
|
c.strokeStyle = colour;
|
|
c.lineWidth = 1.0;
|
|
bstep = 2;
|
|
if(type=="az"){
|
|
maxb = (typeof this.projection.maxb==="number") ? this.projection.maxb : 90-bstep;
|
|
minb = 0;
|
|
}else{
|
|
maxb = 90-bstep;
|
|
minb = -maxb;
|
|
}
|
|
var maxl = this.maxLine(5);
|
|
old = {x:-1,y:-1,moved:false};
|
|
step *= this.d2r;
|
|
bstep *= this.d2r;
|
|
minb *= this.d2r;
|
|
maxb *= this.d2r;
|
|
// Draw grid lines in elevation/declination/latitude
|
|
for(a = 0 ; a < Math.PI*2 ; a += step){
|
|
old.moved = false;
|
|
for(b = minb; b <= maxb ; b+= bstep) old = joinpoint(this,type,a,b,old,maxl);
|
|
}
|
|
c.stroke();
|
|
c.beginPath();
|
|
if(type=="az"){
|
|
minb = 0;
|
|
maxb = 90-bstep*this.r2d;
|
|
}else{
|
|
minb = -90+step*this.r2d;
|
|
maxb = 90;
|
|
}
|
|
minb *= this.d2r;
|
|
maxb *= this.d2r;
|
|
old = {x:-1,y:-1,moved:false};
|
|
// Draw grid lines in azimuth/RA/longitude
|
|
for(b = minb; b < maxb ; b += step){
|
|
old.moved = false;
|
|
for(a = 0 ; a <= 2*Math.PI ; a += bstep) old = joinpoint(this,type,a,b,old,maxl);
|
|
}
|
|
c.stroke();
|
|
return this;
|
|
}
|
|
|
|
VirtualSky.prototype.drawCardinalPoints = function(){
|
|
if(!this.cardinalpoints) return this;
|
|
var i,x,y,pos,ang,f,m,r;
|
|
var azs = new Array(0,90,180,270);
|
|
var d = [this.getPhrase('N'),this.getPhrase('E'),this.getPhrase('S'),this.getPhrase('W')];
|
|
var pt = 15;
|
|
var c = this.ctx;
|
|
c.beginPath();
|
|
c.fillStyle = this.col.cardinal;
|
|
var fontsize = this.fontsize();
|
|
for(i = 0 ; i < azs.length ; i++){
|
|
if(c.measureText){
|
|
m = c.measureText(d[i]);
|
|
r = (m.width > fontsize) ? m.width/2 : fontsize/2;
|
|
}else r = fontsize/2;
|
|
ang = (azs[i]-this.az_off)*this.d2r;
|
|
if(this.polartype){
|
|
f = (this.tall/2) - r*1.5;
|
|
x = -f*Math.sin(ang);
|
|
y = -f*Math.cos(ang);
|
|
x = isFinite(x) ? this.wide/2 + x - r : 0;
|
|
y = isFinite(y) ? this.tall/2 + y + r: 0;
|
|
}else{
|
|
pos = this.azel2xy(ang,0,this.wide,this.tall);
|
|
x = isFinite(pos.x) ? pos.x - r : 0;
|
|
y = isFinite(pos.y) ? pos.y - pt/2 : 0;
|
|
if(x < 0 || x > this.wide-pt) x = -r;
|
|
}
|
|
if(x > 0) c.fillText(d[i],x,y);
|
|
}
|
|
c.fill();
|
|
|
|
return this;
|
|
}
|
|
|
|
// Assume decimal Ra/Dec
|
|
VirtualSky.prototype.highlight = function(i,colour){
|
|
var p = this.pointers[i];
|
|
if(this.pointers[i].ra && this.pointers[i].dec){
|
|
colour = p.colour || colour || "rgba(255,0,0,1)";
|
|
if(this.negative) colour = this.getNegative(colour);
|
|
var pos = this.radec2xy(p.ra*this.d2r, p.dec*this.d2r);
|
|
var c = this.ctx;
|
|
if(this.isVisible(pos.el)){
|
|
p.az = pos.az;
|
|
p.el = pos.el;
|
|
p.x = pos.x;
|
|
p.y = pos.y;
|
|
c.fillStyle = colour;
|
|
c.strokeStyle = colour;
|
|
c.beginPath();
|
|
// Draw a square to distinguish from other objects
|
|
// c.arc(p.x,p.y,p.d/2,0,2*Math.PI);
|
|
c.fillRect(p.x-p.d/2,p.y-p.d/2,p.d,p.d);
|
|
c.fill();
|
|
this.drawLabel(p.x,p.y,p.d,colour,p.label);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
// Function to join the dots
|
|
function joinpoint(s,type,a,b,old,maxl){
|
|
var x,y,show,c,pos;
|
|
c = s.ctx;
|
|
if(type=="az") pos = s.azel2xy((a-s.az_off*s.d2r),b,s.wide,s.tall);
|
|
else if(type=="eq") pos = s.radec2xy(a,b);
|
|
else if(type=="ec") pos = s.ecliptic2xy(a,b,s.times.LST);
|
|
else if(type=="gal") pos = s.gal2xy(a,b);
|
|
x = pos.x;
|
|
y = pos.y;
|
|
if(type=="az") show = true;
|
|
else show = ((s.isVisible(pos.el)) ? true : false);
|
|
if(show && isFinite(x) && isFinite(y)){
|
|
if(type=="az"){
|
|
if(!old.moved || Math.sqrt(Math.pow(old.x-x,2)+Math.pow(old.y-y,2)) > s.tall/2) c.moveTo(x,y);
|
|
c.lineTo(x,y);
|
|
old.moved = true;
|
|
}else{
|
|
// If the last point on s contour is more than a canvas width away
|
|
// it is probably supposed to be behind us so we won't draw a line
|
|
if(!old.moved || Math.sqrt(Math.pow(old.x-x,2)+Math.pow(old.y-y,2)) > maxl){
|
|
c.moveTo(x,y);
|
|
old.moved = true;
|
|
}else c.lineTo(x,y);
|
|
}
|
|
old.x = x;
|
|
old.y = y;
|
|
}
|
|
return old;
|
|
}
|
|
|
|
VirtualSky.prototype.maxLine = function(f){
|
|
if(this.projection.id==="gnomic") return this.tall;
|
|
if(typeof f!=="number") f = 3;
|
|
return this.tall/f;
|
|
}
|
|
|
|
// Expects a latitude,longitude string (comma separated)
|
|
VirtualSky.prototype.setGeo = function(pos){
|
|
if(typeof pos!=="string") return this;
|
|
pos = pos.split(',');
|
|
this.setLatitude(pos[0]);
|
|
this.setLongitude(pos[1]);
|
|
return this;
|
|
}
|
|
|
|
// Input: latitude (deg)
|
|
VirtualSky.prototype.setLatitude = function(l){
|
|
this.latitude = inrangeEl(parseFloat(l)*this.d2r);
|
|
return this;
|
|
}
|
|
|
|
// Input: longitude (deg)
|
|
VirtualSky.prototype.setLongitude = function(l){
|
|
this.longitude = parseFloat(l)*this.d2r;
|
|
while(this.longitude <= -Math.PI) this.longitude += 2*Math.PI;
|
|
while(this.longitude > Math.PI) this.longitude -= 2*Math.PI;
|
|
return this;
|
|
}
|
|
|
|
VirtualSky.prototype.setRADec = function(r,d){
|
|
return this.setRA(r).setDec(d);
|
|
}
|
|
|
|
VirtualSky.prototype.setRA = function(r){
|
|
this.ra_off = (r%360)*this.d2r;
|
|
return this;
|
|
}
|
|
|
|
VirtualSky.prototype.setDec = function(d){
|
|
this.dc_off = d*this.d2r
|
|
return this;
|
|
}
|
|
|
|
// Pan the view to the specified RA,Dec
|
|
// Inputs: RA (deg), Dec (deg), duration (seconds)
|
|
VirtualSky.prototype.panTo = function(ra,dec,s){
|
|
if(!s) s = 1000;
|
|
if(typeof ra!=="number" || typeof dec!=="number") return this;
|
|
this.panning = { s: { ra:this.ra_off*this.r2d, dec:this.dc_off*this.r2d }, e: { ra: ra, dec: dec}, duration: s, start: new Date() };
|
|
this.panning.dr = this.panning.e.ra-this.panning.s.ra;
|
|
this.panning.dd = this.panning.e.dec-this.panning.s.dec;
|
|
if(this.panning.dr > 180) this.panning.dr = -(360-this.panning.dr);
|
|
if(this.panning.dr < -180) this.panning.dr = (360+this.panning.dr);
|
|
return this.panStep();
|
|
}
|
|
|
|
// shim layer with setTimeout fallback
|
|
window.requestAnimFrame = (function(){
|
|
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function( callback ){ window.setTimeout(callback, 1000 / 60); };
|
|
})();
|
|
|
|
// Animation step for the panning
|
|
VirtualSky.prototype.panStep = function(){
|
|
var ra,dc;
|
|
var now = new Date();
|
|
var t = (now - this.panning.start)/this.panning.duration;
|
|
ra = this.panning.s.ra + (this.panning.dr)*(t);
|
|
dc = this.panning.s.dec + (this.panning.dd)*(t);
|
|
|
|
// Still animating
|
|
if(t < 1){
|
|
// update and draw
|
|
this.setRADec(ra,dc).draw();
|
|
var _obj = this;
|
|
// request new frame
|
|
requestAnimFrame(function() { _obj.panStep(); });
|
|
}else{
|
|
// We've ended
|
|
this.setRADec(this.panning.e.ra,this.panning.e.dec).draw();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
VirtualSky.prototype.liveSky = function(pos){
|
|
this.islive = !this.islive;
|
|
if(this.islive) interval = window.setInterval(function(sky){ sky.setClock('now'); },1000,this);
|
|
else{
|
|
if(interval!==undefined) clearInterval(interval);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
VirtualSky.prototype.start = function(){
|
|
this.islive = true;
|
|
// Clear existing interval
|
|
if(interval!==undefined) clearInterval(interval);
|
|
interval = window.setInterval(function(sky){ sky.setClock('now'); },1000,this);
|
|
}
|
|
VirtualSky.prototype.stop = function(){
|
|
this.islive = false;
|
|
// Clear existing interval
|
|
if(interval!==undefined) clearInterval(interval);
|
|
}
|
|
// Increment the clock by the amount specified
|
|
VirtualSky.prototype.advanceTime = function(by,wait){
|
|
if(by===undefined){
|
|
this.updateClock(new Date());
|
|
}else{
|
|
by = parseFloat(by);
|
|
if(!wait) wait = 1000/this.fps; // ms between frames
|
|
var fn = function(vs,by){ vs.setClock(by); };
|
|
clearInterval(this.interval_time)
|
|
clearInterval(this.interval_calendar)
|
|
this.interval_time = window.setInterval(fn,wait,this,by);
|
|
// Whilst animating we'll periodically check to see if the calendar events need calling
|
|
this.interval_calendar = window.setInterval(function(vs){ vs.calendarUpdate(); },1000,this);
|
|
}
|
|
return this;
|
|
}
|
|
// Send a Javascript Date() object and update the clock
|
|
VirtualSky.prototype.updateClock = function(d){
|
|
this.clock = d;
|
|
this.times = this.astronomicalTimes();
|
|
}
|
|
// Call any calendar-based events
|
|
VirtualSky.prototype.calendarUpdate = function(){
|
|
for(var e = 0; e < this.calendarevents.length; e++){
|
|
if(is(this.calendarevents[e],"function")) this.calendarevents[e].call(this);
|
|
}
|
|
return this;
|
|
}
|
|
VirtualSky.prototype.setClock = function(seconds){
|
|
if(seconds === undefined){
|
|
return this;
|
|
}if(typeof seconds==="string"){
|
|
seconds = convertTZ(seconds);
|
|
if(!this.input.clock){
|
|
if(seconds==="now") this.updateClock(new Date());
|
|
else this.updateClock(new Date(seconds));
|
|
}else{
|
|
this.updateClock((typeof this.input.clock==="string") ? this.input.clock.replace(/%20/g,' ') : this.input.clock);
|
|
if(typeof this.clock==="string") this.updateClock(new Date(this.clock));
|
|
}
|
|
}else if(typeof seconds==="object"){
|
|
this.updateClock(seconds);
|
|
}else{
|
|
this.updateClock(new Date(this.clock.getTime() + seconds*1000));
|
|
}
|
|
this.draw();
|
|
return this;
|
|
}
|
|
VirtualSky.prototype.toggleAtmosphere = function(){ this.gradient = !this.gradient; this.draw(); return this; }
|
|
VirtualSky.prototype.toggleStars = function(){ this.showstars = !this.showstars; this.draw(); return this; }
|
|
VirtualSky.prototype.toggleStarLabels = function(){ this.showstarlabels = !this.showstarlabels; this.draw(); return this; }
|
|
VirtualSky.prototype.toggleNegative = function(){ this.negative = !this.negative; this.col = this.colours[(this.negative ? "negative" : "normal")]; this.draw(); return this; }
|
|
VirtualSky.prototype.toggleConstellationLines = function(){ this.constellation.lines = !this.constellation.lines; this.draw(); return this; }
|
|
VirtualSky.prototype.toggleConstellationBoundaries = function(){ this.constellation.boundaries = !this.constellation.boundaries; this.draw(); return this; }
|
|
VirtualSky.prototype.toggleConstellationLabels = function(){ this.constellation.labels = !this.constellation.labels; this.draw(); return this; }
|
|
VirtualSky.prototype.toggleMeteorShowers = function(){ this.meteorshowers = !this.meteorshowers; this.draw(); return this; }
|
|
VirtualSky.prototype.toggleCardinalPoints = function(){ this.cardinalpoints = !this.cardinalpoints; this.draw(); return this; }
|
|
VirtualSky.prototype.toggleGridlinesAzimuthal = function(){ this.grid.az = !this.grid.az; this.draw(); return this; }
|
|
VirtualSky.prototype.toggleGridlinesEquatorial = function(){ this.grid.eq = !this.grid.eq; this.draw(); return this; }
|
|
VirtualSky.prototype.toggleGridlinesGalactic = function(){ this.grid.gal = !this.grid.gal; this.draw(); return this; }
|
|
VirtualSky.prototype.toggleEcliptic = function(){ this.ecliptic = !this.ecliptic; this.draw(); return this; }
|
|
VirtualSky.prototype.toggleMeridian = function(){ this.meridian = !this.meridian; this.draw(); return this; }
|
|
VirtualSky.prototype.toggleGround = function(){ this.ground = !this.ground; this.draw(); return this; }
|
|
VirtualSky.prototype.toggleGalaxy = function(){ this.showgalaxy = !this.showgalaxy; this.draw(); return this; }
|
|
VirtualSky.prototype.toggleMeteorShowers = function(){ this.meteorshowers = !this.meteorshowers; this.draw(); return this; }
|
|
VirtualSky.prototype.togglePlanetHints = function(){ this.showplanets = !this.showplanets; this.draw(); return this; }
|
|
VirtualSky.prototype.togglePlanetLabels = function(){ this.showplanetlabels = !this.showplanetlabels; this.draw(); return this; }
|
|
VirtualSky.prototype.toggleOrbits = function(){ this.showorbits = !this.showorbits; this.draw(); return this; }
|
|
VirtualSky.prototype.toggleAzimuthMove = function(az){
|
|
if(this.az_step===0){
|
|
this.az_step = (typeof az==="number") ? az : -1;
|
|
this.moveIt();
|
|
}else{
|
|
this.az_step = 0;
|
|
if(this.timer_az!==undefined) clearTimeout(this.timer_az);
|
|
}
|
|
return this;
|
|
}
|
|
VirtualSky.prototype.addPointer = function(input){
|
|
// Check if we've already added this
|
|
var style,url,img,label,credit;
|
|
var matched = -1;
|
|
var p;
|
|
for(var i = 0 ; i < this.pointers.length ; i++){
|
|
if(this.pointers[i].ra == input.ra && this.pointers[i].dec == input.dec && this.pointers[i].label == input.label) matched = i;
|
|
}
|
|
// Hasn't been added already
|
|
if(matched < 0){
|
|
input.ra *= 1; // Correct for a bug
|
|
input.dec *= 1;
|
|
i = this.pointers.length;
|
|
p = input;
|
|
p.d = is(p.d, "number")?p.d:5;
|
|
if(typeof p.html !== "string"){
|
|
style = p.style || "width:128px;height:128px;";
|
|
url = p.url || "http://server1.wikisky.org/v2?ra="+(p.ra/15)+"&de="+(p.dec)+"&zoom=6&img_source=DSS2";
|
|
img = p.img || 'http://server7.sky-map.org/imgcut?survey=DSS2&w=128&h=128&ra='+(p.ra/15)+'&de='+p.dec+'&angle=0.25&output=PNG';
|
|
label = p.credit || "View in Wikisky";
|
|
credit = p.credit || "DSS2/Wikisky";
|
|
p.html = p.html ||
|
|
'<div class="virtualsky_infocredit">'+
|
|
'<a href="'+url+'" style="color: white;">'+credit+'</a>'+
|
|
'</div>'+
|
|
'<a href="'+url+'" style="display:block;'+style+'">'+
|
|
'<img src="'+img+'" style="border:0px;'+style+'" title="'+label+'" />'+
|
|
'</a>';
|
|
}
|
|
this.pointers[i] = p;
|
|
}
|
|
return (this.pointers.length);
|
|
}
|
|
VirtualSky.prototype.changeAzimuth = function(inc){
|
|
this.az_off += (typeof inc==="number") ? inc : 5;
|
|
this.draw();
|
|
return this;
|
|
}
|
|
VirtualSky.prototype.moveIt = function(){
|
|
// Send 'this' context to the setTimeout function so we can redraw
|
|
this.timer_az = window.setTimeout(function(mysky){ mysky.az_off += mysky.az_step; mysky.draw(); mysky.moveIt(); },100,this);
|
|
return this;
|
|
}
|
|
VirtualSky.prototype.spinIt = function(tick,wait){
|
|
if(typeof tick==="number") this.spin = (tick == 0) ? 0 : (this.spin+tick);
|
|
else{
|
|
var t = 1.0/this.fps;
|
|
var s = 2;
|
|
// this.spin is the number of seconds to update the clock by
|
|
if(this.spin == 0) this.spin = (tick == "up") ? t : -t;
|
|
else{
|
|
if(Math.abs(this.spin) < 1) s *= 2;
|
|
if(this.spin > 0) this.spin = (tick == "up") ? (this.spin*s) : (this.spin/s);
|
|
else if(this.spin < 0) this.spin = (tick == "up") ? (this.spin/s) : (this.spin*s);
|
|
if(this.spin < t && this.spin > -t) this.spin = 0;
|
|
}
|
|
}
|
|
if(this.interval_time!==undefined)
|
|
clearInterval(this.interval_time);
|
|
if(this.spin != 0)
|
|
this.advanceTime(this.spin,wait);
|
|
return this;
|
|
}
|
|
VirtualSky.prototype.getOffset = function(el){
|
|
var _x = 0;
|
|
var _y = 0;
|
|
while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
|
|
_x += el.offsetLeft - el.scrollLeft;
|
|
_y += el.offsetTop - el.scrollTop;
|
|
el = el.parentNode;
|
|
}
|
|
return { top: _y, left: _x };
|
|
}
|
|
VirtualSky.prototype.getJD = function(clock) {
|
|
// The Julian Date of the Unix Time epoch is 2440587.5
|
|
if(!clock) clock = this.clock;
|
|
return ( clock.getTime() / 86400000.0 ) + 2440587.5;
|
|
}
|
|
VirtualSky.prototype.getNegative = function(colour){
|
|
var end = (colour.indexOf("rgb") == 0) ? (colour.lastIndexOf(")")) : 0;
|
|
if(end == 0) return colour;
|
|
var rgb = colour.substring(colour.indexOf("(")+1,end).split(",");
|
|
return (rgb.length==3) ? ('rgb('+(255-rgb[0])+','+(255-rgb[1])+','+(255-rgb[2])+')') : ('rgba('+(255-rgb[0])+','+(255-rgb[1])+','+(255-rgb[2])+','+(rgb[3])+')');
|
|
}
|
|
// Calculate the Great Circle angular distance (in radians) between two points defined by d1,l1 and d2,l2
|
|
VirtualSky.prototype.greatCircle = function(l1,d1,l2,d2){
|
|
return Math.acos(Math.cos(d1)*Math.cos(d2)*Math.cos(l1-l2)+Math.sin(d1)*Math.sin(d2));
|
|
}
|
|
|
|
// Bind events
|
|
VirtualSky.prototype.bind = function(ev,fn){
|
|
if(typeof ev!=="string" || typeof fn!=="function")
|
|
return this;
|
|
if(this.events[ev])
|
|
this.events[ev].push(fn);
|
|
else this.events[ev] = [fn];
|
|
return this;
|
|
}
|
|
// Trigger a defined event with arguments. This is meant for internal use
|
|
// sky.trigger("zoom",args)
|
|
VirtualSky.prototype.trigger = function(ev,args){
|
|
if(typeof ev!=="string") return;
|
|
if(typeof args!=="object") args = {};
|
|
var o = [];
|
|
var _obj = this;
|
|
if(typeof this.events[ev]==="object")
|
|
for(i = 0 ; i < this.events[ev].length ; i++)
|
|
if(typeof this.events[ev][i]==="function")
|
|
o.push(this.events[ev][i].call(_obj,args))
|
|
if(o.length > 0) return o
|
|
}
|
|
|
|
// Some useful functions
|
|
function convertTZ(s){
|
|
function formatHour(h){
|
|
var s = (h >= 0 ? "+" : "-");
|
|
h = Math.abs(h);
|
|
var m = (h - Math.floor(h))*60;
|
|
var h = Math.floor(h);
|
|
return s+(h < 10 ? "0"+h : h)+(m < 10 ? "0"+m : m);
|
|
}
|
|
var tzs = { A:1, ACDT:10.5, ACST:9.5, ADT:-3, AEDT:11, AEST:10, AKDT:-8, AKST:-9,
|
|
AST:-4, AWST:8, B:2, BST:1, C:3, CDT:-5, CEDT:2, CEST:2, CET:1, CST:-6, CXT:7,
|
|
D:4, E:5, EDT:-4, EEDT:3, EEST:3, EET:2, EST:-5, F:6, G:7, GMT:0, H:8, HAA:-3,
|
|
HAC:-5, HADT:-9, HAE:-4, HAP:-7, HAR:-6, HAST:-10, HAT:-2.5, HAY:-8, HNA:-4, HNC:-6,
|
|
HNE:-5, HNP:-8, HNR:-7, HNT:-3.5, HNY:-9, I:9, IST:9, IST:1, JST:9, K:10, L:11,
|
|
M:12, MDT:-6, MESZ:2, MEZ:1, MST:-7, N:-1, NDT:-2.5, NFT:11.5, NST:-3.5, O:-2, P:-3,
|
|
PDT:-7, PST:-8, Q:-4, R:-5, S:-6, T:-7, U:-8, UTC:0, UT:0, V:-9, W:-10, WEDT:1, WEST:1,
|
|
WET:0, WST:8, X:-11, Y:-12, Z:0 }
|
|
// Get location of final space character
|
|
var i = s.lastIndexOf(' ');
|
|
// Replace the time zone with the +XXXX version
|
|
if(i > 0 && tzs[s.substr(i+1)]){
|
|
return s.substring(0,i)+" "+formatHour(tzs[s.substr(i+1)]);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
$.virtualsky = function(placeholder,input) {
|
|
if(typeof input==="object") input.container = placeholder;
|
|
else {
|
|
if(typeof placeholder==="string") input = { container: placeholder };
|
|
else input = placeholder;
|
|
}
|
|
input.plugins = $.virtualsky.plugins;
|
|
return new VirtualSky(input);
|
|
};
|
|
|
|
$.virtualsky.plugins = [];
|
|
})(jQuery);
|