dashboard
Version:
Create dashboards with gadgets on node.js
500 lines (409 loc) • 15.8 kB
text/xml
xml version="1.0" encoding="UTF-8"
<Module>
<ModulePrefs title="Sky Viewport"
height="400" scrolling="true">
<Require feature="dynamic-height"/>
<Require feature="opensocial-0.8"/>
<Require feature="minimessage"/>
</ModulePrefs>
<Content type="html"><![CDATA[
<script src="http://maps.google.com/jsapi?key=ABQIAAAAKYHb1B3NpT-mpL4s83EIgRSop_PCxtS7_vOEwBFafrYxod0JxBQK6BarsnpmKY7-BFhZ1QAd3zSaLg"
type="text/javascript"></script>
<script src="http://sky.astro.washington.edu/gadgets/js/gDataInterface.js" type="text/javascript"></script>
<script src="http://students.washington.edu/csayres/widgetlib1.js" type="text/javascript"></script>
<script src="http://sky.astro.washington.edu/gadgets/js/astroObjectLib.js" type="text/javascript"></script>
<script type="text/javascript">
// run some triggers just in case it took us a while to start up and there are already some public vars we need to react to
// refreshKMLFromList("VP_Point_List", _KMLPointList);
// Writes to the console if debug (global) is true
function debugLog(msg){
if ((debug === true) && (window.console && console.log)) {
console.log(GADGET_NAME + ": " + msg);
}
return;
}
var vpe = null;
// CONSTANTS
var GADGET_NAME = "ViewportSky" + "_" + Math.floor(Math.random()*100000+1).toString()
debug = true;
function init(){
//debugger;
vpe = new ViewportEarth();
gadgets.window.adjustHeight();
}
function ViewportEarth() {
var my = new Object();
// CONSTANTS
var DEFAULT_NAMESPACE = "NS1",
MM_TIMER_DURATION = 4,
TIMER_VIEWCHANGEEND = 200; // ms to wait to trigger the ge viewchangeend event
// PRIVATE
var _namespaceList = [ DEFAULT_NAMESPACE ]; // list of our current Namespaces. Just init with the default
var di = new gDataInterface(GADGET_NAME, DEFAULT_NAMESPACE); // make dataInterface object, init with default
var miniMsg = new gadgets.MiniMessage();// make MiniMessage object
var msg = miniMsg.createStaticMessage("Attempting to register gadget...");
var triggerArray = [ ["KML_List", showKML],
// ["Dataset_1", updateSelectedPoints],
// ["DatasetList", updateDatasetList] ];
["VP_Point_List", function(){refreshKMLFromList("VP_Point_List", _KMLPointList);} ],
["VP_Speed", setFlyToSpeed],
["VP_Coord", moveMap],
["VP_Coord_Center", moveMapCenter],
["DatasetList", updateDatasetList] ];
di.registerMe( { gadgetName : GADGET_NAME,
namespaceList : _namespaceList,
variableList : [ "VP_Coord_Center", "VP_Coord", "KML_List", "VP_Point_List", "VP_Current_Bounds", "VP_Speed", "DatasetList" ],
triggerList : triggerArray,
successCallback : registrationSuccess,
failureCallback : registrationFailure
} );
// Callback after successful registration
function registrationSuccess(){
debugLog('Registration successful');
if (msg) {
miniMsg.dismissMessage(msg);
// free up msg
msg = null;
miniMsg.createTimerMessage("Registration Succesful.", MM_TIMER_DURATION);
}
}
// Callback after a failure of registration
// displays message showing failure
function registrationFailure(){
debugLog('Registration FAILED!');
miniMsg.dismissMessage(msg);
miniMsg.createDismissibleMessage("Unable to register gadget.\nWe cannot communicate with other gadgets.");
}
// private Point List
// first make the list item constructor
function KMLListItem(){}
KMLListItem.prototype = {
KMLObj : null,
listItemId : -1,
isPlotted : false,
id : null,
};
var _KMLPointList = [];
var tidsViewchangeend = []; // array of timer IDs for ge viewchangeend event
var iconUrlStyles = { grey: { url: "http://sky.astro.washington.edu/gadgets/images/target_grey.png", kmlStyle: null },
red: { url: "http://sky.astro.washington.edu/gadgets/images/target_red.gif", kmlStyle: null },
green: { url: "http://sky.astro.washington.edu/gadgets/images/target_green.gif", kmlStyle: null },
yellow: { url: "http://sky.astro.washington.edu/gadgets/images/target_yellow.gif", kmlStyle: null },
blue: { url: "http://sky.astro.washington.edu/gadgets/images/target_blue.png", kmlStyle: null }
};
// INIT Functions
// init google earth (ge)
var ge = null; // the google earth object is within the viewport scope
google.load("earth", "1");
google.setOnLoadCallback(initGE);
function updateDatasetList(){
var list = di.readVariable(DEFAULT_NAMESPACE, "DatasetList").slice(0); // copy array
// register trigger to this dataset variable so updates happen when it changes
var datasetName = list[0];
di.registerVariable(DEFAULT_NAMESPACE, datasetName, null);
//di.registerTrigger(DEFAULT_NAMESPACE, datasetName, function(){plotAllFromDataset(datasetName);});
di.registerTrigger(DEFAULT_NAMESPACE, datasetName, function(){updatePlacemarkStyles(datasetName);});
// TODO: need to unregister the previous dataset!
}
function initGE() {
google.earth.createInstance('sky_map', initCB, failureCB);
}
// callback on successful creation of ge plugin instance
function initCB(instance) {
ge = instance;
ge.getWindow().setVisibility(true);
ge.getOptions().setMapType(ge.MAP_TYPE_SKY);
ge.getNavigationControl().setVisibility(ge.VISIBILITY_AUTO);
ge.getOptions().setStatusBarVisibility(true);
// Add event listeners
google.earth.addEventListener(ge.getView(), "viewchangeend", function(){
// write the new VP bounds to the public variable
// TODO: This will need a timer, because the viewchangeend event fires so often when dragging slowly.
// We need to make sure that we arent writing the current bounds more often than once every 250ms or so
writeCurrentBounds();
});
// fill the iconUrlStyles variable with actual KML styles
createIconStyles(iconUrlStyles);
// we can implement this stuff later if we want...
//Initialize map to Sombrero Galaxy
// setTimeout(function() {
// var oldFlyToSpeed = ge.getOptions().getFlyToSpeed();
// ge.getOptions().setFlyToSpeed(.2); // Slow down the camera flyTo speed.
// var lookAt = ge.getView().copyAsLookAt(ge.ALTITUDE_RELATIVE_TO_GROUND);
// lookAt.set(-11.623055555555556, 9.997500000000002, 0,ge.ALTITUDE_RELATIVE_TO_GROUND, 262.87, 0, 6039.328125684229);
// ge.getView().setAbstractView(lookAt);
// ge.getOptions().setFlyToSpeed(oldFlyToSpeed);
// }, 1000); // Start the zoom-in after one second.
}
function failureCB(errorCode) {
alert("*** Error initializing google earth plugin ***");
}
// METHODS
// Sets the flytospeed between 0 and 5
function setFlyToSpeed() {
var speed = parseFloat(di.readVariable(DEFAULT_NAMESPACE, "VP_Speed"));
if (!isNaN(speed)) {
if ((speed >= 0) && (speed <= 5)) {
ge.getOptions().setFlyToSpeed(speed); // fastest speed before ge.SPEED_TELEPORT
}
}
}
// Slews map to point (center) at new coordinate
// TODO: with range? or bounding box?
function moveMap (){
var coord = di.readVariable(DEFAULT_NAMESPACE, "VP_Coord");
var lon = coord.getLongitude();
var lat = coord.getLatitude();
//alert("RA: " + ra + "\nDEC: " + dec);
// check that ra and dec are numbers
if (!isNaN(lon) && !isNaN(lat)){
var lookAt = ge.createLookAt('');
lookAt.setLatitude(lat);
lookAt.setLongitude(lon);
lookAt.setAltitude(0);
lookAt.setHeading(0);
lookAt.setRange(7000.0);
ge.getView().setAbstractView(lookAt);
}
}
// Slews map to point (center) at new coordinate
// TODO: with range? or bounding box?
function moveMapCenter (){
var coord = di.readVariable(DEFAULT_NAMESPACE, "VP_Coord_Center");
var lon = coord.getLongitude();
var lat = coord.getLatitude();
//alert("RA: " + ra + "\nDEC: " + dec);
// check that ra and dec are numbers
if (!isNaN(lon) && !isNaN(lat)){
var lookAt = ge.createLookAt('');
lookAt.setLatitude(lat);
lookAt.setLongitude(lon);
lookAt.setAltitude(0);
lookAt.setHeading(0);
lookAt.setRange(ge.getView().copyAsLookAt(ge.ALTITUDE_RELATIVE_TO_GROUND).getRange());
ge.getView().setAbstractView(lookAt);
}
}
writeCurrentBounds = function(){
// we need to wait a few ms so we don't trigger this event too often
if (tidsViewchangeend.length !== 0){
// if the timer was already set, then clear them to reset them.
clearTimerArray(tidsViewchangeend);
}
// now there is no timer set
// so make one
var tid = setTimeout(function(){
boxBounds = ge.getView().getViewportGlobeBounds();
topLeft = new UW.astro.CelestialCoordinate(0,0);
topLeft.setLongitude(boxBounds.getWest());
topLeft.setLatitude(boxBounds.getNorth());
bottomRight = new UW.astro.CelestialCoordinate(0,0);
bottomRight.setLongitude(boxBounds.getEast());
bottomRight.setLatitude(boxBounds.getSouth());
curBounds = [topLeft, bottomRight]; // 2-element array of CelestialCoordinates. [topLeft, bottomRight]
di.writeVariable(DEFAULT_NAMESPACE, "VP_Current_Bounds", curBounds);
}, TIMER_VIEWCHANGEEND);
// add the new timer id to the array
tidsViewchangeend.push(tid);
}
// KML
function clearKML() {
if (ge !== null){
var features = ge.getFeatures();
while (features.getFirstChild()) {
features.removeChild(features.getFirstChild());
}
}
}
function showKML() {
var kmlList = di.readVariable(DEFAULT_NAMESPACE, "KML_List");
if (kmlList !== null) {
if (kmlList.length === 0){
//clearKML();
}
for (var i=0, len=kmlList.length; i<len; i++) {
parsed = ge.parseKml(kmlList[i]);
if (parsed !== null){
// TODO: better test for ge
if (ge !== null){
ge.getFeatures().appendChild(parsed);
}
}
else{
debugLog('Problem parsing element ' + i + ' in KML_List');
}
}
}
}
function updatePlacemarkStyles(dsName) {
var pubDataset = di.readVariable(DEFAULT_NAMESPACE, dsName).toJSON();
placemarks = _KMLPointList;
debugger;
for (var i=0, len=pubDataset.length; i<len; i++) {
var rec = pubDataset[i];
if (rec.hasOwnProperty("db_CelestialCoord") && rec.hasOwnProperty("db_color")) {
var cc = rec["db_CelestialCoord"];
var color = rec["db_color"];
var kmlItem = getKmlItemById(placemarks, cc.getId());
if (kmlItem === null) {
// create the kml and append it
createPlacemarkFromCC(cc);
// TODO: make this cleaner
// sets the cc id as the kmlid
debugger;
cc.setId(parsed.getId());
} else {
// look up the (possibly new) color and set the style based on it
if (iconUrlStyles.hasOwnProperty(color)) {
kmlItem.setStyleSelector(iconUrlStyles[color].style);
} else if (color === "" || color === "default") {
kmlItem.setStyleSelector(iconUrlStyles["grey"].style);
}
}
}
}
}
function createPlacemarkFromCC(cc) {
var geFeatures = ge.getFeatures();
parsed = ge.parseKml(cc.getKML());
geFeatures.appendChild(parsed);
var kmlItem = new KMLListItem();
kmlItem.KMLObj = parsed;
_KMLPointList.push(kmlItem);
}
function getKmlItemById(items, id) {
for (var i=0, len=items.length; i<len; i++) {;
// TODO: check ids for duplicates including "" !
var id = items[i].KMLObj.getId();
if (id !== "" && items[i].KMLObj.getId() === id) {
return items[i].KMLObj;
}
}
return null;
}
function plotAllFromDataset(dsName) {
var pubDataset = di.readVariable(DEFAULT_NAMESPACE, dsName).toJSON();
var toPlotStrings = [];
//clearKML();
for (var i=0, len=pubDataset.length; i<len; i++) {
if (pubDataset[i].hasOwnProperty("db_CelestialCoord")) {
toPlotStrings.push(pubDataset[i].db_CelestialCoord.getKML());
}
}
// PLOT the kml objects in toPlot
if (ge !== null){
var geFeatures = ge.getFeatures(); // speed up the getFeatures methods
for (var i=0,len=toPlotStrings.length; i<len; i++){
parsed = ge.parseKml(toPlotStrings[i]);
geFeatures.appendChild(parsed);
var kmlItem = new KMLListItem();
kmlItem.KMLObj = parsed;
_KMLPointList.push(kmlItem);
// THIS WORKS - It's possible to modify a KML object after it's been appended
//parsed.setDescription("MODIFIED");
}
}
}
function refreshKMLFromDataset(dsName, privList) {
var pubDataset = di.readVariable(DEFAULT_NAMESPACE, dsName);
var toPlot = [];
var toRemove = [];
} // END refreshKMLFromDataset
function refreshKMLFromList(pubListName, privList) {
// Sync the pub and priv lists
var pubList = di.readVariable(DEFAULT_NAMESPACE, pubListName);
var toPlot = []; // array of ids to plot
var toRemove = []; // array of ids to remove
// compare the ids from pubList to the ids in the privList
// first find points to remove
// TODO: Is there a faster way to search these arrays?
// loop thru privList looking for points NOT in pubList
// loop backwards so we can splice array in loop
for (var i=privList.length-1; i>=0; i--) {
if ((pubList.getItemById(privList[i].listItemId) === null) || (pubList.isIdModified(privList[i].listItemId))) {
// has been removed from the pubList, so add it to the toRemove list
// and remove it from the privList
toRemove.push(privList[i].KMLobj); // add the KMLobj to the remove list
privList.splice(i,1); // remove it from privList
}
}
// TODO: check isPlotted!
// next find the new points to plot
// loop thru pub list looking for points NOT in priv list (they are new points)
for (var i=0, len=pubList.getLength(); i<len; i++){
var pubItemId = pubList.getIdByIndex(i);
var found = false;
// check for this id in the priv list
for (var i2=0, len2=privList.length; i2<len2; i2++){
if (privList[i2].listItemId === pubItemId) {
found = true;
//break;
// break actually breaks out of both for's, so lets just reset i=len
// TODO: CHANGE THIS BAD FORM
i2 = len;
}
}
if (!found){
// we found a NEW point in the pubList (not in privList)
// add it to the toPlot array
toPlot.push(pubItemId)
}
}
// Find indexes of modified points
var modifiedIdsList = pubList.getModifiedIds();
for (var i=0, len=modifiedIdsList.length; i<len; i++){
// toPlot.push(modifiedIdsList[i]);
pubList.setModifiedById(modifiedIdsList[i]);
}
// alert('Modified IDs: ' + modifiedIdsList);
// sync the GE to the priv list
// TODO: better test for GE
if (ge !== null){
var geFeatures = ge.getFeatures(); // speed up the getFeatures methods
// REMOVE the kmlobjects in toRemove
for (var i=0,len=toRemove.length; i<len; i++){
geFeatures.removeChild(toRemove[i]);
}
// PLOT the kml objects in toPlot
for (var i=0,len=toPlot.length; i<len; i++){
// first we plot it
var kmlText = pubList.getItemByIndex(toPlot[i]).getKML();
parsed = ge.parseKml(kmlText);
geFeatures.appendChild(parsed);
// then add it to the privList
var privItem = new KMLListItem();
privItem.KMLobj = parsed;
privItem.listItemId = toPlot[i];
privItem.isPlotted = true;
privList.push(privItem);
}
}
}
// HELPER
function clearTimerArray(timers){
if (timers instanceof Array){
for (var i=0, len=timers.length; i<len; i++){
clearTimeout(timers.splice(i,1)); // splice returns the removed element
}
}
}
function createIconStyles(styles) {
for (var color in styles) {
if (styles.hasOwnProperty(color)) {
var icon = ge.createIcon('');
icon.setHref(styles[color].url);
var style = ge.createStyle('');
style.getIconStyle().setIcon(icon);
styles[color].kmlStyle = style;
}
}
}
return my;
}
gadgets.util.registerOnLoadHandler(init);
</script>
<div id="sky_map" style="height:500px; width:100%;">
<!-- <input type="button" value="clear KML" onclick="ks.clearKML()"> -->
</div>
]]></Content>
</Module>