3962 lines
146 KiB
JavaScript
3962 lines
146 KiB
JavaScript
// Copyright 2015 Olaf Frohn https://github.com/ofrohn, see LICENSE
|
||
!(function() {
|
||
var Celestial = {
|
||
version: '0.6.8',
|
||
container: null,
|
||
data: []
|
||
};
|
||
|
||
var ANIMDISTANCE = 0.035, // Rotation animation threshold, ~2deg in radians
|
||
ANIMSCALE = 1.4, // Zoom animation threshold, scale factor
|
||
ANIMINTERVAL_R = 2000, // Rotation duration scale in ms
|
||
ANIMINTERVAL_P = 2500, // Projection duration in ms
|
||
ANIMINTERVAL_Z = 1500, // Zoom duration scale in ms
|
||
ZOOMEXTENT = 10; // Maximum extent of zoom (max/min)
|
||
|
||
var cfg, prjMap, zoom, map, circle;
|
||
|
||
// Show it all, with the given config, otherwise with default settings
|
||
Celestial.display = function(config) {
|
||
var par, container = Celestial.container,
|
||
animations = [], current = 0, repeat = false, aID;
|
||
|
||
//Mash config with default settings
|
||
cfg = settings.set(config);
|
||
cfg.stars.size = cfg.stars.size || 7; // Nothing works without these
|
||
cfg.stars.exponent = cfg.stars.exponent || -0.28;
|
||
cfg.center = cfg.center || [0,0];
|
||
if (!cfg.lang || cfg.lang.search(/^de|es$/) === -1) cfg.lang = "name";
|
||
|
||
var parent = $(cfg.container);
|
||
if (parent) {
|
||
par = "#"+cfg.container;
|
||
var st = window.getComputedStyle(parent, null);
|
||
if (!parseInt(st.width) && !cfg.width) parent.style.width = px(parent.parentNode.clientWidth);
|
||
} else {
|
||
par = "body";
|
||
parent = null;
|
||
}
|
||
|
||
var margin = [16, 16],
|
||
width = getWidth(),
|
||
proj = getProjection(cfg.projection);
|
||
if (cfg.lines.graticule.lat && cfg.lines.graticule.lat.pos[0] === "outline") proj.scale -= 2;
|
||
|
||
if (!proj) return;
|
||
|
||
var trans = cfg.transform || "equatorial",
|
||
ratio = proj.ratio,
|
||
height = width / ratio,
|
||
scale = proj.scale * width/1024,
|
||
starbase = cfg.stars.size,
|
||
dsobase = cfg.dsos.size || starbase,
|
||
starexp = cfg.stars.exponent,
|
||
dsoexp = cfg.dsos.exponent || starexp, //Object size base & exponent
|
||
adapt = 1,
|
||
rotation = getAngles(cfg.center),
|
||
path = cfg.datapath || "";
|
||
path = path.replace(/([^\/]$)/, "$1\/");
|
||
|
||
|
||
if (par != "body") $(cfg.container).style.height = px(height);
|
||
|
||
prjMap = Celestial.projection(cfg.projection).rotate(rotation).translate([width/2, height/2]).scale(scale);
|
||
|
||
zoom = d3.geo.zoom().projection(prjMap).center([width/2, height/2]).scaleExtent([scale, scale*ZOOMEXTENT]).on("zoom.redraw", redraw);
|
||
|
||
var canvas = d3.select(par).selectAll("canvas");
|
||
if (canvas[0].length === 0) canvas = d3.select(par).append("canvas");
|
||
canvas.attr("width", width).attr("height", height);
|
||
var context = canvas.node().getContext("2d");
|
||
|
||
var graticule = d3.geo.graticule().minorStep([15,10]);
|
||
|
||
map = d3.geo.path().projection(prjMap).context(context);
|
||
|
||
//parent div with id #celestial-map or body
|
||
if (container) container.selectAll("*").remove();
|
||
else container = d3.select(par).append("container");
|
||
|
||
if (cfg.interactive) canvas.call(zoom);
|
||
else canvas.attr("style", "cursor: default!important");
|
||
|
||
setClip(proj.clip);
|
||
|
||
d3.select(window).on('resize', resize);
|
||
d3.select(par).on('dblclick', function () { zoomBy(1.5625); return false; });
|
||
|
||
if (cfg.controls === true && $("celestial-zoomin") === null) {
|
||
d3.select(par).append("input").attr("type", "button").attr("id", "celestial-zoomin").attr("value", "\u002b").on("click", function () { zoomBy(1.25); return false; });
|
||
d3.select(par).append("input").attr("type", "button").attr("id", "celestial-zoomout").attr("value", "\u2212").on("click", function () { zoomBy(0.8); return false; });
|
||
}
|
||
|
||
if (cfg.location === true) {
|
||
circle = d3.geo.circle().angle([90]);
|
||
container.append("path").datum(circle).attr("class", "horizon");
|
||
if ($("loc") === null) geo(cfg);
|
||
else if (cfg.follow === "zenith") rotate({center:Celestial.zenith()});
|
||
fldEnable("horizon-show", proj.clip);
|
||
}
|
||
|
||
if (cfg.form === true && $("params") === null) form(cfg);
|
||
if ($("error") === null) d3.select("body").append("div").attr("id", "error");
|
||
|
||
|
||
function load() {
|
||
//Celestial planes
|
||
for (var key in cfg.lines) {
|
||
if (!has(cfg.lines, key)) continue;
|
||
if (key === "graticule") {
|
||
container.append("path").datum(graticule).attr("class", "graticule");
|
||
if (has(cfg.lines.graticule, "lon") && cfg.lines.graticule.lon.pos.length > 0)
|
||
container.selectAll(".gridvalues_lon")
|
||
.data(getGridValues("lon", cfg.lines.graticule.lon.pos))
|
||
.enter().append("path")
|
||
.attr("class", "graticule_lon");
|
||
if (has(cfg.lines.graticule, "lat") && cfg.lines.graticule.lat.pos.length > 0)
|
||
container.selectAll(".gridvalues_lat")
|
||
.data(getGridValues("lat", cfg.lines.graticule.lat.pos))
|
||
.enter().append("path")
|
||
.attr("class", "graticule_lat");
|
||
} else {
|
||
container.append("path")
|
||
.datum(d3.geo.circle().angle([90]).origin(transformDeg(poles[key], euler[trans])) )
|
||
.attr("class", key);
|
||
}
|
||
}
|
||
|
||
//Milky way outline
|
||
d3.json(path + "mw.json", function(error, json) {
|
||
if (error) {
|
||
//window.alert("Your Browser doesn't support local file loading or the file doesn't exist. See readme.md");
|
||
return console.warn(error);
|
||
}
|
||
|
||
var mw = getData(json, trans);
|
||
|
||
container.selectAll(".mway")
|
||
.data(mw.features)
|
||
.enter().append("path")
|
||
.attr("class", "mw");
|
||
redraw();
|
||
});
|
||
|
||
//Constellation names or designation
|
||
d3.json(path + "constellations.json", function(error, json) {
|
||
if (error) return console.warn(error);
|
||
|
||
var con = getData(json, trans);
|
||
|
||
container.selectAll(".constnames")
|
||
.data(con.features)
|
||
.enter().append("text")
|
||
.attr("class", "constname");
|
||
|
||
var l = getConstellationList(json, trans);
|
||
if ($("constellation")) {
|
||
var sel = d3.select("#constellation"),
|
||
selected = 0,
|
||
list = Object.keys(l).map( function (key, i) {
|
||
if (key === config.constellation) selected = i;
|
||
return {o:key, n:l[key].name};
|
||
});
|
||
list = [{o:"", n:"(Select constellation)"}].concat(list);
|
||
|
||
sel.selectAll('option').data(list).enter().append('option')
|
||
.attr("value", function (d) { return d.o; })
|
||
.text(function (d) { return d.n; });
|
||
sel.property("selectedIndex", selected);
|
||
//$("constellation").firstChild.disabled = true;
|
||
}
|
||
Celestial.constellations = l;
|
||
|
||
redraw();
|
||
});
|
||
|
||
//Constellation boundaries
|
||
d3.json(path + "constellations.bounds.json", function(error, json) {
|
||
if (error) return console.warn(error);
|
||
|
||
var conb = getData(json, trans);
|
||
|
||
container.selectAll(".bounds")
|
||
.data(conb.features)
|
||
.enter().append("path")
|
||
.attr("class", "boundaryline");
|
||
redraw();
|
||
});
|
||
|
||
//Constellation lines
|
||
d3.json(path + "constellations.lines.json", function(error, json) {
|
||
if (error) return console.warn(error);
|
||
|
||
var conl = getData(json, trans);
|
||
|
||
container.selectAll(".lines")
|
||
.data(conl.features)
|
||
.enter().append("path")
|
||
.attr("class", "constline");
|
||
redraw();
|
||
});
|
||
|
||
//Stars
|
||
d3.json(path + cfg.stars.data, function(error, json) {
|
||
if (error) return console.warn(error);
|
||
|
||
var st = getData(json, trans);
|
||
|
||
container.selectAll(".stars")
|
||
.data(st.features)
|
||
.enter().append("path")
|
||
.attr("class", "star");
|
||
|
||
redraw();
|
||
});
|
||
|
||
//Deep space objects
|
||
d3.json(path + cfg.dsos.data, function(error, json) {
|
||
if (error) return console.warn(error);
|
||
|
||
var ds = getData(json, trans);
|
||
|
||
container.selectAll(".dsos")
|
||
.data(ds.features)
|
||
.enter().append("path")
|
||
.attr("class", "dso" );
|
||
|
||
redraw();
|
||
});
|
||
|
||
//Planets, Sun & (Moon tbi)
|
||
d3.json(path + "planets.json", function(error, json) {
|
||
if (error) return console.warn(error);
|
||
|
||
var pl = getPlanets(json, trans);
|
||
|
||
container.selectAll(".planets")
|
||
.data(pl)
|
||
.enter().append("path")
|
||
.attr("class", "planet");
|
||
|
||
redraw();
|
||
});
|
||
|
||
if (Celestial.data.length > 0) {
|
||
Celestial.data.forEach( function(d) {
|
||
if (has(d, "file")) d3.json(d.file, d.callback);
|
||
else setTimeout(d.callback, 0);
|
||
}, this);
|
||
}
|
||
}
|
||
|
||
// Zoom by factor; >1 larger <1 smaller
|
||
function zoomBy(factor) {
|
||
if (!factor || factor === 1) return;
|
||
var sc0 = prjMap.scale(),
|
||
sc1 = sc0 * factor,
|
||
ext = zoom.scaleExtent(),
|
||
interval = ANIMINTERVAL_Z * Math.sqrt(Math.abs(1-factor));
|
||
|
||
if (sc1 < ext[0]) sc1 = ext[0];
|
||
if (sc1 > ext[1]) sc1 = ext[1];
|
||
var zTween = d3.interpolateNumber(sc0, sc1);
|
||
d3.select({}).transition().duration(interval).tween("scale", function () {
|
||
return function(t) {
|
||
var z = zTween(t);
|
||
prjMap.scale(z);
|
||
redraw();
|
||
};
|
||
}).transition().duration(0).tween("scale", function () {
|
||
zoom.scale(sc1);
|
||
redraw();
|
||
});
|
||
return interval;
|
||
}
|
||
|
||
function apply(config) {
|
||
cfg = cfg.set(config);
|
||
redraw();
|
||
}
|
||
|
||
|
||
function rotate(config) {
|
||
var cFrom = cfg.center,
|
||
rot = prjMap.rotate(),
|
||
sc = prjMap.scale(),
|
||
interval = ANIMINTERVAL_R,
|
||
keep = false,
|
||
cTween, zTween, oTween,
|
||
oof = cfg.orientationfixed;
|
||
|
||
if (Round(rot[1], 1) === -Round(config.center[1], 1)) keep = true; //keep lat fixed if equal
|
||
cfg = cfg.set(config);
|
||
var d = Round(d3.geo.distance(cFrom, cfg.center), 2);
|
||
var o = d3.geo.distance([cFrom[2],0], [cfg.center[2],0]);
|
||
if (d < ANIMDISTANCE && o < ANIMDISTANCE) {
|
||
rotation = getAngles(cfg.center);
|
||
prjMap.rotate(rotation);
|
||
redraw();
|
||
} else {
|
||
// Zoom interpolator
|
||
if (sc > scale * ANIMSCALE) zTween = d3.interpolateNumber(sc, scale);
|
||
else zTween = function () { return sc; };
|
||
// Orientation interpolator
|
||
if (o === 0) oTween = function () { return rot[2]; };
|
||
else oTween = interpolateAngle(cFrom[2], cfg.center[2]);
|
||
if (d > 3.14) cfg.center[0] -= 0.01; //180deg turn doesn't work well
|
||
cfg.orientationfixed = false;
|
||
// Rotation interpolator
|
||
if (d === 0) cTween = function () { return cfg.center; };
|
||
else cTween = d3.geo.interpolate(cFrom, cfg.center);
|
||
interval = (d !== 0) ? interval * d : interval * o; // duration scaled by ang. distance
|
||
d3.select({}).transition().duration(interval).tween("center", function () {
|
||
return function(t) {
|
||
var c = getAngles(cTween(t));
|
||
c[2] = oTween(t);
|
||
var z = t < 0.5 ? zTween(t) : zTween(1-t);
|
||
if (keep) c[1] = rot[1];
|
||
prjMap.scale(z);
|
||
prjMap.rotate(c);
|
||
redraw();
|
||
};
|
||
}).transition().duration(0).tween("center", function () {
|
||
cfg.orientationfixed = oof;
|
||
rotation = getAngles(cfg.center);
|
||
prjMap.rotate(rotation);
|
||
redraw();
|
||
});
|
||
}
|
||
return interval;
|
||
}
|
||
|
||
function resize(set) {
|
||
width = getWidth();
|
||
if (cfg.width === width && !set) return;
|
||
height = width/ratio;
|
||
scale = proj.scale * width/1024;
|
||
canvas.attr("width", width).attr("height", height);
|
||
zoom.scaleExtent([scale, scale*ZOOMEXTENT]).scale(scale);
|
||
prjMap.translate([width/2, height/2]).scale(scale);
|
||
if (parent) parent.style.height = px(height);
|
||
redraw();
|
||
}
|
||
|
||
function reproject(config) {
|
||
var prj = getProjection(config.projection);
|
||
if (!prj) return;
|
||
|
||
var rot = prjMap.rotate(), ctr = prjMap.center(), sc = prjMap.scale(), ext = zoom.scaleExtent(),
|
||
prjFrom = Celestial.projection(cfg.projection).center(ctr).translate([width/2, height/2]).scale([ext[0]]),
|
||
interval = ANIMINTERVAL_P,
|
||
delay = 0,
|
||
rTween = d3.interpolateNumber(ratio, prj.ratio);
|
||
|
||
if (proj.clip != prj.clip) interval = 0; // Different clip = no transition
|
||
|
||
var prjTo = Celestial.projection(config.projection).center(ctr).translate([width/2, width/prj.ratio/2]).scale([prj.scale * width/1024]);
|
||
var bAdapt = cfg.adaptable;
|
||
|
||
if (sc > ext[0]) {
|
||
delay = zoomBy(0.1);
|
||
setTimeout(reproject, delay, config);
|
||
return delay + interval;
|
||
}
|
||
|
||
if (cfg.location) fldEnable("horizon-show", prj.clip);
|
||
|
||
prjMap = projectionTween(prjFrom, prjTo);
|
||
cfg.adaptable = false;
|
||
|
||
d3.select({}).transition().duration(interval).tween("projection", function () {
|
||
return function(_) {
|
||
prjMap.alpha(_).rotate(rot);
|
||
map.projection(prjMap);
|
||
setClip(prj.clip);
|
||
ratio = rTween(_);
|
||
height = width/ratio;
|
||
canvas.attr("width", width).attr("height", height);
|
||
if (parent) parent.style.height = px(height);
|
||
redraw();
|
||
};
|
||
}).transition().duration(0).tween("projection", function () {
|
||
proj = prj;
|
||
ratio = proj.ratio;
|
||
height = width / proj.ratio;
|
||
scale = proj.scale * width/1024;
|
||
canvas.attr("width", width).attr("height", height);
|
||
if (parent) parent.style.height = px(height);
|
||
cfg.projection = config.projection;
|
||
prjMap = Celestial.projection(config.projection).rotate(rot).translate([width/2, height/2]).scale(scale);
|
||
map.projection(prjMap);
|
||
setClip(proj.clip);
|
||
zoom.projection(prjMap).scaleExtent([scale, scale*ZOOMEXTENT]).scale(scale);
|
||
cfg.adaptable = bAdapt;
|
||
redraw();
|
||
});
|
||
return interval;
|
||
}
|
||
|
||
|
||
function redraw() {
|
||
var rot = prjMap.rotate();
|
||
|
||
if (cfg.adaptable) adapt = Math.sqrt(prjMap.scale()/scale);
|
||
if (!adapt) adapt = 1;
|
||
starbase = cfg.stars.size;
|
||
starexp = cfg.stars.exponent;
|
||
dsobase = cfg.dsos.size || starbase;
|
||
dsoexp = cfg.dsos.exponent;
|
||
|
||
if (cfg.orientationfixed) {
|
||
rot[2] = cfg.center[2];
|
||
prjMap.rotate(rot);
|
||
}
|
||
cfg.center = [-rot[0], -rot[1], rot[2]];
|
||
|
||
setCenter(cfg.center, cfg.transform);
|
||
clear();
|
||
|
||
drawOutline();
|
||
|
||
//Draw all types of objects on the canvas
|
||
if (cfg.mw.show) {
|
||
container.selectAll(".mw").each(function(d) { setStyle(cfg.mw.style); map(d); context.fill(); });
|
||
}
|
||
|
||
for (var key in cfg.lines) {
|
||
if (!has(cfg.lines, key)) continue;
|
||
if (cfg.lines[key].show !== true) continue;
|
||
setStyle(cfg.lines[key]);
|
||
container.selectAll("."+key).attr("d", map);
|
||
context.stroke();
|
||
}
|
||
|
||
if (has(cfg.lines.graticule, "lon")) {
|
||
setTextStyle(cfg.lines.graticule.lon);
|
||
container.selectAll(".graticule_lon").each(function(d, i) {
|
||
if (clip(d.geometry.coordinates)) {
|
||
var pt = prjMap(d.geometry.coordinates);
|
||
gridOrientation(pt, d.properties.orientation);
|
||
context.fillText(d.properties.value, pt[0], pt[1]);
|
||
}
|
||
});
|
||
}
|
||
|
||
if (has(cfg.lines.graticule, "lat")) {
|
||
setTextStyle(cfg.lines.graticule.lat);
|
||
container.selectAll(".graticule_lat").each(function(d, i) {
|
||
if (clip(d.geometry.coordinates)) {
|
||
var pt = prjMap(d.geometry.coordinates);
|
||
gridOrientation(pt, d.properties.orientation);
|
||
context.fillText(d.properties.value, pt[0], pt[1]);
|
||
}
|
||
});
|
||
}
|
||
|
||
drawOutline(true);
|
||
|
||
if (cfg.constellations.show) {
|
||
if (cfg.constellations.bounds) {
|
||
container.selectAll(".boundaryline").each(function(d) {
|
||
setStyle(cfg.constellations.boundstyle);
|
||
if (Celestial.constellation && Celestial.constellation === d.id) {
|
||
context.lineWidth *= 1.5;
|
||
context.setLineDash([]);
|
||
}
|
||
map(d);
|
||
context.stroke();
|
||
});
|
||
drawOutline(true);
|
||
}
|
||
|
||
if (cfg.constellations.names) {
|
||
setTextStyle(cfg.constellations.namestyle);
|
||
container.selectAll(".constname").each( function(d) {
|
||
if (clip(d.geometry.coordinates)) {
|
||
setConstStyle(d.properties.rank, cfg.constellations.namestyle.font);
|
||
var pt = prjMap(d.geometry.coordinates);
|
||
context.fillText(constName(d), pt[0], pt[1]);
|
||
}
|
||
});
|
||
}
|
||
|
||
if (cfg.constellations.lines) {
|
||
container.selectAll(".constline").each(function(d) {
|
||
setStyle(cfg.constellations.linestyle);
|
||
map(d);
|
||
context.stroke();
|
||
});
|
||
}
|
||
|
||
}
|
||
|
||
|
||
if (cfg.stars.show) {
|
||
setStyle(cfg.stars.style);
|
||
container.selectAll(".star").each(function(d) {
|
||
if (clip(d.geometry.coordinates) && d.properties.mag <= cfg.stars.limit) {
|
||
var pt = prjMap(d.geometry.coordinates),
|
||
r = starSize(d);
|
||
context.fillStyle = starColor(d);
|
||
context.beginPath();
|
||
context.arc(pt[0], pt[1], r, 0, 2 * Math.PI);
|
||
context.closePath();
|
||
context.fill();
|
||
if (cfg.stars.names && d.properties.mag <= cfg.stars.namelimit*adapt) {
|
||
setTextStyle(cfg.stars.namestyle);
|
||
context.fillText(starName(d), pt[0]+r, pt[1]);
|
||
}
|
||
if (cfg.stars.proper && d.properties.mag <= cfg.stars.propernamelimit*adapt) {
|
||
setTextStyle(cfg.stars.propernamestyle);
|
||
context.fillText(starProperName(d), pt[0]-r, pt[1]);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
if (cfg.dsos.show) {
|
||
container.selectAll(".dso").each(function(d) {
|
||
if (clip(d.geometry.coordinates) && dsoDisplay(d.properties, cfg.dsos.limit)) {
|
||
var pt = prjMap(d.geometry.coordinates),
|
||
type = d.properties.type;
|
||
setStyle(cfg.dsos.symbols[type]);
|
||
var r = dsoSymbol(d, pt);
|
||
if (has(cfg.dsos.symbols[type], "stroke")) context.stroke();
|
||
else context.fill();
|
||
|
||
if (cfg.dsos.names && dsoDisplay(d.properties, cfg.dsos.namelimit)) {
|
||
setTextStyle(cfg.dsos.namestyle);
|
||
context.fillStyle = cfg.dsos.symbols[type].fill;
|
||
context.fillText(dsoName(d), pt[0]+r, pt[1]-r);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
if (cfg.location && cfg.transform === "equatorial" && cfg.planets.show && Celestial.origin) {
|
||
var dt = Celestial.date(),
|
||
o = Celestial.origin(dt).spherical();
|
||
container.selectAll(".planet").each(function(d) {
|
||
var id = d.id();
|
||
var p = d(dt).equatorial(o);
|
||
if (clip(p.pos)) {
|
||
var pt = prjMap(p.pos),
|
||
sym = cfg.planets.symbols[id];
|
||
if (id === "lun") {
|
||
Canvas.symbol().type("crescent").size(144).age(p.age).position(pt)(context);
|
||
} else if (id === 'added-planet') {
|
||
const pointStyle = {
|
||
stroke: 'rgba(255, 0, 204, 1)',
|
||
fill: 'rgba(255, 0, 204, 0.15)',
|
||
width: 5,
|
||
};
|
||
const textStyle = {
|
||
fill:'rgba(255, 0, 204, 1)',
|
||
font: '15px Helvetica, Arial, sans-serif',
|
||
align: 'left',
|
||
baseline: 'bottom'
|
||
};
|
||
const r = 5;
|
||
setStyle(pointStyle);
|
||
// Start the drawing path
|
||
context.beginPath();
|
||
// Thats a circle in html5 canvas
|
||
context.arc(pt[0], pt[1], r, 0, 2 * Math.PI);
|
||
// Finish the drawing path
|
||
context.closePath();
|
||
// Draw a line along the path with the prevoiusly set stroke color and line width
|
||
context.stroke();
|
||
// Fill the object path with the prevoiusly set fill color
|
||
context.fill();
|
||
// Set text styles
|
||
//setTextStyle(textStyle);
|
||
// and draw text on canvas
|
||
//context.fillText('assss', pt[0]+r, pt[1]+r);
|
||
} else {
|
||
setTextStyle(cfg.planets.style);
|
||
context.fillStyle = sym.fill;
|
||
context.fillText(sym.symbol, pt[0], pt[1]);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
if (Celestial.data.length > 0) {
|
||
Celestial.data.forEach( function(d) {
|
||
d.redraw();
|
||
});
|
||
}
|
||
|
||
// drawOutline(true);
|
||
|
||
if (cfg.location && cfg.horizon.show && !proj.clip) {
|
||
circle.origin(Celestial.nadir());
|
||
setStyle(cfg.horizon);
|
||
container.selectAll(".horizon").datum(circle).attr("d", map);
|
||
context.fill();
|
||
if (cfg.horizon.stroke) context.stroke();
|
||
}
|
||
|
||
if (cfg.controls) {
|
||
zoomState(prjMap.scale());
|
||
}
|
||
}
|
||
|
||
function drawOutline(stroke) {
|
||
var rot = prjMap.rotate();
|
||
|
||
prjMap.rotate([0,0]);
|
||
setStyle(cfg.background);
|
||
container.selectAll(".outline").attr("d", map);
|
||
if (stroke === true)
|
||
context.stroke();
|
||
else
|
||
context.fill();
|
||
prjMap.rotate(rot);
|
||
}
|
||
|
||
// Helper functions -------------------------------------------------
|
||
|
||
function clip(coords) {
|
||
return proj.clip && d3.geo.distance(cfg.center, coords) > halfπ ? 0 : 1;
|
||
}
|
||
|
||
function setStyle(s) {
|
||
context.fillStyle = s.fill || null;
|
||
context.strokeStyle = s.stroke || null;
|
||
context.lineWidth = s.width || null;
|
||
context.globalAlpha = s.opacity || 1;
|
||
context.font = s.font || null;
|
||
if (has(s, "dash")) context.setLineDash(s.dash); else context.setLineDash([]);
|
||
context.beginPath();
|
||
}
|
||
|
||
function setTextStyle(s) {
|
||
context.fillStyle = s.fill;
|
||
context.textAlign = s.align || "left";
|
||
context.textBaseline = s.baseline || "bottom";
|
||
context.globalAlpha = s.opacity || 1;
|
||
context.font = s.font;
|
||
}
|
||
|
||
function setConstStyle(rank, font) {
|
||
if (!isArray(font)) context.font = font;
|
||
else if (font.length === 1) context.font = font[0];
|
||
else if (rank > font.length) context.font = font[font.length-1];
|
||
else context.font = font[rank-1];
|
||
}
|
||
|
||
|
||
function zoomState(sc) {
|
||
var czi = $("celestial-zoomin"),
|
||
czo = $("celestial-zoomout");
|
||
if (!czi || !czo) return;
|
||
czi.disabled = sc >= scale*ZOOMEXTENT*0.99;
|
||
czo.disabled = sc <= scale;
|
||
}
|
||
|
||
function setClip(setit) {
|
||
if (setit) {
|
||
prjMap.clipAngle(90);
|
||
container.selectAll(".outline").remove();
|
||
container.append("path").datum(d3.geo.circle().angle([90])).attr("class", "outline");
|
||
} else {
|
||
prjMap.clipAngle(null);
|
||
container.selectAll(".outline").remove();
|
||
container.append("path").datum(graticule.outline).attr("class", "outline");
|
||
}
|
||
}
|
||
|
||
function dsoDisplay(prop, limit) {
|
||
return prop.mag === 999 && Math.sqrt(parseInt(prop.dim)) > limit ||
|
||
prop.mag !== 999 && prop.mag <= limit;
|
||
}
|
||
|
||
function dsoSymbol(d, pt) {
|
||
var prop = d.properties;
|
||
var size = dsoSize(prop) || 9,
|
||
type = dsoShape(prop.type);
|
||
Canvas.symbol().type(type).size(size).position(pt)(context);
|
||
return Math.sqrt(size)/2;
|
||
}
|
||
|
||
function dsoShape(type) {
|
||
if (!type || !has(cfg.dsos.symbols, type)) return "circle";
|
||
else return cfg.dsos.symbols[type].shape;
|
||
}
|
||
|
||
function dsoSize(prop) {
|
||
if (!prop.mag || prop.mag === 999) return Math.pow(parseInt(prop.dim) * dsobase * adapt / 7, 0.5);
|
||
return Math.pow(2 * dsobase * adapt - prop.mag, dsoexp);
|
||
}
|
||
|
||
|
||
function dsoName(d) {
|
||
var prop = d.properties;
|
||
if (prop.name === "") return;
|
||
if (cfg.dsos.desig && prop.desig) return prop.desig;
|
||
return prop.name;
|
||
}
|
||
|
||
/*Star designation, if desig = false, no long desigs */
|
||
function starName(d) {
|
||
var name = d.properties.desig;
|
||
if (!cfg.stars.desig) return name.replace(/^(HD|HIP|V\d{3}).+/, "");
|
||
return name;
|
||
}
|
||
|
||
function starProperName(d) {
|
||
var name = d.properties.name;
|
||
|
||
return name;
|
||
}
|
||
|
||
function starSize(d) {
|
||
var mag = d.properties.mag;
|
||
if (mag === null) return 0.1;
|
||
var r = starbase * adapt * Math.exp(starexp * (mag+2));
|
||
return Math.max(r, 0.1);
|
||
}
|
||
|
||
function starColor(d) {
|
||
var bv = d.properties.bv;
|
||
if (!cfg.stars.colors || isNaN(bv)) {return cfg.stars.style.fill; }
|
||
return bvcolor(bv);
|
||
}
|
||
|
||
function constName(d) {
|
||
return cfg.constellations.desig ? d.properties.desig : d.properties[cfg.lang];
|
||
}
|
||
|
||
function gridOrientation(pos, orient) {
|
||
var o = orient.split(""), h = "center", v = "middle";
|
||
for (var i = o.length-1; i >= 0; i--) {
|
||
switch(o[i]) {
|
||
case "N": v = "bottom"; break;
|
||
case "S": v = "top"; break;
|
||
case "E": h = "left"; pos[0] += 2; break;
|
||
case "W": h = "right"; pos[0] -= 2; break;
|
||
}
|
||
}
|
||
context.textAlign = h;
|
||
context.textBaseline = v;
|
||
return pos;
|
||
}
|
||
|
||
function clear() {
|
||
context.clearRect(0, 0, width + margin[0], height + margin[1]);
|
||
}
|
||
|
||
function getWidth() {
|
||
if (cfg.width && cfg.width > 0) return cfg.width;
|
||
if (parent) return parent.clientWidth - margin[0];
|
||
return window.innerWidth - margin[0]*2;
|
||
}
|
||
|
||
function getProjection(p) {
|
||
if (!has(projections, p)) return;
|
||
var res = projections[p];
|
||
if (!has(res, "ratio")) res.ratio = 2; // Default w/h ratio 2:1
|
||
return res;
|
||
}
|
||
|
||
function getAngles(coords) {
|
||
if (coords === null) return [0,0,0];
|
||
var rot = eulerAngles.equatorial;
|
||
if (!coords[2]) coords[2] = 0;
|
||
return [rot[0] - coords[0], rot[1] - coords[1], rot[2] + coords[2]];
|
||
}
|
||
|
||
|
||
function animate() {
|
||
if (!animations || animations.length < 1) return;
|
||
|
||
var d, a = animations[current];
|
||
|
||
switch (a.param) {
|
||
case "projection": d = reproject({projection:a.value}); break;
|
||
case "center": d = rotate({center:a.value}); break;
|
||
case "zoom": d = zoomBy(a.value);
|
||
}
|
||
if (a.callback) setTimeout(a.callback, d);
|
||
current++;
|
||
if (repeat === true && current === animations.length) current = 0;
|
||
d = a.duration === 0 || a.duration < d ? d : a.duration;
|
||
if (current < animations.length) aID = setTimeout(animate, d);
|
||
}
|
||
|
||
function stop() {
|
||
clearTimeout(aID);
|
||
//current = 0;
|
||
//repeat = false;
|
||
}
|
||
|
||
// Exported objects and functions for adding data
|
||
this.container = container;
|
||
this.clip = clip;
|
||
this.map = map;
|
||
this.mapProjection = prjMap;
|
||
this.context = context;
|
||
this.setStyle = setStyle;
|
||
this.setTextStyle = setTextStyle;
|
||
this.setConstStyle = setConstStyle;
|
||
this.dsoSymbol = dsoSymbol;
|
||
this.redraw = redraw;
|
||
this.resize = function(config) {
|
||
if (config && has(config, "width")) cfg.width = config.width;
|
||
resize(true);
|
||
};
|
||
this.reload = function(config) {
|
||
if (!config || !has(config, "transform")) return;
|
||
trans = cfg.transform = config.transform;
|
||
if (trans === "equatorial") graticule.minorStep([15,10]);
|
||
else graticule.minorStep([10,10]);
|
||
container.selectAll("*").remove();
|
||
setClip();
|
||
container.append("path").datum(circle).attr("class", "horizon");
|
||
load();
|
||
};
|
||
this.apply = function(config) { apply(config); };
|
||
this.reproject = function(config) { return reproject(config); };
|
||
this.rotate = function(config) { if (!config) return cfg.center; return rotate(config); };
|
||
this.zoomBy = function(factor) { if (!factor) return prjMap.scale()/scale; return zoomBy(factor); };
|
||
this.color = function(type) {
|
||
if (!type) return "#000";
|
||
if (has(cfg.dsos.symbols, type)) return cfg.dsos.symbols[type].fill;
|
||
return "#000";
|
||
};
|
||
this.animate = function(anims, dorepeat) {
|
||
if (!anims) return;
|
||
animations = anims;
|
||
current = 0;
|
||
repeat = dorepeat ? true : false;
|
||
animate();
|
||
};
|
||
this.stop = function(wipe) {
|
||
stop();
|
||
if (wipe === true) animations = [];
|
||
};
|
||
this.go = function(index) {
|
||
if (animations.length < 1) return;
|
||
if (index && index < animations.length) current = index;
|
||
animate();
|
||
};
|
||
if (!has(this, "date"))
|
||
this.date = function() { console.log("Celestial.date() needs config.location = true to work." ); };
|
||
|
||
load();
|
||
};
|
||
|
||
|
||
//Flipped projection generated on the fly
|
||
Celestial.projection = function(projection) {
|
||
var p, raw, forward;
|
||
|
||
if (!has(projections, projection)) { throw new Error("Projection not supported: " + projection); }
|
||
p = projections[projection];
|
||
|
||
if (p.arg !== null) {
|
||
raw = d3.geo[projection].raw(p.arg);
|
||
} else {
|
||
raw = d3.geo[projection].raw;
|
||
}
|
||
|
||
forward = function(λ, φ) {
|
||
var coords = raw(-λ, φ);
|
||
return coords;
|
||
};
|
||
|
||
forward.invert = function(x, y) {
|
||
try {
|
||
var coords = raw.invert(x, y);
|
||
coords[0] = coords && -coords[0];
|
||
return coords;
|
||
} catch(e) { console.log(e); }
|
||
};
|
||
|
||
return d3.geo.projection(forward);
|
||
};
|
||
|
||
|
||
function projectionTween(a, b) {
|
||
var prj = d3.geo.projection(raw).scale(1),
|
||
center = prj.center,
|
||
translate = prj.translate,
|
||
α;
|
||
|
||
function raw(λ, φ) {
|
||
var pa = a([λ *= 180 / Math.PI, φ *= 180 / Math.PI]), pb = b([λ, φ]);
|
||
return [(1 - α) * pa[0] + α * pb[0], (α - 1) * pa[1] - α * pb[1]];
|
||
}
|
||
|
||
prj.alpha = function(_) {
|
||
if (!arguments.length) return α;
|
||
α = +_;
|
||
var ca = a.center(), cb = b.center(),
|
||
ta = a.translate(), tb = b.translate();
|
||
|
||
center([(1 - α) * ca[0] + α * cb[0], (1 - α) * ca[1] + α * cb[1]]);
|
||
translate([(1 - α) * ta[0] + α * tb[0], (1 - α) * ta[1] + α * tb[1]]);
|
||
return prj;
|
||
};
|
||
|
||
delete prj.translate;
|
||
delete prj.center;
|
||
return prj.alpha(0);
|
||
}
|
||
|
||
var eulerAngles = {
|
||
"equatorial": [0.0, 0.0, 0.0],
|
||
"ecliptic": [0.0, 0.0, 23.4393],
|
||
"galactic": [93.5949, 28.9362, -58.5988],
|
||
"supergalactic": [137.3100, 59.5283, 57.7303]
|
||
// "mars": [97.5,23.5,29]
|
||
};
|
||
|
||
var poles = {
|
||
"equatorial": [0.0, 90.0],
|
||
"ecliptic": [-90.0, 66.5607],
|
||
"galactic": [-167.1405, 27.1283],
|
||
"supergalactic": [-76.2458, 15.7089]
|
||
// "mars": [-42.3186, 52.8865]
|
||
};
|
||
|
||
Celestial.eulerAngles = function () { return eulerAngles; };
|
||
Celestial.poles = function () { return poles; };
|
||
|
||
|
||
var τ = Math.PI*2,
|
||
halfπ = Math.PI/2,
|
||
deg2rad = Math.PI/180;
|
||
|
||
|
||
//Transform equatorial into any coordinates, degrees
|
||
function transformDeg(c, euler) {
|
||
var res = transform( c.map( function(d) { return d * deg2rad; } ), euler);
|
||
return res.map( function(d) { return d / deg2rad; } );
|
||
}
|
||
|
||
//Transform equatorial into any coordinates, radians
|
||
function transform(c, euler) {
|
||
var x, y, z, β, γ, λ, φ, dψ, ψ, θ,
|
||
ε = 1.0e-5;
|
||
|
||
if (!euler) return c;
|
||
|
||
λ = c[0]; // celestial longitude 0..2pi
|
||
if (λ < 0) λ += τ;
|
||
φ = c[1]; // celestial latitude -pi/2..pi/2
|
||
|
||
λ -= euler[0]; // celestial longitude - celestial coordinates of the native pole
|
||
β = euler[1]; // inclination between the poles (colatitude)
|
||
γ = euler[2]; // native coordinates of the celestial pole
|
||
|
||
x = Math.sin(φ) * Math.sin(β) - Math.cos(φ) * Math.cos(β) * Math.cos(λ);
|
||
if (Math.abs(x) < ε) {
|
||
x = -Math.cos(φ + β) + Math.cos(φ) * Math.cos(β) * (1 - Math.cos(λ));
|
||
}
|
||
y = -Math.cos(φ) * Math.sin(λ);
|
||
|
||
if (x !== 0 || y !== 0) {
|
||
dψ = Math.atan2(y, x);
|
||
} else {
|
||
dψ = λ - Math.PI;
|
||
}
|
||
ψ = (γ + dψ);
|
||
if (ψ > Math.PI) ψ -= τ;
|
||
|
||
if (λ % Math.PI === 0) {
|
||
θ = φ + Math.cos(λ) * β;
|
||
if (θ > halfπ) θ = Math.PI - θ;
|
||
if (θ < -halfπ) θ = -Math.PI - θ;
|
||
} else {
|
||
z = Math.sin(φ) * Math.cos(β) + Math.cos(φ) * Math.sin(β) * Math.cos(λ);
|
||
if (Math.abs(z) > 0.99) {
|
||
θ = Math.abs(Math.acos(Math.sqrt(x*x+y*y)));
|
||
if (z < 0) θ *= -1;
|
||
} else {
|
||
θ = Math.asin(z);
|
||
}
|
||
}
|
||
|
||
return [ψ, θ];
|
||
}
|
||
|
||
|
||
var euler = {
|
||
"ecliptic": [-90.0, 23.4393, 90.0],
|
||
"inverse ecliptic": [90.0, 23.4393, -90.0],
|
||
"galactic": [-167.1405, 62.8717, 122.9319],
|
||
"inverse galactic": [122.9319, 62.8717, -167.1405],
|
||
"supergalactic": [283.7542, 74.2911, 26.4504],
|
||
"inverse supergalactic": [26.4504, 74.2911, 283.7542],
|
||
"init": function () {
|
||
for (var key in this) {
|
||
if (this[key].constructor == Array) {
|
||
this[key] = this[key].map( function(val) { return val * deg2rad; } );
|
||
}
|
||
}
|
||
},
|
||
"add": function(name, ang) {
|
||
if (!ang || !name || ang.length !== 3 || this.hasOwnProperty(name)) return;
|
||
this[name] = ang.map( function(val) { return val * deg2rad; } );
|
||
return this[name];
|
||
}
|
||
};
|
||
|
||
euler.init();
|
||
Celestial.euler = function () { return euler; };
|
||
|
||
|
||
var horizontal = function(dt, pos, loc) {
|
||
//dt: datetime, pos: celestial coordinates [lat,lng], loc: location [lat,lng]
|
||
var ha = getMST(dt, loc[1]) - pos[0];
|
||
if (ha < 0) ha = ha + 360;
|
||
|
||
ha = ha * deg2rad;
|
||
var dec = pos[1] * deg2rad;
|
||
var lat = loc[0] * deg2rad;
|
||
|
||
var alt = Math.asin(Math.sin(dec) * Math.sin(lat) + Math.cos(dec) * Math.cos(lat) * Math.cos(ha));
|
||
var az = Math.acos((Math.sin(dec) - Math.sin(alt) * Math.sin(lat)) / (Math.cos(alt) * Math.cos(lat)));
|
||
|
||
if (Math.sin(ha) > 0) az = Math.PI * 2 - az;
|
||
|
||
return [alt / deg2rad, az / deg2rad, 0];
|
||
};
|
||
|
||
horizontal.inverse = function(dt, hor, loc) {
|
||
|
||
var alt = hor[0] * deg2rad;
|
||
var az = hor[1] * deg2rad;
|
||
var lat = loc[0] * deg2rad;
|
||
|
||
var dec = Math.asin((Math.sin(alt) * Math.sin(lat)) + (Math.cos(alt) * Math.cos(lat) * Math.cos(az)));
|
||
var ha = ((Math.sin(alt) - (Math.sin(dec) * Math.sin(lat))) / (Math.cos(dec) * Math.cos(lat))).toFixed(6);
|
||
|
||
ha = Math.acos(ha);
|
||
ha = ha / deg2rad;
|
||
|
||
var ra = getMST(dt, loc[1]) - ha;
|
||
//if (ra < 0) ra = ra + 360;
|
||
|
||
return [ra, dec / deg2rad, 0];
|
||
};
|
||
|
||
function getMST(dt, lng)
|
||
{
|
||
var yr = dt.getUTCFullYear();
|
||
var mo = dt.getUTCMonth() + 1;
|
||
var dy = dt.getUTCDate();
|
||
var h = dt.getUTCHours();
|
||
var m = dt.getUTCMinutes();
|
||
var s = dt.getUTCSeconds();
|
||
|
||
if ((mo == 1)||(mo == 2)) {
|
||
yr = yr - 1;
|
||
mo = mo + 12;
|
||
}
|
||
|
||
var a = Math.floor(yr / 100);
|
||
var b = 2 - a + Math.floor(a / 4);
|
||
var c = Math.floor(365.25 * yr);
|
||
var d = Math.floor(30.6001 * (mo + 1));
|
||
|
||
// days since J2000.0
|
||
var jd = b + c + d - 730550.5 + dy + (h + m/60.0 + s/3600.0)/24.0;
|
||
|
||
// julian centuries since J2000.0
|
||
var jt = jd/36525.0;
|
||
|
||
// the mean sidereal time in degrees
|
||
var mst = 280.46061837 + 360.98564736629*jd + 0.000387933*jt*jt - jt*jt*jt/38710000 + lng;
|
||
|
||
// in degrees modulo 360.0
|
||
if (mst > 0.0)
|
||
while (mst > 360.0) mst = mst - 360.0;
|
||
else
|
||
while (mst < 0.0) mst = mst + 360.0;
|
||
|
||
return mst;
|
||
}
|
||
|
||
Celestial.horizontal = horizontal;
|
||
|
||
//Add more JSON data to the map
|
||
|
||
Celestial.add = function(dat) {
|
||
var res = {};
|
||
//dat: {file: path, type:'json|raw', callback: func(), redraw: func()}
|
||
//or {file:file, size:null, shape:null, color:null} TBI
|
||
// with size,shape,color: "prop=val:result;.." || function(prop) { .. return res; }
|
||
if (!has(dat, "type")) return console.log("Missing type");
|
||
|
||
if ((dat.type === "dso" || dat.type === "json") && (!has(dat, "file") || !has(dat, "callback"))) return console.log("Can't add data file");
|
||
if ((dat.type === "line" || dat.type === "raw") && !has(dat, "callback")) return console.log("Can't add line");
|
||
|
||
if (has(dat, "file")) res.file = dat.file;
|
||
res.type = dat.type;
|
||
if (has(dat, "callback")) res.callback = dat.callback;
|
||
if (has(dat, "redraw")) res.redraw = dat.redraw;
|
||
Celestial.data.push(res);
|
||
};
|
||
|
||
Celestial.remove = function(i) {
|
||
if (i !== null && i < Celestial.data.length) {
|
||
return Celestial.data.splice(i,1);
|
||
}
|
||
};
|
||
|
||
Celestial.clear = function() {
|
||
Celestial.data = [];
|
||
};
|
||
|
||
|
||
//load data and transform coordinates
|
||
|
||
|
||
function getPoint(coords, trans) {
|
||
return transformDeg(coords, euler[trans]);
|
||
}
|
||
|
||
function getData(d, trans) {
|
||
if (trans === "equatorial") return d;
|
||
|
||
var leo = euler[trans],
|
||
f = d.features;
|
||
|
||
for (var i=0; i<f.length; i++)
|
||
f[i].geometry.coordinates = translate(f[i], leo);
|
||
|
||
return d;
|
||
}
|
||
|
||
function getPlanets(d) {
|
||
var res = [];
|
||
|
||
for (var key in d) {
|
||
if (!has(d, key)) continue;
|
||
if (cfg.planets.which.indexOf(key) === -1) continue;
|
||
var dat = Kepler().id(key);
|
||
if (has(d[key], "parent")) dat.parentBody(d[key].parent);
|
||
dat.elements(d[key].elements[0]);
|
||
|
||
if (key === "ter")
|
||
Celestial.origin = dat;
|
||
else res.push(dat);
|
||
}
|
||
res.push(Kepler().id("sol"));
|
||
res.push(Kepler().id("lun"));
|
||
return res;
|
||
}
|
||
|
||
function getConstellationList(d, trans) {
|
||
var res = {},
|
||
leo = euler[trans],
|
||
f = d.features;
|
||
|
||
for (var i=0; i<f.length; i++) {
|
||
res[f[i].id] = {
|
||
name: f[i].properties.name,
|
||
center: f[i].properties.display.slice(0,2),
|
||
scale: f[i].properties.display[2]
|
||
};
|
||
}
|
||
return res;
|
||
}
|
||
|
||
|
||
function translate(d, leo) {
|
||
var res = [];
|
||
switch (d.geometry.type) {
|
||
case "Point": res = transformDeg(d.geometry.coordinates, leo); break;
|
||
case "LineString": res.push(transLine(d.geometry.coordinates, leo)); break;
|
||
case "MultiLineString": res = transMultiLine(d.geometry.coordinates, leo); break;
|
||
case "Polygon": res.push(transLine(d.geometry.coordinates[0], leo)); break;
|
||
case "MultiPolygon": res.push(transMultiLine(d.geometry.coordinates[0], leo)); break;
|
||
}
|
||
|
||
return res;
|
||
}
|
||
|
||
function getGridValues(type, loc) {
|
||
var lines = [];
|
||
if (!loc) return [];
|
||
if (!isArray(loc)) loc = [loc];
|
||
//center, outline, values
|
||
for (var i=0; i < loc.length; i++) {
|
||
switch (loc[i]) {
|
||
case "center":
|
||
if (type === "lat")
|
||
lines = lines.concat(getLine(type, cfg.center[0], "N"));
|
||
else
|
||
lines = lines.concat(getLine(type, cfg.center[1], "S"));
|
||
break;
|
||
case "outline":
|
||
if (type === "lon") {
|
||
lines = lines.concat(getLine(type, cfg.center[1]-89.99, "S"));
|
||
lines = lines.concat(getLine(type, cfg.center[1]+89.99), "N");
|
||
} else {
|
||
// TODO: hemi
|
||
lines = lines.concat(getLine(type, cfg.center[0]-179.99, "E"));
|
||
lines = lines.concat(getLine(type, cfg.center[0]+179.99, "W"));
|
||
}
|
||
break;
|
||
default: if (isNumber(loc[i])) {
|
||
if (type === "lat")
|
||
lines = lines.concat(getLine(type, loc[i], "N"));
|
||
else
|
||
lines = lines.concat(getLine(type, loc[i], "S"));
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
//return [{coordinates, value, orientation}, ...]
|
||
return jsonGridValues(lines);
|
||
}
|
||
|
||
function jsonGridValues(lines) {
|
||
var res = [];
|
||
for (var i=0; i < lines.length; i++) {
|
||
var f = {type: "Feature", "id":i, properties: {}, geometry:{type:"Point"}};
|
||
f.properties.value = lines[i].value;
|
||
f.properties.orientation = lines[i].orientation;
|
||
f.geometry.coordinates = lines[i].coordinates;
|
||
res.push(f);
|
||
}
|
||
return res;
|
||
}
|
||
|
||
function getLine(type, loc, orient) {
|
||
var min, max, step, val, coord,
|
||
tp = type,
|
||
res = [],
|
||
lr = loc;
|
||
if (cfg.transform === "equatorial" && tp === "lon") tp = "ra";
|
||
|
||
if (tp === "ra") {
|
||
min = 0; max = 23; step = 1;
|
||
} else if (tp === "lon") {
|
||
min = 0; max = 350; step = 10;
|
||
} else {
|
||
min = -80; max = 80; step = 10;
|
||
}
|
||
for (var i=min; i<=max; i+=step) {
|
||
var o = orient;
|
||
if (tp === "lat") {
|
||
coord = [lr, i];
|
||
val = i.toString() + "\u00b0";
|
||
if (i < 0) o += "S"; else o += "N";
|
||
} else if (tp === "ra") {
|
||
coord = [i * 15, lr];
|
||
val = i.toString() + "\u02b0";
|
||
} else {
|
||
coord = [i, lr];
|
||
val = i.toString() + "\u00b0";
|
||
}
|
||
|
||
res.push({coordinates: coord, value: val, orientation: o});
|
||
}
|
||
return res;
|
||
}
|
||
|
||
function transLine(c, leo) {
|
||
var line = [];
|
||
|
||
for (var i=0; i<c.length; i++)
|
||
line.push(transformDeg(c[i], leo));
|
||
|
||
return line;
|
||
}
|
||
|
||
function transMultiLine(c, leo) {
|
||
var lines = [];
|
||
|
||
for (var i=0; i<c.length; i++)
|
||
lines.push(transLine(c[i], leo));
|
||
|
||
return lines;
|
||
}
|
||
|
||
Celestial.getData = getData;
|
||
Celestial.getPoint = getPoint;
|
||
|
||
//Defaults
|
||
var settings = {
|
||
width: 0, // Default width; height is determined by projection
|
||
projection: "aitoff", // Map projection used: airy, aitoff, armadillo, august, azimuthalEqualArea, azimuthalEquidistant, baker, berghaus, boggs, bonne, bromley, collignon, craig, craster, cylindricalEqualArea, cylindricalStereographic, eckert1, eckert2, eckert3, eckert4, eckert5, eckert6, eisenlohr, equirectangular, fahey, foucaut, ginzburg4, ginzburg5, ginzburg6, ginzburg8, ginzburg9, gringorten, hammer, hatano, healpix, hill, homolosine, kavrayskiy7, lagrange, larrivee, laskowski, loximuthal, mercator, miller, mollweide, mtFlatPolarParabolic, mtFlatPolarQuartic, mtFlatPolarSinusoidal, naturalEarth, nellHammer, orthographic, patterson, polyconic, rectangularPolyconic, robinson, sinusoidal, stereographic, times, twoPointEquidistant, vanDerGrinten, vanDerGrinten2, vanDerGrinten3, vanDerGrinten4, wagner4, wagner6, wagner7, wiechel, winkel3
|
||
transform: "equatorial", // Coordinate transformation: equatorial (default), ecliptic, galactic, supergalactic
|
||
center: null, // Initial center coordinates in equatorial transformation [hours, degrees, degrees],
|
||
// otherwise [degrees, degrees, degrees], 3rd parameter is orientation, null = default center
|
||
geopos: null, // optional initial geographic position [lat,lon] in degrees, overrides center
|
||
follow: "zenith", // on which coordinates to center the map, default: zenith, if location enabled, otherwise center
|
||
orientationfixed: true, // Keep orientation angle the same as center[2]
|
||
adaptable: true, // Sizes are increased with higher zoom-levels
|
||
interactive: true, // Enable zooming and rotation with mousewheel and dragging
|
||
form: false, // Display settings form
|
||
location: false, // Display location settings
|
||
daterange: [], // Calender date range; null: displaydate-+10; [n<100]: displaydate-+n; [yr]: yr-+10;
|
||
// [yr, n<100]: [yr-n, yr+n]; [yr0, yr1]
|
||
controls: true, // Display zoom controls
|
||
lang: "", // Language for names, so far only for constellations: de: german, es: spanish
|
||
// Default:en or empty string for english
|
||
container: "celestial-map", // ID of parent element, e.g. div
|
||
datapath: "data/", // Path/URL to data files, empty = subfolder 'data'
|
||
stars: {
|
||
show: true, // Show stars
|
||
limit: 6, // Show only stars brighter than limit magnitude
|
||
colors: true, // Show stars in spectral colors, if not use fill-style
|
||
style: { fill: "#ffffff", opacity: 1 }, // Default style for stars
|
||
names: true, // Show star names (Bayer, Flamsteed, Variable star, Gliese, whichever applies first)
|
||
proper: false, // Show proper name (if present)
|
||
desig: false, // Show all names, including Draper and Hipparcos
|
||
namestyle: { fill: "#ddddbb", font: "11px 'Palatino Linotype', Georgia, Times, 'Times Roman', serif", align: "left", baseline: "top" },
|
||
namelimit: 2.5, // Show only names for stars brighter than namelimit
|
||
propernamestyle: { fill: "#ddddbb", font: "13px 'Palatino Linotype', Georgia, Times, 'Times Roman', serif", align: "right", baseline: "bottom" },
|
||
propernamelimit: 1.5, // Show proper names for stars brighter than propernamelimit
|
||
size: 7, // Scale size (radius) of star circle in pixels
|
||
exponent: -0.28, // Scale exponent for star size, larger = more linear
|
||
data: "stars.6.json" // Data source for stellar data
|
||
},
|
||
dsos: {
|
||
show: true, // Show Deep Space Objects
|
||
limit: 6, // Show only DSOs brighter than limit magnitude
|
||
names: true, // Show DSO names
|
||
desig: true, // Show short DSO names
|
||
namestyle: { fill: "#cccccc", font: "11px 'Lucida Sans Unicode', Helvetica, Arial, serif", align: "left", baseline: "top" },
|
||
namelimit: 4, // Show only names for DSOs brighter than namelimit
|
||
size: null, // Optional seperate scale size for DSOs, null = stars.size
|
||
exponent: 1.4, // Scale exponent for DSO size, larger = more non-linear
|
||
data: "dsos.bright.json", // Data source for DSOs
|
||
symbols: { // DSO symbol styles
|
||
gg: {shape: "circle", fill: "#ff0000"}, // Galaxy cluster
|
||
g: {shape: "ellipse", fill: "#ff0000"}, // Generic galaxy
|
||
s: {shape: "ellipse", fill: "#ff0000"}, // Spiral galaxy
|
||
s0: {shape: "ellipse", fill: "#ff0000"}, // Lenticular galaxy
|
||
sd: {shape: "ellipse", fill: "#ff0000"}, // Dwarf galaxy
|
||
e: {shape: "ellipse", fill: "#ff0000"}, // Elliptical galaxy
|
||
i: {shape: "ellipse", fill: "#ff0000"}, // Irregular galaxy
|
||
oc: {shape: "circle", fill: "#ff9900", stroke: "#ff9900", width: 2}, // Open cluster
|
||
gc: {shape: "circle", fill: "#ff9900"}, // Globular cluster
|
||
en: {shape: "square", fill: "#ff00cc"}, // Emission nebula
|
||
bn: {shape: "square", fill: "#ff00cc"}, // Generic bright nebula
|
||
sfr:{shape: "square", fill: "#cc00ff"}, // Star forming region
|
||
rn: {shape: "square", fill: "#0000ff"}, // Reflection nebula
|
||
pn: {shape: "diamond", fill: "#00cccc"}, // Planetary nebula
|
||
snr:{shape: "diamond", fill: "#ff00cc"}, // Supernova remnant
|
||
dn: {shape: "square", fill: "#999999", stroke: "#999999", width: 2}, // Dark nebula
|
||
pos:{shape: "marker", fill: "#cccccc", stroke: "#cccccc", width: 1.5} // Generic marker
|
||
}
|
||
},
|
||
constellations: {
|
||
show: true, // Show constellations
|
||
names: true, // Show constellation names
|
||
desig: true, // Show short constellation names (3 letter designations)
|
||
namestyle: { fill:"#cccc99", align: "center", baseline: "middle", opacity:0.8,
|
||
font: ["14px 'Lucida Sans Unicode', Helvetica, Arial, sans-serif", // Different fonts for brighter &
|
||
"12px 'Lucida Sans Unicode', Helvetica, Arial, sans-serif", // sdarker constellations
|
||
"11px 'Lucida Sans Unicode', Helvetica, Arial, sans-serif"]},
|
||
lines: true, // Show constellation lines
|
||
linestyle: { stroke: "#cccccc", width: 1.5, opacity: 0.6 },
|
||
bounds: false, // Show constellation boundaries
|
||
boundstyle: { stroke: "#ccff00", width: 1, opacity: 0.8, dash: [2, 4] }
|
||
},
|
||
mw: {
|
||
show: true, // Show Milky Way as filled polygons
|
||
style: { fill: "#ffffff", opacity: "0.15" } // style for each MW-layer (5 on top of each other)
|
||
},
|
||
lines: {
|
||
graticule: { show: true, stroke: "#cccccc", width: 0.6, opacity: 0.8, // Show graticule lines
|
||
// grid values: "outline", "center", or [lat,...] specific position
|
||
lon: {pos: [""], fill: "#eee", font: "10px 'Lucida Sans Unicode', Helvetica, Arial, sans-serif"},
|
||
// grid values: "outline", "center", or [lon,...] specific position
|
||
lat: {pos: [""], fill: "#eee", font: "10px 'Lucida Sans Unicode', Helvetica, Arial, sans-serif"}},
|
||
equatorial: { show: true, stroke: "#aaaaaa", width: 1.3, opacity: 0.7 }, // Show equatorial plane
|
||
ecliptic: { show: true, stroke: "#66cc66", width: 1.3, opacity: 0.7 }, // Show ecliptic plane
|
||
galactic: { show: false, stroke: "#cc6666", width: 1.3, opacity: 0.7 }, // Show galactic plane
|
||
supergalactic: { show: false, stroke: "#cc66cc", width: 1.3, opacity: 0.7 } // Show supergalactic plane
|
||
//mars: { show: false, stroke:"#cc0000", width:1.3, opacity:.7 }
|
||
}, // Background style
|
||
background: {
|
||
fill: "#000000",
|
||
opacity: 1,
|
||
stroke: "#000000", // Outline
|
||
width: 1.5
|
||
},
|
||
horizon: { //Show horizon marker, if geo-position and date-time is set
|
||
show: false,
|
||
stroke: "#000099", // Line
|
||
width: 1.0,
|
||
fill: "#000000", // Area below horizon
|
||
opacity: 0.5
|
||
},
|
||
planets: { //Show planet locations, if date-time is set
|
||
show: false,
|
||
which: ["sol", "mer", "ven", "ter", "lun", "mar", "jup", "sat", "ura", "nep"],
|
||
style: { fill: "#00ccff", font: "bold 17px 'Lucida Sans Unicode', Consolas, sans-serif", align: "center", baseline: "middle" },
|
||
symbols: {
|
||
"sol": {symbol: "\u2609", fill: "#ffff00"},
|
||
"mer": {symbol: "\u263f", fill: "#cccccc"},
|
||
"ven": {symbol: "\u2640", fill: "#eeeecc"},
|
||
"ter": {symbol: "\u2295", fill: "#00ffff"},
|
||
"lun": {symbol: "\u25cf", fill: "#ffffff"},
|
||
"mar": {symbol: "\u2642", fill: "#ff9999"},
|
||
"cer": {symbol: "\u26b3", fill: "#cccccc"},
|
||
"ves": {symbol: "\u26b6", fill: "#cccccc"},
|
||
"jup": {symbol: "\u2643", fill: "#ff9966"},
|
||
"sat": {symbol: "\u2644", fill: "#ffcc66"},
|
||
"ura": {symbol: "\u2645", fill: "#66ccff"},
|
||
"nep": {symbol: "\u2646", fill: "#6666ff"},
|
||
"plu": {symbol: "\u2647", fill: "#aaaaaa"},
|
||
"eri": {symbol: "\u26aa", fill: "#eeeeee"}
|
||
}
|
||
},
|
||
daylight: { // Show daylight marker (tbi)
|
||
show: false,
|
||
fill: "#fff",
|
||
opacity: 0.4
|
||
},
|
||
set: function(cfg) { // Override defaults with values of cfg
|
||
var prop, key, res = {};
|
||
if (!cfg) return this;
|
||
for (prop in this) {
|
||
if (!has(this, prop)) continue;
|
||
//if (typeof(this[prop]) === 'function');
|
||
if (!has(cfg, prop) || cfg[prop] === null) {
|
||
res[prop] = this[prop];
|
||
} else if (this[prop] === null || this[prop].constructor != Object ) {
|
||
res[prop] = cfg[prop];
|
||
} else {
|
||
res[prop] = {};
|
||
for (key in this[prop]) {
|
||
if (has(cfg[prop], key)) {
|
||
res[prop][key] = cfg[prop][key];
|
||
} else {
|
||
res[prop][key] = this[prop][key];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return res;
|
||
}
|
||
|
||
};
|
||
|
||
Celestial.settings = function () { return settings; };
|
||
|
||
//b-v color index to rgb color value scale
|
||
var bvcolor =
|
||
d3.scale.quantize().domain([3.347, -0.335]) //main sequence <= 1.7
|
||
.range([ '#ff4700', '#ff4b00', '#ff4f00', '#ff5300', '#ff5600', '#ff5900', '#ff5b00', '#ff5d00', '#ff6000', '#ff6300', '#ff6500', '#ff6700', '#ff6900', '#ff6b00', '#ff6d00', '#ff7000', '#ff7300', '#ff7500', '#ff7800', '#ff7a00', '#ff7c00', '#ff7e00', '#ff8100', '#ff8300', '#ff8506', '#ff870a', '#ff8912', '#ff8b1a', '#ff8e21', '#ff9127', '#ff932c', '#ff9631', '#ff9836', '#ff9a3c', '#ff9d3f', '#ffa148', '#ffa34b', '#ffa54f', '#ffa753', '#ffa957', '#ffab5a', '#ffad5e', '#ffb165', '#ffb269', '#ffb46b', '#ffb872', '#ffb975', '#ffbb78', '#ffbe7e', '#ffc184', '#ffc489', '#ffc78f', '#ffc892', '#ffc994', '#ffcc99', '#ffce9f', '#ffd1a3', '#ffd3a8', '#ffd5ad', '#ffd7b1', '#ffd9b6', '#ffdbba', '#ffddbe', '#ffdfc2', '#ffe1c6', '#ffe3ca', '#ffe4ce', '#ffe8d5', '#ffe9d9', '#ffebdc', '#ffece0', '#ffefe6', '#fff0e9', '#fff2ec', '#fff4f2', '#fff5f5', '#fff6f8', '#fff9fd', '#fef9ff', '#f9f6ff', '#f6f4ff', '#f3f2ff', '#eff0ff', '#ebeeff', '#e9edff', '#e6ebff', '#e3e9ff', '#e0e7ff', '#dee6ff', '#dce5ff', '#d9e3ff', '#d7e2ff', '#d3e0ff', '#c9d9ff', '#bfd3ff', '#b7ceff', '#afc9ff', '#a9c5ff', '#a4c2ff', '#9fbfff', '#9bbcff']);
|
||
|
||
/* Default parameters for each supported projection
|
||
arg: constructor argument, if any
|
||
scale: scale parameter so that they all have ~equal width, normalized to 1024 pixels
|
||
ratio: width/height ratio, 2.0 if none
|
||
clip: projection clipped to 90 degrees from center, otherwise to antimeridian
|
||
*/
|
||
var projections = {
|
||
"airy": {n:"Airy’s Minimum Error", arg:Math.PI/2, scale:360, ratio:1.0, clip:true},
|
||
"aitoff": {n:"Aitoff", arg:null, scale:162},
|
||
"armadillo": {n:"Armadillo", arg:0, scale:250},
|
||
"august": {n:"August", arg:null, scale:94, ratio:1.4},
|
||
"azimuthalEqualArea": {n:"Azimuthal Equal Area", arg:null, scale:340, ratio:1.0, clip:true},
|
||
"azimuthalEquidistant": {n:"Azimuthal Equidistant", arg:null, scale:320, ratio:1.0, clip:true},
|
||
"baker": {n:"Baker Dinomic", arg:null, scale:160, ratio:1.4},
|
||
"berghaus": {n:"Berghaus Star", arg:0, scale:320, ratio:1.0, clip:true},
|
||
"boggs": {n:"Boggs Eumorphic", arg:null, scale:170},
|
||
"bonne": {n:"Bonne", arg:Math.PI/2.5, scale:225, ratio:0.88},
|
||
"bromley": {n:"Bromley", arg:null, scale:162},
|
||
// "butterfly": {n:"Butterfly", arg:null, scale:31, ratio:1.1, clip:true},
|
||
"cassini": {n:"Cassini", arg:null, scale:325, ratio:1.0, clip:true},
|
||
"collignon": {n:"Collignon", arg:null, scale:100, ratio:2.6},
|
||
"craig": {n:"Craig Retroazimuthal", arg:0, scale:310, ratio:1.5, clip:true},
|
||
"craster": {n:"Craster Parabolic", arg:null, scale:160},
|
||
"cylindricalEqualArea": {n:"Cylindrical Equal Area", arg:Math.PI/6, scale:190, ratio:2.3},
|
||
"cylindricalStereographic": {n:"Cylindrical Stereographic", arg:Math.PI/4, scale:230, ratio:1.3},
|
||
"eckert1": {n:"Eckert I", arg:null, scale:175},
|
||
"eckert2": {n:"Eckert II", arg:null, scale:175},
|
||
"eckert3": {n:"Eckert III", arg:null, scale:190},
|
||
"eckert4": {n:"Eckert IV", arg:null, scale:190},
|
||
"eckert5": {n:"Eckert V", arg:null, scale:182},
|
||
"eckert6": {n:"Eckert VI", arg:null, scale:182},
|
||
"eisenlohr": {n:"Eisenlohr", arg:null, scale:102},
|
||
"equirectangular": {n:"Equirectangular", arg:null, scale:165},
|
||
"fahey": {n:"Fahey", arg:null, scale:196, ratio:1.4},
|
||
"mtFlatPolarParabolic": {n:"Flat Polar Parabolic", arg:null, scale:175},
|
||
"mtFlatPolarQuartic": {n:"Flat Polar Quartic", arg:null, scale:230, ratio:1.65},
|
||
"mtFlatPolarSinusoidal": {n:"Flat Polar Sinusoidal", arg:null, scale:175, ratio:1.9},
|
||
"foucaut": {n:"Foucaut", arg:null, scale:142},
|
||
"ginzburg4": {n:"Ginzburg IV", arg:null, scale:180, ratio:1.7},
|
||
"ginzburg5": {n:"Ginzburg V", arg:null, scale:196, ratio:1.55},
|
||
"ginzburg6": {n:"Ginzburg VI", arg:null, scale:190, ratio:1.4},
|
||
"ginzburg8": {n:"Ginzburg VIII", arg:null, scale:205, ratio:1.3},
|
||
"ginzburg9": {n:"Ginzburg IX", arg:null, scale:190, ratio:1.4},
|
||
//"guyou": {n:"Guyou", arg:null, scale:160, ratio:2, clip:true},
|
||
//"bonne": {n:"Heart", arg:Math.PI/2.5, scale:225, ratio:0.88},
|
||
"homolosine": {n:"Goode Homolosine", arg:null, scale:160, ratio:2.2},
|
||
"hammer": {n:"Hammer", arg:2, scale:180},
|
||
"hatano": {n:"Hatano", arg:null, scale:186},
|
||
"healpix": {n:"HEALPix", arg:1, scale:320, ratio:1.2},
|
||
"hill": {n:"Hill Eucyclic", arg:2, scale:190, ratio:1.1},
|
||
"kavrayskiy7": {n:"Kavrayskiy VII", arg:null, scale:185, ratio:1.75},
|
||
"lagrange": {n:"Lagrange", arg:Math.PI/4, scale:88, ratio:1.6, clip:false},
|
||
"larrivee": {n:"l'Arrivée", arg:null, scale:160, ratio:1.25},
|
||
"laskowski": {n:"Laskowski Tri-Optimal", arg:null, scale:165, ratio:1.7},
|
||
"loximuthal": {n:"Loximuthal", arg:Math.PI/4, scale:175, ratio:1.8},
|
||
"mercator": {n:"Mercator", arg:null, scale:160, ratio:1.3},
|
||
"miller": {n:"Miller", arg:null, scale:160, ratio:1.5},
|
||
"mollweide": {n:"Mollweide", arg:null, scale:180},
|
||
"naturalEarth": {n:"Natural Earth", arg:null, scale:185, ratio:1.85},
|
||
"nellHammer": {n:"Nell Hammer", arg:null, scale:160, ratio:2.6},
|
||
"orthographic": {n:"Orthographic", arg:null, scale:480, ratio:1.0, clip:true},
|
||
"patterson": {n:"Patterson Cylindrical", arg:null, scale:160, ratio:1.75},
|
||
"polyconic": {n:"Polyconic", arg:null, scale:160, ratio:1.3},
|
||
"quincuncial": {n:"Quincuncial", arg:null, scale:160, ratio:1.3},
|
||
"rectangularPolyconic": {n:"Rectangular Polyconic", arg:0, scale:160, ratio:1.65},
|
||
"robinson": {n:"Robinson", arg:null, scale:160},
|
||
"sinusoidal": {n:"Sinusoidal", arg:null, scale:160, ratio:2},
|
||
"stereographic": {n:"Stereographic", arg:null, scale:500, ratio:1.0, clip:true},
|
||
"times": {n:"Times", arg:null, scale:210, ratio:1.4},
|
||
"twoPointEquidistant": {n:"Two-Point Equidistant", arg:Math.PI/2, scale:320, ratio:1.15, clip:true},
|
||
"vanDerGrinten": {n:"van Der Grinten", arg:null, scale:160, ratio:1.0},
|
||
"vanDerGrinten2": {n:"van Der Grinten II", arg:null, scale:160, ratio:1.0},
|
||
"vanDerGrinten3": {n:"van Der Grinten III", arg:null, scale:160, ratio:1.0},
|
||
"vanDerGrinten4": {n:"van Der Grinten IV", arg:null, scale:160, ratio:1.6},
|
||
"wagner4": {n:"Wagner IV", arg:null, scale:185},
|
||
"wagner6": {n:"Wagner VI", arg:null, scale:160},
|
||
"wagner7": {n:"Wagner VII", arg:null, scale:190, ratio:1.8},
|
||
"wiechel": {n:"Wiechel", arg:null, scale:360, ratio:1.0, clip:true},
|
||
"winkel3": {n:"Winkel Tripel", arg:null, scale:196, ratio:1.7}
|
||
};
|
||
|
||
Celestial.projections = function () { return projections; };
|
||
|
||
|
||
|
||
var Canvas = {};
|
||
|
||
Canvas.symbol = function () {
|
||
// parameters and default values
|
||
var type = d3.functor("circle"),
|
||
size = d3.functor(64),
|
||
age = d3.functor(Math.PI), //crescent shape 0..2Pi
|
||
color = d3.functor("#fff"),
|
||
text = d3.functor(""),
|
||
padding = d3.functor([2,2]),
|
||
pos;
|
||
|
||
function canvas_symbol(context) {
|
||
draw_symbol[type()](context);
|
||
}
|
||
|
||
var draw_symbol = {
|
||
"circle": function(ctx) {
|
||
var s = Math.sqrt(size()),
|
||
r = s/2;
|
||
ctx.arc(pos[0], pos[1], r, 0, 2 * Math.PI);
|
||
return r;
|
||
},
|
||
"square": function(ctx) {
|
||
var s = Math.sqrt(size()),
|
||
r = s/1.7;
|
||
ctx.moveTo(pos[0]-r, pos[1]-r);
|
||
ctx.lineTo(pos[0]+r, pos[1]-r);
|
||
ctx.lineTo(pos[0]+r, pos[1]+r);
|
||
ctx.lineTo(pos[0]-r, pos[1]+r);
|
||
ctx.closePath();
|
||
return r;
|
||
},
|
||
"diamond": function(ctx) {
|
||
var s = Math.sqrt(size()),
|
||
r = s/1.5;
|
||
ctx.moveTo(pos[0], pos[1]-r);
|
||
ctx.lineTo(pos[0]+r, pos[1]);
|
||
ctx.lineTo(pos[0], pos[1]+r);
|
||
ctx.lineTo(pos[0]-r, pos[1]);
|
||
ctx.closePath();
|
||
return r;
|
||
},
|
||
"triangle": function(ctx) {
|
||
var s = Math.sqrt(size()),
|
||
r = s/Math.sqrt(3);
|
||
ctx.moveTo(pos[0], pos[1]-r);
|
||
ctx.lineTo(pos[0]+r, pos[1]+r);
|
||
ctx.lineTo(pos[0]-r, pos[1]+r);
|
||
ctx.closePath();
|
||
return r;
|
||
},
|
||
"ellipse": function(ctx) {
|
||
var s = Math.sqrt(size()),
|
||
r = s/2;
|
||
ctx.save();
|
||
ctx.translate(pos[0], pos[1]);
|
||
ctx.scale(1.6, 0.8);
|
||
ctx.beginPath();
|
||
ctx.arc(0, 0, r, 0, 2 * Math.PI);
|
||
ctx.closePath();
|
||
ctx.restore();
|
||
return r;
|
||
},
|
||
"marker": function(ctx) {
|
||
var s = Math.sqrt(size()),
|
||
r = s/2;
|
||
ctx.moveTo(pos[0], pos[1]-r);
|
||
ctx.lineTo(pos[0], pos[1]+r);
|
||
ctx.moveTo(pos[0]-r, pos[1]);
|
||
ctx.lineTo(pos[0]+r, pos[1]);
|
||
ctx.closePath();
|
||
return r;
|
||
},
|
||
"cross-circle": function(ctx) {
|
||
var s = Math.sqrt(size()),
|
||
r = s/2;
|
||
ctx.moveTo(pos[0], pos[1]-s);
|
||
ctx.lineTo(pos[0], pos[1]+s);
|
||
ctx.moveTo(pos[0]-s, pos[1]);
|
||
ctx.lineTo(pos[0]+s, pos[1]);
|
||
ctx.stroke();
|
||
ctx.beginPath();
|
||
ctx.moveTo(pos[0], pos[1]);
|
||
ctx.arc(pos[0], pos[1], r, 0, 2 * Math.PI);
|
||
ctx.closePath();
|
||
return r;
|
||
},
|
||
"stroke-circle": function(ctx) {
|
||
var s = Math.sqrt(size()),
|
||
r = s/2;
|
||
ctx.moveTo(pos[0], pos[1]-s);
|
||
ctx.lineTo(pos[0], pos[1]+s);
|
||
ctx.stroke();
|
||
ctx.beginPath();
|
||
ctx.moveTo(pos[0], pos[1]);
|
||
ctx.arc(pos[0], pos[1], r, 0, 2 * Math.PI);
|
||
ctx.closePath();
|
||
return r;
|
||
},
|
||
"crescent": function(ctx) {
|
||
var s = Math.sqrt(size()),
|
||
r = s/2,
|
||
ag = age(),
|
||
ph = 0.5 * (1 - Math.cos(ag)),
|
||
e = 1.6 * Math.abs(ph - 0.5) + 0.01,
|
||
dir = ag > Math.PI,
|
||
termdir = Math.abs(ph) > 0.5 ? dir : !dir;
|
||
|
||
ctx.save();
|
||
ctx.fillStyle = "#557";
|
||
ctx.moveTo(pos[0], pos[1]);
|
||
ctx.arc(pos[0], pos[1], r, 0, 2 * Math.PI);
|
||
ctx.closePath();
|
||
ctx.fill();
|
||
ctx.fillStyle = "#eee";
|
||
ctx.beginPath();
|
||
ctx.moveTo(pos[0], pos[1]);
|
||
ctx.arc(pos[0], pos[1], r, -Math.PI/2, Math.PI/2, dir);
|
||
ctx.scale(e, 1);
|
||
ctx.arc(pos[0]/e, pos[1], r, Math.PI/2, -Math.PI/2, termdir);
|
||
ctx.closePath();
|
||
ctx.fill();
|
||
ctx.restore();
|
||
|
||
return r;
|
||
}
|
||
};
|
||
|
||
|
||
canvas_symbol.type = function(_) {
|
||
if (!arguments.length) return type;
|
||
type = d3.functor(_);
|
||
return canvas_symbol;
|
||
};
|
||
canvas_symbol.size = function(_) {
|
||
if (!arguments.length) return size;
|
||
size = d3.functor(_);
|
||
return canvas_symbol;
|
||
};
|
||
canvas_symbol.age = function(_) {
|
||
if (!arguments.length) return age;
|
||
age = d3.functor(_);
|
||
return canvas_symbol;
|
||
};
|
||
canvas_symbol.text = function(_) {
|
||
if (!arguments.length) return text;
|
||
text = d3.functor(_);
|
||
return canvas_symbol;
|
||
};
|
||
canvas_symbol.position = function(_) {
|
||
if (!arguments.length) return;
|
||
pos = _;
|
||
return canvas_symbol;
|
||
};
|
||
|
||
return canvas_symbol;
|
||
};
|
||
|
||
Celestial.Canvas = Canvas;
|
||
|
||
|
||
/*var color = "#fff", angle = 0, align = "center", baseline = "middle", font = "10px sans-serif", padding = [0,0], aPos, sText;
|
||
|
||
canvas.text = function () {
|
||
|
||
function txt(ctx){
|
||
ctx.fillStyle = color;
|
||
ctx.textAlign = align;
|
||
ctx.textBaseline = baseline;
|
||
|
||
//var pt = projection(d.geometry.coordinates);
|
||
if (angle) {
|
||
canvas.save();
|
||
canvas.translate(aPos[0], aPos[1]);
|
||
canvas.rotate(angle);
|
||
canvas.fillText(sText, 0, 0);
|
||
canvas.restore();
|
||
} else
|
||
canvas.fillText(sText, aPos[0], aPos[1]);
|
||
}
|
||
|
||
txt.angle = function(x) {
|
||
if (!arguments.length) return angle * 180 / Math.PI;
|
||
color = x * Math.PI / 180;
|
||
return txt;
|
||
};
|
||
txt.color = function(s) {
|
||
if (!arguments.length) return color;
|
||
color = s;
|
||
return txt;
|
||
};
|
||
txt.align = function(s) {
|
||
if (!arguments.length) return align;
|
||
align = s;
|
||
return txt;
|
||
};
|
||
txt.baseline = function(s) {
|
||
if (!arguments.length) return baseline;
|
||
baseline = s;
|
||
return txt;
|
||
};
|
||
txt.padding = function(a) {
|
||
if (!arguments.length) return padding;
|
||
padding = a;
|
||
return txt;
|
||
};
|
||
txt.text = function(s) {
|
||
if (!arguments.length) return sText;
|
||
sText = s;
|
||
return txt;
|
||
};
|
||
txt.font = function(s) {
|
||
if (!arguments.length) return font;
|
||
font = s;
|
||
return txt;
|
||
};
|
||
txt.style = function(o) {
|
||
if (!arguments.length) return;
|
||
if (o.fill) color = o.fill;
|
||
if (o.font) font = o.font;
|
||
return txt;
|
||
};
|
||
|
||
}
|
||
|
||
function ctxPath(d) {
|
||
var pt;
|
||
//d.map( function(axe, i) {
|
||
context.beginPath();
|
||
for (var i = 0; i < d.length; i++) {
|
||
pt = projection(d[i]);
|
||
if (i === 0)
|
||
context.moveTo(pt[0], pt[1]);
|
||
else
|
||
context.lineTo(pt[0], pt[1]);
|
||
}
|
||
context.fill();
|
||
}
|
||
|
||
|
||
function ctxText(d, ang) {
|
||
var pt = projection(d.geometry.coordinates);
|
||
if (ang) {
|
||
canvas.save();
|
||
canvas.translate(pt[0], pt[1]);
|
||
canvas.rotate(Math.PI/2);
|
||
canvas.fillText(txt, 0, 0);
|
||
canvas.restore();
|
||
} else
|
||
canvas.fillText(d.properties.txt, pt[0], pt[1]);
|
||
}
|
||
|
||
|
||
*/
|
||
|
||
function $(id) { return document.getElementById(id); }
|
||
function px(n) { return n + "px"; }
|
||
function Round(x, dg) { return(Math.round(Math.pow(10,dg)*x)/Math.pow(10,dg)); }
|
||
function sign(x) { return x ? x < 0 ? -1 : 1 : 0; }
|
||
function pad(n) { return n < 10 ? '0' + n : n; }
|
||
|
||
|
||
function has(o, key) { return o !== null && hasOwnProperty.call(o, key); }
|
||
function when(o, key, val) { return o !== null && hasOwnProperty.call(o, key) ? o[key] : val; }
|
||
function isNumber(n) { return !isNaN(parseFloat(n)) && isFinite(n); }
|
||
function isArray(o) { return Object.prototype.toString.call(o) === "[object Array]"; }
|
||
function isObject(o) { var type = typeof o; return type === 'function' || type === 'object' && !!o; }
|
||
function isFunction(o) { return typeof o == 'function' || false; }
|
||
|
||
function findPos(o) {
|
||
var l = 0, t = 0;
|
||
if (o.offsetParent) {
|
||
do {
|
||
l += o.offsetLeft;
|
||
t += o.offsetTop;
|
||
} while ((o = o.offsetParent) !== null);
|
||
}
|
||
return [l, t];
|
||
}
|
||
|
||
function hasParent(t, id){
|
||
while(t.parentNode){
|
||
if(t.id === id) return true;
|
||
t = t.parentNode;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function attach(node, event, func) {
|
||
if (node.addEventListener) node.addEventListener(event, func, false);
|
||
else node.attachEvent("on" + event, func);
|
||
}
|
||
|
||
function stopPropagation(e) {
|
||
if (typeof e.stopPropagation != "undefined") e.stopPropagation();
|
||
else e.cancelBubble = true;
|
||
}
|
||
|
||
function dateDiff(dt1, dt2, type) {
|
||
var diff = dt2.valueOf() - dt1.valueOf(),
|
||
tp = type || "d";
|
||
switch (tp) {
|
||
case 'y': case 'yr': diff /= 31556926080; break;
|
||
case 'm': case 'mo': diff /= 2629800000; break;
|
||
case 'd': case 'dy': diff /= 86400000; break;
|
||
case 'h': case 'hr': diff /= 3600000; break;
|
||
case 'n': case 'mn': diff /= 60000; break;
|
||
case 's': case 'sec': diff /= 1000; break;
|
||
case 'ms': break;
|
||
}
|
||
return Math.floor(diff);
|
||
}
|
||
|
||
function dateParse(s) {
|
||
if (!s) return;
|
||
var t = s.split(".");
|
||
if (t.length < 1) return;
|
||
t = t[0].split("-");
|
||
t[0] = t[0].replace(/\D/g, "");
|
||
if (!t[0]) return;
|
||
t[1] = t[1] ? t[1].replace(/\D/g, "") : "1";
|
||
t[2] = t[2] ? t[2].replace(/\D/g, "") : "1";
|
||
//Fraction -> h:m:s
|
||
return new Date(Date.UTC(t[0], t[1]-1, t[2]));
|
||
}
|
||
|
||
|
||
function interpolateAngle(a1, a2, t) {
|
||
a1 = (a1*deg2rad +τ) % τ;
|
||
a2 = (a2*deg2rad + τ) % τ;
|
||
if (Math.abs(a1 - a2) > Math.PI) {
|
||
if (a1 > a2) a1 = a1 - τ;
|
||
else if (a2 > a1) a2 = a2 - τ;
|
||
}
|
||
return d3.interpolateNumber(a1/deg2rad, a2/deg2rad);
|
||
}
|
||
|
||
var Trig = {
|
||
sinh: function (val) { return (Math.pow(Math.E, val)-Math.pow(Math.E, -val))/2; },
|
||
cosh: function (val) { return (Math.pow(Math.E, val)+Math.pow(Math.E, -val))/2; },
|
||
tanh: function (val) { return 2.0 / (1.0 + Math.exp(-2.0 * val)) - 1.0; },
|
||
asinh: function (val) { return Math.log(val + Math.sqrt(val * val + 1)); },
|
||
acosh: function (val) { return Math.log(val + Math.sqrt(val * val - 1)); },
|
||
normalize0: function(val) { return ((val + Math.PI*3) % (Math.PI*2)) - Math.PI; },
|
||
normalize: function(val) { return ((val + Math.PI*2) % (Math.PI*2)); },
|
||
cartesian: function(p) {
|
||
var ϕ = p[0], θ = halfπ - p[1], r = p[2];
|
||
return {"x": r * Math.sin(θ) * Math.cos(ϕ), "y": r * Math.sin(θ) * Math.sin(ϕ), "z": r * Math.cos(θ)};
|
||
},
|
||
spherical: function(p) {
|
||
var r = Math.sqrt(p.x * p.x + p.y * p.y + p.z * p.z),
|
||
θ = Math.atan(p.y / p.x),
|
||
ϕ = Math.acos(p.z / r);
|
||
return [θ / deg2rad, ϕ / deg2rad, r];
|
||
}
|
||
};
|
||
|
||
|
||
|
||
//display settings form in div with id "celestial-form"
|
||
function form(cfg) {
|
||
var config = settings.set(cfg);
|
||
|
||
var prj = Celestial.projections(), leo = Celestial.eulerAngles();
|
||
var ctrl = d3.select("#celestial-form").append("div").attr("class", "ctrl");
|
||
var frm = ctrl.append("form").attr("id", "params").attr("name", "params").attr("method", "get").attr("action" ,"#");
|
||
|
||
//Map parameters
|
||
var col = frm.append("div").attr("class", "col").attr("id", "general");
|
||
|
||
col.append("label").attr("title", "Map width in pixel, 0 indicates full width").attr("for", "width").html("Width ");
|
||
col.append("input").attr("type", "number").attr("maxlength", "4").attr("max", "20000").attr("min", "0").attr("title", "Map width").attr("id", "width").attr("value", config.width).on("change", resize);
|
||
col.append("span").html("px");
|
||
|
||
col.append("label").attr("title", "Map projection, (hemi) indicates hemispherical projection").attr("for", "projection").html("Projection");
|
||
var sel = col.append("select").attr("id", "projection").on("change", reproject);
|
||
var selected = 0;
|
||
var list = Object.keys(prj).map( function (key, i) {
|
||
var n = prj[key].clip && prj[key].clip === true ? prj[key].n + " (hemi)" : prj[key].n;
|
||
if (key === config.projection) selected = i;
|
||
return {o:key, n:n};
|
||
});
|
||
sel.selectAll('option').data(list).enter().append('option')
|
||
.attr("value", function (d) { return d.o; })
|
||
.text(function (d) { return d.n; });
|
||
sel.property("selectedIndex", selected);
|
||
|
||
selected = 0;
|
||
col.append("label").attr("title", "Coordinate space in which the map is displayed").attr("for", "transform").html("Coordinates");
|
||
sel = col.append("select").attr("id", "transform").on("change", reload);
|
||
list = Object.keys(leo).map(function (key, i) {
|
||
if (key === config.transform) selected = i;
|
||
return {o:key, n:key.replace(/^([a-z])/, function(s, m) { return m.toUpperCase(); } )};
|
||
});
|
||
sel.selectAll("option").data(list).enter().append('option')
|
||
.attr("value", function (d) { return d.o; })
|
||
.text(function (d) { return d.n; });
|
||
sel.property("selectedIndex", selected);
|
||
|
||
col.append("br");
|
||
col.append("label").attr("title", "Center coordinates long/lat in selected coordinate space").attr("for", "centerx").html("Center");
|
||
col.append("input").attr("type", "number").attr("id", "centerx").attr("title", "Center right ascension/longitude").attr("max", "24").attr("min", "0").attr("step", "0.1").on("change", turn);
|
||
col.append("span").attr("id", "cxunit").html("h");
|
||
//addList("centerx", "ra");
|
||
|
||
col.append("input").attr("type", "number").attr("id", "centery").attr("title", "Center declination/latitude").attr("max", "90").attr("min", "-90").attr("step", "0.1").on("change", turn);
|
||
col.append("span").html("\u00b0");
|
||
|
||
col.append("label").attr("title", "Orientation").attr("for", "centerz").html("Orientation");
|
||
col.append("input").attr("type", "number").attr("id", "centerz").attr("title", "Center orientation").attr("max", "180").attr("min", "-180").attr("step", "0.1").on("change", turn);
|
||
col.append("span").html("\u00b0");
|
||
|
||
col.append("label").attr("for", "orientationfixed").html("Fixed");
|
||
col.append("input").attr("type", "checkbox").attr("id", "orientationfixed").property("checked", config.orientationfixed).on("change", apply);
|
||
|
||
col.append("label").attr("title", "Center and zoom in on this constellation").attr("for", "constellation").html("Show");
|
||
col.append("select").attr("id", "constellation").on("change", showConstellation);
|
||
|
||
setCenter(config.center, config.transform);
|
||
|
||
// Stars
|
||
col = frm.append("div").attr("class", "col").attr("id", "stars");
|
||
|
||
col.append("label").attr("class", "header").attr("for", "stars-show").html("Stars");
|
||
col.append("input").attr("type", "checkbox").attr("id", "stars-show").property("checked", config.stars.show).on("change", apply);
|
||
|
||
col.append("label").attr("for", "stars-limit").html("down to magnitude");
|
||
col.append("input").attr("type", "number").attr("id", "stars-limit").attr("title", "Star display limit (magnitude)").attr("value", config.stars.limit).attr("max", "6").attr("min", "-1").attr("step", "0.1").on("change", apply);
|
||
|
||
col.append("label").attr("for", "stars-colors").html("with spectral colors");
|
||
col.append("input").attr("type", "checkbox").attr("id", "stars-colors").property("checked", config.stars.colors).on("change", apply);
|
||
|
||
col.append("label").attr("for", "stars-color").html("or default color ");
|
||
col.append("input").attr("type", "color").attr("autocomplete", "off").attr("id", "stars-style-fill").attr("title", "Star color").property("value", config.stars.style.fill).on("change", apply);
|
||
col.append("br");
|
||
|
||
col.append("label").attr("for", "stars-names").html("Show designations");
|
||
col.append("input").attr("type", "checkbox").attr("id", "stars-names").property("checked", config.stars.names).on("change", apply);
|
||
|
||
col.append("label").attr("for", "stars-namelimit").html("down to mag");
|
||
col.append("input").attr("type", "number").attr("id", "stars-namelimit").attr("title", "Star designaton display limit (magnitude)").attr("value", config.stars.namelimit).attr("max", "6").attr("min", "-1").attr("step", "0.1").on("change", apply);
|
||
|
||
col.append("label").attr("for", "stars-desig").attr("title", "include HD/HIP designations").html("all");
|
||
col.append("input").attr("type", "checkbox").attr("id", "stars-desig").property("checked", config.stars.desig).on("change", apply);
|
||
|
||
col.append("label").attr("for", "stars-proper").html("proper names");
|
||
col.append("input").attr("type", "checkbox").attr("id", "stars-proper").property("checked", config.stars.proper).on("change", apply);
|
||
|
||
col.append("label").attr("for", "stars-propernamelimit").html("down to mag");
|
||
col.append("input").attr("type", "number").attr("id", "stars-propernamelimit").attr("title", "Star name display limit (magnitude)").attr("value", config.stars.propernamelimit).attr("max", "6").attr("min", "-1").attr("step", "0.1").on("change", apply);
|
||
col.append("br");
|
||
|
||
col.append("label").attr("for", "stars-size").html("Stellar disk size: base");
|
||
col.append("input").attr("type", "number").attr("id", "stars-size").attr("title", "Size of the displayed star disk; base").attr("value", config.stars.size).attr("max", "100").attr("min", "0").attr("step", "0.1").on("change", apply);
|
||
|
||
col.append("label").attr("for", "stars-exponent").html(" * e ^ (exponent");
|
||
col.append("input").attr("type", "number").attr("id", "stars-exponent").attr("title", "Size of the displayed star disk; exponent").attr("value", config.stars.exponent).attr("max", "3").attr("min", "-1").attr("step", "0.01").on("change", apply);
|
||
col.append("span").text(" * (magnitude + 2)) [* adaptation]");
|
||
|
||
enable($("stars-show"));
|
||
|
||
// DSOs
|
||
col = frm.append("div").attr("class", "col").attr("id", "dsos");
|
||
|
||
col.append("label").attr("class", "header").attr("title", "Deep Space Objects").attr("for", "dsos-show").html("DSOs");
|
||
col.append("input").attr("type", "checkbox").attr("id", "dsos-show").property("checked", config.dsos.show).on("change", apply);
|
||
|
||
col.append("label").attr("for", "dsos-limit").html("down to mag");
|
||
col.append("input").attr("type", "number").attr("id", "dsos-limit").attr("title", "DSO display limit (magnitude)").attr("value", config.dsos.limit).attr("max", "6").attr("min", "0").attr("step", "0.1").on("change", apply);
|
||
|
||
col.append("label").attr("for", "dsos-names").html("with names");
|
||
col.append("input").attr("type", "checkbox").attr("id", "dsos-names").property("checked", config.dsos.names).on("change", apply);
|
||
|
||
col.append("label").attr("for", "dsos-desig").html("or designations");
|
||
col.append("input").attr("type", "checkbox").attr("id", "dsos-desig").property("checked", config.dsos.desig).on("change", apply);
|
||
|
||
col.append("label").attr("for", "dsos-namelimit").html("down to mag");
|
||
col.append("input").attr("type", "number").attr("id", "dsos-namelimit").attr("title", "DSO name display limit (magnitude)").attr("value", config.dsos.namelimit).attr("max", "6").attr("min", "0").attr("step", "0.1").on("change", apply);
|
||
col.append("br");
|
||
|
||
col.append("label").attr("for", "dsos-size").html("DSO symbol size: (base");
|
||
col.append("input").attr("type", "number").attr("id", "dsos-size").attr("title", "Size of the displayed symbol: base").attr("value", config.dsos.size).attr("max", "100").attr("min", "0").attr("step", "0.1").on("change", apply);
|
||
|
||
col.append("label").attr("for", "dsos-exponent").html(" * 2 [* adaptation] - magnitude) ^ exponent");
|
||
col.append("input").attr("type", "number").attr("id", "dsos-exponent").attr("title", "Size of the displayed symbol; exponent").attr("value", config.dsos.exponent).attr("max", "3").attr("min", "-1").attr("step", "0.01").on("change", apply);
|
||
|
||
enable($("dsos-show"));
|
||
|
||
// Constellations
|
||
col = frm.append("div").attr("class", "col").attr("id", "constellations");
|
||
col.append("label").attr("class", "header").html("Constellations");
|
||
//col.append("input").attr("type", "checkbox").attr("id", "constellations-show").property("checked", config.constellations.show).on("change", apply);
|
||
|
||
col.append("label").attr("for", "constellations-names").html("Show names");
|
||
col.append("input").attr("type", "checkbox").attr("id", "constellations-names").property("checked", config.constellations.names).on("change", apply);
|
||
|
||
col.append("label").attr("for", "constellations-desig").html("abbreviated");
|
||
col.append("input").attr("type", "checkbox").attr("id", "constellations-desig").property("checked", config.constellations.desig).on("change", apply);
|
||
|
||
col.append("label").attr("for", "constellations-lines").html("with lines");
|
||
col.append("input").attr("type", "checkbox").attr("id", "constellations-lines").property("checked", config.constellations.lines).on("change", apply);
|
||
|
||
col.append("label").attr("for", "constellations-bounds").html("with boundaries");
|
||
col.append("input").attr("type", "checkbox").attr("id", "constellations-bounds").property("checked", config.constellations.bounds).on("change", apply);
|
||
|
||
enable($("constellations-names"));
|
||
|
||
// graticules & planes
|
||
col = frm.append("div").attr("class", "col").attr("id", "lines");
|
||
col.append("label").attr("class", "header").html("Lines");
|
||
|
||
col.append("label").attr("title", "Laitudet/longitude grid lines").attr("for", "lines-graticule").html("Graticule");
|
||
col.append("input").attr("type", "checkbox").attr("id", "lines-graticule-show").property("checked", config.lines.graticule.show).on("change", apply);
|
||
|
||
col.append("label").attr("for", "lines-equatorial").html("Equator");
|
||
col.append("input").attr("type", "checkbox").attr("id", "lines-equatorial-show").property("checked", config.lines.equatorial.show).on("change", apply);
|
||
|
||
col.append("label").attr("for", "lines-ecliptic").html("Ecliptic");
|
||
col.append("input").attr("type", "checkbox").attr("id", "lines-ecliptic-show").property("checked", config.lines.ecliptic.show).on("change", apply);
|
||
|
||
col.append("label").attr("for", "lines-galactic").html("Galactic plane");
|
||
col.append("input").attr("type", "checkbox").attr("id", "lines-galactic-show").property("checked", config.lines.galactic.show).on("change", apply);
|
||
|
||
col.append("label").attr("for", "lines-supergalactic").html("Supergalactic plane");
|
||
col.append("input").attr("type", "checkbox").attr("id", "lines-supergalactic-show").property("checked", config.lines.supergalactic.show).on("change", apply);
|
||
|
||
// Other
|
||
col = frm.append("div").attr("class", "col").attr("id", "other");
|
||
col.append("label").attr("class", "header").html("Other");
|
||
|
||
col.append("label").attr("for", "mw-show").html("Milky Way");
|
||
col.append("input").attr("type", "checkbox").attr("id", "mw-show").property("checked", config.mw.show).on("change", apply);
|
||
|
||
col.append("label").attr("for", "mw-style-fill").html(" color");
|
||
col.append("input").attr("type", "color").attr("id", "mw-style-fill").attr("title", "Milky Way color").attr("value", config.mw.style.fill).on("change", apply);
|
||
|
||
col.append("label").attr("for", "mw-style-opacity").html(" opacity");
|
||
col.append("input").attr("type", "number").attr("id", "mw-style-opacity").attr("title", "Transparency of each Milky Way layer").attr("value", config.mw.style.opacity).attr("max", "1").attr("min", "0").attr("step", "0.01").on("change", apply);
|
||
col.append("br");
|
||
|
||
col.append("label").attr("for", "background").html("Background color");
|
||
col.append("input").attr("type", "color").attr("id", "background-fill").attr("title", "Background color").attr("value", config.background.fill).on("change", apply);
|
||
|
||
col.append("label").attr("title", "Star/DSO sizes are increased with higher zoom-levels").attr("for", "adaptable").html("Adaptable object sizes");
|
||
col.append("input").attr("type", "checkbox").attr("id", "adaptable").property("checked", config.adaptable).on("change", apply);
|
||
|
||
setLimits();
|
||
setUnit(config.transform);
|
||
|
||
function resize() {
|
||
var src = this,
|
||
w = src.value;
|
||
if (testNumber(src) === false) return;
|
||
config.width = w;
|
||
Celestial.resize({width:w});
|
||
}
|
||
|
||
function reload() {
|
||
var src = this,
|
||
trans = src.value,
|
||
cx = setUnit(trans, config.transform);
|
||
if (cx !== null) config.center[0] = cx;
|
||
config.transform = trans;
|
||
Celestial.reload({transform:trans});
|
||
}
|
||
|
||
function reproject() {
|
||
var src = this;
|
||
if (!src) return;
|
||
config.projection = src.value;
|
||
Celestial.reproject(config);
|
||
}
|
||
|
||
function turn() {
|
||
if (testNumber(this) === false) return;
|
||
if (getCenter() === false) return;
|
||
Celestial.rotate(config);
|
||
}
|
||
|
||
function getCenter() {
|
||
var cx = $("centerx"), cy = $("centery"), cz = $("centerz"),
|
||
rot = [];
|
||
|
||
if (!cx || !cy) return;
|
||
|
||
if (config.transform !== "equatorial") config.center[0] = parseFloat(cx.value);
|
||
else {
|
||
var vx = parseFloat(cx.value);
|
||
config.center[0] = vx > 12 ? vx * 15 - 360 : vx * 15;
|
||
}
|
||
config.center[1] = parseFloat(cy.value);
|
||
|
||
var vz = parseFloat(cz.value);
|
||
config.center[2] = isNaN(vz) ? 0 : vz;
|
||
|
||
return cx.value !== "" && cy.value !== "";
|
||
}
|
||
|
||
function showConstellation() {
|
||
var id = this.value, anims = [];
|
||
if (id === "") {
|
||
Celestial.constellation = null;
|
||
Celestial.redraw();
|
||
return;
|
||
}
|
||
var con = Celestial.constellations[id];
|
||
config.center = con.center;
|
||
setCenter(config.center, config.transform);
|
||
//config.lines.graticule.lat.pos = [Round(con.center[0])];
|
||
//config.lines.graticule.lon.pos = [Round(con.center[1])];
|
||
//Celestial.apply(config);
|
||
|
||
//if zoomed, zoom out
|
||
var z = Celestial.zoomBy();
|
||
if (z !== 1) anims.push({param:"zoom", value:1/z, duration:0});
|
||
//rotate
|
||
anims.push({param:"center", value:con.center, duration:0});
|
||
//and zoom in
|
||
var sc = con.scale > 10 ? 10 : con.scale;
|
||
anims.push({param:"zoom", value:sc, duration:0});
|
||
Celestial.constellation = id;
|
||
Celestial.animate(anims, false);
|
||
}
|
||
|
||
function apply() {
|
||
var value, src = this;
|
||
|
||
switch (src.type) {
|
||
case "checkbox": value = src.checked; enable(src); break;
|
||
case "number": if (testNumber(src) === false) return;
|
||
value = parseFloat(src.value); break;
|
||
case "color": if (testColor(src) === false) return;
|
||
value = src.value; break;
|
||
case "text": if (src.id.search(/fill$/) === -1) return;
|
||
if (testColor(src) === false) return;
|
||
value = src.value; break;
|
||
}
|
||
if (value === null) return;
|
||
set(src.id, value);
|
||
getCenter();
|
||
Celestial.apply(config);
|
||
}
|
||
|
||
function set(prop, val) {
|
||
var a = prop.split("-");
|
||
switch (a.length) {
|
||
case 1: config[a[0]] = val; break;
|
||
case 2: config[a[0]][a[1]] = val; break;
|
||
case 3: config[a[0]][a[1]][a[2]] = val; break;
|
||
default: return;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Dependend fields relations
|
||
var depends = {
|
||
"stars-show": ["stars-limit", "stars-colors", "stars-style-fill", "stars-names", "stars-size", "stars-exponent"],
|
||
"stars-names": ["stars-proper", "stars-desig", "stars-namelimit"],
|
||
"stars-proper": ["stars-propernamelimit"],
|
||
"dsos-show": ["dsos-limit", "dsos-names", "dsos-size", "dsos-exponent"],
|
||
"dsos-names": ["dsos-desig", "dsos-namelimit"],
|
||
"mw-show": ["mw-style-opacity", "mw-style-fill"],
|
||
"constellations-names": ["constellations-desig"]
|
||
};
|
||
|
||
// De/activate fields depending on selection of dependencies
|
||
function enable(source) {
|
||
var fld = source.id, off;
|
||
|
||
switch (fld) {
|
||
case "stars-show":
|
||
off = !$(fld).checked;
|
||
for (var i=0; i< depends[fld].length; i++) { fldEnable(depends[fld][i], off); }
|
||
/* falls through */
|
||
case "stars-names":
|
||
off = !$("stars-names").checked || !$("stars-show").checked;
|
||
for (i=0; i< depends["stars-names"].length; i++) { fldEnable(depends["stars-names"][i], off); }
|
||
/* falls through */
|
||
case "stars-proper":
|
||
off = !$("stars-names").checked || !$("stars-show").checked || !$("stars-proper").checked;
|
||
for (i=0; i< depends["stars-proper"].length; i++) { fldEnable(depends["stars-proper"][i], off); }
|
||
break;
|
||
case "dsos-show":
|
||
off = !$(fld).checked;
|
||
for (i=0; i< depends[fld].length; i++) { fldEnable(depends[fld][i], off); }
|
||
/* falls through */
|
||
case "dsos-names":
|
||
off = !$("dsos-names").checked || !$("dsos-show").checked;
|
||
for (i=0; i< depends["dsos-names"].length; i++) { fldEnable(depends["dsos-names"][i], off); }
|
||
break;
|
||
case "constellations-show":
|
||
off = !$(fld).checked;
|
||
for (i=0; i< depends[fld].length; i++) { fldEnable(depends[fld][i], off); }
|
||
break;
|
||
case "mw-show":
|
||
off = !$(fld).checked;
|
||
for (i=0; i< depends[fld].length; i++) { fldEnable(depends[fld][i], off); }
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Enable/disable field d to status off
|
||
function fldEnable(d, off) {
|
||
var node = $(d);
|
||
node.disabled = off;
|
||
node.previousSibling.style.color = off ? "#999" : "#000";
|
||
}
|
||
|
||
// Error notification
|
||
function popError(nd, err) {
|
||
var p = findPos(nd);
|
||
d3.select("#error").html(err).style( {top:px(p[1] + nd.offsetHeight + 1), left:px(p[0]), opacity:1} );
|
||
nd.focus();
|
||
}
|
||
|
||
//Check numeric field
|
||
function testNumber(node) {
|
||
var v, adj = node.id === "hr" || node.id === "min" || node.id === "sec" ? 1 : 0;
|
||
if (node.validity) {
|
||
v = node.validity;
|
||
if (v.typeMismatch || v.badInput) { popError(node, node.title + ": check field value"); return false; }
|
||
if (v.rangeOverflow || v.rangeUnderflow) { popError(node, node.title + " must be between " + (parseInt(node.min) + adj) + " and " + (parseInt(node.max) - adj)); return false; }
|
||
} else {
|
||
v = node.value;
|
||
if (!isNumber(v)) { popError(node, node.title + ": check field value"); return false; }
|
||
v = parseFloat(v);
|
||
if (v < node.min || v > node.max ) { popError(node, node.title + " must be between " + (node.min + adj) + " and " + (+node.max - adj)); return false; }
|
||
}
|
||
d3.select("#error").style( {top:"-9999px", left:"-9999px", opacity:0} );
|
||
return true;
|
||
}
|
||
|
||
//Check color field
|
||
function testColor(node) {
|
||
var v;
|
||
if (node.validity) {
|
||
v = node.validity;
|
||
if (v.typeMismatch || v.badInput) { popError(node, node.title + ": check field value"); return false; }
|
||
if (node.value.search(/^#[0-9A-F]{6}$/i) === -1) { popError(node, node.title + ": not a color value"); return false; }
|
||
} else {
|
||
v = node.value;
|
||
if (v === "") return true;
|
||
if (v.search(/^#[0-9A-F]{6}$/i) === -1) { popError(node, node.title + ": not a color value"); return false; }
|
||
}
|
||
d3.select("#error").style( {top:"-9999px", left:"-9999px", opacity:0} );
|
||
return true;
|
||
}
|
||
|
||
function setUnit(trans, old) {
|
||
var cx = $("centerx");
|
||
if (!cx) return null;
|
||
|
||
if (old) {
|
||
if (trans === "equatorial" && old !== "equatorial") {
|
||
cx.value = (cx.value/15).toFixed(1);
|
||
if (cx.value < 0) cx.value += 24;
|
||
} else if (trans !== "equatorial" && old === "equatorial") {
|
||
cx.value = (cx.value * 15).toFixed(1);
|
||
if (cx.value > 180) cx.value -= 360;
|
||
}
|
||
}
|
||
if (trans === 'equatorial') {
|
||
cx.min = "0";
|
||
cx.max = "24";
|
||
$("cxunit").innerHTML = "h";
|
||
} else {
|
||
cx.min = "-180";
|
||
cx.max = "180";
|
||
$("cxunit").innerHTML = "\u00b0";
|
||
}
|
||
return cx.value;
|
||
}
|
||
|
||
function setCenter(ctr, trans) {
|
||
var cx = $("centerx"), cy = $("centery"), cz = $("centerz");
|
||
if (!cx || !cy) return;
|
||
|
||
if (ctr === null) ctr = [0,0,0];
|
||
if (ctr.length <= 2) ctr[2] = 0;
|
||
//config.center = ctr;
|
||
if (trans !== "equatorial") cx.value = ctr[0].toFixed(1);
|
||
else cx.value = ctr[0] < 0 ? (ctr[0] / 15 + 24).toFixed(1) : (ctr[0] / 15).toFixed(1);
|
||
|
||
cy.value = ctr[1].toFixed(1);
|
||
cz.value = ctr[2] !== null ? ctr[2].toFixed(1) : 0;
|
||
}
|
||
|
||
// Set max input limits depending on data
|
||
function setLimits() {
|
||
var t, rx = /\d+(\.\d+)?/g,
|
||
s, d, res = {s:6, d:6},
|
||
config = Celestial.settings();
|
||
|
||
d = config.dsos.data;
|
||
|
||
//test dso limit
|
||
t = d.match(rx);
|
||
if (t !== null) {
|
||
res.d = parseFloat(t[t.length-1]);
|
||
}
|
||
|
||
if (res.d != 6) {
|
||
$("dsos-limit").max = res.d;
|
||
$("dsos-namelimit").max = res.d;
|
||
}
|
||
|
||
s = config.stars.data;
|
||
|
||
//test star limit
|
||
t = s.match(rx);
|
||
if (t !== null) {
|
||
res.s = parseFloat(t[t.length-1]);
|
||
}
|
||
|
||
if (res.s != 6) {
|
||
$("stars-limit").max = res.s;
|
||
$("stars-namelimit").max = res.s;
|
||
}
|
||
|
||
return res;
|
||
}
|
||
|
||
|
||
|
||
function geo(cfg) {
|
||
var frm = d3.select("#celestial-form").append("div").attr("id", "loc"),
|
||
dtFormat = d3.time.format("%Y-%m-%d %H:%M:%S"),
|
||
zenith = [0,0],
|
||
geopos = [0,0],
|
||
date = new Date(),
|
||
zone = date.getTimezoneOffset();
|
||
|
||
var dtpick = new datetimepicker(cfg, function(date, tz) {
|
||
$("datetime").value = dateFormat(date, tz);
|
||
zone = tz;
|
||
go();
|
||
});
|
||
|
||
if (has(cfg, "geopos") && cfg.geopos !== null && cfg.geopos.length === 2) geopos = cfg.geopos;
|
||
var col = frm.append("div").attr("class", "col").attr("id", "location");
|
||
//Latitude & longitude fields
|
||
col.append("label").attr("title", "Location coordinates long/lat").attr("for", "lat").html("Location");
|
||
col.append("input").attr("type", "number").attr("id", "lat").attr("title", "Latitude").attr("placeholder", "Latitude").attr("max", "90").attr("min", "-90").attr("step", "0.0001").attr("value", geopos[0]).on("change", function () {
|
||
if (testNumber(this) === true) go();
|
||
});
|
||
col.append("span").html("\u00b0");
|
||
col.append("input").attr("type", "number").attr("id", "lon").attr("title", "Longitude").attr("placeholder", "Longitude").attr("max", "180").attr("min", "-180").attr("step", "0.0001").attr("value", geopos[1]).on("change", function () {
|
||
if (testNumber(this) === true) go();
|
||
});
|
||
col.append("span").html("\u00b0");
|
||
//Here-button if supported
|
||
if ("geolocation" in navigator) {
|
||
col.append("input").attr("type", "button").attr("value", "Here").attr("id", "here").on("click", here);
|
||
}
|
||
//Datetime field with dtpicker-button
|
||
col.append("label").attr("title", "Local date/time").attr("for", "datetime").html(" Date/time");
|
||
col.append("input").attr("type", "button").attr("id", "day-left").attr("title", "One day back").on("click", function () {
|
||
date.setDate(date.getDate() - 1);
|
||
$("datetime").value = dateFormat(date, zone);
|
||
go();
|
||
});
|
||
col.append("input").attr("type", "text").attr("id", "datetime").attr("title", "Date and time").attr("value", dateFormat(date, zone))
|
||
.on("click", showpick, true).on("input", function () {
|
||
this.value = dateFormat(date, zone);
|
||
if (!dtpick.isVisible()) showpick();
|
||
});
|
||
col.append("div").attr("id", "datepick").on("click", showpick);
|
||
col.append("input").attr("type", "button").attr("id", "day-right").attr("title", "One day forward").on("click", function () {
|
||
date.setDate(date.getDate() + 1);
|
||
$("datetime").value = dateFormat(date, zone);
|
||
go();
|
||
});
|
||
//Now -button sets current time & date of device
|
||
col.append("input").attr("type", "button").attr("value", "Now").attr("id", "now").on("click", now);
|
||
//Horizon marker
|
||
col.append("br");
|
||
col.append("label").attr("title", "Show horizon marker").attr("for", "horizon-show").html(" Horizon marker");
|
||
col.append("input").attr("type", "checkbox").attr("id", "horizon-show").property("checked", cfg.horizon.show).on("change", go);
|
||
col.append("label").attr("title", "Show solar system objects").attr("for", "planets-show").html(" Planets, Sun & Moon");
|
||
col.append("input").attr("type", "checkbox").attr("id", "planets-show").property("checked", cfg.planets.show).on("change", go);
|
||
|
||
d3.select(document).on("mousedown", function () {
|
||
if (!hasParent(d3.event.target, "celestial-date") && dtpick.isVisible()) dtpick.hide();
|
||
});
|
||
|
||
function now() {
|
||
date.setTime(Date.now());
|
||
$("datetime").value = dateFormat(date, zone);
|
||
go();
|
||
}
|
||
|
||
function here() {
|
||
navigator.geolocation.getCurrentPosition( function(pos) {
|
||
geopos = [Round(pos.coords.latitude, 4), Round(pos.coords.longitude, 4)];
|
||
$("lat").value = geopos[0];
|
||
$("lon").value = geopos[1];
|
||
go();
|
||
});
|
||
}
|
||
|
||
function showpick() {
|
||
dtpick.show(date);
|
||
}
|
||
|
||
function dateFormat(dt, tz) {
|
||
var tzs;
|
||
if (!tz || tz === "0") tzs = " ±0000";
|
||
else {
|
||
var h = Math.floor(Math.abs(tz) / 60),
|
||
m = Math.abs(tz) - (h * 60),
|
||
s = tz < 0 ? " +" : " −";
|
||
tzs = s + pad(h) + pad(m);
|
||
}
|
||
return dtFormat(dt) + tzs;
|
||
}
|
||
|
||
function go() {
|
||
var lon = $("lon").value,
|
||
lat = $("lat").value;
|
||
|
||
date = dtFormat.parse($("datetime").value.slice(0,-6));
|
||
|
||
var tz = date.getTimezoneOffset();
|
||
var dtc = new Date(date.valueOf() + (zone - tz) * 60000);
|
||
|
||
cfg.horizon.show = !!$("horizon-show").checked;
|
||
cfg.planets.show = !!$("planets-show").checked;
|
||
|
||
if (lon !== "" && lat !== "") {
|
||
geopos = [parseFloat(lat), parseFloat(lon)];
|
||
zenith = Celestial.getPoint(horizontal.inverse(dtc, [90, 0], geopos), cfg.transform);
|
||
zenith[2] = 0;
|
||
if (cfg.follow === "zenith") {
|
||
Celestial.rotate({center:zenith, horizon:cfg.horizon});
|
||
} else {
|
||
Celestial.apply({horizon:cfg.horizon});
|
||
}
|
||
}
|
||
}
|
||
|
||
Celestial.getPosition = function (p) {
|
||
|
||
};
|
||
|
||
Celestial.date = function (dt) {
|
||
if (!dt) return date;
|
||
if (dtpick.isVisible()) return;
|
||
date.setTime(dt.valueOf());
|
||
$("datetime").value = dateFormat(dt, zone);
|
||
Celestial.redraw();
|
||
};
|
||
Celestial.position = function () { return geopos; };
|
||
Celestial.zenith = function () { return zenith; };
|
||
Celestial.nadir = function () {
|
||
var b = -zenith[1],
|
||
l = zenith[0] + 180;
|
||
if (l > 180) l -= 360;
|
||
return [l, b-0.001];
|
||
};
|
||
|
||
setTimeout(go, 1000);
|
||
|
||
}
|
||
|
||
|
||
var gmdat = {
|
||
"sol": 0.0002959122082855911025, // AU^3/d^2
|
||
"mer": 164468599544771, //km^3/d^2
|
||
"ven": 2425056445892137,
|
||
"ter": 2975536307796296,
|
||
"lun": 36599199229256,
|
||
"mar": 319711652803400,
|
||
"cer": 467549107200,
|
||
"ves": 129071530155,
|
||
"jup": 945905743547733000,
|
||
"sat": 283225255921345000,
|
||
"ura": 43256076555832200,
|
||
"nep": 51034453325494200,
|
||
"plu": 7327611364884,
|
||
"eri": 8271175680000
|
||
},
|
||
symbols = {
|
||
"sol":"\u2609", "mer":"\u263f", "ven":"\u2640", "ter":"\u2295", "lun":"\u25cf", "mar":"\u2642", "cer":"\u26b3",
|
||
"ves":"\u26b6", "jup":"\u2643", "sat":"\u2644", "ura":"\u2645", "nep":"\u2646", "plu":"\u2647", "eri":"\u26aa"
|
||
},
|
||
|
||
ε = 23.43928 * deg2rad,
|
||
sinε = Math.sin(ε),
|
||
cosε = Math.cos(ε),
|
||
kelements = ["a","e","i","w","M","L","W","N","n","ep","ref","lecl","becl","Tilt"];
|
||
/*
|
||
ep = epoch (iso-date)
|
||
N = longitude of the ascending node (deg) Ω
|
||
i = inclination to the refrence plane, default:ecliptic (deg)
|
||
w = argument of periapsis (deg) ω
|
||
a = semi-major axis, or mean distance from parent body (AU,km)
|
||
e = eccentricity (0=circle, 0-1=ellipse, 1=parabola, >1=hyperbola ) (-)
|
||
M = mean anomaly (0 at periapsis; increases uniformly with time) (deg)
|
||
n = mean daily motion = 360/P (deg/day)
|
||
|
||
W = N + w = longitude of periapsis ϖ
|
||
L = M + W = mean longitude
|
||
q = a*(1-e) = periapsis distance
|
||
Q = a*(1+e) = apoapsis distance
|
||
P = 2π * sqrt(a^3/GM) = orbital period (years)
|
||
T = Epoch_of_M - (M(deg)/360_deg) / P = time of periapsis
|
||
v = true anomaly (angle between position and periapsis) ν
|
||
E = eccentric anomaly
|
||
|
||
Mandatory: a, e, i, N, w|W, M|L, dM|n
|
||
|
||
*/
|
||
|
||
var Kepler = function () {
|
||
var gm = gmdat.sol,
|
||
parentBody = "sol",
|
||
elem = {}, dat = {},
|
||
id, name, symbol;
|
||
|
||
|
||
function kepler(date) {
|
||
dates(date);
|
||
if (id === "sol") {
|
||
dat.x = 0;
|
||
dat.y = 0;
|
||
dat.z = 0;
|
||
return kepler;
|
||
}
|
||
coordinates();
|
||
return kepler;
|
||
}
|
||
|
||
var dates = function(date) {
|
||
var dt;
|
||
dat = [];
|
||
if (date) {
|
||
if (date instanceof Date) { dt = new Date(date.valueOf()); }
|
||
else { dt = dateParse(date); }
|
||
}
|
||
if (!dt) { dt = new Date(); }
|
||
dat.jd = JD(dt);
|
||
|
||
dt = dateParse(elem.ep);
|
||
if (!dt) dt = dateParse("2000-01-01");
|
||
dat.jd0 = JD(dt);
|
||
dat.d = dat.jd - dat.jd0;
|
||
dat.cy = dat.d / 36525;
|
||
};
|
||
|
||
var coordinates = function() {
|
||
var key;
|
||
if (id === "lun") {
|
||
dat = moon_elements(dat);
|
||
if (!dat) return;
|
||
} else {
|
||
for (var i=0; i<kelements.length; i++) {
|
||
key = kelements[i];
|
||
if (!has(elem, key)) continue;
|
||
if (has(elem, "d"+key)) {
|
||
dat[key] = elem[key] + elem["d"+key] * dat.cy;
|
||
} else if (has(elem, key)) {
|
||
dat[key] = elem[key];
|
||
}
|
||
}
|
||
if (has(dat, "M") && !has(dat, "dM") && has(dat, "n")) {
|
||
dat.M += (dat.n * dat.d);
|
||
}
|
||
}
|
||
derive();
|
||
trueAnomaly();
|
||
cartesian();
|
||
};
|
||
|
||
kepler.cartesian = function() {
|
||
return dat;
|
||
};
|
||
|
||
kepler.spherical = function() {
|
||
spherical();
|
||
return dat;
|
||
};
|
||
|
||
kepler.equatorial = function(pos) {
|
||
equatorial(pos);
|
||
return dat;
|
||
};
|
||
|
||
kepler.transpose = function() {
|
||
transpose(dat);
|
||
return dat;
|
||
};
|
||
|
||
kepler.elements = function(_) {
|
||
var key;
|
||
|
||
if (!arguments.length) return elem;
|
||
|
||
for (var i=0; i<kelements.length; i++) {
|
||
key = kelements[i];
|
||
if (!has(_, key)) continue;
|
||
elem[key] = _[key];
|
||
|
||
if (key === "a" || key === "e") elem[key] *= 1.0;
|
||
else if (key !== "ref" && key !== "ep") elem[key] *= deg2rad;
|
||
|
||
if (has(_, "d" + key)) {
|
||
key = "d" + key;
|
||
elem[key] = _[key];
|
||
if (key === "da" || key === "de") elem[key] *= 1.0;
|
||
else elem[key] *= deg2rad;
|
||
}
|
||
}
|
||
return kepler;
|
||
};
|
||
|
||
kepler.parentBody = function(_) {
|
||
if (!arguments.length) return parentBody;
|
||
parentBody = _;
|
||
gm = gmdat[parentBody];
|
||
return kepler;
|
||
};
|
||
|
||
kepler.id = function(_) {
|
||
if (!arguments.length) return id;
|
||
id = _;
|
||
symbol = symbols[_];
|
||
return kepler;
|
||
};
|
||
|
||
kepler.name = function(_) {
|
||
if (!arguments.length) return name;
|
||
name = _;
|
||
return kepler;
|
||
};
|
||
|
||
kepler.symbol = function(_) {
|
||
if (!arguments.length) return symbol;
|
||
symbol = symbols[_];
|
||
return kepler;
|
||
};
|
||
|
||
|
||
function near_parabolic(E, e) {
|
||
var anom2 = e > 1.0 ? E*E : -E*E,
|
||
term = e * anom2 * E / 6.0,
|
||
rval = (1.0 - e) * E - term,
|
||
n = 4;
|
||
|
||
while(Math.abs(term) > 1e-15) {
|
||
term *= anom2 / (n * (n + 1));
|
||
rval -= term;
|
||
n += 2;
|
||
}
|
||
return(rval);
|
||
}
|
||
|
||
function anomaly() {
|
||
var curr, err, trial, tmod,
|
||
e = dat.e, M = dat.M,
|
||
thresh = 1e-8,
|
||
offset = 0.0,
|
||
delta_curr = 1.9,
|
||
is_negative = false,
|
||
n_iter = 0;
|
||
|
||
if (!M) return(0.0);
|
||
|
||
if (e < 1.0) {
|
||
if (M < -Math.PI || M > Math.PI) {
|
||
tmod = Trig.normalize0(M);
|
||
offset = M - tmod;
|
||
M = tmod;
|
||
}
|
||
|
||
if (e < 0.9) {
|
||
curr = Math.atan2(Math.sin(M), Math.cos(M) - e);
|
||
do {
|
||
err = (curr - e * Math.sin(curr) - M) / (1.0 - e * Math.cos(curr));
|
||
curr -= err;
|
||
} while (Math.abs(err) > thresh);
|
||
return curr; // + offset;
|
||
}
|
||
}
|
||
|
||
if ( M < 0.0) {
|
||
M = -M;
|
||
is_negative = true;
|
||
}
|
||
|
||
curr = M;
|
||
thresh = thresh * Math.abs(1.0 - e);
|
||
/* Due to roundoff error, there's no way we can hope to */
|
||
/* get below a certain minimum threshhold anyway: */
|
||
if ( thresh < 1e-15) { thresh = 1e-15; }
|
||
if ( (e > 0.8 && M < Math.PI / 3.0) || e > 1.0) { /* up to 60 degrees */
|
||
trial = M / Math.abs( 1.0 - e);
|
||
|
||
if (trial * trial > 6.0 * Math.abs(1.0 - e)) { /* cubic term is dominant */
|
||
if (M < Math.PI) {
|
||
trial = Math.pow(6.0 * M, 1/3);
|
||
} else { /* hyperbolic w/ 5th & higher-order terms predominant */
|
||
trial = Trig.asinh( M / e);
|
||
}
|
||
}
|
||
curr = trial;
|
||
}
|
||
if (e > 1.0 && M > 4.0) { /* hyperbolic, large-mean-anomaly case */
|
||
curr = Math.log(M);
|
||
}
|
||
if (e < 1.0) {
|
||
while(Math.abs(delta_curr) > thresh) {
|
||
if ( n_iter++ > 8) {
|
||
err = near_parabolic(curr, e) - M;
|
||
} else {
|
||
err = curr - e * Math.sin(curr) - M;
|
||
}
|
||
delta_curr = -err / (1.0 - e * Math.cos(curr));
|
||
curr += delta_curr;
|
||
}
|
||
} else {
|
||
while (Math.abs(delta_curr) > thresh) {
|
||
if (n_iter++ > 7) {
|
||
err = -near_parabolic(curr, e) - M;
|
||
} else {
|
||
err = e * Trig.sinh(curr) - curr - M;
|
||
}
|
||
delta_curr = -err / (e * Trig.cosh(curr) - 1.0);
|
||
curr += delta_curr;
|
||
}
|
||
}
|
||
return( is_negative ? offset - curr : offset + curr);
|
||
}
|
||
|
||
function trueAnomaly() {
|
||
var x, y, r0, g, t;
|
||
|
||
if (dat.e === 1.0) { /* parabolic */
|
||
t = dat.jd0 - dat.T;
|
||
g = dat.w0 * t * 0.5;
|
||
|
||
y = Math.pow(g + Math.sqrt(g * g + 1.0), 1/3);
|
||
dat.v = 2.0 * Math.atan(y - 1.0 / y);
|
||
} else { /* got the mean anomaly; compute eccentric, then true */
|
||
dat.E = anomaly();
|
||
if (dat.e > 1.0) { /* hyperbolic case */
|
||
x = (dat.e - Trig.cosh(dat.E));
|
||
y = Trig.sinh(dat.E);
|
||
} else { /* elliptical case */
|
||
x = (Math.cos(dat.E) - dat.e);
|
||
y = Math.sin(dat.E);
|
||
}
|
||
y *= Math.sqrt(Math.abs(1.0 - dat.e * dat.e));
|
||
dat.v = Math.atan2(y, x);
|
||
}
|
||
|
||
r0 = dat.q * (1.0 + dat.e);
|
||
dat.r = r0 / (1.0 + dat.e * Math.cos(dat.v));
|
||
}
|
||
|
||
function derive() {
|
||
if (!dat.hasOwnProperty("w")) {
|
||
dat.w = dat.W - dat.N;
|
||
}
|
||
if (!dat.hasOwnProperty("M")) {
|
||
dat.M = dat.L - dat.W;
|
||
}
|
||
if (dat.e < 1.0) { dat.M = Trig.normalize0(dat.M); }
|
||
//dat.P = Math.pow(Math.abs(dat.a), 1.5);
|
||
dat.P = τ * Math.sqrt(Math.pow(dat.a, 3) / gm) / 365.25;
|
||
dat.T = dat.jd0 - (dat.M / halfπ) / dat.P;
|
||
|
||
if (dat.e !== 1.0) { /* for non-parabolic orbits: */
|
||
dat.q = dat.a * (1.0 - dat.e);
|
||
dat.t0 = dat.a * Math.sqrt(Math.abs(dat.a) / gm);
|
||
} else {
|
||
dat.w0 = (3.0 / Math.sqrt(2)) / (dat.q * Math.sqrt(dat.q / gm));
|
||
dat.a = 0.0;
|
||
dat.t0 = 0.0;
|
||
}
|
||
dat.am = Math.sqrt(gm * dat.q * (1.0 + dat.e));
|
||
}
|
||
|
||
function transpose() {
|
||
if (!dat.ref || dat.ref === "ecl") {
|
||
dat.tx = dat.x;
|
||
dat.ty = dat.y;
|
||
dat.tz = dat.z;
|
||
return;
|
||
}
|
||
var a0 = dat.lecl,// - Math.PI/2,
|
||
a1 = Math.PI/2 - dat.becl,
|
||
angles = [0, a1, -a0];
|
||
transform(dat, angles);
|
||
var tp = Trig.cartesian([dat.tl, dat.tb, dat.r]);
|
||
dat.tx = tp.x;
|
||
dat.ty = tp.y;
|
||
dat.tz = tp.z;
|
||
}
|
||
|
||
function equatorial(pos) {
|
||
ε = (23.439292 - 0.0130042 * dat.cy - 1.667e-7 * dat.cy * dat.cy + 5.028e-7 * dat.cy * dat.cy * dat.cy) * deg2rad;
|
||
sinε = Math.sin(ε);
|
||
cosε = Math.cos(ε);
|
||
var o = (id === "lun") ? {x:0, y:0, z:0} : {x:pos.x, y:pos.y, z:pos.z};
|
||
dat.xeq = dat.x - o.x;
|
||
dat.yeq = (dat.y - o.y) * cosε - (dat.z - o.z) * sinε;
|
||
dat.zeq = (dat.y - o.y) * sinε + (dat.z - o.z) * cosε;
|
||
|
||
dat.ra = Trig.normalize(Math.atan2(dat.yeq, dat.xeq));
|
||
dat.dec = Math.atan2(dat.zeq, Math.sqrt(dat.xeq*dat.xeq + dat.yeq*dat.yeq));
|
||
if (id === "lun") dat = moon_corr(dat, pos);
|
||
dat.pos = [dat.ra / deg2rad, dat.dec / deg2rad];
|
||
dat.rt = Math.sqrt(dat.xeq*dat.xeq + dat.yeq*dat.yeq + dat.zeq*dat.zeq);
|
||
}
|
||
|
||
function cartesian() {
|
||
var u = dat.v + dat.w;
|
||
dat.x = dat.r * (Math.cos(dat.N) * Math.cos(u) - Math.sin(dat.N) * Math.sin(u) * Math.cos(dat.i));
|
||
dat.y = dat.r * (Math.sin(dat.N) * Math.cos(u) + Math.cos(dat.N) * Math.sin(u) * Math.cos(dat.i));
|
||
dat.z = dat.r * (Math.sin(u) * Math.sin(dat.i));
|
||
return dat;
|
||
}
|
||
|
||
function spherical() {
|
||
var lon = Math.atan2(dat.y, dat.x),
|
||
lat = Math.atan2(dat.z, Math.sqrt(dat.x*dat.x + dat.y*dat.y));
|
||
dat.l = Trig.normalize(lon);
|
||
dat.b = lat;
|
||
return dat;
|
||
}
|
||
|
||
function polar2cart(pos) {
|
||
var rclat = Math.cos(pos.lat) * pos.r;
|
||
pos.x = rclat * Math.cos(pos.lon);
|
||
pos.y = rclat * Math.sin(pos.lon);
|
||
pos.z = pos.r * Math.sin(pos.lat);
|
||
return pos;
|
||
}
|
||
|
||
|
||
function JD(dt) {
|
||
var yr = dt.getUTCFullYear(),
|
||
mo = dt.getUTCMonth() + 1,
|
||
dy = dt.getUTCDate(),
|
||
frac = (dt.getUTCHours() - 12 + dt.getUTCMinutes()/60.0 + dt.getUTCSeconds()/3600.0) / 24,
|
||
IYMIN = -4799; /* Earliest year allowed (4800BC) */
|
||
|
||
if (yr < IYMIN) return -1;
|
||
var a = Math.floor((14 - mo) / 12),
|
||
y = yr + 4800 - a,
|
||
m = mo + (12 * a) - 3;
|
||
var jdn = dy + Math.floor((153 * m + 2)/5) + (365 * y) + Math.floor(y / 4) - Math.floor(y / 100) + Math.floor(y / 400) - 32045;
|
||
return jdn + frac;
|
||
}
|
||
|
||
function mst(lon) {
|
||
var l = lon || 0; // lon=0 -> gmst
|
||
return (18.697374558 + 24.06570982441908 * dat.d) * 15 + l;
|
||
}
|
||
|
||
|
||
function observer(pos) {
|
||
var flat = 298.257223563, // WGS84 flattening of earth
|
||
re = 6378.137, // GRS80/WGS84 semi major axis of earth ellipsoid
|
||
h = pos.h || 0,
|
||
cart = {},
|
||
gmst = mst();
|
||
|
||
var cosl = Math.cos(pos.lat),
|
||
sinl = Math.sin(pos.lat),
|
||
fl = 1.0 - 1.0 / flat;
|
||
var fl2 = fl * fl;
|
||
|
||
var u = 1.0 / Math.sqrt (cosl * cosl + fl2 * sinl * sinl),
|
||
a = re * u + h,
|
||
b = re * fl2 * u + h,
|
||
r = Math.sqrt (a * a * cosl * cosl + b * b * sinl * sinl); // geocentric distance from earth center
|
||
|
||
cart.lat = Math.acos (a * cosl / r);
|
||
cart.lon = pos.lon;
|
||
cart.r = h;
|
||
|
||
if (pos.lat < 0.0) cart.lat *= -1;
|
||
|
||
polar2cart(cart);
|
||
|
||
// rotate around earth's polar axis to align coordinate system from Greenwich to vernal equinox
|
||
var angle = gmst * deg2rad; // sideral time gmst given in hours. Convert to radians
|
||
|
||
cart.x = cart.x * Math.cos(angle) - cart.y * Math.sin(angle);
|
||
cart.y = cart.x * Math.sin(angle) + cart.y * Math.cos(angle);
|
||
return(cart);
|
||
}
|
||
|
||
function moon_elements(dat) {
|
||
if ((typeof Moon !== "undefined")) return Moon.elements(dat);
|
||
}
|
||
|
||
function moon_corr(dat, pos) {
|
||
spherical();
|
||
if ((typeof Moon !== "undefined")) return Moon.corr(dat, pos);
|
||
}
|
||
|
||
return kepler;
|
||
};
|
||
Celestial.Kepler = Kepler;
|
||
|
||
|
||
var Moon = {
|
||
elements: function(dat) {
|
||
var t = (dat.jd - 2451545) / 36525,
|
||
t2 = t * t,
|
||
t3 = t * t2,
|
||
t4 = t * t3,
|
||
t5 = t * t4,
|
||
t2e4 = t2 * 1e-4,
|
||
t3e6 = t3 * 1e-6,
|
||
t4e8 = t4 * 1e-8;
|
||
|
||
// semimajor axis
|
||
var sa = 3400.4 * Math.cos(deg2rad * (235.7004 + 890534.2230 * t - 32.601 * t2e4
|
||
+ 3.664 * t3e6 - 1.769 * t4e8))
|
||
- 635.6 * Math.cos(deg2rad * (100.7370 + 413335.3554 * t - 122.571 * t2e4
|
||
- 10.684 * t3e6 + 5.028 * t4e8))
|
||
- 235.6 * Math.cos(deg2rad * (134.9634 + 477198.8676 * t + 89.970 * t2e4
|
||
+ 14.348 * t3e6 - 6.797 * t4e8))
|
||
+ 218.1 * Math.cos(deg2rad * (238.1713 + 854535.1727 * t - 31.065 * t2e4
|
||
+ 3.623 * t3e6 - 1.769 * t4e8))
|
||
+ 181.0 * Math.cos(deg2rad * (10.6638 + 1367733.0907 * t + 57.370 * t2e4
|
||
+ 18.011 * t3e6 - 8.566 * t4e8))
|
||
- 39.9 * Math.cos(deg2rad * (103.2079 + 377336.3051 * t - 121.035 * t2e4
|
||
- 10.724 * t3e6 + 5.028 * t4e8))
|
||
- 38.4 * Math.cos(deg2rad * (233.2295 + 926533.2733 * t - 34.136 * t2e4
|
||
+ 3.705 * t3e6 - 1.769 * t4e8))
|
||
+ 33.8 * Math.cos(deg2rad * (336.4374 + 1303869.5784 * t - 155.171 * t2e4
|
||
- 7.020 * t3e6 + 3.259 * t4e8))
|
||
+ 28.8 * Math.cos(deg2rad * (111.4008 + 1781068.4461 * t - 65.201 * t2e4
|
||
+ 7.328 * t3e6 - 3.538 * t4e8))
|
||
+ 12.6 * Math.cos(deg2rad * (13.1347 + 1331734.0404 * t + 58.906 * t2e4
|
||
+ 17.971 * t3e6 - 8.566 * t4e8))
|
||
+ 11.4 * Math.cos(deg2rad * (186.5442 + 966404.0351 * t - 68.058 * t2e4
|
||
- 0.567 * t3e6 + 0.232 * t4e8))
|
||
- 11.1 * Math.cos(deg2rad * (222.5657 - 441199.8173 * t - 91.506 * t2e4
|
||
- 14.307 * t3e6 + 6.797 * t4e8))
|
||
- 10.2 * Math.cos(deg2rad * (269.9268 + 954397.7353 * t + 179.941 * t2e4
|
||
+ 28.695 * t3e6 - 13.594 * t4e8))
|
||
+ 9.7 * Math.cos(deg2rad * (145.6272 + 1844931.9583 * t + 147.340 * t2e4
|
||
+ 32.359 * t3e6 - 15.363 * t4e8))
|
||
+ 9.6 * Math.cos(deg2rad * (240.6422 + 818536.1225 * t - 29.529 * t2e4
|
||
+ 3.582 * t3e6 - 1.769 * t4e8))
|
||
+ 8.0 * Math.cos(deg2rad * (297.8502 + 445267.1115 * t - 16.300 * t2e4
|
||
+ 1.832 * t3e6 - 0.884 * t4e8))
|
||
- 6.2 * Math.cos(deg2rad * (132.4925 + 513197.9179 * t + 88.434 * t2e4
|
||
+ 14.388 * t3e6 - 6.797 * t4e8))
|
||
+ 6.0 * Math.cos(deg2rad * (173.5506 + 1335801.3346 * t - 48.901 * t2e4
|
||
+ 5.496 * t3e6 - 2.653 * t4e8))
|
||
+ 3.7 * Math.cos(deg2rad * (113.8717 + 1745069.3958 * t - 63.665 * t2e4
|
||
+ 7.287 * t3e6 - 3.538 * t4e8))
|
||
+ 3.6 * Math.cos(deg2rad * (338.9083 + 1267870.5281 * t - 153.636 * t2e4
|
||
- 7.061 * t3e6 + 3.259 * t4e8))
|
||
+ 3.2 * Math.cos(deg2rad * (246.3642 + 2258267.3137 * t + 24.769 * t2e4
|
||
+ 21.675 * t3e6 - 10.335 * t4e8))
|
||
- 3.0 * Math.cos(deg2rad * (8.1929 + 1403732.1410 * t + 55.834 * t2e4
|
||
+ 18.052 * t3e6 - 8.566 * t4e8))
|
||
+ 2.3 * Math.cos(deg2rad * (98.2661 + 449334.4057 * t - 124.107 * t2e4
|
||
- 10.643 * t3e6 + 5.028 * t4e8))
|
||
- 2.2 * Math.cos(deg2rad * (357.5291 + 35999.0503 * t - 1.536 * t2e4
|
||
+ 0.041 * t3e6 + 0.000 * t4e8))
|
||
- 2.0 * Math.cos(deg2rad * (38.5872 + 858602.4669 * t - 138.871 * t2e4
|
||
- 8.852 * t3e6 + 4.144 * t4e8))
|
||
- 1.8 * Math.cos(deg2rad * (105.6788 + 341337.2548 * t - 119.499 * t2e4
|
||
- 10.765 * t3e6 + 5.028 * t4e8))
|
||
- 1.7 * Math.cos(deg2rad * (201.4740 + 826670.7108 * t - 245.142 * t2e4
|
||
- 21.367 * t3e6 + 10.057 * t4e8))
|
||
+ 1.6 * Math.cos(deg2rad * (184.1196 + 401329.0556 * t + 125.428 * t2e4
|
||
+ 18.579 * t3e6 - 8.798 * t4e8))
|
||
- 1.4 * Math.cos(deg2rad * (308.4192 - 489205.1674 * t + 158.029 * t2e4
|
||
+ 14.915 * t3e6 - 7.029 * t4e8))
|
||
+ 1.3 * Math.cos(deg2rad * (325.7736 - 63863.5122 * t - 212.541 * t2e4
|
||
- 25.031 * t3e6 + 11.826 * t4e8));
|
||
|
||
var sapp = - 0.55 * Math.cos(deg2rad * (238.2 + 854535.2 * t))
|
||
+ 0.10 * Math.cos(deg2rad * (103.2 + 377336.3 * t))
|
||
+ 0.10 * Math.cos(deg2rad * (233.2 + 926533.3 * t));
|
||
|
||
var sma = 383397.6 + sa + sapp * t;
|
||
|
||
// orbital eccentricity
|
||
|
||
var se = 0.014217 * Math.cos(deg2rad * (100.7370 + 413335.3554 * t - 122.571 * t2e4
|
||
- 10.684 * t3e6 + 5.028 * t4e8))
|
||
+ 0.008551 * Math.cos(deg2rad * (325.7736 - 63863.5122 * t - 212.541 * t2e4
|
||
- 25.031 * t3e6 + 11.826 * t4e8))
|
||
- 0.001383 * Math.cos(deg2rad * (134.9634 + 477198.8676 * t + 89.970 * t2e4
|
||
+ 14.348 * t3e6 - 6.797 * t4e8))
|
||
+ 0.001353 * Math.cos(deg2rad * (10.6638 + 1367733.0907 * t + 57.370 * t2e4
|
||
+ 18.011 * t3e6 - 8.566 * t4e8))
|
||
- 0.001146 * Math.cos(deg2rad * (66.5106 + 349471.8432 * t - 335.112 * t2e4
|
||
- 35.715 * t3e6 + 16.854 * t4e8))
|
||
- 0.000915 * Math.cos(deg2rad * (201.4740 + 826670.7108 * t - 245.142 * t2e4
|
||
- 21.367 * t3e6 + 10.057 * t4e8))
|
||
+ 0.000869 * Math.cos(deg2rad * (103.2079 + 377336.3051 * t - 121.035 * t2e4
|
||
- 10.724 * t3e6 + 5.028 * t4e8))
|
||
- 0.000628 * Math.cos(deg2rad * (235.7004 + 890534.2230 * t - 32.601 * t2e4
|
||
+ 3.664 * t3e6 - 1.769 * t4e8))
|
||
- 0.000393 * Math.cos(deg2rad * (291.5472 - 127727.0245 * t - 425.082 * t2e4
|
||
- 50.062 * t3e6 + 23.651 * t4e8))
|
||
+ 0.000284 * Math.cos(deg2rad * (328.2445 - 99862.5625 * t - 211.005 * t2e4
|
||
- 25.072 * t3e6 + 11.826 * t4e8))
|
||
- 0.000278 * Math.cos(deg2rad * (162.8868 - 31931.7561 * t - 106.271 * t2e4
|
||
- 12.516 * t3e6 + 5.913 * t4e8))
|
||
- 0.000240 * Math.cos(deg2rad * (269.9268 + 954397.7353 * t + 179.941 * t2e4
|
||
+ 28.695 * t3e6 - 13.594 * t4e8))
|
||
+ 0.000230 * Math.cos(deg2rad * (111.4008 + 1781068.4461 * t - 65.201 * t2e4
|
||
+ 7.328 * t3e6 - 3.538 * t4e8))
|
||
+ 0.000229 * Math.cos(deg2rad * (167.2476 + 762807.1986 * t - 457.683 * t2e4
|
||
- 46.398 * t3e6 + 21.882 * t4e8))
|
||
- 0.000202 * Math.cos(deg2rad * ( 83.3826 - 12006.2998 * t + 247.999 * t2e4
|
||
+ 29.262 * t3e6 - 13.826 * t4e8))
|
||
+ 0.000190 * Math.cos(deg2rad * (190.8102 - 541062.3799 * t - 302.511 * t2e4
|
||
- 39.379 * t3e6 + 18.623 * t4e8))
|
||
+ 0.000177 * Math.cos(deg2rad * (357.5291 + 35999.0503 * t - 1.536 * t2e4
|
||
+ 0.041 * t3e6 + 0.000 * t4e8))
|
||
+ 0.000153 * Math.cos(deg2rad * (32.2842 + 285608.3309 * t - 547.653 * t2e4
|
||
- 60.746 * t3e6 + 28.679 * t4e8))
|
||
- 0.000137 * Math.cos(deg2rad * (44.8902 + 1431596.6029 * t + 269.911 * t2e4
|
||
+ 43.043 * t3e6 - 20.392 * t4e8))
|
||
+ 0.000122 * Math.cos(deg2rad * (145.6272 + 1844931.9583 * t + 147.340 * t2e4
|
||
+ 32.359 * t3e6 - 15.363 * t4e8))
|
||
+ 0.000116 * Math.cos(deg2rad * (302.2110 + 1240006.0662 * t - 367.713 * t2e4
|
||
- 32.051 * t3e6 + 15.085 * t4e8))
|
||
- 0.000111 * Math.cos(deg2rad * (203.9449 + 790671.6605 * t - 243.606 * t2e4
|
||
- 21.408 * t3e6 + 10.057 * t4e8))
|
||
- 0.000108 * Math.cos(deg2rad * (68.9815 + 313472.7929 * t - 333.576 * t2e4
|
||
- 35.756 * t3e6 + 16.854 * t4e8))
|
||
+ 0.000096 * Math.cos(deg2rad * (336.4374 + 1303869.5784 * t - 155.171 * t2e4
|
||
- 7.020 * t3e6 + 3.259 * t4e8))
|
||
- 0.000090 * Math.cos(deg2rad * (98.2661 + 449334.4057 * t - 124.107 * t2e4
|
||
- 10.643 * t3e6 + 5.028 * t4e8))
|
||
+ 0.000090 * Math.cos(deg2rad * (13.1347 + 1331734.0404 * t + 58.906 * t2e4
|
||
+ 17.971 * t3e6 - 8.566 * t4e8))
|
||
+ 0.000056 * Math.cos(deg2rad * (55.8468 - 1018261.2475 * t - 392.482 * t2e4
|
||
- 53.726 * t3e6 + 25.420 * t4e8))
|
||
- 0.000056 * Math.cos(deg2rad * (238.1713 + 854535.1727 * t - 31.065 * t2e4
|
||
+ 3.623 * t3e6 - 1.769 * t4e8))
|
||
+ 0.000052 * Math.cos(deg2rad * (308.4192 - 489205.1674 * t + 158.029 * t2e4
|
||
+ 14.915 * t3e6 - 7.029 * t4e8))
|
||
- 0.000050 * Math.cos(deg2rad * (133.0212 + 698943.6863 * t - 670.224 * t2e4
|
||
- 71.429 * t3e6 + 33.708 * t4e8))
|
||
- 0.000049 * Math.cos(deg2rad * (267.9846 + 1176142.5540 * t - 580.254 * t2e4
|
||
- 57.082 * t3e6 + 26.911 * t4e8))
|
||
- 0.000049 * Math.cos(deg2rad * (184.1196 + 401329.0556 * t + 125.428 * t2e4
|
||
+ 18.579 * t3e6 - 8.798 * t4e8))
|
||
- 0.000045 * Math.cos(deg2rad * (49.1562 - 75869.8120 * t + 35.458 * t2e4
|
||
+ 4.231 * t3e6 - 2.001 * t4e8))
|
||
+ 0.000044 * Math.cos(deg2rad * (257.3208 - 191590.5367 * t - 637.623 * t2e4
|
||
- 75.093 * t3e6 + 35.477 * t4e8))
|
||
+ 0.000042 * Math.cos(deg2rad * (105.6788 + 341337.2548 * t - 119.499 * t2e4
|
||
- 10.765 * t3e6 + 5.028 * t4e8))
|
||
+ 0.000042 * Math.cos(deg2rad * (160.4159 + 4067.2942 * t - 107.806 * t2e4
|
||
- 12.475 * t3e6 + 5.913 * t4e8))
|
||
+ 0.000040 * Math.cos(deg2rad * (246.3642 + 2258267.3137 * t + 24.769 * t2e4
|
||
+ 21.675 * t3e6 - 10.335 * t4e8))
|
||
- 0.000040 * Math.cos(deg2rad * (156.5838 - 604925.8921 * t - 515.053 * t2e4
|
||
- 64.410 * t3e6 + 30.448 * t4e8))
|
||
+ 0.000036 * Math.cos(deg2rad * (169.7185 + 726808.1483 * t - 456.147 * t2e4
|
||
- 46.439 * t3e6 + 21.882 * t4e8))
|
||
+ 0.000029 * Math.cos(deg2rad * (113.8717 + 1745069.3958 * t - 63.665 * t2e4
|
||
+ 7.287 * t3e6 - 3.538 * t4e8))
|
||
- 0.000029 * Math.cos(deg2rad * (297.8502 + 445267.1115 * t - 16.300 * t2e4
|
||
+ 1.832 * t3e6 - 0.884 * t4e8))
|
||
- 0.000028 * Math.cos(deg2rad * (294.0181 - 163726.0747 * t - 423.546 * t2e4
|
||
- 50.103 * t3e6 + 23.651 * t4e8))
|
||
+ 0.000027 * Math.cos(deg2rad * (263.6238 + 381403.5993 * t - 228.841 * t2e4
|
||
- 23.199 * t3e6 + 10.941 * t4e8))
|
||
- 0.000026 * Math.cos(deg2rad * (358.0578 + 221744.8187 * t - 760.194 * t2e4
|
||
- 85.777 * t3e6 + 40.505 * t4e8))
|
||
- 0.000026 * Math.cos(deg2rad * (8.1929 + 1403732.1410 * t + 55.834 * t2e4
|
||
+ 18.052 * t3e6 - 8.566 * t4e8));
|
||
|
||
var sedp = -0.0022 * Math.cos(deg2rad * (103.2 + 377336.3 * t));
|
||
|
||
var ecc = 0.055544 + se + 1e-3 * t * sedp;
|
||
|
||
// sine of half the inclination
|
||
|
||
var sg = 0.0011776 * Math.cos(deg2rad * (49.1562 - 75869.8120 * t + 35.458 * t2e4
|
||
+ 4.231 * t3e6 - 2.001 * t4e8))
|
||
- 0.0000971 * Math.cos(deg2rad * (235.7004 + 890534.2230 * t - 32.601 * t2e4
|
||
+ 3.664 * t3e6 - 1.769 * t4e8))
|
||
+ 0.0000908 * Math.cos(deg2rad * (186.5442 + 966404.0351 * t - 68.058 * t2e4
|
||
- 0.567 * t3e6 + 0.232 * t4e8))
|
||
+ 0.0000623 * Math.cos(deg2rad * (83.3826 - 12006.2998 * t + 247.999 * t2e4
|
||
+ 29.262 * t3e6 - 13.826 * t4e8))
|
||
+ 0.0000483 * Math.cos(deg2rad * (51.6271 - 111868.8623 * t + 36.994 * t2e4
|
||
+ 4.190 * t3e6 - 2.001 * t4e8))
|
||
+ 0.0000348 * Math.cos(deg2rad * (100.7370 + 413335.3554 * t - 122.571 * t2e4
|
||
- 10.684 * t3e6 + 5.028 * t4e8))
|
||
- 0.0000316 * Math.cos(deg2rad * (308.4192 - 489205.1674 * t + 158.029 * t2e4
|
||
+ 14.915 * t3e6 - 7.029 * t4e8))
|
||
- 0.0000253 * Math.cos(deg2rad * (46.6853 - 39870.7617 * t + 33.922 * t2e4
|
||
+ 4.272 * t3e6 - 2.001 * t4e8))
|
||
- 0.0000141 * Math.cos(deg2rad * (274.1928 - 553068.6797 * t - 54.513 * t2e4
|
||
- 10.116 * t3e6 + 4.797 * t4e8))
|
||
+ 0.0000127 * Math.cos(deg2rad * (325.7736 - 63863.5122 * t - 212.541 * t2e4
|
||
- 25.031 * t3e6 + 11.826 * t4e8))
|
||
+ 0.0000117 * Math.cos(deg2rad * (184.1196 + 401329.0556 * t + 125.428 * t2e4
|
||
+ 18.579 * t3e6 - 8.798 * t4e8))
|
||
- 0.0000078 * Math.cos(deg2rad * (98.3124 - 151739.6240 * t + 70.916 * t2e4
|
||
+ 8.462 * t3e6 - 4.001 * t4e8))
|
||
- 0.0000063 * Math.cos(deg2rad * (238.1713 + 854535.1727 * t - 31.065 * t2e4
|
||
+ 3.623 * t3e6 - 1.769 * t4e8))
|
||
+ 0.0000063 * Math.cos(deg2rad * (134.9634 + 477198.8676 * t + 89.970 * t2e4
|
||
+ 14.348 * t3e6 - 6.797 * t4e8))
|
||
+ 0.0000036 * Math.cos(deg2rad * (321.5076 + 1443602.9027 * t + 21.912 * t2e4
|
||
+ 13.780 * t3e6 - 6.566 * t4e8))
|
||
- 0.0000035 * Math.cos(deg2rad * (10.6638 + 1367733.0907 * t + 57.370 * t2e4
|
||
+ 18.011 * t3e6 - 8.566 * t4e8))
|
||
+ 0.0000024 * Math.cos(deg2rad * (149.8932 + 337465.5434 * t - 87.113 * t2e4
|
||
- 6.453 * t3e6 + 3.028 * t4e8))
|
||
+ 0.0000024 * Math.cos(deg2rad * (170.9849 - 930404.9848 * t + 66.523 * t2e4
|
||
+ 0.608 * t3e6 - 0.232 * t4e8));
|
||
|
||
var sgp = - 0.0203 * Math.cos(deg2rad * (125.0 - 1934.1 * t))
|
||
+ 0.0034 * Math.cos(deg2rad * (220.2 - 1935.5 * t));
|
||
|
||
var gamma = 0.0449858 + sg + 1e-3 * sgp;
|
||
|
||
// longitude of perigee
|
||
|
||
var sp = - 15.448 * Math.sin(deg2rad * (100.7370 + 413335.3554 * t - 122.571 * t2e4
|
||
- 10.684 * t3e6 + 5.028 * t4e8))
|
||
- 9.642 * Math.sin(deg2rad * (325.7736 - 63863.5122 * t - 212.541 * t2e4
|
||
- 25.031 * t3e6 + 11.826 * t4e8))
|
||
- 2.721 * Math.sin(deg2rad * (134.9634 + 477198.8676 * t + 89.970 * t2e4
|
||
+ 14.348 * t3e6 - 6.797 * t4e8))
|
||
+ 2.607 * Math.sin(deg2rad * (66.5106 + 349471.8432 * t - 335.112 * t2e4
|
||
- 35.715 * t3e6 + 16.854 * t4e8))
|
||
+ 2.085 * Math.sin(deg2rad * (201.4740 + 826670.7108 * t - 245.142 * t2e4
|
||
- 21.367 * t3e6 + 10.057 * t4e8))
|
||
+ 1.477 * Math.sin(deg2rad * (10.6638 + 1367733.0907 * t + 57.370 * t2e4
|
||
+ 18.011 * t3e6 - 8.566 * t4e8))
|
||
+ 0.968 * Math.sin(deg2rad * (291.5472 - 127727.0245 * t - 425.082 * t2e4
|
||
- 50.062 * t3e6 + 23.651 * t4e8))
|
||
- 0.949 * Math.sin(deg2rad * (103.2079 + 377336.3051 * t - 121.035 * t2e4
|
||
- 10.724 * t3e6 + 5.028 * t4e8))
|
||
- 0.703 * Math.sin(deg2rad * (167.2476 + 762807.1986 * t - 457.683 * t2e4
|
||
- 46.398 * t3e6 + 21.882 * t4e8))
|
||
- 0.660 * Math.sin(deg2rad * (235.7004 + 890534.2230 * t - 32.601 * t2e4
|
||
+ 3.664 * t3e6 - 1.769 * t4e8))
|
||
- 0.577 * Math.sin(deg2rad * (190.8102 - 541062.3799 * t - 302.511 * t2e4
|
||
- 39.379 * t3e6 + 18.623 * t4e8))
|
||
- 0.524 * Math.sin(deg2rad * (269.9268 + 954397.7353 * t + 179.941 * t2e4
|
||
+ 28.695 * t3e6 - 13.594 * t4e8))
|
||
- 0.482 * Math.sin(deg2rad * (32.2842 + 285608.3309 * t - 547.653 * t2e4
|
||
- 60.746 * t3e6 + 28.679 * t4e8))
|
||
+ 0.452 * Math.sin(deg2rad * (357.5291 + 35999.0503 * t - 1.536 * t2e4
|
||
+ 0.041 * t3e6 + 0.000 * t4e8))
|
||
- 0.381 * Math.sin(deg2rad * (302.2110 + 1240006.0662 * t - 367.713 * t2e4
|
||
- 32.051 * t3e6 + 15.085 * t4e8))
|
||
- 0.342 * Math.sin(deg2rad * (328.2445 - 99862.5625 * t - 211.005 * t2e4
|
||
- 25.072 * t3e6 + 11.826 * t4e8))
|
||
- 0.312 * Math.sin(deg2rad * (44.8902 + 1431596.6029 * t + 269.911 * t2e4
|
||
+ 43.043 * t3e6 - 20.392 * t4e8))
|
||
+ 0.282 * Math.sin(deg2rad * (162.8868 - 31931.7561 * t - 106.271 * t2e4
|
||
- 12.516 * t3e6 + 5.913 * t4e8))
|
||
+ 0.255 * Math.sin(deg2rad * (203.9449 + 790671.6605 * t - 243.606 * t2e4
|
||
- 21.408 * t3e6 + 10.057 * t4e8))
|
||
+ 0.252 * Math.sin(deg2rad * (68.9815 + 313472.7929 * t - 333.576 * t2e4
|
||
- 35.756 * t3e6 + 16.854 * t4e8))
|
||
- 0.211 * Math.sin(deg2rad * (83.3826 - 12006.2998 * t + 247.999 * t2e4
|
||
+ 29.262 * t3e6 - 13.826 * t4e8))
|
||
+ 0.193 * Math.sin(deg2rad * (267.9846 + 1176142.5540 * t - 580.254 * t2e4
|
||
- 57.082 * t3e6 + 26.911 * t4e8))
|
||
+ 0.191 * Math.sin(deg2rad * (133.0212 + 698943.6863 * t - 670.224 * t2e4
|
||
- 71.429 * t3e6 + 33.708 * t4e8))
|
||
- 0.184 * Math.sin(deg2rad * (55.8468 - 1018261.2475 * t - 392.482 * t2e4
|
||
- 53.726 * t3e6 + 25.420 * t4e8))
|
||
+ 0.182 * Math.sin(deg2rad * (145.6272 + 1844931.9583 * t + 147.340 * t2e4
|
||
+ 32.359 * t3e6 - 15.363 * t4e8))
|
||
- 0.158 * Math.sin(deg2rad * (257.3208 - 191590.5367 * t - 637.623 * t2e4
|
||
- 75.093 * t3e6 + 35.477 * t4e8))
|
||
+ 0.148 * Math.sin(deg2rad * (156.5838 - 604925.8921 * t - 515.053 * t2e4
|
||
- 64.410 * t3e6 + 30.448 * t4e8))
|
||
- 0.111 * Math.sin(deg2rad * (169.7185 + 726808.1483 * t - 456.147 * t2e4
|
||
- 46.439 * t3e6 + 21.882 * t4e8))
|
||
+ 0.101 * Math.sin(deg2rad * (13.1347 + 1331734.0404 * t + 58.906 * t2e4
|
||
+ 17.971 * t3e6 - 8.566 * t4e8))
|
||
+ 0.100 * Math.sin(deg2rad * (358.0578 + 221744.8187 * t - 760.194 * t2e4
|
||
- 85.777 * t3e6 + 40.505 * t4e8))
|
||
+ 0.087 * Math.sin(deg2rad * (98.2661 + 449334.4057 * t - 124.107 * t2e4
|
||
- 10.643 * t3e6 + 5.028 * t4e8))
|
||
+ 0.080 * Math.sin(deg2rad * (42.9480 + 1653341.4216 * t - 490.283 * t2e4
|
||
- 42.734 * t3e6 + 20.113 * t4e8))
|
||
+ 0.080 * Math.sin(deg2rad * (222.5657 - 441199.8173 * t - 91.506 * t2e4
|
||
- 14.307 * t3e6 + 6.797 * t4e8))
|
||
+ 0.077 * Math.sin(deg2rad * (294.0181 - 163726.0747 * t - 423.546 * t2e4
|
||
- 50.103 * t3e6 + 23.651 * t4e8))
|
||
- 0.073 * Math.sin(deg2rad * (280.8834 - 1495460.1151 * t - 482.452 * t2e4
|
||
- 68.074 * t3e6 + 32.217 * t4e8))
|
||
- 0.071 * Math.sin(deg2rad * (304.6819 + 1204007.0159 * t - 366.177 * t2e4
|
||
- 32.092 * t3e6 + 15.085 * t4e8))
|
||
- 0.069 * Math.sin(deg2rad * (233.7582 + 1112279.0417 * t - 792.795 * t2e4
|
||
- 82.113 * t3e6 + 38.736 * t4e8))
|
||
- 0.067 * Math.sin(deg2rad * (34.7551 + 249609.2807 * t - 546.117 * t2e4
|
||
- 60.787 * t3e6 + 28.679 * t4e8))
|
||
- 0.067 * Math.sin(deg2rad * (263.6238 + 381403.5993 * t - 228.841 * t2e4
|
||
- 23.199 * t3e6 + 10.941 * t4e8))
|
||
+ 0.055 * Math.sin(deg2rad * (21.6203 - 1082124.7597 * t - 605.023 * t2e4
|
||
- 78.757 * t3e6 + 37.246 * t4e8))
|
||
+ 0.055 * Math.sin(deg2rad * (308.4192 - 489205.1674 * t + 158.029 * t2e4
|
||
+ 14.915 * t3e6 -7.029 * t4e8))
|
||
- 0.054 * Math.sin(deg2rad * (8.7216 + 1589477.9094 * t - 702.824 * t2e4
|
||
- 67.766 * t3e6 + 31.939 * t4e8))
|
||
- 0.052 * Math.sin(deg2rad * (179.8536 + 1908795.4705 * t + 359.881 * t2e4
|
||
+ 57.390 * t3e6 - 27.189 * t4e8))
|
||
- 0.050 * Math.sin(deg2rad * (98.7948 + 635080.1741 * t - 882.765 * t2e4
|
||
- 96.461 * t3e6 + 45.533 * t4e8))
|
||
- 0.049 * Math.sin(deg2rad * (128.6604 - 95795.2683 * t - 318.812 * t2e4
|
||
- 37.547 * t3e6 + 17.738 * t4e8))
|
||
- 0.047 * Math.sin(deg2rad * (17.3544 + 425341.6552 * t - 370.570 * t2e4
|
||
- 39.946 * t3e6 + 18.854 * t4e8))
|
||
- 0.044 * Math.sin(deg2rad * (160.4159 + 4067.2942 * t - 107.806 * t2e4
|
||
- 12.475 * t3e6 + 5.913 * t4e8))
|
||
- 0.043 * Math.sin(deg2rad * (238.1713 + 854535.1727 * t - 31.065 * t2e4
|
||
+ 3.623 * t3e6 - 1.769 * t4e8))
|
||
+ 0.042 * Math.sin(deg2rad * (270.4555 + 1140143.5037 * t - 578.718 * t2e4
|
||
- 57.123 * t3e6 + 26.911 * t4e8))
|
||
- 0.042 * Math.sin(deg2rad * (132.4925 + 513197.9179 * t + 88.434 * t2e4
|
||
+ 14.388 * t3e6 - 6.797 * t4e8))
|
||
- 0.041 * Math.sin(deg2rad * (122.3573 - 668789.4043 * t - 727.594 * t2e4
|
||
- 89.441 * t3e6 + 42.274 * t4e8))
|
||
- 0.040 * Math.sin(deg2rad * (105.6788 + 341337.2548 * t - 119.499 * t2e4
|
||
- 10.765 * t3e6 + 5.028 * t4e8))
|
||
+ 0.038 * Math.sin(deg2rad * (135.4921 + 662944.6361 * t - 668.688 * t2e4
|
||
- 71.470 * t3e6 + 33.708 * t4e8))
|
||
- 0.037 * Math.sin(deg2rad * (242.3910 - 51857.2124 * t - 460.540 * t2e4
|
||
- 54.293 * t3e6 + 25.652 * t4e8))
|
||
+ 0.036 * Math.sin(deg2rad * (336.4374 + 1303869.5784 * t - 155.171 * t2e4
|
||
- 7.020 * t3e6 + 3.259 * t4e8))
|
||
+ 0.035 * Math.sin(deg2rad * (223.0943 - 255454.0489 * t - 850.164 * t2e4
|
||
- 100.124 * t3e6 + 47.302 * t4e8))
|
||
- 0.034 * Math.sin(deg2rad * (193.2811 - 577061.4302 * t - 300.976 * t2e4
|
||
- 39.419 * t3e6 + 18.623 * t4e8))
|
||
+ 0.031 * Math.sin(deg2rad * (87.6023 - 918398.6850 * t - 181.476 * t2e4
|
||
- 28.654 * t3e6 + 13.594 * t4e8));
|
||
|
||
var spp = 2.4 * Math.sin(deg2rad * (103.2 + 377336.3 * t));
|
||
|
||
var lp = 83.353 + 4069.0137 * t - 103.238 * t2e4
|
||
- 12.492 * t3e6 + 5.263 * t4e8 + sp + 1e-3 * t * spp;
|
||
|
||
// longitude of the ascending node
|
||
|
||
var sr = - 1.4979 * Math.sin(deg2rad * (49.1562 - 75869.8120 * t + 35.458 * t2e4
|
||
+ 4.231 * t3e6 - 2.001 * t4e8))
|
||
- 0.1500 * Math.sin(deg2rad * (357.5291 + 35999.0503 * t - 1.536 * t2e4
|
||
+ 0.041 * t3e6 + 0.000 * t4e8))
|
||
- 0.1226 * Math.sin(deg2rad * (235.7004 + 890534.2230 * t - 32.601 * t2e4
|
||
+ 3.664 * t3e6 - 1.769 * t4e8))
|
||
+ 0.1176 * Math.sin(deg2rad * (186.5442 + 966404.0351 * t - 68.058 * t2e4
|
||
- 0.567 * t3e6 + 0.232 * t4e8))
|
||
- 0.0801 * Math.sin(deg2rad * (83.3826 - 12006.2998 * t + 247.999 * t2e4
|
||
+ 29.262 * t3e6 - 13.826 * t4e8))
|
||
- 0.0616 * Math.sin(deg2rad * (51.6271 - 111868.8623 * t + 36.994 * t2e4
|
||
+ 4.190 * t3e6 - 2.001 * t4e8))
|
||
+ 0.0490 * Math.sin(deg2rad * (100.7370 + 413335.3554 * t - 122.571 * t2e4
|
||
- 10.684 * t3e6 + 5.028 * t4e8))
|
||
+ 0.0409 * Math.sin(deg2rad * (308.4192 - 489205.1674 * t + 158.029 * t2e4
|
||
+ 14.915 * t3e6 - 7.029 * t4e8))
|
||
+ 0.0327 * Math.sin(deg2rad * (134.9634 + 477198.8676 * t + 89.970 * t2e4
|
||
+ 14.348 * t3e6 - 6.797 * t4e8))
|
||
+ 0.0324 * Math.sin(deg2rad * (46.6853 - 39870.7617 * t + 33.922 * t2e4
|
||
+ 4.272 * t3e6 - 2.001 * t4e8))
|
||
+ 0.0196 * Math.sin(deg2rad * (98.3124 - 151739.6240 * t + 70.916 * t2e4
|
||
+ 8.462 * t3e6 - 4.001 * t4e8))
|
||
+ 0.0180 * Math.sin(deg2rad * (274.1928 - 553068.6797 * t - 54.513 * t2e4
|
||
- 10.116 * t3e6 + 4.797 * t4e8))
|
||
+ 0.0150 * Math.sin(deg2rad * (325.7736 - 63863.5122 * t - 212.541 * t2e4
|
||
- 25.031 * t3e6 + 11.826 * t4e8))
|
||
- 0.0150 * Math.sin(deg2rad * (184.1196 + 401329.0556 * t + 125.428 * t2e4
|
||
+ 18.579 * t3e6 - 8.798 * t4e8))
|
||
- 0.0078 * Math.sin(deg2rad * (238.1713 + 854535.1727 * t - 31.065 * t2e4
|
||
+ 3.623 * t3e6 - 1.769 * t4e8))
|
||
- 0.0045 * Math.sin(deg2rad * (10.6638 + 1367733.0907 * t + 57.370 * t2e4
|
||
+ 18.011 * t3e6 - 8.566 * t4e8))
|
||
+ 0.0044 * Math.sin(deg2rad * (321.5076 + 1443602.9027 * t + 21.912 * t2e4
|
||
+ 13.780 * t3e6 - 6.566 * t4e8))
|
||
- 0.0042 * Math.sin(deg2rad * (162.8868 - 31931.7561 * t - 106.271 * t2e4
|
||
- 12.516 * t3e6 + 5.913 * t4e8))
|
||
- 0.0031 * Math.sin(deg2rad * (170.9849 - 930404.9848 * t + 66.523 * t2e4
|
||
+ 0.608 * t3e6 - 0.232 * t4e8))
|
||
+ 0.0031 * Math.sin(deg2rad * (103.2079 + 377336.3051 * t - 121.035 * t2e4
|
||
- 10.724 * t3e6 + 5.028 * t4e8))
|
||
+ 0.0029 * Math.sin(deg2rad * (222.6120 - 1042273.8471 * t + 103.516 * t2e4
|
||
+ 4.798 * t3e6 - 2.232 * t4e8))
|
||
+ 0.0028 * Math.sin(deg2rad * (184.0733 + 1002403.0853 * t - 69.594 * t2e4
|
||
- 0.526 * t3e6 + 0.232 * t4e8));
|
||
|
||
var srp = 25.9 * Math.sin(deg2rad * (125.0 - 1934.1 * t))
|
||
- 4.3 * Math.sin(deg2rad * (220.2 - 1935.5 * t));
|
||
|
||
var srpp = 0.38 * Math.sin(deg2rad * (357.5 + 35999.1 * t));
|
||
|
||
var raan = 125.0446 - 1934.13618 * t + 20.762 * t2e4
|
||
+ 2.139 * t3e6 - 1.650 * t4e8 + sr
|
||
+ 1e-3 * (srp + srpp * t);
|
||
|
||
// mean longitude
|
||
|
||
var sl = - 0.92581 * Math.sin(deg2rad * (235.7004 + 890534.2230 * t - 32.601 * t2e4
|
||
+ 3.664 * t3e6 - 1.769 * t4e8))
|
||
+ 0.33262 * Math.sin(deg2rad * (100.7370 + 413335.3554 * t - 122.571 * t2e4
|
||
- 10.684 * t3e6 + 5.028 * t4e8))
|
||
- 0.18402 * Math.sin(deg2rad * (357.5291 + 35999.0503 * t - 1.536 * t2e4
|
||
+ 0.041 * t3e6 + 0.000 * t4e8))
|
||
+ 0.11007 * Math.sin(deg2rad * (134.9634 + 477198.8676 * t + 89.970 * t2e4
|
||
+ 14.348 * t3e6 - 6.797 * t4e8))
|
||
- 0.06055 * Math.sin(deg2rad * (238.1713 + 854535.1727 * t - 31.065 * t2e4
|
||
+ 3.623 * t3e6 - 1.769 * t4e8))
|
||
+ 0.04741 * Math.sin(deg2rad * (325.7736 - 63863.5122 * t - 212.541 * t2e4
|
||
- 25.031 * t3e6 + 11.826 * t4e8))
|
||
- 0.03086 * Math.sin(deg2rad * (10.6638 + 1367733.0907 * t + 57.370 * t2e4
|
||
+ 18.011 * t3e6 - 8.566 * t4e8))
|
||
+ 0.02184 * Math.sin(deg2rad * (103.2079 + 377336.3051 * t - 121.035 * t2e4
|
||
- 10.724 * t3e6 + 5.028 * t4e8))
|
||
+ 0.01645 * Math.sin(deg2rad * (49.1562 - 75869.8120 * t + 35.458 * t2e4
|
||
+ 4.231 * t3e6 - 2.001 * t4e8))
|
||
+ 0.01022 * Math.sin(deg2rad * (233.2295 + 926533.2733 * t - 34.136 * t2e4
|
||
+ 3.705 * t3e6 - 1.769 * t4e8))
|
||
- 0.00756 * Math.sin(deg2rad * (336.4374 + 1303869.5784 * t - 155.171 * t2e4
|
||
- 7.020 * t3e6 + 3.259 * t4e8))
|
||
- 0.00530 * Math.sin(deg2rad * (222.5657 - 441199.8173 * t - 91.506 * t2e4
|
||
- 14.307 * t3e6 + 6.797 * t4e8))
|
||
- 0.00496 * Math.sin(deg2rad * (162.8868 - 31931.7561 * t - 106.271 * t2e4
|
||
- 12.516 * t3e6 + 5.913 * t4e8))
|
||
- 0.00472 * Math.sin(deg2rad * (297.8502 + 445267.1115 * t - 16.300 * t2e4
|
||
+ 1.832 * t3e6 - 0.884 * t4e8))
|
||
- 0.00271 * Math.sin(deg2rad * (240.6422 + 818536.1225 * t - 29.529 * t2e4
|
||
+ 3.582 * t3e6 - 1.769 * t4e8))
|
||
+ 0.00264 * Math.sin(deg2rad * (132.4925 + 513197.9179 * t + 88.434 * t2e4
|
||
+ 14.388 * t3e6 - 6.797 * t4e8))
|
||
- 0.00254 * Math.sin(deg2rad * (186.5442 + 966404.0351 * t - 68.058 * t2e4
|
||
- 0.567 * t3e6 + 0.232 * t4e8))
|
||
+ 0.00234 * Math.sin(deg2rad * (269.9268 + 954397.7353 * t + 179.941 * t2e4
|
||
+ 28.695 * t3e6 - 13.594 * t4e8))
|
||
- 0.00220 * Math.sin(deg2rad * (13.1347 + 1331734.0404 * t + 58.906 * t2e4
|
||
+ 17.971 * t3e6 - 8.566 * t4e8))
|
||
- 0.00202 * Math.sin(deg2rad * (355.0582 + 71998.1006 * t - 3.072 * t2e4
|
||
+ 0.082 * t3e6 + 0.000 * t4e8))
|
||
+ 0.00167 * Math.sin(deg2rad * (328.2445 - 99862.5625 * t - 211.005 * t2e4
|
||
- 25.072 * t3e6 + 11.826 * t4e8))
|
||
- 0.00143 * Math.sin(deg2rad * (173.5506 + 1335801.3346 * t - 48.901 * t2e4
|
||
+ 5.496 * t3e6 - 2.653 * t4e8))
|
||
- 0.00121 * Math.sin(deg2rad * (98.2661 + 449334.4057 * t - 124.107 * t2e4
|
||
- 10.643 * t3e6 + 5.028 * t4e8))
|
||
- 0.00116 * Math.sin(deg2rad * (145.6272 + 1844931.9583 * t + 147.340 * t2e4
|
||
+ 32.359 * t3e6 - 15.363 * t4e8))
|
||
+ 0.00102 * Math.sin(deg2rad * (105.6788 + 341337.2548 * t - 119.499 * t2e4
|
||
- 10.765 * t3e6 + 5.028 * t4e8))
|
||
- 0.00090 * Math.sin(deg2rad * (184.1196 + 401329.0556 * t + 125.428 * t2e4
|
||
+ 18.579 * t3e6 - 8.798 * t4e8))
|
||
- 0.00086 * Math.sin(deg2rad * (338.9083 + 1267870.5281 * t - 153.636 * t2e4
|
||
- 7.061 * t3e6 + 3.259 * t4e8))
|
||
- 0.00078 * Math.sin(deg2rad * (111.4008 + 1781068.4461 * t - 65.201 * t2e4
|
||
+ 7.328 * t3e6 - 3.538 * t4e8))
|
||
+ 0.00069 * Math.sin(deg2rad * (323.3027 - 27864.4619 * t - 214.077 * t2e4
|
||
- 24.990 * t3e6 + 11.826 * t4e8))
|
||
+ 0.00066 * Math.sin(deg2rad * (51.6271 - 111868.8623 * t + 36.994 * t2e4
|
||
+ 4.190 * t3e6 - 2.001 * t4e8))
|
||
+ 0.00065 * Math.sin(deg2rad * (38.5872 + 858602.4669 * t - 138.871 * t2e4
|
||
- 8.852 * t3e6 + 4.144 * t4e8))
|
||
- 0.00060 * Math.sin(deg2rad * (83.3826 - 12006.2998 * t + 247.999 * t2e4
|
||
+ 29.262 * t3e6 - 13.826 * t4e8))
|
||
+ 0.00054 * Math.sin(deg2rad * (201.4740 + 826670.7108 * t - 245.142 * t2e4
|
||
- 21.367 * t3e6 + 10.057 * t4e8))
|
||
- 0.00052 * Math.sin(deg2rad * (308.4192 - 489205.1674 * t + 158.029 * t2e4
|
||
+ 14.915 * t3e6 - 7.029 * t4e8))
|
||
+ 0.00048 * Math.sin(deg2rad * (8.1929 + 1403732.1410 * t + 55.834 * t2e4
|
||
+ 18.052 * t3e6 - 8.566 * t4e8))
|
||
- 0.00041 * Math.sin(deg2rad * (46.6853 - 39870.7617 * t + 33.922 * t2e4
|
||
+ 4.272 * t3e6 - 2.001 * t4e8))
|
||
- 0.00033 * Math.sin(deg2rad * (274.1928 - 553068.6797 * t - 54.513 * t2e4
|
||
- 10.116 * t3e6 + 4.797 * t4e8))
|
||
+ 0.00030 * Math.sin(deg2rad * (160.4159 + 4067.2942 * t - 107.806 * t2e4
|
||
- 12.475 * t3e6 + 5.913 * t4e8));
|
||
|
||
var slp = 3.96 * Math.sin(deg2rad * (119.7 + 131.8 * t))
|
||
+ 1.96 * Math.sin(deg2rad * (125.0 - 1934.1 * t));
|
||
|
||
var slpp = 0.463 * Math.sin(deg2rad * (357.5 + 35999.1 * t))
|
||
+ 0.152 * Math.sin(deg2rad * (238.2 + 854535.2 * t))
|
||
- 0.071 * Math.sin(deg2rad * (27.8 + 131.8 * t))
|
||
- 0.055 * Math.sin(deg2rad * (103.2 + 377336.3 * t))
|
||
- 0.026 * Math.sin(deg2rad * (233.2 + 926533.3 * t));
|
||
|
||
var slppp = 14 * Math.sin(deg2rad * (357.5 + 35999.1 * t))
|
||
+ 5 * Math.sin(deg2rad * (238.2 + 854535.2 * t));
|
||
|
||
var lambda = 218.31665 + 481267.88134 * t - 13.268 * t2e4
|
||
+ 1.856 * t3e6 - 1.534 * t4e8 + sl
|
||
+ 1e-3 * (slp + slpp * t + slppp * t2e4);
|
||
|
||
dat.a = sma;
|
||
dat.e = ecc;
|
||
dat.i = 2.0 * Math.asin(gamma);
|
||
dat.w = Trig.normalize(deg2rad * (lp - raan));
|
||
dat.N = Trig.normalize(deg2rad * raan);
|
||
dat.M = Trig.normalize(deg2rad * (lambda - lp));
|
||
return dat;
|
||
},
|
||
corr: function(dat, sol) {
|
||
var M = Trig.normalize(sol.M + Math.PI),
|
||
w = Trig.normalize(sol.w + Math.PI),
|
||
L = dat.M + dat.w, // Argument of latitude
|
||
E = L + dat.N - M - w; // Mean elongation
|
||
|
||
var lon =
|
||
-0.022234 * Math.sin(dat.M - 2*E) + // Evection
|
||
0.011494 * Math.sin(2*E) + // Variation
|
||
-0.003246 * Math.sin(M) + // Yearly Equation
|
||
-0.001029 * Math.sin(2*dat.M - 2*E) +
|
||
-9.94838e-4 * Math.sin(dat.M - 2*E + M) +
|
||
9.25025e-4 * Math.sin(dat.M + 2*E) +
|
||
8.02851e-4 * Math.sin(2*E - M) +
|
||
7.15585e-4 * Math.sin(dat.M - M) +
|
||
-6.10865e-4 * Math.sin(E) +
|
||
-5.41052e-4 * Math.sin(dat.M + M) +
|
||
-2.61799e-4 * Math.sin(2*L - 2*E) +
|
||
1.91986e-4 * Math.sin(dat.M - 4*E);
|
||
dat.ra += lon;
|
||
var lat =
|
||
-0.003019 * Math.sin(L - 2*E) +
|
||
-9.59931e-4 * Math.sin(dat.M - L - 2*E) +
|
||
-8.02851e-4 * Math.sin(dat.M + L - 2*E) +
|
||
5.75958e-4 * Math.sin(L + 2*E) +
|
||
2.96706e-4 * Math.sin(2*dat.M + L);
|
||
dat.dec += lat;
|
||
|
||
dat.age = Trig.normalize(dat.l - sol.l + Math.PI);
|
||
dat.phase = 0.5 * (1 - Math.cos(dat.age));
|
||
|
||
return dat;
|
||
}
|
||
|
||
};
|
||
|
||
var datetimepicker = function(cfg, callback) {
|
||
var date = new Date(),
|
||
tzFormat = d3.time.format("%Z"),
|
||
tz = [{"−12:00":720}, {"−11:00":660}, {"−10:00":600}, {"−09:30":570}, {"−09:00":540}, {"−08:00":480}, {"−07:00":420}, {"−06:00":360}, {"−05:00":300}, {"−04:30":270}, {"−04:00":240}, {"−03:30":210}, {"−03:00":180}, {"−02:00":120}, {"−01:00":60}, {"±00:00":0}, {"+01:00":-60}, {"+02:00":-120}, {"+03:00":-180}, {"+03:30":-210}, {"+04:00":-240}, {"+04:30":-270}, {"+05:00":-300}, {"+05:30":-330}, {"+05:45":-345}, {"+06:00":-360}, {"+06:30":-390}, {"+07:00":-420}, {"+08:00":-480}, {"+08:30":-510}, {"+08:45":-525}, {"+09:00":-540}, {"+09:30":-570}, {"+10:00":-600}, {"+10:30":-630}, {"+11:00":-660}, {"+12:00":-720}, {"+12:45":-765}, {"+13:00":-780}, {"+14:00":-840}],
|
||
months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
|
||
days = ["Su", "M", "Tu", "W", "Th", "F", "Sa"],
|
||
years = getYears(date),
|
||
dateFormat = d3.time.format("%Y-%m-%d"),
|
||
dtrange = cfg.daterange || [];
|
||
|
||
var picker = d3.select("#celestial-form").append("div").attr("id", "celestial-date");
|
||
nav("left");
|
||
monSel();
|
||
yrSel();
|
||
nav("right");
|
||
|
||
var cal = picker.append("div").attr("id", "cal");
|
||
|
||
daySel();
|
||
|
||
timeSel();
|
||
tzSel();
|
||
|
||
function daySel() {
|
||
var mo = $("mon").value, yr = $("yr").value,
|
||
curdt = new Date(yr, mo, 1),
|
||
cal = d3.select("#cal"),
|
||
today = new Date();
|
||
yr = parseInt(yr);
|
||
mo = parseInt(mo);
|
||
curdt.setDate(curdt.getDate() - curdt.getDay());
|
||
var nd = cal.node();
|
||
while (nd.firstChild) nd.removeChild(nd.firstChild);
|
||
|
||
for (var i=0; i<7; i++) {
|
||
cal.append("div").classed({"date": true, "weekday": true}).html(days[i]);
|
||
}
|
||
for (i=0; i<42; i++) {
|
||
var curmon = curdt.getMonth(), curday = curdt.getDay(), curid = dateFormat(curdt);
|
||
cal.append("div").classed({
|
||
"date": true,
|
||
"grey": curmon !== mo,
|
||
"weekend": curmon === mo && (curday === 0 || curday === 6),
|
||
"today": dateDiff(curdt, today) === 0,
|
||
"selected": dateDiff(curdt, date) === 0
|
||
}).attr("id", curid)
|
||
.on("click", pick)
|
||
.html(curdt.getDate().toString());
|
||
|
||
curdt.setDate(curdt.getDate()+1);
|
||
}
|
||
}
|
||
|
||
function yrSel() {
|
||
picker.append("select").attr("title", "Year").attr("id", "yr").on("change", daySel);
|
||
|
||
fillYrSel();
|
||
}
|
||
|
||
function fillYrSel() {
|
||
var sel = d3.select("select#yr"),
|
||
year = date.getFullYear(),
|
||
selected = 0,
|
||
years = getYears(date);
|
||
|
||
sel.selectAll("*").remove();
|
||
sel.selectAll('option').data(years).enter().append('option')
|
||
.text(function (d, i) {
|
||
if (d === year) selected = i;
|
||
return d.toString();
|
||
});
|
||
sel.property("selectedIndex", selected);
|
||
}
|
||
|
||
function monSel() {
|
||
var sel = picker.append("select").attr("title", "Month").attr("id", "mon").on("change", daySel),
|
||
selected = 0,
|
||
month = date.getMonth();
|
||
|
||
sel.selectAll('option').data(months).enter().append('option')
|
||
.attr("value", function (d, i) {
|
||
if (i === month) selected = i;
|
||
return i;
|
||
})
|
||
.text(function (d) { return d; });
|
||
sel.property("selectedIndex", selected);
|
||
}
|
||
|
||
function nav(dir) {
|
||
var lnk = picker.append("div").attr("id", dir).on("click", function () {
|
||
var mon = $("mon"), yr = $("yr");
|
||
|
||
if (dir === "left") {
|
||
if (mon.selectedIndex === 0) {
|
||
mon.selectedIndex = 11;
|
||
yr.selectedIndex--;
|
||
} else mon.selectedIndex--;
|
||
} else {
|
||
if (mon.selectedIndex === 11) {
|
||
mon.selectedIndex = 0;
|
||
yr.selectedIndex++;
|
||
} else mon.selectedIndex++;
|
||
}
|
||
daySel();
|
||
});
|
||
}
|
||
|
||
function timeSel() {
|
||
picker.append("input").attr("type", "number").attr("id", "hr").attr("title", "Hours").attr("max", "24").attr("min", "-1").attr("step", "1").attr("value", date.getHours()).on("change", function () { if (testNumber(this) === true) pick(); });
|
||
|
||
picker.append("input").attr("type", "number").attr("id", "min").attr("title", "Minutes").attr("max", "60").attr("min", "-1").attr("step", "1").attr("value", date.getMinutes()).on("change", function () { if (testNumber(this) === true) pick(); });
|
||
|
||
picker.append("input").attr("type", "number").attr("id", "sec").attr("title", "Seconds").attr("max", "60").attr("min", "-1").attr("step", "1").attr("value", date.getSeconds()).on("change", function () { if (testNumber(this) === true) pick(); });
|
||
}
|
||
|
||
function tzSel() {
|
||
var sel = picker.append("select").attr("title", "Time zone offset from UTC").attr("id", "tz").on("change", pick),
|
||
selected = 15,
|
||
timezone = date.getTimezoneOffset();
|
||
sel.selectAll('option').data(tz).enter().append('option')
|
||
.attr("value", function (d, i) {
|
||
var k = Object.keys(d)[0];
|
||
if (d[k] === timezone) selected = i;
|
||
return d[k];
|
||
})
|
||
.text(function (d) { return Object.keys(d)[0]; });
|
||
sel.property("selectedIndex", selected);
|
||
}
|
||
|
||
function getYears(dt) {
|
||
var r = getDateRange(dt.getFullYear()), res = [];
|
||
for (var i = r[0]; i <= r[1]; i++) res.push(i);
|
||
return res;
|
||
}
|
||
|
||
function getDateRange(yr) {
|
||
if (!dtrange || dtrange.length < 1) return [yr - 10, yr + 10];
|
||
|
||
if (dtrange.length === 1 && isNumber(dtrange[0])) {
|
||
if (dtrange[0] >= 100) return [dtrange[0] - 10, dtrange[0] + 10];
|
||
else return [yr - dtrange[0], yr + dtrange[0]];
|
||
}
|
||
if (dtrange.length === 2 && isNumber(dtrange[0])&& isNumber(dtrange[1])) {
|
||
if (dtrange[1] >= 100) return [dtrange[0], dtrange[1]];
|
||
else return [dtrange[0] - dtrange[1], dtrange[0] + dtrange[1]];
|
||
}
|
||
return [yr - 10, yr + 10];
|
||
}
|
||
|
||
function select(id, val) {
|
||
var sel = $(id);
|
||
for (var i=0; i<sel.childNodes.length; i++) {
|
||
if (sel.childNodes[i].value == val) {
|
||
sel.selectedIndex = i;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
function set(dt) {
|
||
if (dt) date.setTime(dt.valueOf());
|
||
|
||
select("yr", date.getFullYear());
|
||
select("mon", date.getMonth());
|
||
daySel();
|
||
$("hr").value = date.getHours();
|
||
$("min").value = date.getMinutes();
|
||
$("sec").value = date.getSeconds();
|
||
}
|
||
|
||
this.show = function(dt) {
|
||
var nd = $("celestial-date"),
|
||
src = $("datepick"),
|
||
left = src.offsetLeft + src.offsetWidth - nd.offsetWidth,
|
||
top = src.offsetTop - nd.offsetHeight - 1;
|
||
|
||
if (nd.offsetTop === -9999) {
|
||
date.setTime(dt.valueOf());
|
||
set();
|
||
d3.select("#celestial-date").style({"top": px(top), "left": px(left), "opacity": 1});
|
||
d3.select("#datepick").classed("active", true);
|
||
} else {
|
||
vanish();
|
||
}
|
||
};
|
||
|
||
this.isVisible = function () {
|
||
return $("celestial-date").offsetTop !== -9999;
|
||
};
|
||
|
||
this.hide = function () {
|
||
vanish();
|
||
};
|
||
|
||
function vanish() {
|
||
d3.select("#celestial-date").style("opacity", 0);
|
||
d3.select("#error").style( {top:"-9999px", left:"-9999px", opacity:0} );
|
||
d3.select("#datepick").classed("active", false);
|
||
setTimeout(function () { $("celestial-date").style.top = px(-9999); }, 600);
|
||
}
|
||
|
||
function pick() {
|
||
var h = $("hr").value, m = $("min").value,
|
||
s = $("sec").value, tz = $("tz").value;
|
||
|
||
if (this.id && this.id.search(/^\d/) !== -1) {
|
||
date = dateFormat.parse(this.id);
|
||
}
|
||
fillYrSel();
|
||
|
||
date.setHours(h, m, s);
|
||
set();
|
||
|
||
callback(date, tz);
|
||
}
|
||
|
||
};
|
||
// Copyright 2014, Jason Davies, http://www.jasondavies.com
|
||
// See LICENSE.txt for details.
|
||
(function() {
|
||
|
||
var radians = Math.PI / 180,
|
||
degrees = 180 / Math.PI;
|
||
|
||
// TODO make incremental rotate optional
|
||
|
||
d3.geo.zoom = function() {
|
||
var projection,
|
||
duration;
|
||
|
||
var zoomPoint,
|
||
zooming = 0,
|
||
event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"),
|
||
zoom = d3.behavior.zoom()
|
||
.on("zoomstart", function() {
|
||
var mouse0 = d3.mouse(this),
|
||
rotate = quaternionFromEuler(projection.rotate()),
|
||
point = position(projection, mouse0);
|
||
if (point) zoomPoint = point;
|
||
|
||
zoomOn.call(zoom, "zoom", function() {
|
||
projection.scale(view.k = d3.event.scale);
|
||
var mouse1 = d3.mouse(this),
|
||
between = rotateBetween(zoomPoint, position(projection, mouse1));
|
||
projection.rotate(view.r = eulerFromQuaternion(rotate = between
|
||
? multiply(rotate, between)
|
||
: multiply(bank(projection, mouse0, mouse1), rotate)));
|
||
mouse0 = mouse1;
|
||
zoomed(event.of(this, arguments));
|
||
});
|
||
zoomstarted(event.of(this, arguments));
|
||
})
|
||
.on("zoomend", function() {
|
||
zoomOn.call(zoom, "zoom", null);
|
||
zoomended(event.of(this, arguments));
|
||
}),
|
||
zoomOn = zoom.on,
|
||
view = {r: [0, 0, 0], k: 1};
|
||
|
||
zoom.rotateTo = function(location) {
|
||
var between = rotateBetween(cartesian(location), cartesian([-view.r[0], -view.r[1]]));
|
||
return eulerFromQuaternion(multiply(quaternionFromEuler(view.r), between));
|
||
};
|
||
|
||
zoom.projection = function(_) {
|
||
if (!arguments.length) return projection;
|
||
projection = _;
|
||
view = {r: projection.rotate(), k: projection.scale()};
|
||
return zoom.scale(view.k);
|
||
};
|
||
|
||
zoom.duration = function(_) {
|
||
return arguments.length ? (duration = _, zoom) : duration;
|
||
};
|
||
|
||
zoom.event = function(g) {
|
||
g.each(function() {
|
||
var g = d3.select(this),
|
||
dispatch = event.of(this, arguments),
|
||
view1 = view,
|
||
transition = d3.transition(g);
|
||
|
||
if (transition !== g) {
|
||
transition
|
||
.each("start.zoom", function() {
|
||
if (this.__chart__) { // pre-transition state
|
||
view = this.__chart__;
|
||
if (!view.hasOwnProperty("r")) view.r = projection.rotate();
|
||
}
|
||
projection.rotate(view.r).scale(view.k);
|
||
zoomstarted(dispatch);
|
||
})
|
||
.tween("zoom:zoom", function() {
|
||
var width = zoom.size()[0],
|
||
i = interpolateBetween(quaternionFromEuler(view.r), quaternionFromEuler(view1.r)),
|
||
d = d3.geo.distance(view.r, view1.r),
|
||
smooth = d3.interpolateZoom([0, 0, width / view.k], [d, 0, width / view1.k]);
|
||
if (duration) transition.duration(duration(smooth.duration * .001)); // see https://github.com/mbostock/d3/pull/2045
|
||
return function(t) {
|
||
var uw = smooth(t);
|
||
this.__chart__ = view = {r: eulerFromQuaternion(i(uw[0] / d)), k: width / uw[2]};
|
||
projection.rotate(view.r).scale(view.k);
|
||
zoom.scale(view.k);
|
||
zoomed(dispatch);
|
||
};
|
||
})
|
||
.each("end.zoom", function() {
|
||
zoomended(dispatch);
|
||
});
|
||
try { // see https://github.com/mbostock/d3/pull/1983
|
||
transition
|
||
.each("interrupt.zoom", function() {
|
||
zoomended(dispatch);
|
||
});
|
||
} catch (e) { console.log(e); }
|
||
} else {
|
||
this.__chart__ = view;
|
||
zoomstarted(dispatch);
|
||
zoomed(dispatch);
|
||
zoomended(dispatch);
|
||
}
|
||
});
|
||
};
|
||
|
||
function zoomstarted(dispatch) {
|
||
if (!zooming++) dispatch({type: "zoomstart"});
|
||
}
|
||
|
||
function zoomed(dispatch) {
|
||
dispatch({type: "zoom"});
|
||
}
|
||
|
||
function zoomended(dispatch) {
|
||
if (!--zooming) dispatch({type: "zoomend"});
|
||
}
|
||
|
||
return d3.rebind(zoom, event, "on");
|
||
};
|
||
|
||
function bank(projection, p0, p1) {
|
||
var t = projection.translate(),
|
||
angle = Math.atan2(p0[1] - t[1], p0[0] - t[0]) - Math.atan2(p1[1] - t[1], p1[0] - t[0]);
|
||
return [Math.cos(angle / 2), 0, 0, Math.sin(angle / 2)];
|
||
}
|
||
|
||
function position(projection, point) {
|
||
var spherical = projection.invert(point);
|
||
return spherical && isFinite(spherical[0]) && isFinite(spherical[1]) && cartesian(spherical);
|
||
}
|
||
|
||
function quaternionFromEuler(euler) {
|
||
var λ = .5 * euler[0] * radians,
|
||
φ = .5 * euler[1] * radians,
|
||
γ = .5 * euler[2] * radians,
|
||
sinλ = Math.sin(λ), cosλ = Math.cos(λ),
|
||
sinφ = Math.sin(φ), cosφ = Math.cos(φ),
|
||
sinγ = Math.sin(γ), cosγ = Math.cos(γ);
|
||
return [
|
||
cosλ * cosφ * cosγ + sinλ * sinφ * sinγ,
|
||
sinλ * cosφ * cosγ - cosλ * sinφ * sinγ,
|
||
cosλ * sinφ * cosγ + sinλ * cosφ * sinγ,
|
||
cosλ * cosφ * sinγ - sinλ * sinφ * cosγ
|
||
];
|
||
}
|
||
|
||
function multiply(a, b) {
|
||
var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3],
|
||
b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
|
||
return [
|
||
a0 * b0 - a1 * b1 - a2 * b2 - a3 * b3,
|
||
a0 * b1 + a1 * b0 + a2 * b3 - a3 * b2,
|
||
a0 * b2 - a1 * b3 + a2 * b0 + a3 * b1,
|
||
a0 * b3 + a1 * b2 - a2 * b1 + a3 * b0
|
||
];
|
||
}
|
||
|
||
function rotateBetween(a, b) {
|
||
if (!a || !b) return;
|
||
var axis = cross(a, b),
|
||
norm = Math.sqrt(dot(axis, axis)),
|
||
halfγ = .5 * Math.acos(Math.max(-1, Math.min(1, dot(a, b)))),
|
||
k = Math.sin(halfγ) / norm;
|
||
return norm && [Math.cos(halfγ), axis[2] * k, -axis[1] * k, axis[0] * k];
|
||
}
|
||
|
||
// Interpolate between two quaternions (slerp).
|
||
function interpolateBetween(a, b) {
|
||
var d = Math.max(-1, Math.min(1, dot(a, b))),
|
||
s = d < 0 ? -1 : 1,
|
||
θ = Math.acos(s * d),
|
||
sinθ = Math.sin(θ);
|
||
return sinθ ? function(t) {
|
||
var A = s * Math.sin((1 - t) * θ) / sinθ,
|
||
B = Math.sin(t * θ) / sinθ;
|
||
return [
|
||
a[0] * A + b[0] * B,
|
||
a[1] * A + b[1] * B,
|
||
a[2] * A + b[2] * B,
|
||
a[3] * A + b[3] * B
|
||
];
|
||
} : function() { return a; };
|
||
}
|
||
|
||
function eulerFromQuaternion(q) {
|
||
return [
|
||
Math.atan2(2 * (q[0] * q[1] + q[2] * q[3]), 1 - 2 * (q[1] * q[1] + q[2] * q[2])) * degrees,
|
||
Math.asin(Math.max(-1, Math.min(1, 2 * (q[0] * q[2] - q[3] * q[1])))) * degrees,
|
||
Math.atan2(2 * (q[0] * q[3] + q[1] * q[2]), 1 - 2 * (q[2] * q[2] + q[3] * q[3])) * degrees
|
||
];
|
||
}
|
||
|
||
function cartesian(spherical) {
|
||
var λ = spherical[0] * radians,
|
||
φ = spherical[1] * radians,
|
||
cosφ = Math.cos(φ);
|
||
return [
|
||
cosφ * Math.cos(λ),
|
||
cosφ * Math.sin(λ),
|
||
Math.sin(φ)
|
||
];
|
||
}
|
||
|
||
function dot(a, b) {
|
||
for (var i = 0, n = a.length, s = 0; i < n; ++i) s += a[i] * b[i];
|
||
return s;
|
||
}
|
||
|
||
function cross(a, b) {
|
||
return [
|
||
a[1] * b[2] - a[2] * b[1],
|
||
a[2] * b[0] - a[0] * b[2],
|
||
a[0] * b[1] - a[1] * b[0]
|
||
];
|
||
}
|
||
|
||
// Like d3.dispatch, but for custom events abstracting native UI events. These
|
||
// events have a target component (such as a brush), a target element (such as
|
||
// the svg:g element containing the brush) and the standard arguments `d` (the
|
||
// target element's data) and `i` (the selection index of the target element).
|
||
function d3_eventDispatch(target) {
|
||
var i = 0,
|
||
n = arguments.length,
|
||
argumentz = [];
|
||
|
||
while (++i < n) argumentz.push(arguments[i]);
|
||
|
||
var dispatch = d3.dispatch.apply(null, argumentz);
|
||
|
||
// Creates a dispatch context for the specified `thiz` (typically, the target
|
||
// DOM element that received the source event) and `argumentz` (typically, the
|
||
// data `d` and index `i` of the target element). The returned function can be
|
||
// used to dispatch an event to any registered listeners; the function takes a
|
||
// single argument as input, being the event to dispatch. The event must have
|
||
// a "type" attribute which corresponds to a type registered in the
|
||
// constructor. This context will automatically populate the "sourceEvent" and
|
||
// "target" attributes of the event, as well as setting the `d3.event` global
|
||
// for the duration of the notification.
|
||
dispatch.of = function(thiz, argumentz) {
|
||
return function(e1) {
|
||
try {
|
||
var e0 =
|
||
e1.sourceEvent = d3.event;
|
||
e1.target = target;
|
||
d3.event = e1;
|
||
dispatch[e1.type].apply(thiz, argumentz);
|
||
} finally {
|
||
d3.event = e0;
|
||
}
|
||
};
|
||
};
|
||
|
||
return dispatch;
|
||
}
|
||
|
||
})();
|
||
this.Celestial = Celestial;
|
||
})();
|