JavaScript DHTML/Page Components/Rotating globe
Rotating globe in JavaScript
<source lang="html4strict">
<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>rotating globe</title>
<script language="JavaScript" > /*------------------------------------------------------------------------------
Copyright (c) 2005 Tyrell Corporation. This file is part of the Locative Blog project. http://locblog.sourceforge.net/ Author : $Author: darkeye $ Version : $Revision: 1.1.1.1 $ Location : $Source: /cvsroot/locblog/rotatingGlobe/var/public_html/misc.js,v $ Abstract : Utility functions. Copyright notice: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/**
* Constructor to reate an object, based on a named element from the document, * browser-independently. * The created object will have two properties: * obj - the named object itself * style - the stylesheet object for the named object. * * based on http://www.quirksmode.org/js/dhtmloptions.html * * @param name the name of the element to create this object upon. */
function getObj(name) {
if (document.getElementById) { this.obj = document.getElementById(name); this.style = document.getElementById(name).style; } else if (document.all) { this.obj = document.all[name]; this.style = document.all[name].style; } else if (document.layers) { this.obj = document.layers[name]; this.style = document.layers[name]; }
} /**
* Find the X coordinate of an element in the document. * based on http://www.quirksmode.org/js/findpos.html * * @param obj the element in the document. * @return the x coordinate for this element. */
function findPosX(obj) {
var curleft = 0; if (obj.offsetParent) { while (obj.offsetParent) { curleft += obj.offsetLeft obj = obj.offsetParent; } } else if (obj.x) { curleft += obj.x; } return curleft;
} /**
* Find the Y coordinate of an element in the document. * based on http://www.quirksmode.org/js/findpos.html * * @param obj the element in the document. * @return the x coordinate for this element. */
function findPosY(obj) {
var curtop = 0; if (obj.offsetParent) { while (obj.offsetParent) { curtop += obj.offsetTop obj = obj.offsetParent; } } else if (obj.y) { curtop += obj.y; } return curtop;
} /**
* Replace the contenst of a DOM element. * based on http://www.quirksmode.org/js/layerwrite.html * * @param id the id of the element. * @param text the new text of the element. */
function replaceContent(id, text) {
if (document.getElementById) { x = document.getElementById(id); x.innerHTML = ""; x.innerHTML = text; } else if (document.all) { x = document.all[id]; x.innerHTML = text; } else if (document.layers) { x = document.layers[id];text2 = "
" + text + "
";x.document.open(); x.document.write(text2); x.document.close(); }
}
/**
* Function to calculate the X coordinate of an orthographic projection. * based on http://mathworld.wolfram.ru/OrthographicProjection.html * * @param phi the lattitude of the point on the sphere * @param lambda the longitude of the point on the sphere * @param phi1 the lattitude of the viewpoint * @param lambda1 the longitued of the viewpoint * @return the x coordinate of the specified point. */
function orthographicX(phi, lambda, phi1, lambda1) {
var x = Math.cos(phi) * Math.sin(lambda - lambda1); return x;
} /**
* Function to calculate the Y coordinate of an orthographic projection * based on http://mathworld.wolfram.ru/OrthographicProjection.html * * @param phi the lattitude of the point on the sphere * @param lambda the longitude of the point on the sphere * @param phi1 the lattitude of the viewpoint * @param lambda1 the longitued of the viewpoint * @return the y coordinate of the specified point. */
function orthographicY(phi, lambda, phi1, lambda1) {
var y = Math.cos(phi1) * Math.sin(phi) - Math.sin(phi1) * Math.cos(phi) * Math.cos(lambda - lambda1); return y;
} /**
* Helper function to convert a latitude or longitued into radii. * * @param degrees the latitude or longitude * @return the same value, in radii */
function toRadii(degrees) {
return (degrees / 180.0) * Math.PI;
}
</script>
<script language="JavaScript"> /*------------------------------------------------------------------------------
Copyright (c) 2005 Tyrell Corporation. This file is part of the Locative Blog project. http://locblog.sourceforge.net/ Author : $Author: darkeye $ Version : $Revision: 1.1.1.1 $ Location : $Source: /cvsroot/locblog/rotatingGlobe/var/public_html/GeoLocation.js,v $ Abstract : A class representing a geogpraphical location. Copyright notice: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/**
* Constructor for a geographic location, with a link. * * @param id the id of the location (no spaces, etc.) * @param name the name of the location * @param longitude the longitude coordinate of the location * @param latitude the latitude coordinate of the location * @param url the url for more info on the location */
function GeoLocation(id,
name, longitude, latitude, url)
{
// the id of the location this.id = id; // the name of the location this.name = name; // the longitude coordinate of the location this.longitude = longitude < 0 ? 360 + longitude : longitude; // the latitude coordinate of the location this.latitude = latitude; // the url for more info on the location this.url = url; // the image used to mark the location visually this.markerImage = "globeImages/5x5.png"; // the width of the marker image this.markerWidth = 5; // the height of the marker image this.markerHeight = 5; // the x coordinate of this location, relative to the page window this.x = 0; // the y coordinate of this location, relative to the page window this.y = 0; // create the CSS definition for this location this.styleDefinition = geoLocationStyleDefinition; // create the HTML element source for this location this.htmlDefinition = geoLocationHtmlDefinition; // display the location object on a RotatingGlobe object this.display = geoLocationDisplay; // move the marker depiciting the location to the specified position this.moveMarker = geoLocationMoveMarker; // hide the marker depiciting the location this.hideMarker = geoLocationHideMarker; // show the location popup near the geo location this.showPopup = geoLocationShowPopup; // hide the location popup near the geo location this.hidePopup = geoLocationHidePopup; // Tell if the longitude coordinate of the GeoLocation object is visible // from a reference viewpoint. this.longitudeVisible = geoLocationLongitudeVisible;
} /**
* Create the CSS definition for this location * * @return the CSS definition for this location, as a string */
function geoLocationStyleDefinition() {
return "#" + this.id + "{" + " position: absolute; " + " top: 0px; " + " left: 0px; " + "}\n";
} /**
* Create the HTML element source for this location * * @return the HTML element source for this location, as a string. */
function geoLocationHtmlDefinition() {
return "<a id="" + this.id + "" href="" + this.url + "">" + "<img name="" + this.id + "" alt="" + this.name + "" " + "src="" + this.markerImage + "" " + "width="" + this.markerWidth + "" " + "height="" + this.markerHeight + "" border="0"/>" + "</a>\n";
} /**
* Move the marker depicting the location to the specified coordinates. */
function geoLocationMoveMarker() {
var locationObject = new getObj(this.id); locationObject.style.top = this.y + "px"; locationObject.style.left = this.x + "px"; locationObject.style.visibility = "visible";
} /**
* Hide the Marker depicting the location. */
function geoLocationHideMarker() {
var locationObject = new getObj(this.id); locationObject.style.visibility = "hidden";
} /**
* Show the popup text near the location. */
function geoLocationShowPopup() {
replaceContent("locationPopup", this.name); var popup = new getObj("locationPopup"); // display a bit to the right and above popup.style.left = (this.x + 10) + "px"; popup.style.top = (this.y - 15) + "px"; popup.style.visibility = "visible";
} /**
* Hide the popup near the location. */
function geoLocationHidePopup() {
var popup = new getObj("locationPopup"); popup.style.visibility = "hidden";
}
/**
* Display the location on the map. * * @param globe a RotatingGlobe object, on which to display the location */
function geoLocationDisplay(globe) {
longStepSize = 360.0 / globe.maxPosition; viewLongitude = globe.position * longStepSize; if (!this.longitudeVisible(viewLongitude)) { this.hideMarker(); return; } lambda = toRadii(this.longitude); phi = toRadii(this.latitude); lambda1 = toRadii(viewLongitude); phi1 = toRadii(0); x = (orthographicX(phi, lambda, phi1, lambda1) * (globe.realGlobeWidth/2)) + (globe.globeImage.width / 2); y = globe.globeImage.height - ((orthographicY(phi, lambda, phi1, lambda1) * (globe.realGlobeHeight/2)) + (globe.globeImage.height / 2) ); this.x = globe.globeImageX + x; this.y = globe.globeImageY + y; this.moveMarker();
} /**
* Tell if the longitude coordinate of the GeoLocation object is visible * from a reference viewpoint. * It"s not visible if it would be "on the other side of the planet" * * @param refLongitude the reference view point, to check from. * @return true if longitude is visible, false otherwise */
function geoLocationLongitudeVisible(refLongitude) {
var westEdge = refLongitude - 90; var eastEdge = refLongitude + 90; if (westEdge >= 0 && eastEdge < 360) { return westEdge <= this.longitude && this.longitude <= eastEdge; } else if (eastEdge >= 360) { eastEdge -= 360; return westEdge <= this.longitude || this.longitude <= eastEdge; } else { westEdge += 360; return westEdge <= this.longitude || this.longitude <= eastEdge; }
}
</script>
<script language="JavaScript"> /*------------------------------------------------------------------------------
Copyright (c) 2005 Tyrell Corporation. This file is part of the Locative Blog project. http://locblog.sourceforge.net/ Author : $Author: darkeye $ Version : $Revision: 1.1.1.1 $ Location : $Source: /cvsroot/locblog/rotatingGlobe/var/public_html/RotatingGlobe.js,v $ Abstract : A rotating globe JavaScript object. Copyright notice: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/**
* Event handler for a preloaded image finishing loading. * This function expects the rotating globe, for which preloaded images * are checked, being available at the global variable named "globe". * * @return true if all images are loaded, false otherwise. */
function globeImageLoaded() {
if (globe.imagesLoaded()) { globe.init(); return true; } return false;
} /**
* Start dragging the image by the mouse move. * This function expects the rotating globe at a global variable named "globe" */
function globeDragImage() {
globe.startDragImage(); document.globe.onmousemove = globeTrackMouseMove;
} /**
* Stop dragging the image by the mouse move. * This function expects the rotating globe at a global variable named "globe" */
function globeUndragImage() {
document.globe.onmousemove = null; globe.stopDragImage();
} /**
* Track the mouse movement, and change the globe view if necessary. * This function expects the rotating globe at a global variable named "globe" * * @param event the mouse movement event. */
function globeTrackMouseMove(event) {
// first tell the current mouse position, possibly in all browsers // thanks to http://www.quirksmode.org/js/events_properties.html var posx = 0; var posy = 0; if (!event) { var event = window.event; } if (event.pageX || event.pageY) { posx = event.pageX; posy = event.pageY; } else if (event.clientX || event.clientY) { posx = event.clientX + document.body.scrollLeft; posy = event.clientY + document.body.scrollTop; } // display the detected coordinates, for debug reasons document.test.x.value = posx; document.test.y.value = posy; globe.updateView(posx, posy);
}
/**
* The contructor class depicting the rotating globe. * * @param globeImage the globe <img/> tag in the page, * that holds the globe images. * @param globeBorder the number of pixels the image has a border around * the enclosed globe. it is expected that the image has a globe at * its center, which is surrounded by a border on each side with * this many pixels. thus the actual globe is * globeImage.width - (2 * globeBorder) wide, and * globeImage.height - (2 * globeBorder) in height. * @param locations an array holding GeoLocation object, the list of * interesting locations. */
function RotatingGlobe(globeImage, globeBorder, locations) {
// the <img/> element in the page, holding the globe images this.globeImage = globeImage; // the size of the border around the globe in the image this.globeBorder = globeBorder; // the interesting locations on the globe this.locations = locations; // the X coordinate of the upper-left corner of the globe image // in the browser window this.globeImageX = findPosX(document.globe); // the Y coordinate of the upper-left corner of the globe image // in the browser window this.globeImageY = findPosY(document.globe); // the real height of the globe itself, inside the image this.realGlobeHeight = this.globeImage.width - (2 * this.globeBorder); // the real width of the globe itself, inside the image this.realGlobeWidth = this.globeImage.height - (2 * this.globeBorder); // the number of view positions available this.maxPosition = 36; // the current view position this.position = 0; // the prefix for each globe image // the image names are: imagePrefix + position + imagePostfix this.imagePrefix = "globeImages/globe"; // the postfix for each globe image // the image names are: imagePrefix + position + imagePostfix this.imagePostfix = ".jpg"; // the globe images, as an array this.images = Array(this.maxPosition); // the x coordinate, which if passed by the mouse, triggers a move to east this.eastThreshold = 0; // the x coordinate, which if passed by the mouse, triggers a move to west this.westThreshold = 0; // the size of the x coordinate window to trigger a move to east or west // by the mouse movement this.thresholdStep = 20; // a flag indicating if this object has already been initialized this.initialized = false; // preload all the images for the globe this.preloadImages = rotatingGlobePreloadImages; // tell if all images that are to be preloaded have completed loading. this.imagesLoaded = rotatingGlobeImagesLoaded; // initialize the globe, after all the images have been loaded this.init = rotatingGlobeInit; // start mouse dragging this.startDragImage = rotatingGlobeStartDragImage; // stop mouse dragging this.stopDragImage = rotatingGlobeStopDragImage; // update the view point according to the new mouse coordinates, if needed this.updateView = rotatingGlobeUpdateView; // function to return the name of an image for a specified position this.imageName = rotatingGlobeImageName; // rotate the viewpoint one step to the east this.rotateEast = rotatingGlobeRotateEast; // rotate the viewpoint one step to the west this.rotateWest = rotatingGlobeRotateWest; // reset the mouse movement thresholds triggering view point change this.resetThreshold = rotatingGlobeResetThreshold; // display the locations this.displayLocations = rotatingGlobeDisplayLocations;
} /**
* Function returning the name of an image for a specified position * * @param position the view position to get the image for, must be * in the interval [0:maxPosition[ * @return the name of the appropariate globe image, as a string */
function rotatingGlobeImageName(position) {
return this.imagePrefix + position + this.imagePostfix;
} /**
* Rotate the current viewpoint one step to the east. */
function rotatingGlobeRotateEast() {
this.position = (++this.position) % this.maxPosition; this.globeImage.src = this.images[this.position].src; this.displayLocations();
} /**
* Rotate the current viewpoint one step to the east. */
function rotatingGlobeRotateWest() {
this.position = (--this.position) % this.maxPosition; if (this.position < 0) { this.position += this.maxPosition; } this.globeImage.src = this.images[this.position].src; this.displayLocations();
} /**
* Preload all the images, so that they are in the browsers cache. */
function rotatingGlobePreloadImages() {
this.initialized = false; for (i = 0; i < this.maxPosition; ++i) { this.images[i] = new Image(this.globeImage.width, this.globeImage.height); this.images[i].onload = globeImageLoaded; this.images[i].src = this.imageName(i); }
} /**
* Tell if all images that are to be preloaded have completed loading. * * @return true if all images have been loaded, false otherwise */
function rotatingGlobeImagesLoaded() {
var allCompleted = true; for (i = 0; i < this.maxPosition; ++i) { if (!this.images[i].ruplete) { allCompleted = false; break; } } return allCompleted;
} /**
* Initialize the globe, after all the images have been loaded. * This means grabbing event handlers, etc. */
function rotatingGlobeInit() {
if (this.initialized) { return; } this.thresholdStep = this.globeImage.width / (this.maxPosition + 1); this.globeImage.onmousedown = globeDragImage; this.globeImage.onmouseup = globeUndragImage; this.globeImage.src = this.images[0].src; this.initialized = true; for (i = 0; i < locations.length; ++i) { var obj = new getObj(locations[i].id); obj.obj.onmouseover = locationShowPopup; obj.obj.onmouseout = locationHidePopup; } this.displayLocations();
} /**
* Find a location in an array of locations, by its id. * * @param locations an array of GeoLocation objects. * @param id the id of the location to look for * @return the requested GeoLocation object, or null if not found. */
function getLocationById(locations, id) {
for (i = 0; i < locations.length; ++i) { if (locations[i].id == id) { return locations[i]; } } return null;
} /**
* Event hanlder for showing the popup near a location. * * @param event the event triggering this call. it is expected that the event * target name is a valid GeoLocation id in the locations array. */
function locationShowPopup(event) {
geoLocation = getLocationById(locations, event.target.name); if (geoLocation != null ) { geoLocation.showPopup(); }
} /**
* Event hanlder for hiding the popup near a location. * * @param event the event triggering this call. it is expected that the event * target name is a valid GeoLocation id in the locations array. */
function locationHidePopup(event) {
geoLocation = getLocationById(locations, event.target.name); if (geoLocation != null ) { geoLocation.hidePopup(); }
} /**
* Reset the mouse movement thresholds. Set the new limits for when * to change the view point, if the mouse move beyonds these limints. * * @param posx the current position, from where the relative thresholds * have to be set up. */
function rotatingGlobeResetThreshold(posx) {
this.eastThreshold = posx - this.thresholdStep; this.westThreshold = posx + this.thresholdStep;
} /**
* Start mouse dragging */
function rotatingGlobeStartDragImage() {
this.eastThreshold = 0; this.westThreshold = 0;
} /**
* Stop mouse dragging. */
function rotatingGlobeStopDragImage() {
this.eastThreshold = 0; this.westThreshold = 0;
} /**
* Update the view point according to the new mouse coordinates, if needed. * * @param x the new x coordinate for the mouse * @param y the new y coordinate for the mouse */
function rotatingGlobeUpdateView(x, y) {
// if this is the first event after started dragging, initialize // the appropriate thresholds if (this.eastThreshold == 0 && this.westThreshold == 0) { this.resetThreshold(x); } // change the view point, if needed if (x > this.westThreshold) { this.rotateWest(); this.resetThreshold(x); } else if (x < this.eastThreshold) { this.rotateEast(); this.resetThreshold(x); }
} /**
* Display all interesting locations on the map. */
function rotatingGlobeDisplayLocations() {
for (i = 0; i < locations.length; ++i) { this.locations[i].display(this); }
}
</script>
<script language="JavaScript"> /**
* The global rotating globe object, the event handlers depend on this. */
var globe; /**
* The locations. */
var locations = Array(); </script>
<script language="JavaScript"> /*------------------------------------------------------------------------------
Copyright (c) 2005 Tyrell Corporation. This file is part of the Locative Blog project. http://locblog.sourceforge.net/ Author : $Author: darkeye $ Version : $Revision: 1.1.1.1 $ Location : $Source: /cvsroot/locblog/rotatingGlobe/var/public_html/locations.js,v $ Abstract : A sample location data definition file. Copyright notice: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
locations.push(new GeoLocation(
"amsterdam", "Amsterdam", 4.54, 52.23, "/amsterdam"));
locations.push(new GeoLocation(
"bangkok", "Bangkok", 100.50, 13.73, "/bangkok"));
locations.push(new GeoLocation(
"budapest", "Budapest", 19.05, 47.50, "/budapest"));
locations.push(new GeoLocation(
"chiang_mai", "Chiang Mai", 99.03, 18.82, "/chiang_mai"));
locations.push(new GeoLocation(
"linz", "Linz", 14.18, 48.19, "/linz"));
locations.push(new GeoLocation(
"london", "London", -0.16, 51.50, "/london"));
locations.push(new GeoLocation(
"luang_prabang", "Luang Prabang", 102.14, 19.88, "/luang_prabang"));
locations.push(new GeoLocation(
"new_york", "New York", -74.00, 40.70, "/new_york"));
locations.push(new GeoLocation(
"Paris", "Paris", 2.33, 48.87, "/paris"));
locations.push( new GeoLocation(
"philadelphia", "Philadelphia", -75.12, 39.96, "/philadelphia"));
locations.push( new GeoLocation(
"prague", "Prague", 14.43, 50.10, "/prague"));
locations.push( new GeoLocation(
"rotterdam", "Rotterdam", 4.29, 51.55, "/rotterdam"));
locations.push( new GeoLocation(
"taipei", "Taipei", 121.36, 25.01, "/taipei"));
locations.push( new GeoLocation(
"tokyo", "Tokyo", 139.50, 35.75, "/tokyo"));
locations.push( new GeoLocation(
"vienna", "Vienna", 16.36, 48.20, "/vienna"));
locations.push( new GeoLocation(
"washington", "Washington DC", -77.03, 38.88, "/washington"));
</script> <script language="JavaScript"> /**
* Initialize the page, after it has loaded. */
function onPageLoad() {
globe = new RotatingGlobe(document.globe, 30, locations); // this sets up event handlers for the preloaded images globe.preloadImages(); // as not all event handlers are called (damn these lame browswers) // set a timout value and call the load image hanlder ourselves makeSureImagesAreLoaded()
} /**
* Make sure all images are loaded, by checking on this regularly. */
function makeSureImagesAreLoaded() {
if (!globeImageLoaded()) { // if not ready, check a second later as well setTimeout("makeSureImagesAreLoaded()", 1000); }
}
/**
* Rotate the globe one step to the east. */
function rotateEast() {
globe.rotateEast();
} /**
* Rotate the globe one step to the west. */
function rotateWest() {
globe.rotateWest();
} </script> <script language="JavaScript"> // create the style sheet definition for each geo location document.write("<style type="text/css">"); for (i = 0; i < locations.length; ++i) {
document.write(locations[i].styleDefinition());
} document.write("</style>"); </script> <style type="text/css">
- locationPopup {
position: absolute; top: 0px; left: 0px; visibility: hidden; font: 14px arial; font-weight: bold; color: yellow;
}
</style>
</head>
<body onLoad="onPageLoad();">
<a href="#" onClick="javascript:rotateWest(); return false;">west</a>
|
<a href="#" onClick="javascript:rotateEast(); return false;">east</a>
<img name="globe" src="images/loading.png" width="650" height="650" alt="globe"/>
<form name="test">
X: <input type="text" name="x" size="4"> Y: <input type="text" name="y" size="4">
</form> <script language="JavaScript"> // create the HTML element for each geo location for (i = 0; i < locations.length; ++i) {
document.write(locations[i].htmlDefinition());
} </script>
</body>
</source>
<A href="http://www.wbex.ru/Code/JavaScriptDownload/rotatingGlobe-0.1.zip">rotatingGlobe-0.1.zip( 4,605 k)</a>